样式与主题

ktr/template/style.css 的结构、继承自 HeroUI 的颜色 token、换肤与主题变量下发机制

模板用 Tailwind CSS v4 写样式,颜色体系继承 HeroUI v3 默认主题。本页讲样式入口、可用的颜色类、换肤方式和主题变量的注入时机。

样式入口

所有模板共用 ktr/template/style.css(ktr init 生成,缺失时首次启动自动补):

ktr/template/style.css
@import 'tailwindcss';
@import '@karinjs/template-react/styles';
@source './**/*.{ts,tsx}';
  • 第 1 行:Tailwind v4 本体;保持标准 CSS import 写法,编辑器和 CSS 工具链都能正常识别。
  • 第 2 行:ktr 样式基座(含 HeroUI 全套样式与默认主题变量,随包自带,不用单独装)。
  • 第 3 行:让 Tailwind 扫描模板目录下的类名(v4 按需生成)。

ktr 会在内部把 Tailwind 扫描范围收紧到显式 @source,避免 Vite 与 tsdown 的不同 JS 产物带来无关工具类。下游 CSS 不需要写 source(none)。

不要再写 @theme { --color-accent: var(--accent); ... } 这类颜色映射块。 HeroUI 已用 @theme inline 做好桥接,普通 @theme 会把它盖掉,导致元素级主题注入失效。

加字体、自定义 token 时,在这三行之后继续写即可。

用依赖包里的 CSS 与字体

字体包(@lobehub/webfont-harmony-sans-sc 这类)把 @font-face 和字体文件一起发布,直接在样式入口里 @import 即可。构建期由 Tailwind/Vite 解析并展平,包内 url('./fonts/x.woff2') 这样的相对引用也会一并处理:

ktr/template/style.css
@import 'tailwindcss';
@import '@karinjs/template-react/styles';
@source './**/*.{ts,tsx}';
@import '@lobehub/webfont-harmony-sans-sc/index-full.css';

字体在三种状态下怎么落地:

  • 开发面板:dev server 直接从 node_modules 提供字体文件,pnpm 把真实文件放在 .pnpm/<包名>@<版本>/node_modules/<包名> 下也照样能取到,不需要额外配置。
  • 构建产物:超过 4KB 的字体由 CSS 构建输出到产物 assets/[name]-[hash].woff2,CSS 里保留 /assets/... 引用。
  • 渲染截图:渲染期按 html.assetsInlineLimit 处理这些引用——不超过阈值的内联为 data URI,超过的转成 file:// 绝对路径。截图引擎用 file:// 打开 HTML,字体正常加载,几十 MB 的字体不会 base64 进每一份 HTML(这也是引入字体包最容易踩的坑)。

不想让某个依赖的样式进 Tailwind 管线(例如只想按需注入),用 extraStylePaths 直接写包名:

karin.template.ts
import { defineConfig } from '@karinjs/template-react'

export default defineConfig({
  extraStylePaths: ['@lobehub/webfont-harmony-sans-sc/index-full.css']
})

包名按 Node 的解析规则定位(含 exports 映射与 pnpm 的真实路径),不要手写 node_modules/...:pnpm 只把直接依赖软链到根 node_modules,拼路径会漏。

生产 bundle 不读 karin.template.ts(发布产物里没有配置文件),extraStylePaths 在那里取不到值。这种形态下由插件胶水层显式传:createTemplateRenderer(import.meta.url, { renderer: { extraStylePaths: [...] } })。

独立运行包(ktr build)的 CSS 在构建期就烘进入口,必须自洽,所以那里的资源一律内联。字体包很大时用普通打包(ktrBuildPlugin),走上面的 file:// 路径。

可用的颜色类

命名规律:不带后缀的是背景色,带 -foreground 的是该背景上的文字色。

类名作用
bg-background / text-foreground模板根背景 / 主要文字
bg-surface / text-surface-foreground卡片、面板等表面色
text-muted次要文字
border-border边框、分割线
bg-accent / text-accent / bg-accent-soft主强调色及其浅色背景(各自有 -foreground)
bg-success / bg-warning / bg-danger状态色(各自有 -foreground 和 -soft 变体)
bg-default / bg-overlay中性填充 / 浮层背景

优先用这些语义类而不是 bg-white、text-gray-500 这类写死的颜色,模板才能跟随主题。完整清单见 HeroUI Colors。

换肤

在自己的 style.css 里覆盖 HeroUI 变量即可(ktr init 选"自定义主题块"会生成带注释的骨架):

ktr/template/style.css
@import 'tailwindcss';
@import '@karinjs/template-react/styles';
@source './**/*.{ts,tsx}';

:root {
  --accent: oklch(0.62 0.19 254);
  --radius: 0.5rem;
}

.dark,
[data-theme='dark'] {
  --accent: oklch(0.72 0.16 254);
}

--accent 一改,bg-accent、text-accent、bg-accent-soft 和 HeroUI 组件主色一起跟着变。整套变量见 HeroUI Theming;不想手写就用开发面板「模板主题」抽屉可视化调,「查看代码」生成 CSS 贴进来。

深色模式

  • 组件逻辑:读 ctx.theme?.mode === 'dark'(深色下换图、去投影等)。
  • 纯样式差异:用 Tailwind 的 dark: 变体(如 dark:bg-surface)。

明暗切换靠 dark 类名和 data-theme 属性,ktr 渲染时自动写到 <body>,不用自己处理。

主题变量什么时候注入

框架不发明默认主题色。 ctx.theme 只包含调用方显式提供的字段;没人显式设置时,SSR 和面板沙盒都不注入任何颜色变量,组件库按自身默认主题渲染。

下发路径两条:

  • 面板调试:「模板主题」抽屉里显式调整后,面板把设置经 ctx.theme 传给沙盒组件并写入 CSS 变量。
  • SSR 渲染:渲染选项显式传 theme 字段,框架把变量写到输出 HTML 的 <body> 上,所有后代元素继承生效。

ThemeContext 字段名与 HeroUI 变量一一对应(accent → --accent、accentForeground → --accent-foreground……),传 theme 和改 CSS 变量是同一套东西的两个入口。ctx.theme 每个字段都可能不存在,消费时始终用可选链。

On this page