Theme 主题系统
目录仍然叫 theme,公共导出名继续保持ThemeController。它既使用 daisyUI theme-controller 的输入模式,也提供轻量主题配置能力:Provider/ConfigProvider、theme.useToken、getDesignToken和暗色 / 紧凑算法可以一起用于局部作用域、组合派生和场景覆盖。
API 组织保持轻量,视觉仍然使用 Rue 当前偏轻盈、偏实验的气质:它更适合生成局部主题岛, 而不是把整站配置强耦合到单一入口。
# Scoped theme workbench
把预设主题、算法和 token override 合到一个工作台里,直接观察局部主题岛的真实样子。
Theme Workbench
把预设、算法和 token 覆盖放到同一个工作台里,观察 Rue Theme 的组合结果。
default · default
这个区域只吃当前 Provider 的 token。它不会改动全站视觉,但能单独承载营销卡片、工作台模块或嵌套的品牌子空间。
# Preset snapshot
通过 getDesignToken 直接抽取主题快照,不挂 Provider 也能做主题分析、导出和调试面板。
# Provider render prop
Rue 默认 children 不支持函数子节点,所以 ThemeProvider 提供显式 render prop 来读取运行时 token。
# Component tokens
组件级 token 默认只覆盖当前组件,selector 可接入现成 daisyUI/Rue class。
Component Tokens
组件级 token 只写入当前 Provider 内匹配的组件选择器,Button、Card、Input 可以拥有各自的变量值。
Card token island
Card 单独覆盖 base 色、圆角和阴影,不影响同一 Provider 下的 Input。
# Nested component theme
局部主题可以继续嵌套,子 Provider 默认继承父 token 和组件 token,再覆盖自己的局部配置。
# HashId and theme.useToken
主题配置容器、theme.useToken、hashId、hashed=false 都可以组合使用。
hashed 默认开启,ConfigProvider 根节点会带上稳定 hash class,方便和抽取样式或局部选择器配合。
# CSS variables extraction
支持 cssVar.key、prefix 和 zeroRuntime,配合 extractStyle 预先抽出根变量和组件级变量。
Provider 不再注入组件级 style 标签,但 runtime 和 extractStyle 都能拿到同一份变量。
.brand-alpha-theme { color-scheme: light; color: #0f172a; --color-primary: #445566; --color-primary-content: #f8fafc; --color-secondary: #0f766e; --color-secondary-content: #ecfeff; --color-accent: #9333ea; --color-accent-content: #faf5ff; --color-neutral: #0f172a; --color-neutral-content: #f8fafc; --color-base-100: #f8fafc; --color-base-200: #eef2ff; --color-base-300: #e2e8f0; --color-base-content: #0f172a; --color-info: #0ea5e9; --color-info-content: #082f49; --color-success: #16a34a; --color-success-content: #f0fdf4; --color-warning: #d97706; --color-warning-content: #451a03; --color-error: #dc2626; --color-error-content: #fef2f2; --radius-selector: 0.5rem; --radius-field: 0.9rem; --radius-box: 1.4rem; --size-selector: 0.25rem; --size-field: 0.25rem; --border: 1px; --depth: 1; --noise: 0; --rue-theme-space-xs: 0.375rem; --rue-theme-space-sm: 0.625rem; --rue-theme-space-md: 1rem; --rue-theme-space-lg: 1.5rem; --rue-theme-space-xl: 2rem; --rue-theme-font-family: ui-sans-serif, system-ui, sans-serif; --rue-theme-font-mono: ui-monospace, SFMono-Regular, monospace; --rue-theme-font-size: 1rem; --rue-theme-line-height: 1.5; --rue-theme-shadow-sm: 0 12px 30px rgba(15, 23, 42, 0.08); --rue-theme-shadow-md: 0 20px 50px rgba(15, 23, 42, 0.12); --rue-theme-shadow-lg: 0 30px 70px rgba(15, 23, 42, 0.18); --brand-color-primary: #445566; --brand-color-primary-content: #f8fafc; --brand-color-secondary: #0f766e; --brand-color-secondary-content: #ecfeff; --brand-color-accent: #9333ea; --brand-color-accent-content: #faf5ff; --brand-color-neutral: #0f172a; --brand-color-neutral-content: #f8fafc; --brand-color-base-100: #f8fafc; --brand-color-base-200: #eef2ff; --brand-color-base-300: #e2e8f0; --brand-color-base-content: #0f172a; --brand-color-info: #0ea5e9; --brand-color-info-content: #082f49; --brand-color-success: #16a34a; --brand-color-success-content: #f0fdf4; --brand-color-warning: #d97706; --brand-color-warning-content: #451a03; --brand-color-error: #dc2626; --brand-color-error-content: #fef2f2; --brand-radius-selector: 0.5rem; --brand-radius-field: 0.9rem; --brand-radius-box: 1.4rem; --brand-size-selector: 0.25rem; --brand-size-field: 0.25rem; --brand-border: 1px; --brand-depth: 1; --brand-noise: 0; --brand-rue-theme-space-xs: 0.375rem; --brand-rue-theme-space-sm: 0.625rem; --brand-rue-theme-space-md: 1rem; --brand-rue-theme-space-lg: 1.5rem; --brand-rue-theme-space-xl: 2rem; --brand-rue-theme-font-family: ui-sans-serif, system-ui, sans-serif; --brand-rue-theme-font-mono: ui-monospace, SFMono-Regular, monospace; --brand-rue-theme-font-size: 1rem; --brand-rue-theme-line-height: 1.5; --brand-rue-theme-shadow-sm: 0 12px 30px rgba(15, 23, 42, 0.08); --brand-rue-theme-shadow-md: 0 20px 50px rgba(15, 23, 42, 0.12); --brand-rue-theme-shadow-lg: 0 30px 70px rgba(15, 23, 42, 0.18); }
[data-rue-theme-scope="rue-brand-alpha"]:where(.btn), [data-rue-theme-scope="rue-brand-alpha"] :where(.btn) { --color-primary: #112233; --color-primary-content: #f8fafc; --color-secondary: #0f766e; --color-secondary-content: #ecfeff; --color-accent: #9333ea; --color-accent-content: #faf5ff; --color-neutral: #0f172a; --color-neutral-content: #f8fafc; --color-base-100: #f8fafc; --color-base-200: #eef2ff; --color-base-300: #e2e8f0; --color-base-content: #0f172a; --color-info: #0ea5e9; --color-info-content: #082f49; --color-success: #16a34a; --color-success-content: #f0fdf4; --color-warning: #d97706; --color-warning-content: #451a03; --color-error: #dc2626; --color-error-content: #fef2f2; --radius-selector: 0.5rem; --radius-field: 999px; --radius-box: 1.4rem; --size-selector: 0.25rem; --size-field: 0.25rem; --border: 1px; --depth: 1; --noise: 0; --rue-theme-space-xs: 0.375rem; --rue-theme-space-sm: 0.625rem; --rue-theme-space-md: 1rem; --rue-theme-space-lg: 1.5rem; --rue-theme-space-xl: 2rem; --rue-theme-font-family: ui-sans-serif, system-ui, sans-serif; --rue-theme-font-mono: ui-monospace, SFMono-Regular, monospace; --rue-theme-font-size: 1rem; --rue-theme-line-height: 1.5; --rue-theme-shadow-sm: 0 12px 30px rgba(15, 23, 42, 0.08); --rue-theme-shadow-md: 0 20px 50px rgba(15, 23, 42, 0.12); --rue-theme-shadow-lg: 0 30px 70px rgba(15, 23, 42, 0.18); --brand-color-primary: #112233; --brand-color-primary-content: #f8fafc; --brand-color-secondary: #0f766e; --brand-color-secondary-content: #ecfeff; --brand-color-accent: #9333ea; --brand-color-accent-content: #faf5ff; --brand-color-neutral: #0f172a; --brand-color-neutral-content: #f8fafc; --brand-color-base-100: #f8fafc; --brand-color-base-200: #eef2ff; --brand-color-base-300: #e2e8f0; --brand-color-base-content: #0f172a; --brand-color-info: #0ea5e9; --brand-color-info-content: #082f49; --brand-color-success: #16a34a; --brand-color-success-content: #f0fdf4; --brand-color-warning: #d97706; --brand-color-warning-content: #451a03; --brand-color-error: #dc2626; --brand-color-error-content: #fef2f2; --brand-radius-selector: 0.5rem; --brand-radius-field: 999px; --brand-radius-box: 1.4rem; --brand-size-selector: 0.25rem; --brand-size-field: 0.25rem; --brand-border: 1px; --brand-depth: 1; --brand-noise: 0; --brand-rue-theme-space-xs: 0.375rem; --brand-rue-theme-space-sm: 0.625rem; --brand-rue-theme-space-md: 1rem; --brand-rue-theme-space-lg: 1.5rem; --brand-rue-theme-space-xl: 2rem; --brand-rue-theme-font-family: ui-sans-serif, system-ui, sans-serif; --brand-rue-theme-font-mono: ui-monospace, SFMono-Regular, monospace; --brand-rue-theme-font-size: 1rem; --brand-rue-theme-line-height: 1.5; --brand-rue-theme-shadow-sm: 0 12px 30px rgba(15, 23, 42, 0.08); --brand-rue-theme-shadow-md: 0 20px 50px rgba(15, 23, 42, 0.12); --brand-rue-theme-shadow-lg: 0 30px 70px rgba(15, 23, 42, 0.18); }
[data-rue-theme-scope="rue-brand-alpha"]:where(.brand-card), [data-rue-theme-scope="rue-brand-alpha"] :where(.brand-card) { --color-primary: #445566; --color-primary-content: #f8fafc; --color-secondary: #0f766e; --color-secondary-content: #ecfeff; --color-accent: #9333ea; --color-accent-content: #faf5ff; --color-neutral: #0f172a; --color-neutral-content: #f8fafc; --color-base-100: #f8fafc; --color-base-200: #eef2ff; --color-base-300: #e2e8f0; --color-base-content: #0f172a; --color-info: #0ea5e9; --color-info-content: #082f49; --color-success: #16a34a; --color-success-content: #f0fdf4; --color-warning: #d97706; --color-warning-content: #451a03; --color-error: #dc2626; --color-error-content: #fef2f2; --radius-selector: 0.5rem; --radius-field: 0.9rem; --radius-box: 1.5rem; --size-selector: 0.25rem; --size-field: 0.25rem; --border: 1px; --depth: 1; --noise: 0; --rue-theme-space-xs: 0.375rem; --rue-theme-space-sm: 0.625rem; --rue-theme-space-md: 1rem; --rue-theme-space-lg: 1.5rem; --rue-theme-space-xl: 2rem; --rue-theme-font-family: ui-sans-serif, system-ui, sans-serif; --rue-theme-font-mono: ui-monospace, SFMono-Regular, monospace; --rue-theme-font-size: 1rem; --rue-theme-line-height: 1.5; --rue-theme-shadow-sm: 0 12px 30px rgba(15, 23, 42, 0.08); --rue-theme-shadow-md: 0 20px 50px rgba(15, 23, 42, 0.12); --rue-theme-shadow-lg: 0 30px 70px rgba(15, 23, 42, 0.18); --brand-color-primary: #445566; --brand-color-primary-content: #f8fafc; --brand-color-secondary: #0f766e; --brand-color-secondary-content: #ecfeff; --brand-color-accent: #9333ea; --brand-color-accent-content: #faf5ff; --brand-color-neutral: #0f172a; --brand-color-neutral-content: #f8fafc; --brand-color-base-100: #f8fafc; --brand-color-base-200: #eef2ff; --brand-color-base-300: #e2e8f0; --brand-color-base-content: #0f172a; --brand-color-info: #0ea5e9; --brand-color-info-content: #082f49; --brand-color-success: #16a34a; --brand-color-success-content: #f0fdf4; --brand-color-warning: #d97706; --brand-color-warning-content: #451a03; --brand-color-error: #dc2626; --brand-color-error-content: #fef2f2; --brand-radius-selector: 0.5rem; --brand-radius-field: 0.9rem; --brand-radius-box: 1.5rem; --brand-size-selector: 0.25rem; --brand-size-field: 0.25rem; --brand-border: 1px; --brand-depth: 1; --brand-noise: 0; --brand-rue-theme-space-xs: 0.375rem; --brand-rue-theme-space-sm: 0.625rem; --brand-rue-theme-space-md: 1rem; --brand-rue-theme-space-lg: 1.5rem; --brand-rue-theme-space-xl: 2rem; --brand-rue-theme-font-family: ui-sans-serif, system-ui, sans-serif; --brand-rue-theme-font-mono: ui-monospace, SFMono-Regular, monospace; --brand-rue-theme-font-size: 1rem; --brand-rue-theme-line-height: 1.5; --brand-rue-theme-shadow-sm: 0 12px 30px rgba(15, 23, 42, 0.08); --brand-rue-theme-shadow-md: 0 20px 50px rgba(15, 23, 42, 0.12); --brand-rue-theme-shadow-lg: 0 30px 70px rgba(15, 23, 42, 0.18); }完整的输入模式矩阵
下面这些示例保持了基础的 toggle、checkbox、swap、radio 四种 controller 写法,并额外补了按钮组模式,方便把 ThemeController 直接嵌进当前表单和筛选 UI。 由于 daisyUI 的 theme-controller 天生就是页面级切换器,这里额外做了单一激活控制,避免多个 示例 同时 checked 时互相抢占全局主题。
import { ThemeController, theme } from '@rue-js/design'
export default function ThemeQuickUseDemo() {
const token = ThemeController.getDesignToken({
theme: 'coffee',
algorithm: ThemeController.compactAlgorithm,
})
const { runtime, hashId } = theme.useToken({ theme: 'coffee' })
return (
<div className={hashId}>
{runtime.theme} | {token.colors.primary}
</div>
)
}# Theme Controller using a toggle
最轻量的 controller 入口。
# Theme Controller using a checkbox
适合塞进表单区或者设置面板。
# Theme Controller using a swap
展示了基础的 swap 形式,并补上更完整的视觉提示。
# Theme Controller using radio inputs
适合明确展示当前主题选择。
# Theme Controller using button group
把 ThemeController 当作 join 按钮组使用,适合主题预设切换器。
ThemeController API
| 属性 / API | 说明 | 类型 | 默认值 |
|---|---|---|---|
className | 追加到 theme-controller 输入上的样式类,可与 toggle、checkbox、radio、btn 等 daisyUI 形态组合。 | string | - |
theme | 语义化主题名别名,未传时回退到原生 value。 | string | - |
type | 控制输入类型,支持切换和单选两种主题选择方式。 | 'checkbox' | 'radio' | 'checkbox' |
...native props | 继续透传 checked、name、onChange、disabled、autoComplete 等原生 input 属性。 | HTML input props | - |
ThemeController.Provider API
| 属性 / API | 说明 | 类型 | 默认值 |
|---|---|---|---|
algorithm | 可传单个算法或算法数组,按顺序组合处理 token。 | ThemeAlgorithm | ThemeAlgorithm[] | - |
as | 指定主题作用域根节点标签。 | 'article' | 'div' | 'section' | 'span' | 'div' |
baseToken | 从外部现成 token 继续派生,适合做二次主题生成。 | ThemeDesignToken | - |
components | 组件级 token 配置,按 Button、Card、Input 等组件名局部覆盖变量,可配置 selector 和组件级 algorithm。 | Record<string, ThemeComponentTokenOverride> | - |
cssVar | CSS variables 配置。传 prefix 会生成别名变量,传 key 会生成稳定 scopeId,false 可关闭变量注入。 | boolean | { prefix?: string; key?: string } | true |
hashed | 控制主题作用域是否生成稳定 hash class,默认会追加到根节点。 | boolean | true |
inherit | 是否允许从父主题 runtime 继承 token 和组件级 token,嵌套 Provider 默认开启。 | boolean | true |
zeroRuntime | 关闭运行时组件级 style 标签注入,配合 extractStyle 在构建期或服务端提前抽取 CSS。 | boolean | false |
children | 作用域内部的正常内容节点。 | any | - |
className | 主题容器自身类名。 | string | - |
render | 显式 render prop,可拿到当前运行时 token 信息并返回节点。 | (runtime: ThemeTokenRuntime) => any | - |
style | 额外的作用域样式,支持对象和字符串,会与 token 生成的 CSS 变量一起合并。 | Record<string, string | number> | string | - |
theme | 预设主题名,当前内置 default、garden、retro、synthwave、cyberpunk、night、coffee。 | string | 'default' |
token | 局部 token 覆盖,适合按场景微调主色、圆角、字号、阴影和密度。 | ThemeTokenOverride | - |
Static Theme APIs
| 属性 / API | 说明 | 类型 | 默认值 |
|---|---|---|---|
ConfigProvider | ThemeController.Provider 的主题配置容器别名,可直接从 @rue-js/design 导入。 | FC<ThemeProviderProps> | - |
compactAlgorithm | 把主题压缩成更高密度的控件和间距。 | ThemeAlgorithm | - |
darkAlgorithm | 强制衍生暗色基底和深阴影层级。 | ThemeAlgorithm | - |
defaultAlgorithm | 标准 token 整理算法,负责补充 appearance、resolvedThemeName 等派生字段。 | ThemeAlgorithm | - |
defaultConfig | Theme 的默认配置入口。 | ThemeConfig | - |
defaultSeed | Rue Theme 的默认种子 token,可作为二次定制基线。 | ThemeDesignToken | - |
getDesignToken(config) | 纯函数,返回合并预设、算法和覆盖后的最终 token。 | (config?: ThemeConfig) => ThemeDesignToken | - |
getComponentDesignToken(name, config) | 纯函数,返回某个组件在当前 ThemeConfig 下的最终组件级 token。 | (name: string, config?: ThemeConfig) => ThemeDesignToken | - |
getCssVariables(config) | 纯函数,返回当前主题 token 对应的 CSS variables 对象。 | (config?: ThemeConfig) => ThemeStyleRecord | - |
extractStyle(config, options) | 抽取根变量和组件级变量 CSS 文本,适合 zeroRuntime 或 SSR 场景。 | (config?: ThemeConfig, options?: ThemeExtractStyleOptions) => string | - |
presets | 当前内置主题预设对象,可直接复用或做差量扩展。 | Record<string, ThemeTokenOverride> | - |
useToken(config) | 返回 theme、token、cssVariables、components 和 componentCssVariables,适合页面级预览、调试面板和主题生成器。 | (config?: ThemeConfig) => ThemeTokenRuntime | - |
useTokenTuple(config) | 返回主题运行时三元组:[runtime, token, hashId]。 | (config?: ThemeConfig) => [ThemeTokenRuntime, ThemeDesignToken, string] | - |
theme.useToken(config) | 主题命名空间入口,返回 { runtime, token, hashId }。 | (config?: ThemeConfig) => { runtime: ThemeTokenRuntime; token: ThemeDesignToken; hashId: string } | - |