11111

11111
222222222

主题系统扩展:布局变量 + 组件变体

背景

当前主题系统仅支持颜色变量theme/*/light.css + dark.css),通过 CSS 自定义属性 + Tailwind v4 @theme 实现。所有组件已替换为语义化颜色类(bg-bg-primarytext-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: 构建验证

注意事项

  1. 渐进式替换 — Step 1 和 Step 2 可以分开做,Step 1 是纯 CSS 改动,零风险
  2. 向后兼容 — 所有变体都有 default 值,现有主题不影响
  3. 布局变量 vs Tailwind 默认值 — 如果某个 rounded-* 类没有被替换,它会回退到 Tailwind 默认值,不影响功能
  4. rounded-full 不要替换 — 头像、圆形图标、开关等保持原样
  5. rounded-2xlprose img — 保留,或在 index.css 中统一覆盖