name: fa-theme-customizer description: 为 Fantastic-admin 框架创建和定制主题配色方案,始终同时生成明色(light)和暗色(dark)两套主题。当用户提到以下任何需求时必须使用此技能:换主题颜色、换配色、做一个 XX 风格的主题、主题太单调了、品牌色是 XXX 帮我生成主题、把 tweakcn 配色转成框架主题、想要暗色/冷色/暖色调、根据设计稿颜色生成主题。即使用户只是描述一种感觉("清新"、"沉稳"、"科技感")或提供一个颜色值("#2563EB"),也应触发此技能。支持"吉卜力"、"赛博朋克"、"莫兰迪"、"北欧极简"等专业设计风格关键词。
主题定制器
为 Fantastic-admin 框架生成符合规范的主题配色。框架的 theme.sync 默认为 true,切换颜色方案时明暗共用同一套主题名称,因此始终同时输出明色和暗色两套,缺少任何一套都会导致对应模式下显示异常。
主题文件位置
- 主题定义:
packages/themes/index.ts(在此添加新主题,跨所有应用共享) - 类型推断:自动从
packages/themes/index.ts的 key 推断,无需手动更新类型
主题文件结构
packages/themes/index.ts 包含三个导出,分别控制不同层级的颜色:
| 导出 | 作用 | 格式 |
|---|---|---|
BASE_COLORS |
基础色:背景、文字、卡片、边框、输入框、焦点环等整套基础 token | 'L C H'(纯数值) |
THEMES |
主题色:仅覆盖 --primary / --secondary 品牌色 |
'L C H'(纯数值) |
FRAMEWORK_COLORS |
框架区域色:顶栏、侧边栏、标签栏、工具栏等布局颜色 | 'oklch(...)'(含包裹) |
工作流程
第一步:确认设计风格
如果用户没有明确指定颜色,先提供风格参考。读取 references/design-styles.md 获取完整的风格目录和配色建议。
第二步:生成 OKLCH 色值
框架使用 OKLCH 色彩空间,格式为 L C H(不含 oklch() 包裹,直接写三个数值)。
转换规则:
- 明色背景:
1 0 0(纯白)或接近白色的暖/冷色调 - 暗色背景:
0.141 0.005 285.823(默认深灰)或更深的色调 - 主色(primary):明色通常比暗色亮度(L值)高 0.05~0.1
读取 references/theme-structure.md 了解完整的 CSS 变量说明和取值规范。
第三步:输出主题代码
在 packages/themes/index.ts 中添加新主题,格式严格遵循现有结构。
3a. 添加基础色(添加到 BASE_COLORS)
基础色定义完整的 18 个 shadcn token,明暗各一套,值格式为 'L C H'(纯数值,不含 oklch() 包裹)。
// 添加到 BASE_COLORS 对象中
baseName: {
light: {
'--background': '1 0 0', // oklch(1 0 0)
'--foreground': 'L C H', // oklch(L C H)
'--card': '1 0 0', // oklch(1 0 0)
'--card-foreground': 'L C H', // oklch(L C H)
'--popover': '1 0 0', // oklch(1 0 0)
'--popover-foreground': 'L C H', // oklch(L C H)
'--primary': 'L C H', // oklch(L C H)
'--primary-foreground': 'L C H', // oklch(L C H)
'--secondary': 'L C H', // oklch(L C H)
'--secondary-foreground': 'L C H', // oklch(L C H)
'--muted': 'L C H', // oklch(L C H)
'--muted-foreground': 'L C H', // oklch(L C H)
'--accent': 'L C H', // oklch(L C H)
'--accent-foreground': 'L C H', // oklch(L C H)
'--destructive': '0.577 0.245 27.325', // oklch(0.577 0.245 27.325)
'--border': 'L C H', // oklch(L C H)
'--input': 'L C H', // oklch(L C H)
'--ring': 'L C H', // oklch(L C H)
},
dark: {
'--background': 'L C H', // oklch(L C H)
'--foreground': 'L C H', // oklch(L C H)
'--card': 'L C H', // oklch(L C H)
'--card-foreground': 'L C H', // oklch(L C H)
'--popover': 'L C H', // oklch(L C H)
'--popover-foreground': 'L C H', // oklch(L C H)
'--primary': 'L C H', // oklch(L C H)
'--primary-foreground': 'L C H', // oklch(L C H)
'--secondary': 'L C H', // oklch(L C H)
'--secondary-foreground': 'L C H', // oklch(L C H)
'--muted': 'L C H', // oklch(L C H)
'--muted-foreground': 'L C H', // oklch(L C H)
'--accent': 'L C H', // oklch(L C H)
'--accent-foreground': 'L C H', // oklch(L C H)
'--destructive': '0.704 0.191 22.216', // oklch(0.704 0.191 22.216)
'--border': 'L C H', // oklch(L C H)
'--input': 'L C H', // oklch(L C H)
'--ring': 'L C H', // oklch(L C H)
},
},
3b. 添加主题色(添加到 THEMES)
主题色仅覆盖 --primary / --secondary 及其前景,共 4 个 token。其他 shadcn token 由基础色提供,无需重复。值格式同样为 'L C H'(纯数值)。
// 添加到 THEMES 对象中
themeName: {
light: {
'--primary': 'L C H', // oklch(L C H)
'--primary-foreground': 'L C H', // oklch(L C H)
'--secondary': 'L C H', // oklch(L C H)
'--secondary-foreground': 'L C H', // oklch(L C H)
},
dark: {
'--primary': 'L C H', // oklch(L C H)
'--primary-foreground': 'L C H', // oklch(L C H)
'--secondary': 'L C H', // oklch(L C H)
'--secondary-foreground': 'L C H', // oklch(L C H)
},
},
3c. 框架区域色(FRAMEWORK_COLORS,通常无需修改)
FRAMEWORK_COLORS 是全局共享的,明暗各一套。通常创建新基础色或主题色时不需要修改它。仅当需要调整顶栏/侧边栏/标签栏/工具栏等布局区域的颜色映射关系时才需要修改。
值格式为 'oklch(...)'(含 oklch() 包裹),通过引用 shadcn token 变量实现联动。
明色与暗色的关键差异:
| 区域 | 明色引用 | 暗色引用 | 原因 |
|---|---|---|---|
| 菜单文字色 | accent-foreground |
muted-foreground |
暗色需要更柔和的文字对比 |
| 菜单 hover 背景 | accent |
muted |
暗色使用更低对比度的 hover |
| 菜单激活背景 | primary |
accent |
暗色避免高饱和 primary 刺眼 |
| 主内容区背景 | oklch(0.9612 0 0)(固定值) |
var(--background) |
明色需要层次感,暗色不需要 |
| 标签栏 hover 背景 | var(--border) |
var(--accent) / 50% |
明色用边框色,暗色用半透明强调色 |
| 标签栏激活背景 | var(--background) |
var(--accent) |
明色融入背景,暗色突出显示 |
完整明色模板:
light: {
// 主要区域
'--g-main-area-bg': 'oklch(0.9612 0 0)',
// 头部
'--g-header-bg': 'oklch(var(--background))',
'--g-header-color': 'oklch(var(--foreground))',
'--g-header-menu-color': 'oklch(var(--accent-foreground))',
'--g-header-menu-hover-bg': 'oklch(var(--accent))',
'--g-header-menu-hover-color': 'oklch(var(--accent-foreground))',
'--g-header-menu-active-bg': 'oklch(var(--primary))',
'--g-header-menu-active-color': 'oklch(var(--primary-foreground))',
// 主导航
'--g-main-sidebar-bg': 'oklch(var(--background))',
'--g-main-sidebar-menu-color': 'oklch(var(--accent-foreground))',
'--g-main-sidebar-menu-hover-bg': 'oklch(var(--accent))',
'--g-main-sidebar-menu-hover-color': 'oklch(var(--accent-foreground))',
'--g-main-sidebar-menu-active-bg': 'oklch(var(--primary))',
'--g-main-sidebar-menu-active-color': 'oklch(var(--primary-foreground))',
// 次导航
'--g-sub-sidebar-bg': 'oklch(var(--background))',
'--g-sub-sidebar-menu-color': 'oklch(var(--accent-foreground))',
'--g-sub-sidebar-menu-hover-bg': 'oklch(var(--accent))',
'--g-sub-sidebar-menu-hover-color': 'oklch(var(--accent-foreground))',
'--g-sub-sidebar-menu-active-bg': 'oklch(var(--primary))',
'--g-sub-sidebar-menu-active-color': 'oklch(var(--primary-foreground))',
// 标签栏
'--g-tabbar-bg': 'oklch(var(--background))',
'--g-tabbar-tab-color': 'oklch(var(--accent-foreground) / 50%)',
'--g-tabbar-tab-hover-bg': 'oklch(var(--border))',
'--g-tabbar-tab-hover-color': 'oklch(var(--accent-foreground) / 50%)',
'--g-tabbar-tab-active-bg': 'oklch(var(--background))',
'--g-tabbar-tab-active-color': 'oklch(var(--foreground))',
// 工具栏
'--g-toolbar-bg': 'oklch(var(--background))',
},
完整暗色模板:
dark: {
// 主要区域
'--g-main-area-bg': 'oklch(var(--background))',
// 头部
'--g-header-bg': 'oklch(var(--background))',
'--g-header-color': 'oklch(var(--foreground))',
'--g-header-menu-color': 'oklch(var(--muted-foreground))',
'--g-header-menu-hover-bg': 'oklch(var(--muted))',
'--g-header-menu-hover-color': 'oklch(var(--muted-foreground))',
'--g-header-menu-active-bg': 'oklch(var(--accent))',
'--g-header-menu-active-color': 'oklch(var(--accent-foreground))',
// 主导航
'--g-main-sidebar-bg': 'oklch(var(--background))',
'--g-main-sidebar-menu-color': 'oklch(var(--muted-foreground))',
'--g-main-sidebar-menu-hover-bg': 'oklch(var(--muted))',
'--g-main-sidebar-menu-hover-color': 'oklch(var(--muted-foreground))',
'--g-main-sidebar-menu-active-bg': 'oklch(var(--accent))',
'--g-main-sidebar-menu-active-color': 'oklch(var(--accent-foreground))',
// 次导航
'--g-sub-sidebar-bg': 'oklch(var(--background))',
'--g-sub-sidebar-menu-color': 'oklch(var(--muted-foreground))',
'--g-sub-sidebar-menu-hover-bg': 'oklch(var(--muted))',
'--g-sub-sidebar-menu-hover-color': 'oklch(var(--muted-foreground))',
'--g-sub-sidebar-menu-active-bg': 'oklch(var(--accent))',
'--g-sub-sidebar-menu-active-color': 'oklch(var(--accent-foreground))',
// 标签栏
'--g-tabbar-bg': 'oklch(var(--background))',
'--g-tabbar-tab-color': 'oklch(var(--accent-foreground) / 50%)',
'--g-tabbar-tab-hover-bg': 'oklch(var(--accent) / 50%)',
'--g-tabbar-tab-hover-color': 'oklch(var(--accent-foreground) / 50%)',
'--g-tabbar-tab-active-bg': 'oklch(var(--accent))',
'--g-tabbar-tab-active-color': 'oklch(var(--foreground))',
// 工具栏
'--g-toolbar-bg': 'oklch(var(--background))',
},
第四步:在应用设置中启用
主题添加到 packages/themes/index.ts 后,还需要在对应应用的设置中启用。先执行 ls apps/ 确认有哪些应用,询问用户要在哪个应用中启用,然后修改 apps/<app>/src/settings.ts:
theme: {
baseColorLight: 'baseName',
baseColorDark: 'baseName',
light: 'themeName',
dark: 'themeName',
}
参考资源
- 设计风格目录:
references/design-styles.md— 包含 20+ 种专业设计风格的配色方案和灵感来源 - CSS 变量说明:
references/theme-structure.md— 每个变量的作用、取值规范和明暗差异(含完整暗色模板) - 社区配色:https://tweakcn.com/community — 可直接参考社区主题的 shadcn CSS 变量