SuperAPI AI Directory

styling-system

Frontend & websites

View repository

Updated 10-16 10:18

About this skill

Styling System

References: Tailwind CSS official, shadcn/ui practices, Stripe / Linear design systems, CSS Variables spec, Brad Frost design tokens.

Applicable Scenarios

  • Styling solution selection for new projects
  • Establishing a design token system
  • Theme switching (light / dark)
  • Responsive implementation
  • Component style encapsulation
  • CSS performance optimization

Core Principles

1. 设计 Token 优先
   颜色 / 间距 / 字体 / 圆角全用变量
  1. One styling solution Do not mix Tailwind + CSS-in-JS

  2. Local scoping Avoid global pollution (CSS Modules / Tailwind / CSS-in-JS)

  3. Responsive mobile-first Write for small screens first, override for large screens

  4. Consider dark mode from the start Switch with CSS variables

  5. No magic values Do not use padding: 17px, use padding: var(--space-4)

  6. Performance awareness

    • CSS-in-JS runtime overhead
    • Tailwind + PurgeCSS small bundle
    • vanilla-extract compile-time
  7. Use transforms for animation, not layout properties transform / opacity > top / left / width


## 样式方案对比

| 方案 | 类型 | 包大小 | 类型安全 | 主题切换 | 适合 |
|---|---|---|---|---|---|
| **Tailwind CSS** | 原子化 | 小(PurgeCSS)| ❌ | CSS 变量 | 主流首选 |
| **UnoCSS** | 原子化 | 极小 | ❌ | CSS 变量 | Vue/Nuxt |
| **CSS Modules** | 局部 | 中 | 部分 | CSS 变量 | 简单可靠 |
| **styled-components** | CSS-in-JS | 大(运行时)| TS | props 动态 | 老牌 |
| **Emotion** | CSS-in-JS | 中 | TS | props 动态 | 灵活 |
| **vanilla-extract** | 编译时 CSS-in-JS | 小 | 强 TS | 主题 | 类型安全 |
| **Linaria** | 编译时 | 小 | TS | 静态 | 性能 |
| **Sass / Less** | 预处理 | 中 | ❌ | 变量 | 老项目 |

### 推荐组合

```text
React 新项目(推荐):
  Tailwind CSS + shadcn/ui + Radix UI
  暗黑模式 + 主题切换全靠 CSS 变量

Vue 3 新项目(推荐):
  UnoCSS / Tailwind + Element Plus / Naive UI

设计系统强(自有库):
  vanilla-extract + 自有 Token + Headless UI

老项目兼容:
  CSS Modules + Sass

避免:
  ❌ Tailwind + styled-components 混用
  ❌ inline style 大量
  ❌ 全局 CSS 不限作用域

Design Token System

/* tokens.css */
:root {
  /* Colors - Semantic */
  --color-primary: hsl(220, 90%, 56%);
  --color-primary-foreground: hsl(0, 0%, 100%);
  --color-secondary: hsl(220, 14%, 96%);
  --color-success: hsl(142, 76%, 36%);
  --color-warning: hsl(38, 92%, 50%);
  --color-danger: hsl(0, 84%, 60%);
  --color-muted: hsl(220, 14%, 96%);
  --color-foreground: hsl(220, 9%, 9%);
  --color-background: hsl(0, 0%, 100%);
  --color-border: hsl(220, 13%, 91%);
  
  /* Spacing */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-6: 24px;
  --space-8: 32px;
  --space-12: 48px;
  
  /* Typography */
  --font-sans: 'Inter', system-ui, sans-serif;
  --font-mono: 'JetBrains Mono', monospace;
  --font-size-xs: 12px;
  --font-size-sm: 14px;
  --font-size-base: 16px;
  --font-size-lg: 18px;
  --font-size-xl: 20px;
  --font-size-2xl: 24px;
  
  /* Border */
  --radius-sm: 4px;
  --radius-md: 6px;
  --radius-lg: 8px;
  --radius-xl: 12px;
  --radius-full: 9999px;
  
  /* Shadow */
  --shadow-sm: 0 1px 2px rgba(0,0,0,0.05);
  --shadow-md: 0 4px 6px rgba(0,0,0,0.1);
  --shadow-lg: 0 10px 15px rgba(0,0,0,0.1);
  
  /* Animation */
  --duration-fast: 150ms;
  --duration-normal: 200ms;
  --duration-slow: 300ms;
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  
  /* Layout */
  --container-max: 1280px;
  --header-height: 64px;
  --sidebar-width: 240px;
  --z-modal: 50;
  --z-toast: 60;
  --z-tooltip: 70;
}

/* Dark mode */
[data-theme='dark'] {
  --color-foreground: hsl(220, 9%, 95%);
  --color-background: hsl(220, 9%, 9%);
  --color-muted: hsl(220, 9%, 15%);
  --color-border: hsl(220, 9%, 20%);
}

Tailwind CSS in Practice

// tailwind.config.js
import { fontFamily } from 'tailwindcss/defaultTheme';

/** @type {import('tailwindcss').Config} */
export default {
  darkMode: ['class'],  // 用 class 切换
  content: ['./src/**/*.{ts,tsx,vue}'],
  theme: {
    container: {
      center: true,
      padding: '2rem',
      screens: { '2xl': '1400px' },
    },
    extend: {
      colors: {
        // 用 CSS 变量(与 Token 对接)
        primary: {
          DEFAULT: 'hsl(var(--color-primary))',
          foreground: 'hsl(var(--color-primary-foreground))',
        },
        background: 'hsl(var(--color-background))',
        foreground: 'hsl(var(--color-foreground))',
        muted: 'hsl(var(--color-muted))',
        border: 'hsl(var(--color-border))',
      },
      fontFamily: {
        sans: ['Inter', ...fontFamily.sans],
      },
      borderRadius: {
        lg: 'var(--radius-lg)',
        md: 'var(--radius-md)',
        sm: 'var(--radius-sm)',
      },
    },
  },
  plugins: [
    require('@tailwindcss/forms'),
    require('@tailwindcss/typography'),
    require('tailwindcss-animate'),
  ],
};
// 组件中
<button className="
  inline-flex items-center justify-center
  px-4 py-2 rounded-md
  bg-primary text-primary-foreground
  hover:bg-primary/90
  focus-visible:ring-2 focus-visible:ring-ring
  disabled:opacity-50 disabled:cursor-not-allowed
  transition-colors
">
  Click me
</button>

class-variance-authority (cva) Pattern

import { cva, type VariantProps } from 'class-variance-authority';

const buttonVariants = cva(
  'inline-flex items-center justify-center rounded-md font-medium transition-colors focus-visible:ring-2',
  {
    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',
        link: 'text-primary underline-offset-4 hover:underline',
      },
      size: {
        sm: 'h-8 px-3 text-xs',
        default: 'h-9 px-4 text-sm',
        lg: 'h-10 px-8 text-base',
        icon: 'h-9 w-9',
      },
    },
    defaultVariants: { variant: 'default', size: 'default' },
  }
);

export interface ButtonProps
  extends React.ButtonHTMLAttributes<HTMLButtonElement>,
    VariantProps<typeof buttonVariants> {}

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

CSS Modules (Simple and Reliable)

// Button.module.css
.button {
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-md);
  background: var(--color-primary);
  color: var(--color-primary-foreground);
  transition: background var(--duration-normal) var(--ease-out);
}

.button:hover { background: hsl(220, 90%, 50%); }
.button.destructive { background: var(--color-danger); }
.button.lg { padding: var(--space-3) var(--space-6); }

// Button.tsx
import styles from './Button.module.css';
import { clsx } from 'clsx';

export function Button({ variant = 'default', size = 'md', className, children }) {
  return (
    <button className={clsx(styles.button, styles[variant], styles[size], className)}>
      {children}
    </button>
  );
}

vanilla-extract (Type-Safe + Compile-Time)

// button.css.ts
import { style, styleVariants } from '@vanilla-extract/css';
import { vars } from './theme.css';

const base = style({
  padding: `${vars.space[2]} ${vars.space[4]}`,
  borderRadius: vars.radius.md,
  transition: `background ${vars.duration.normal} ${vars.ease.out}`,
});

export const button = styleVariants({
  default: [base, { background: vars.color.primary }],
  destructive: [base, { background: vars.color.danger }],
  outline: [base, { border: `1px solid ${vars.color.border}` }],
});

Dark Mode Implementation

Option A: CSS Variables + class switching (recommended)

:root { --color-bg: white; --color-fg: black; }
[data-theme='dark'] { --color-bg: #0a0a0a; --color-fg: #f5f5f5; }

.app { background: var(--color-bg); color: var(--color-fg); }
// 主题切换 hook
function useTheme() {
  const [theme, setTheme] = useState<'light' | 'dark'>('light');
  
  useEffect(() => {
    const saved = localStorage.getItem('theme') as 'light' | 'dark';
    const system = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
    const initial = saved ?? system;
    setTheme(initial);
    document.documentElement.dataset.theme = initial;
  }, []);
  
  const toggle = () => {
    const next = theme === 'light' ? 'dark' : 'light';
    setTheme(next);
    document.documentElement.dataset.theme = next;
    localStorage.setItem('theme', next);
  };
  
  return { theme, toggle };
}

Option B: next-themes (Next.js)

import { ThemeProvider } from 'next-themes';

<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
  <App />
</ThemeProvider>

// 用法
const { theme, setTheme } = useTheme();

Responsive Strategy

Mobile-First:
  默认样式 → 手机
  sm: ≥ 640px
  md: ≥ 768px
  lg: ≥ 1024px
  xl: ≥ 1280px
  2xl: ≥ 1536px

Tailwind 范式:
  <div class="text-sm md:text-base lg:text-lg">
  
CSS:
  .title { font-size: 14px; }
  @media (min-width: 768px) { .title { font-size: 16px; } }
  @media (min-width: 1024px) { .title { font-size: 18px; } }

Workflow

1. 选样式方案(基于团队 + 项目)
   ↓
2. 建立设计 Token(与 UI/UX 同步)
   - 颜色 / 间距 / 字体 / 圆角 / 阴影
   ↓
3. 配置框架(Tailwind / Postcss)
   ↓
4. 暗黑模式从一开始
   ↓
5. 实现组件样式
   - 用 Token 不写魔术值
   ↓
6. 响应式(mobile-first)
   ↓
7. 性能验证
   - PurgeCSS / Tree-shake
   - 包大小

Supporting Templates

  • templates/styling-system-template.md — Design tokens + Tailwind config + dark mode + responsive

Quality Self-Check

□ 一种样式方案
□ 设计 Token 完整(与 UI/UX 同步)
□ 不写魔术值(颜色 / 间距 / 圆角)
□ 暗黑模式支持
□ 响应式 mobile-first
□ 焦点环可见(focus-visible)
□ 颜色对比度 ≥ 4.5:1
□ 动画 transform / opacity(不用 layout)
□ 减少 reflow
□ Tailwind / UnoCSS PurgeCSS 启用
□ 包大小(CSS < 50KB)
□ 不全局污染
□ 不混合方案

Common Pitfalls

  1. Mixed solutions——Tailwind + styled-components coexisting
  2. Hardcoded colors——color: #4287f5 instead of tokens
  3. No dark mode——high refactoring cost later
  4. CSS-in-JS runtime overhead——affects rendering performance
  5. No PurgeCSS——Tailwind bundle 3MB
  6. Inline style abuse——not reusable
  7. !important abuse——cascade collapse
  8. z-index magic values——z-index: 99999
  9. Animating with width / top——massive reflow
  10. Invisible focus——a11y failure
  11. Responsive desktop-first——mobile styles difficult
  12. CSS variables not using hsl——cannot adjust alpha
  13. Icons using png——should use SVG
  14. Large fonts without fallback——FOUT / FOIT

Collaboration with Other Skills

上游:
  ui-ux-designer 工作流 design-tokens skill → Token 来源
  ui-ux-designer atomic-design → 组件层级

下游:
  component-architecture → 组件接受 className
  accessibility-implementation → 焦点 / 对比度
  performance-optimization → 包大小

Related References

  • Project root references/frontend-tech-stack-guide.md — styling solution comparison
  • ui-ux-designer workflow design-tokens skill