11111
222222222
主题系统扩展:布局变量 + 组件变体
背景
当前主题系统仅支持颜色变量(theme/*/light.css + dark.css),通过 CSS 自定义属性 + Tailwind v4 @theme 实现。所有组件已替换为语义化颜色类(bg-bg-primary、text-text-primary 等)。
现在需要扩展主题系统,让不同主题不仅能改变颜色,还能改变布局观感(圆角、间距、字体、页面宽度)乃至组件结构(导航布局、卡片样式、Hero 样式等)。
目标架构
theme/
default/
light.css ← 颜色变量(已有)
dark.css ← 颜色变量(已有)
theme.json ← 元数据 + 布局配置(扩展)
layout.css ← 布局变量(新增)
ocean/ ← 未来新增
light.css
dark.css
theme.json
layout.css
Step 1:CSS 布局变量(零组件改动)
1.1 新增布局变量列表
在 theme/default/layout.css 中定义以下变量:
:root {
/* ─── 页面布局 ─── */
--layout-max-width: 72rem; /* 页面内容最大宽度(默认 6xl = 1152px) */
--layout-gap: 2.5rem; /* 主内容与侧边栏间距 */
--layout-sidebar-width: 20rem; /* 侧边栏宽度 */
--layout-section-spacing: 3rem; /* 区块间距(section 之间) */
--layout-content-padding: 1.5rem; /* 内容区域水平内边距 */
/* ─── 圆角 ─── */
--radius-card: 1rem; /* 卡片圆角(对应 rounded-2xl) */
--radius-card-lg: 1.25rem; /* 大卡片圆角 */
--radius-button: 0.75rem; /* 按钮圆角(对应 rounded-xl) */
--radius-badge: 0.375rem; /* 标签圆角(对应 rounded) */
--radius-input: 0.75rem; /* 输入框圆角 */
--radius-modal: 1rem; /* 弹窗圆角 */
/* ─── 字体 ─── */
--font-family-base: 'Inter', system-ui, -apple-system, sans-serif;
--font-family-mono: 'JetBrains Mono', 'Fira Code', monospace;
/* ─── 间距缩放 ─── */
--space-unit: 0.25rem; /* 间距单位,默认 4px,可整体缩放 */
}
1.2 在 src/index.css 注册
@import "../theme/default/layout.css" layer(theme);
@theme {
/* 现有颜色变量... */
/* 新增布局变量 */
--radius-card: var(--radius-card);
--radius-card-lg: var(--radius-card-lg);
--radius-button: var(--radius-button);
--radius-badge: var(--radius-badge);
--radius-input: var(--radius-input);
--radius-modal: var(--radius-modal);
}
1.3 组件中的替换规则
| 旧类名 | 新语义类名 | 说明 |
|---|---|---|
rounded-2xl |
rounded-card |
卡片圆角 |
rounded-xl |
rounded-button |
按钮圆角 |
rounded / rounded-md |
rounded-badge |
标签圆角 |
rounded-lg |
rounded-card |
合并到 card |
rounded-3xl |
rounded-card-lg |
大圆角卡片 |
注意: 有些 rounded-* 是用于头像、图标容器等特定场景,不应替换:
rounded-full(头像、圆形图标)→ 保持原样rounded-xl用于图片缩略图等 → 根据上下文判断
1.4 不同主题的布局变量差异示例
| 变量 | 默认主题 | 海洋主题 | 极简主题 |
|---|---|---|---|
--radius-card |
1rem (圆润) | 0.5rem (微圆) | 0 (直角) |
--radius-button |
0.75rem | 0.5rem | 0.25rem |
--layout-max-width |
72rem | 80rem (更宽) | 64rem (更窄) |
--font-family-base |
Inter | 'Playfair Display', serif | system-ui |
Step 2:组件布局变体(需要改组件)
2.1 扩展 theme.json 结构
{
"name": "默认主题",
"name_en": "Default",
"author": "Jalpei",
"version": "1.0.0",
"description": "简洁中性风格,琥珀色强调色",
"description_en": "Clean neutral style with amber accent",
"layout": {
"headerVariant": "default",
"heroVariant": "default",
"cardVariant": "default",
"articleCardStyle": "default",
"sidebarPosition": "right",
"footerVariant": "default",
"navStyle": "tabs"
}
}
2.2 可用的变体枚举
| 组件 | 变体 | 说明 |
|---|---|---|
headerVariant |
default / centered / minimal / floating |
导航布局 |
heroVariant |
default / fullscreen / split / minimal |
Hero 区域 |
cardVariant |
default / horizontal / minimal / elevated |
卡片风格 |
articleCardStyle |
default / minimal / cover-first |
文章卡片 |
sidebarPosition |
right / left / hidden |
侧边栏位置 |
footerVariant |
default / compact / minimal |
页脚风格 |
navStyle |
tabs / pills / underline / minimal |
导航样式 |
2.3 实现方案
A. 创建 src/lib/themeLayout.ts
// 从当前 themeName 加载对应的布局配置
export interface ThemeLayoutConfig {
headerVariant: 'default' | 'centered' | 'minimal' | 'floating';
heroVariant: 'default' | 'fullscreen' | 'split' | 'minimal';
cardVariant: 'default' | 'horizontal' | 'minimal' | 'elevated';
articleCardStyle: 'default' | 'minimal' | 'cover-first';
sidebarPosition: 'right' | 'left' | 'hidden';
footerVariant: 'default' | 'compact' | 'minimal';
navStyle: 'tabs' | 'pills' | 'underline' | 'minimal';
}
const DEFAULT_LAYOUT: ThemeLayoutConfig = {
headerVariant: 'default',
heroVariant: 'default',
cardVariant: 'default',
articleCardStyle: 'default',
sidebarPosition: 'right',
footerVariant: 'default',
navStyle: 'tabs',
};
// 从 theme.json 加载布局配置
const LAYOUT_REGISTRY: Record<string, ThemeLayoutConfig> = {
default: { ...DEFAULT_LAYOUT },
ocean: {
headerVariant: 'centered',
heroVariant: 'fullscreen',
cardVariant: 'elevated',
articleCardStyle: 'cover-first',
sidebarPosition: 'left',
footerVariant: 'compact',
navStyle: 'underline',
},
};
export function getThemeLayout(themeName: string): ThemeLayoutConfig {
return LAYOUT_REGISTRY[themeName] || DEFAULT_LAYOUT;
}
B. 在 DataContext 中暴露 themeLayout
// DataContext 中新增
const [themeLayout, setThemeLayout] = useState<ThemeLayoutConfig>(() =>
getThemeLayout(themeName)
);
// 切换主题时同步更新
const setThemeName = (name: string) => {
setThemeNameState(name);
setThemeLayout(getThemeLayout(name));
// ...
};
C. 组件中根据布局配置渲染不同变体
// Header.tsx 示例
const { themeLayout } = useData();
if (themeLayout.headerVariant === 'centered') {
return <HeaderCentered />;
}
if (themeLayout.headerVariant === 'minimal') {
return <HeaderMinimal />;
}
if (themeLayout.headerVariant === 'floating') {
return <HeaderFloating />;
}
return <HeaderDefault />;
每个变体可以放在 src/components/HeaderVariants/ 目录下,或直接在同一文件中用条件分支。
2.4 具体变体说明
Header 变体:
default— 当前样式(左 logo + 中导航 + 右工具)centered— Logo 居中,导航在 logo 下方一行minimal— 只有 logo + 汉堡菜单,展开全屏导航floating— 透明背景浮在内容上方,滚动后变实心
Hero 变体:
default— 当前样式(头像 + 简介 + CTA 按钮)fullscreen— 全屏大图/视频背景,文字居中叠加split— 左文右图分栏布局minimal— 只有大字标题 + 一句话简介
Card 变体:
default— 当前卡片样式(白色背景,圆角,阴影)horizontal— 卡片内图片在左,文字在右minimal— 无背景无边框,只有文字elevated— 大阴影,hover 上浮效果更强
执行顺序
Step 1.1: 创建 theme/default/layout.css(布局变量)
↓
Step 1.2: 修改 src/index.css 注册 @theme 布局变量
↓
Step 1.3: 全局替换 rounded-* 类为语义化类名
↓
Step 1.4: 确保不同主题的 layout.css 可独立覆盖
↓
(以上为零组件逻辑改动)
↓
Step 2.1: 创建 src/lib/themeLayout.ts
↓
Step 2.2: 在 DataContext 中集成 themeLayout
↓
Step 2.3: 逐个组件实现变体(Header → Hero → Card → Footer)
↓
Step 2.4: 扩展 AdminSettings 布局配置面板
↓
Step 2.5: 构建验证
注意事项
- 渐进式替换 — Step 1 和 Step 2 可以分开做,Step 1 是纯 CSS 改动,零风险
- 向后兼容 — 所有变体都有
default值,现有主题不影响 - 布局变量 vs Tailwind 默认值 — 如果某个
rounded-*类没有被替换,它会回退到 Tailwind 默认值,不影响功能 rounded-full不要替换 — 头像、圆形图标、开关等保持原样rounded-2xl在prose img中 — 保留,或在index.css中统一覆盖