mcpField
S
styling-system

styling-system

github.com/mgd34msu设计与 UI647 次安装10 周前更新
代码仓库

关于 styling-system

styling-system 是一项面向前端项目的样式设计与实现技能,帮助开发者选择 CSS 架构并搭建一致的样式系统。它涵盖设计令牌、主题、响应式布局及组件变体,并提供验证脚本和比较参考。

主要功能

  1. 01比较 Tailwind、CSS Modules、CSS-in-JS 与原生 CSS
  2. 02规划样式配置与架构
  3. 03建立并集成设计令牌
  4. 04设计暗色模式和响应式布局
  5. 05构建组件变体与状态样式
  6. 06使用脚本验证样式实现

适用场景

  1. 01为新项目选择样式方案
  2. 02为现有界面添加暗色模式
  3. 03统一组件的设计令牌与变体
  4. 04迁移样式架构并检查实现

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/mgd34msu/goodvibes-plugin --skill styling-system

安装后在 Claude Code 中输入/styling-system确认已加载。

附带文件references/styling-comparison.mdscripts/validate-styling.sh

详细介绍

资源

scripts/
  validate-styling.sh
references/
  styling-comparison.md

样式系统

本技能将指导你使用 GoodVibes 精准工具做出 CSS 架构决策并进行实现。在搭建样式基础设施、创建设计系统或实现主题与响应式模式时,请使用此工作流。

何时使用此技能

在以下情况下加载此技能:

  • 为新项目搭建样式基础设施
  • 在 Tailwind、CSS Modules、CSS-in-JS 或原生 CSS 之间进行选择
  • 实现设计令牌系统
  • 添加深色模式支持
  • 创建响应式布局和断点策略
  • 在不同样式方案之间迁移
  • 构建组件变体系统
  • 搭建动画模式

触发短语:“搭建样式”“添加 Tailwind”“实现深色模式”“设计令牌”“响应式设计”“CSS 架构”“主题系统”。

核心工作流

阶段 1:发现

在做出样式决策之前,先了解项目的当前状态。

步骤 1.1:检测现有样式方案

使用 discover 查找整个代码库中的样式模式。

discover:
  queries:
    - id: tailwind_usage
      type: grep
      pattern: "(className=|class=).*\".*\\b(flex|grid|text-|bg-|p-|m-)"
      glob: "**/*.{tsx,jsx,vue,svelte}"
    - id: css_modules
      type: glob
      patterns: ["**/*.module.css", "**/*.module.scss"]
    - id: css_in_js
      type: grep
      pattern: "(styled\\.|styled\\(|css`|makeStyles|createStyles)"
      glob: "**/*.{ts,tsx,js,jsx}"
    - id: design_tokens
      type: glob
      patterns: ["**/tokens.{ts,js,json}", "**/design-tokens.{ts,js,json}", "**/theme.{ts,js}"]
    - id: dark_mode
      type: grep
      pattern: "(dark:|data-theme|ThemeProvider|useTheme|darkMode)"
      glob: "**/*.{ts,tsx,js,jsx,css,scss}"
  verbosity: count_only

这会揭示:

  • 主要样式方案(Tailwind、CSS Modules、CSS-in-JS、原生 CSS)
  • 是否存在设计令牌
  • 深色模式的实现状态
  • 代码库中的一致性
步骤 1.2:检查配置文件

读取现有配置,了解当前设置。

precision_read:
  files:
    - path: "tailwind.config.js"
      extract: content
    - path: "tailwind.config.ts"
      extract: content
    - path: "postcss.config.js"
      extract: content
    - path: "src/styles/globals.css"
      extract: outline
  verbosity: minimal
步骤 1.3:分析组件样式模式

读取有代表性的组件,了解相关约定。

precision_read:
  files:
    - path: "src/components/Button.tsx"  # or discovered component
      extract: content
    - path: "src/components/Card.tsx"
      extract: content
  output:
    max_per_item: 100
  verbosity: standard
阶段 2:做出决策

选择适合项目需求的样式方案。完整的决策树请参阅 references/styling-comparison.md。

快速决策指南

适用于以下情况:Tailwind CSS

  • 希望使用工具类快速构建原型
  • 团队间的一致性是优先事项
  • 相比完全自由,更偏好设计约束
  • 正在构建基于组件的 UI(React、Vue、Svelte)

适用于以下情况:CSS Modules

  • 希望样式作用域隔离,且无需增加构建复杂性
  • 更喜欢编写传统 CSS
  • 需要与现有全局样式共存
  • 包体积是主要考量(比 Tailwind 更小)

适用于以下情况:CSS-in-JS

  • 需要运行时动态主题
  • 希望为样式属性提供 TypeScript 类型
  • 需要结合 JS 逻辑实现组件作用域样式
  • 正在使用 styled-components 或 Emotion

适用于以下情况:原生 CSS

  • 正在构建交互性较少的简单网站
  • 希望最大限度地掌控,并尽量减少依赖
  • 渐进增强至关重要
  • 更喜欢级联和继承模式

有关针对特定框架的详细模式,请参阅 references/styling-comparison.md。

阶段 3:配置设置
步骤 3.1:安装依赖

根据所选方案安装所需的软件包。

Tailwind CSS:

precision_exec:
  commands:
    - cmd: "npm install -D tailwindcss postcss autoprefixer"
      expect:
        exit_code: 0
    - cmd: "npx tailwindcss init -p"
      expect:
        exit_code: 0
  verbosity: minimal

CSS-in-JS(styled-components):

precision_exec:
  commands:
    - cmd: "npm install styled-components"
    - cmd: "npm install -D @types/styled-components"
  verbosity: minimal
步骤 3.2:创建配置

遵循最佳实践编写配置文件。

Tailwind 配置示例:

import type { Config } from 'tailwindcss';

const config: Config = {
  content: [
    './src/pages/**/*.{js,ts,jsx,tsx,mdx}',
    './src/components/**/*.{js,ts,jsx,tsx,mdx}',
    './src/app/**/*.{js,ts,jsx,tsx,mdx}',
  ],
  darkMode: 'class', // or 'media' for system preference
  theme: {
    extend: {
      colors: {
        primary: {
          50: '#f0f9ff',
          100: '#e0f2fe',
          // ... full scale
          900: '#0c4a6e',
        },
      },
      fontFamily: {
        sans: ['var(--font-inter)', 'system-ui', 'sans-serif'],
      },
      spacing: {
        '18': '4.5rem',
      },
    },
  },
  plugins: [],
};

export default config;

最佳实践:

  • 配置文件使用 TypeScript(确保类型安全)
  • 扩展主题,而不是替换默认值
  • 使用 CSS 变量表示动态值
  • 明确指定内容路径,避免构建速度变慢
  • 深色模式使用 class 策略(控制力更强)
步骤 3.3:编写配置文件
precision_write:
  files:
    - path: "tailwind.config.ts"
      content: |
        import type { Config } from 'tailwindcss';
        // ... [full config]
    - path: "src/styles/globals.css"
      content: |
        @tailwind base;
        @tailwind components;
        @tailwind utilities;
        
        @layer base {
          :root {
            --background: 0 0% 100%;
            --foreground: 222.2 84% 4.9%;
            /* ... design tokens */
          }
          
          .dark {
            --background: 222.2 84% 4.9%;
            --foreground: 210 40% 98%;
          }
        }
  verbosity: count_only
阶段 4:设计令牌系统
步骤 4.1:定义令牌结构

创建集中式令牌系统,确保一致性。

令牌类别:

  • 颜色:主色、次色、中性色、语义色(成功、错误、警告)
  • 排版:字体系列、字号、字重、行高
  • 间距:一致的比例(以 4px 为基数,按 8px 递增)
  • 阴影:层级系统
  • 边框:圆角半径、宽度
  • 断点:响应式设计系统
  • 动画:时长、缓动函数

实现模式:

// src/styles/tokens.ts
export const tokens = {
  colors: {
    primary: {
      light: '#3b82f6',
      DEFAULT: '#2563eb',
      dark: '#1d4ed8',
    },
    semantic: {
      success: '#10b981',
      error: '#ef4444',
      warning: '#f59e0b',
    },
  },
  spacing: {
    xs: '0.25rem',   // 4px
    sm: '0.5rem',    // 8px
    md: '1rem',      // 16px
    lg: '1.5rem',    // 24px
    xl: '2rem',      // 32px
  },
  typography: {
    fontFamily: {
      sans: ['Inter', 'system-ui', 'sans-serif'],
      mono: ['Fira Code', 'monospace'],
    },
    fontSize: {
      xs: ['0.75rem', { lineHeight: '1rem' }],
      sm: ['0.875rem', { lineHeight: '1.25rem' }],
      base: ['1rem', { lineHeight: '1.5rem' }],
      lg: ['1.125rem', { lineHeight: '1.75rem' }],
      xl: ['1.25rem', { lineHeight: '1.75rem' }],
    },
  },
} as const;

export type Tokens = typeof tokens;

// Integration with ThemeProvider (React Context)
type Theme = {
  tokens: Tokens;
  mode: 'light' | 'dark';
};

// Integration with styled-components
import 'styled-components';
declare module 'styled-components' {
  export interface DefaultTheme extends Tokens {
    mode: 'light' | 'dark';
  }
}

// Usage in components
import { useTheme } from 'styled-components';
const Button = styled.button`
  background: ${props => props.theme.colors.primary};
  padding: ${props => props.theme.spacing[4]};
`;
步骤 4.2:将令牌集成到 Tailwind 中
// tailwind.config.ts
import { tokens } from './src/styles/tokens';

const config: Config = {
  theme: {
    extend: {
      colors: tokens.colors,
      spacing: tokens.spacing,
      fontFamily: tokens.typography.fontFamily,
      fontSize: tokens.typography.fontSize,
    },
  },
};
阶段 5:实现深色模式
步骤 5.1:选择深色模式策略

基于类的方式(推荐):

  • 更好地控制切换行为
  • 可以保留用户偏好
  • 适用于 next-themes 或类似库

基于媒体查询的方式:

  • 仅遵循系统偏好
  • 无需 JS
  • 灵活性较低
步骤 5.2:配置 next-themes(React/Next.js)
// src/components/ThemeProvider.tsx
import { ThemeProvider as NextThemesProvider } from 'next-themes';
import type { ReactNode } from 'react';

interface ThemeProviderProps {
  children: ReactNode;
}

export function ThemeProvider({ children }: ThemeProviderProps) {
  return (
    <NextThemesProvider
      attribute="class"
      defaultTheme="system"
      enableSystem
      disableTransitionOnChange
    >
      {children}
    </NextThemesProvider>
  );
}
步骤 5.3:定义深色模式色阶

使用 CSS 变量,实现流畅的主题切换。

/* globals.css */
@layer base {
  :root {
    --background: 0 0% 100%;
    --foreground: 222.2 84% 4.9%;
    --card: 0 0% 100%;
    --card-foreground: 222.2 84% 4.9%;
    --primary: 221.2 83.2% 53.3%;
    --primary-foreground: 210 40% 98%;
  }
  
  .dark {
    --background: 222.2 84% 4.9%;
    --foreground: 210 40% 98%;
    --card: 222.2 84% 4.9%;
    --card-foreground: 210 40% 98%;
    --primary: 217.2 91.2% 59.8%;
    --primary-foreground: 222.2 47.4% 11.2%;
  }
}

最佳实践:

  • 使用 HSL 值,以便更轻松地调整
  • 保持语义化命名(background、foreground、primary)
  • 测试对比度,确保无障碍(WCAG AA:4.5:1)
  • 深色模式下避免使用纯黑色(#000),改用深灰色
阶段 6:响应式设计模式
步骤 6.1:制定断点策略

移动优先方法(推荐):

/* Base styles for mobile */
.container {
  padding: 1rem;
}

/* Tablet and up */
@media (min-width: 768px) {
  .container {
    padding: 2rem;
  }
}

/* Desktop and up */
@media (min-width: 1024px) {
  .container {
    padding: 3rem;
  }
}

Tailwind 断点:

<div className="p-4 md:p-8 lg:p-12">
  {/* padding increases with screen size */}
</div>
步骤 6.2:容器查询(现代方式)

使用容器查询实现组件级响应式布局。

.card-container {
  container-type: inline-size;
}

@container (min-width: 400px) {
  .card {
    display: grid;
    grid-template-columns: 1fr 2fr;
  }
}

Tailwind 容器查询:

// tailwind.config.ts
plugins: [require('@tailwindcss/container-queries')]
<div className="@container">
  <div className="@md:grid @md:grid-cols-2">
    {/* Responsive to container, not viewport */}
  </div>
</div>
阶段 7:组件变体系统
步骤 7.1:安装 class-variance-authority(CVA)
precision_exec:
  commands:
    - cmd: "npm install class-variance-authority clsx tailwind-merge"
  verbosity: minimal
步骤 7.2:创建 cn 工具函数
// src/lib/utils.ts
import { type ClassValue, clsx } from 'clsx';
import { twMerge } from 'tailwind-merge';

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs));
}
步骤 7.3:构建变体组件
// src/components/Button.tsx
import { cva, type VariantProps } from 'class-variance-authority';
import { cn } from '@/lib/utils';
import type { ButtonHTMLAttributes } from 'react';

const buttonVariants = cva(
  // Base styles
  'inline-flex items-center justify-center rounded-md font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50',
  {
    variants: {
      variant: {
        default: 'bg-primary text-primary-foreground hover:bg-primary/90',
        destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
        outline: 'border border-input bg-background hover:bg-accent',
        ghost: 'hover:bg-accent hover:text-accent-foreground',
      },
      size: {
        default: 'h-10 px-4 py-2',
        sm: 'h-9 rounded-md px-3',
        lg: 'h-11 rounded-md px-8',
        icon: 'h-10 w-10',
      },
    },
    defaultVariants: {
      variant: 'default',
      size: 'default',
    },
  }
);

interface ButtonProps
  extends ButtonHTMLAttributes<HTMLButtonElement>,
    VariantProps<typeof buttonVariants> {
  asChild?: boolean;
}

export function Button({
  className,
  variant,
  size,
  ...props
}: ButtonProps) {
  return (
    <button
      className={cn(buttonVariants({ variant, size, className }))}
      {...props}
    />
  );
}

优点:

  • 类型安全的变体属性
  • 自动合并 Tailwind 类
  • 统一的组件 API
  • 易于扩展新变体
阶段 8:错误状态样式
步骤 8.1:表单验证状态
// src/components/Input.tsx
import { cva } from 'class-variance-authority';
import { cn } from '@/lib/utils';

const inputVariants = cva(
  'w-full rounded-md border px-3 py-2 text-sm transition-colors',
  {
    variants: {
      state: {
        default: 'border-input focus:border-primary focus:ring-2 focus:ring-primary/20',
        error: 'border-destructive focus:border-destructive focus:ring-2 focus:ring-destructive/20',
        success: 'border-green-500 focus:border-green-500 focus:ring-2 focus:ring-green-500/20',
      },
    },
    defaultVariants: {
      state: 'default',
    },
  }
);

interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
  error?: string;
  success?: boolean;
}

export function Input({ error, success, className, ...props }: InputProps) {
  const state = error ? 'error' : success ? 'success' : 'default';
  
  return (
    <div className="space-y-1">
      <input
        className={cn(inputVariants({ state }), className)}
        aria-invalid={!!error}
        aria-describedby={error ? 'input-error' : undefined}
        {...props}
      />
      {error && (
        <p id="input-error" className="text-sm text-destructive" role="alert">
          {error}
        </p>
      )}
    </div>
  );
}
步骤 8.2:Toast 通知
// src/components/Toast.tsx
import { cva } from 'class-variance-authority';

const toastVariants = cva(
  'rounded-lg border p-4 shadow-lg',
  {
    variants: {
      variant: {
        default: 'bg-background border-border',
        success: 'bg-green-50 border-green-200 text-green-900 dark:bg-green-950 dark:border-green-800 dark:text-green-100',
        error: 'bg-red-50 border-red-200 text-red-900 dark:bg-red-950 dark:border-red-800 dark:text-red-100',
        warning: 'bg-yellow-50 border-yellow-200 text-yellow-900 dark:bg-yellow-950 dark:border-yellow-800 dark:text-yellow-100',
      },
    },
  }
);
步骤 8.3:加载/骨架屏状态
/* Skeleton pulse animation */
@keyframes pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.5; }
}

.skeleton {
  animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
  background-color: hsl(var(--muted));
  border-radius: 0.375rem;
}
// src/components/Skeleton.tsx
export function Skeleton({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
  return <div className={cn('skeleton', className)} {...props} />;
}

// Usage in loading states
export function CardSkeleton() {
  return (
    <div className="space-y-3">
      <Skeleton className="h-4 w-full" />
      <Skeleton className="h-4 w-3/4" />
      <Skeleton className="h-20 w-full" />
    </div>
  );
}
阶段 9:动画模式
步骤 9.1:CSS 过渡(轻量级)
.button {
  transition: background-color 200ms ease-in-out;
}

.button:hover {
  background-color: var(--primary-hover);
}

Tailwind:

<button className="transition-colors duration-200 hover:bg-primary-hover">
  Click me
</button>
步骤 9.2:Framer Motion(高级)

用于复杂动画和手势。

// src/components/AnimatedCard.tsx
import { motion } from 'framer-motion';

export function AnimatedCard() {
  return (
    <motion.div
      initial={{ opacity: 0, y: 20 }}
      animate={{ opacity: 1, y: 0 }}
      exit={{ opacity: 0, y: -20 }}
      transition={{ duration: 0.3 }}
      whileHover={{ scale: 1.02 }}
      whileTap={{ scale: 0.98 }}
    >
      <div className="card">Content</div>
    </motion.div>
  );
}

最佳实践:

  • UI 反馈动画时长控制在 300ms 以内
  • 使用缓动函数(ease-in-out、ease-out)
  • 遵循 prefers-reduced-motion 设置
  • 避免为会触发布局变化的属性添加动画(width、height)
  • 优先使用 transform 和 opacity(由 GPU 加速)
阶段 10:验证
步骤 10.1:运行样式验证脚本
bash scripts/validate-styling.sh .

完整的验证套件请参见 scripts/validate-styling.sh。

步骤 10.2:检查构建输出

验证 CSS 包大小,并确保没有未使用的样式。

precision_exec:
  commands:
    - cmd: "npm run build"
      expect:
        exit_code: 0
  verbosity: standard

检查项:

  • CSS 包大小(Tailwind 压缩后小于 50KB 为佳)
  • 没有重复的工具类
  • 正确执行 tree-shaking
  • 正确生成深色模式类
步骤 10.3:无障碍检查

确保颜色对比度符合 WCAG 标准。

precision_exec:
  commands:
    - cmd: "npx @axe-core/cli http://localhost:3000"
  verbosity: standard
步骤 10.4:视觉回归测试

通过自动化截图对比,发现意外的视觉变化。

使用 Playwright 进行视觉测试:

// tests/visual.spec.ts
import { test, expect } from '@playwright/test';

test('button variants match snapshots', async ({ page }) => {
  await page.goto('/components/button');
  
  // Take screenshot and compare
  await expect(page).toHaveScreenshot('button-variants.png', {
    maxDiffPixels: 100,
  });
});

test('dark mode toggle', async ({ page }) => {
  await page.goto('/');
  
  // Light mode
  await expect(page).toHaveScreenshot('home-light.png');
  
  // Toggle to dark
  await page.click('[data-testid="theme-toggle"]');
  await expect(page).toHaveScreenshot('home-dark.png');
});

Chromatic(用于 Storybook):

# Install and setup
npm install --save-dev chromatic

# Run visual tests
npx chromatic --project-token=<token>

Percy(跨浏览器):

import percySnapshot from '@percy/playwright';

test('responsive layout', async ({ page }) => {
  await page.goto('/dashboard');
  await percySnapshot(page, 'Dashboard - Desktop');
  
  await page.setViewportSize({ width: 375, height: 667 });
  await percySnapshot(page, 'Dashboard - Mobile');
});
步骤 10.5:CSS 清除配置

Tailwind v3+(使用 JIT): tailwind.config.ts 中的 content 路径即为清除配置。Tailwind 会在生产构建中自动移除未使用的类。

// tailwind.config.ts
export default {
  content: [
    './src/pages/**/*.{js,ts,jsx,tsx}',
    './src/components/**/*.{js,ts,jsx,tsx}',
    './src/app/**/*.{js,ts,jsx,tsx}',
  ],
  // JIT mode is default in v3+
};

生产构建优化:

# Build with minification
npx tailwindcss -i ./src/styles/globals.css -o ./dist/output.css --minify

# Check final size
du -h dist/output.css

动态类加入安全列表:

// tailwind.config.ts
export default {
  content: ['./src/**/*.{js,ts,jsx,tsx}'],
  safelist: [
    // Dynamic classes that can't be detected statically
    'bg-red-500',
    'bg-green-500',
    'bg-blue-500',
    // Or use patterns
    {
      pattern: /bg-(red|green|blue)-(400|500|600)/,
    },
  ],
};

监控包大小:

precision_exec:
  commands:
    - cmd: "npm run build"
    - cmd: "ls -lh dist/**/*.css"
  verbosity: standard

目标:清除无用样式后,典型 Tailwind 项目的压缩包大小小于 50KB。

常见反模式

不要:

  • 在同一组件中混用样式方案(Tailwind + CSS-in-JS)
  • 对静态值使用内联样式
  • 硬编码颜色/间距(使用设计令牌)
  • 忽略深色模式的对比度
  • 使用 !important 覆盖 Tailwind(应改为解决优先级问题)
  • 为宽度/高度添加动画(会导致布局抖动)
  • 忽略移动优先的响应式设计
  • 到处使用像素值(为提升无障碍性,优先使用 rem)

要:

  • 选择一种主要的样式方案并保持一致
  • 使用 CSS 变量表示动态值
  • 从一开始就实现设计令牌
  • 测试深色模式下的对比度和可读性
  • 遵循 Tailwind 的实用优先理念
  • 为提升性能,对 transform 和 opacity 添加动画
  • 采用移动优先设计,并针对更大屏幕进行优化
  • 使用相对单位(rem、em、%)以便灵活缩放

安全注意事项

CSP(内容安全策略)与样式:

默认安全(构建时):

  • Tailwind CSS - 在构建时编译,不会在运行时注入
  • CSS Modules - 在构建时编译,符合 CSP 要求
  • Vanilla CSS / SCSS - 作为静态文件提供

需要配置 CSP(运行时):

  • styled-components - 在运行时注入 <style> 标签
  • Emotion - 在运行时注入样式
  • 内联样式 - 可能需要 unsafe-inline 或 nonce

CSP 最佳实践:

// Next.js example with CSP for styled-components
import { getCspNonce } from './lib/csp';

export default function RootLayout({ children }) {
  const nonce = getCspNonce();
  return (
    <html>
      <head>
        <meta
          httpEquiv="Content-Security-Policy"
          content={`style-src 'self' 'nonce-${nonce}';`}
        />
      </head>
      <body>
        <StyleSheetManager nonce={nonce}>
          {children}
        </StyleSheetManager>
      </body>
    </html>
  );
}

防止 CSS 注入:

  • 切勿将未经清理的用户输入插入 CSS 值
  • 对用户可配置的颜色/主题使用允许列表
  • 应用前验证颜色值
// DON'T: Unsafe user input interpolation
const BadButton = ({ userColor }) => (
  <div style={{ backgroundColor: userColor }} /> // CSS injection risk!
);

// DO: Validate against allowlist
const ALLOWED_COLORS = ['primary', 'secondary', 'accent'] as const;
type AllowedColor = typeof ALLOWED_COLORS[number];

const SafeButton = ({ color }: { color: AllowedColor }) => (
  <div className={`bg-${color}`} />
);

// DO: Validate hex colors
function isValidHexColor(color: string): boolean {
  return /^#[0-9A-Fa-f]{6}$/.test(color);
}

快速参考

发现阶段:

discover: { queries: [tailwind, css_modules, css_in_js, tokens, dark_mode], verbosity: count_only }
precision_read: { files: [tailwind.config.ts, globals.css], verbosity: minimal }

配置阶段:

precision_exec: { commands: [{ cmd: "npm install -D tailwindcss postcss autoprefixer" }] }
precision_write: { files: [tailwind.config.ts, globals.css, tokens.ts], verbosity: count_only }

验证阶段:

bash scripts/validate-styling.sh .
npm run build

有关详细的决策树、令牌示例和特定框架的模式,请参阅 references/styling-comparison.md。

评论

支持 Markdown