主题定制
主题由文档根元素的 data-theme 和共享 CSS 变量决定,颜色、字体、圆角、间距与阴影均可按需覆盖。独立应用使用 ThemeProvider 管理主题,ThemeControl 提供三种偏好选择,ThemeButton 提供受控的一键深浅切换。已有主题管理器的站点继续由宿主控制。样式覆盖放在组件库之后,优先采用公开变量和属性,生成的 CSS Module 类名不作为稳定接口。
参考资料
主题变量
先导入 theme.css 和 styles.css,再加载应用覆盖样式。常用变量如下,完整清单以源码为准。
| 用途 | 变量 |
|---|---|
| 强调色 | --lk-color-primary、--lk-color-primary-dark、--lk-color-primary-light |
| 背景 | --lk-background-color、--lk-card-background-color |
| 文字 | --lk-font-color-base、--lk-font-color-secondary、--lk-heading-color |
| 中性色 | --lk-color-emphasis-100 至 --lk-color-emphasis-900 |
| 分割线 | --lk-divider-color、--lk-divider-width |
| 状态色 | --lk-color-success、--lk-color-warning、--lk-color-danger |
| 字体 | --lk-font-family、--lk-font-mono、--lk-font-size、--lk-line-height |
| 圆角 | --lk-radius-control、--lk-radius-overlay、--lk-radius-sm、--lk-radius-md、--lk-radius-lg |
| 间距 | --lk-space-1、--lk-space-2、--lk-space-3、--lk-space-4、--lk-space-6、--lk-space-8 等 |
| 阴影与焦点 | --lk-shadow-sm、--lk-shadow-md、--lk-shadow-lg、--lk-focus-ring |
:root,
:root[data-theme] {
--lk-color-primary: #1d9bf0;
--lk-color-primary-dark: #0f8ee3;
--lk-color-primary-light: #36a6f2;
--lk-radius-control: 10px;
}
:root[data-theme='dark'] {
--lk-background-color: #111214;
--lk-card-background-color: #1c1e21;
}
显式指定根元素 data-theme="light" 或 data-theme="dark";没有指定时,默认变量跟随系统偏好。主题按整份文档管理,不把嵌套的深浅色容器当作隔离边界。
--lk-divider-color 默认使用当前主题的 --lk-color-emphasis-200,--lk-divider-width 默认为 1px。Divider、面板内部分区与顶部栏边界共用这两个变量;间距仍由所在布局控制。可在根元素统一调整,或在局部容器中覆盖。
间距与交互变量
| 变量 | 用途 |
|---|---|
--lk-page-padding-block | 页面内容上下留白,默认 clamp(2rem, 4vw, 3rem) |
--lk-page-padding-inline | 页面水平留白,默认 clamp(1rem, 3vw, 2rem) |
--lk-space-section | 内容分区之间的间隔,默认 clamp(2rem, 4vw, 3rem) |
--lk-space-section-header | 分区标题到正文的间隔,默认 1.25rem |
--lk-font-size-meta | 分区附加信息字号,默认 0.875rem |
--lk-radius-xs | 紧凑圆角,默认 4px |
--lk-transition | 短交互过渡,默认 160ms ease |
Page、SectionHeader 与表单组件使用这些变量。应用应按内容角色选择变量,保留不同布局需要的间距;专门的展开、滚动和图形动画可以继续使用自己的时长。
主题偏好
推荐提供「跟随系统、浅色、深色」三个互斥选项。它们是三种偏好,实际显示仍只有浅色和深色两套配色。
| 选择 | 行为 |
|---|---|
| 跟随系统 | 默认选择;立即采用系统外观,并随系统变化继续更新 |
| 浅色 | 保存为固定浅色,直到用户再次修改 |
| 深色 | 保存为固定深色,直到用户再次修改 |
系统外观变化不会覆盖手动选择,即使系统一度变成相同的配色也保留原有偏好。例如,用户固定选择深色后,系统从浅色变成深色、再变回浅色,网站仍保持深色;只有用户选回「跟随系统」才恢复同步。
刷新页面和同源标签页共享保存的选择,切换界面语言也不改变偏好。非法保存值或存储不可用时默认跟随系统;存储不可用不应阻止当前页面手动切换。首次绘制脚本与挂载后的主题管理器读取相同偏好,同时设置原生控件的 color-scheme 和已有的 theme-color,避免页面初始化时闪烁。
导航栏可使用紧凑三项菜单,设置页可使用分段选择器。已有框架提供三态循环按钮时,也可以保留该入口并在设置页明确展示三个选项。图标表示所选偏好:「跟随系统」显示系统图标,当前实际配色变化不会将其变成固定浅色或深色。
独立应用
import { ThemeControl, ThemeProvider } from '@lailai0916/ui';
export function ThemeSettings() {
return (
<ThemeProvider storageKey="my-app.theme">
<ThemeControl
variant="compact"
labels={{ system: '跟随系统', light: '浅色', dark: '深色' }}
/>
</ThemeProvider>
);
}
ThemeProvider 作用于整份文档,在应用根部挂载一次即可。
| 接口 | 行为 |
|---|---|
storageKey | 默认 lailai.theme;需要独立偏好时使用其他键 |
mode | 默认 preference 保存偏好;system 跟随系统并允许当前页临时切换 |
themeColors | 可选 { light, dark },更新已有的 theme-color meta 标签 |
useTheme().preference | system、light 或 dark |
useTheme().resolvedTheme | 当前实际使用的 light 或 dark |
useTheme().setPreference | 更新偏好;仅 preference 模式在可用时保存到本地存储 |
Provider 挂载后读取保存值,监听系统偏好和其他标签页的存储变化。本地存储被禁用时,当前页仍可切换。themeColors 不会创建 meta 标签,应用需要自行提供该标签。
ThemeControl.labels 必须包含三种模式的文案。variant 默认为 segmented,也可使用 compact。紧凑菜单支持方向键、Home、End、Escape,选择后返回触发按钮;Tab 正常离开菜单。
一键深浅切换
ThemeButton 是受控按钮,传入 theme="light" | "dark" 与 onThemeChange。太阳或月亮显示当前主题,点击请求另一种主题;默认提示文案跟随 LaikitProvider 的语言,可用 label 覆盖。按钮支持原生属性和 ref,桌面为 32 px,小于等于 980 px 或触摸设备增至 44 px,尊重减少动态效果设置。
示例只展示按钮状态,不改变本文的主题。独立应用可在已有的根部 Provider 内连接真实主题:
import { ThemeButton, useTheme } from '@lailai0916/ui';
export function AppearanceAction() {
const { resolvedTheme, setPreference } = useTheme();
return <ThemeButton theme={resolvedTheme} onThemeChange={setPreference} />;
}
ThemeProvider mode="system" 是用于兼容临时覆盖需求的可选模式:忽略保存的偏好,当前页允许临时切换,系统变化或重新打开页面后恢复跟随系统。它不符合上方的持久三态偏好约定;普通网站保留默认的 preference 模式。
ThemeButton 只提供深浅切换,不能单独表达「跟随系统」。用它控制整站外观时,需要另有明确的三项偏好入口。
已有 Docusaurus 等主题管理器时,直接将其状态与切换回调传给 ThemeButton,不用添加第二个 Provider。
宿主主题
已有主题管理器的应用继续使用原有机制,通过 CSS 变量连接组件库。以下以 Docusaurus 为例,将其主题变量映射到 --lk-*:
:root,
:root[data-theme] {
--lk-color-primary: var(--ifm-color-primary);
--lk-color-primary-dark: var(--ifm-color-primary-dark);
--lk-color-primary-light: var(--ifm-color-primary-light);
--lk-background-color: var(--ifm-background-color);
--lk-card-background-color: var(--ifm-card-background-color);
--lk-font-color-base: var(--ifm-font-color-base);
--lk-font-family: var(--ifm-font-family-base);
}
这是一组最小映射,完整宿主适配还应覆盖需要跟随设置变化的文字、中性色和字号。页面首次绘制前的偏好恢复由宿主自己的初始化脚本处理;服务端与客户端初始 React 内容保持一致。
局部样式
按顺序考虑组件属性、className、主题变量,再使用公开的 data-lk 标记。支持的标记为 button、avatar、brand、brand-logo、divider、field-control、panel、theme-menu。
.settings [data-lk='field-control'] {
max-width: 28rem;
}
不要选择构建生成的哈希类名。组件库包含减少动态效果的系统偏好适配;应用额外添加的动画也应遵守同一偏好。