设计主题系统Skill design-theme-system

这个技能用于为多站点/多租户 CMS 设计和实现主题化架构,包括设计 tokens、CSS 变量、Tailwind 配置等,支持品牌定制和前端开发集成。适用于网站主题管理、品牌一致性维护和用户体验优化。关键词:主题设计、设计 tokens、CSS 变量、Tailwind、多站点、CMS、前端开发。

前端开发 0 次安装 0 次浏览 更新于 3/11/2026

name: 设计主题系统 description: 为多站点/多租户 CMS 设计 tokens 和主题化架构。包括 CSS 变量、Tailwind 和设计系统集成。 argument-hint: [–format tokens|css-vars|tailwind|all] [–multi-tenant] allowed-tools: Read, Glob, Grep, Task, Skill, AskUserQuestion

设计主题系统命令

设计一个带有设计 tokens 和多站点支持的全面主题化架构。

用法

/cms:design-theme-system --format tokens
/cms:design-theme-system --format css-vars --multi-tenant
/cms:design-theme-system --format tailwind
/cms:design-theme-system --format all

格式选项

  • tokens: 设计 token JSON 模式
  • css-vars: CSS 自定义属性
  • tailwind: Tailwind CSS 配置
  • all: 完整主题系统

工作流程

步骤 1: 解析参数

从命令中提取格式和多租户选项。

步骤 2: 收集需求

使用 AskUserQuestion 来理解:

  • 需要主题化的站点/租户数量是多少?
  • 需要什么级别的定制?
  • 是否有品牌指南需要遵循?
  • 使用什么前端框架?

步骤 3: 调用技能

调用相关技能:

  • design-token-management - Token 架构
  • multi-site-theming - 多租户模式

步骤 4: 设计 Token 模式

Token 层次结构:

tokens:
  # 原始 tokens(原始值)
  primitive:
    colors:
      blue:
        50: "#eff6ff"
        100: "#dbeafe"
        500: "#3b82f6"
        600: "#2563eb"
        900: "#1e3a8a"

      gray:
        50: "#f9fafb"
        100: "#f3f4f6"
        500: "#6b7280"
        900: "#111827"

      success: "#22c55e"
      warning: "#f59e0b"
      error: "#ef4444"

    spacing:
      0: "0"
      1: "0.25rem"
      2: "0.5rem"
      4: "1rem"
      8: "2rem"
      16: "4rem"

    typography:
      font_families:
        sans: "Inter, system-ui, sans-serif"
        serif: "Merriweather, Georgia, serif"
        mono: "JetBrains Mono, monospace"

      font_sizes:
        xs: "0.75rem"
        sm: "0.875rem"
        base: "1rem"
        lg: "1.125rem"
        xl: "1.25rem"
        2xl: "1.5rem"
        4xl: "2.25rem"

      font_weights:
        normal: 400
        medium: 500
        semibold: 600
        bold: 700

    radii:
      none: "0"
      sm: "0.125rem"
      md: "0.375rem"
      lg: "0.5rem"
      full: "9999px"

  # 语义 tokens(目的驱动)
  semantic:
    colors:
      background:
        primary: "{primitive.colors.gray.50}"
        secondary: "{primitive.colors.gray.100}"
        inverse: "{primitive.colors.gray.900}"

      text:
        primary: "{primitive.colors.gray.900}"
        secondary: "{primitive.colors.gray.500}"
        inverse: "{primitive.colors.gray.50}"

      brand:
        primary: "{primitive.colors.blue.600}"
        primary_hover: "{primitive.colors.blue.700}"
        secondary: "{primitive.colors.blue.100}"

      feedback:
        success: "{primitive.colors.success}"
        warning: "{primitive.colors.warning}"
        error: "{primitive.colors.error}"

      border:
        default: "{primitive.colors.gray.200}"
        focus: "{primitive.colors.blue.500}"

    spacing:
      content_padding: "{primitive.spacing.4}"
      section_gap: "{primitive.spacing.8}"
      container_max: "1280px"

    typography:
      body:
        family: "{primitive.typography.font_families.sans}"
        size: "{primitive.typography.font_sizes.base}"
        weight: "{primitive.typography.font_weights.normal}"
        line_height: "1.5"

      heading:
        family: "{primitive.typography.font_families.sans}"
        weight: "{primitive.typography.font_weights.bold}"

  # 组件 tokens
  component:
    button:
      primary:
        background: "{semantic.colors.brand.primary}"
        text: "{semantic.colors.text.inverse}"
        border_radius: "{primitive.radii.md}"
        padding_x: "{primitive.spacing.4}"
        padding_y: "{primitive.spacing.2}"
        font_weight: "{primitive.typography.font_weights.medium}"

      secondary:
        background: "transparent"
        text: "{semantic.colors.brand.primary}"
        border: "1px solid {semantic.colors.brand.primary}"

    card:
      background: "{semantic.colors.background.primary}"
      border: "1px solid {semantic.colors.border.default}"
      border_radius: "{primitive.radii.lg}"
      padding: "{primitive.spacing.4}"
      shadow: "0 1px 3px rgba(0,0,0,0.1)"

    input:
      background: "{semantic.colors.background.primary}"
      border: "1px solid {semantic.colors.border.default}"
      border_radius: "{primitive.radii.md}"
      padding: "{primitive.spacing.2} {primitive.spacing.4}"
      focus_ring: "0 0 0 2px {semantic.colors.border.focus}"

步骤 5: 生成 CSS 变量

CSS 输出:

/* 基础主题(浅色模式) */
:root {
  /* 原始颜色 */
  --color-blue-50: #eff6ff;
  --color-blue-500: #3b82f6;
  --color-blue-600: #2563eb;
  --color-gray-50: #f9fafb;
  --color-gray-500: #6b7280;
  --color-gray-900: #111827;

  /* 语义颜色 */
  --color-bg-primary: var(--color-gray-50);
  --color-bg-secondary: var(--color-gray-100);
  --color-text-primary: var(--color-gray-900);
  --color-text-secondary: var(--color-gray-500);
  --color-brand-primary: var(--color-blue-600);
  --color-border-default: var(--color-gray-200);

  /* 排版 */
  --font-family-sans: 'Inter', system-ui, sans-serif;
  --font-size-base: 1rem;
  --font-weight-normal: 400;
  --font-weight-bold: 700;

  /* 间距 */
  --spacing-1: 0.25rem;
  --spacing-2: 0.5rem;
  --spacing-4: 1rem;
  --spacing-8: 2rem;

  /* 组件 tokens */
  --button-primary-bg: var(--color-brand-primary);
  --button-primary-text: white;
  --button-radius: var(--radius-md);

  --card-bg: var(--color-bg-primary);
  --card-border: 1px solid var(--color-border-default);
  --card-radius: var(--radius-lg);
}

/* 深色模式 */
:root[data-theme="dark"],
.dark {
  --color-bg-primary: var(--color-gray-900);
  --color-bg-secondary: var(--color-gray-800);
  --color-text-primary: var(--color-gray-50);
  --color-text-secondary: var(--color-gray-400);
  --color-border-default: var(--color-gray-700);
}

/* 品牌覆盖示例 */
:root[data-brand="acme"] {
  --color-brand-primary: #ff6b35;
  --color-brand-primary-hover: #e85a2a;
  --font-family-sans: 'Poppins', sans-serif;
}

步骤 6: 生成 Tailwind 配置

Tailwind 配置:

// tailwind.config.js
const tokens = require('./tokens.json');

module.exports = {
  content: ['./src/**/*.{html,js,jsx,ts,tsx,razor}'],

  theme: {
    colors: {
      transparent: 'transparent',
      current: 'currentColor',

      // 映射 CSS 变量以实现运行时主题化
      brand: {
        primary: 'var(--color-brand-primary)',
        'primary-hover': 'var(--color-brand-primary-hover)',
        secondary: 'var(--color-brand-secondary)',
      },

      bg: {
        primary: 'var(--color-bg-primary)',
        secondary: 'var(--color-bg-secondary)',
        inverse: 'var(--color-bg-inverse)',
      },

      text: {
        primary: 'var(--color-text-primary)',
        secondary: 'var(--color-text-secondary)',
        inverse: 'var(--color-text-inverse)',
      },

      border: {
        DEFAULT: 'var(--color-border-default)',
        focus: 'var(--color-border-focus)',
      },

      // 非主题颜色的静态调色板
      ...tokens.primitive.colors,
    },

    fontFamily: {
      sans: 'var(--font-family-sans)',
      serif: 'var(--font-family-serif)',
      mono: 'var(--font-family-mono)',
    },

    extend: {
      spacing: tokens.primitive.spacing,
      borderRadius: tokens.primitive.radii,
    },
  },

  plugins: [
    require('@tailwindcss/forms'),
    require('@tailwindcss/typography'),
  ],
};

步骤 7: 多租户主题解析

主题解析服务:

public class ThemeService
{
    private readonly ITenantResolver _tenantResolver;
    private readonly IThemeRepository _themeRepository;
    private readonly IMemoryCache _cache;

    public async Task<ThemeConfiguration> GetThemeAsync()
    {
        var tenant = await _tenantResolver.ResolveAsync();
        var cacheKey = $"theme:{tenant.Id}";

        return await _cache.GetOrCreateAsync(cacheKey, async entry =>
        {
            entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5);

            // 加载主题层次结构
            var baseTheme = await _themeRepository.GetBaseThemeAsync();
            var brandTheme = await _themeRepository.GetBrandThemeAsync(tenant.BrandId);
            var siteTheme = await _themeRepository.GetSiteThemeAsync(tenant.SiteId);

            // 合并主题,优先级:站点 > 品牌 > 基础
            return MergeThemes(baseTheme, brandTheme, siteTheme);
        });
    }

    public string GenerateCssVariables(ThemeConfiguration theme)
    {
        var sb = new StringBuilder();
        sb.AppendLine(":root {");

        foreach (var token in theme.FlattenTokens())
        {
            sb.AppendLine($"  --{token.Key}: {token.Value};");
        }

        sb.AppendLine("}");
        return sb.ToString();
    }
}

主题 API 端点:

[HttpGet("theme.css")]
[ResponseCache(Duration = 300, VaryByHeader = "Host")]
public async Task<IActionResult> GetThemeCss()
{
    var theme = await _themeService.GetThemeAsync();
    var css = _themeService.GenerateCssVariables(theme);

    return Content(css, "text/css");
}

步骤 8: 主题编辑器 UI

主题自定义 API:

theme_editor:
  sections:
    - name: 颜色
      fields:
        - key: brand_primary
          label: 主品牌颜色
          type: color
          path: semantic.colors.brand.primary

        - key: brand_secondary
          label: 辅助颜色
          type: color
          path: semantic.colors.brand.secondary

    - name: 排版
      fields:
        - key: font_family
          label: 主要字体
          type: font_picker
          path: primitive.typography.font_families.sans

        - key: heading_font
          label: 标题字体
          type: font_picker
          path: semantic.typography.heading.family

    - name: 布局
      fields:
        - key: border_radius
          label: 圆角半径
          type: slider
          min: 0
          max: 24
          path: primitive.radii.md

  preview:
    components: [Button, Card, Input, Typography]
    live_update: true

相关技能

  • design-token-management - Token 架构
  • multi-site-theming - 多租户模式