一键导入
design-system-implementation
Selecting, implementing, and extending UI component libraries and design systems — tokens, composition, theming, and accessibility.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Selecting, implementing, and extending UI component libraries and design systems — tokens, composition, theming, and accessibility.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Expert reference for application security — OWASP Top 10 mitigations, auth/authz, secrets management, cryptography, input validation, dependency hygiene, and secure-by-default code patterns
Expert reference for token counting, prompt compression, cost estimation, and quality preservation when optimizing prompts for Claude models
Experimentation design and A/B testing standards for product teams
Expert reference for digital accessibility — WCAG conformance, ARIA, and inclusive design patterns
Authoritative reference for agent architecture selection, multi-agent orchestration, tool design, memory systems, and failure mode prevention
Expert reference for evaluating LLM systems, RAG pipelines, and AI features in production
| name | design-system-implementation |
| description | Selecting, implementing, and extending UI component libraries and design systems — tokens, composition, theming, and accessibility. |
| version | 1 |
backgroundColor: '#3B82F6' in a component is a bug, not a style. backgroundColor: 'var(--color-action-primary)' is correct. Token names encode semantic intent, not appearance.@storybook/addon-a11y) run on every story. A component is not "done" until its Storybook story documents all variants, states, and usage constraints.aria-label. Modals must trap focus and restore focus on close. Form inputs must have associated <label> or aria-labelledby. Select/combobox components must follow the ARIA authoring practices for listbox or combobox patterns. These are not enhancements — they are part of the component contract.import { Button } from 'antd' without proper tree-shaking imports the entire library. MUI (Material UI) core: ~300KB tree-shaken for a typical component set. Shadcn/ui adds ~0KB to bundle size (code is copied into the repo). Bundle impact is evaluated before any library is adopted.value (controlled) and defaultValue (uncontrolled) without explicit documentation of the contract will produce undefined behavior when consumers accidentally mix both.class="dark") manipulates the variables; JavaScript objects or CSS-in-JS runtime style injection for theme values are secondary approaches that break SSR hydration consistency.babel-plugin-import or antd/es imports.sx prop and styled() API. Ant Design: className + CSS Modules, or theme.components in ConfigProvider. Shadcn/ui: modify the copied source directly. Never use !important unless the library itself injects inline styles that cannot be overridden otherwise.[data-theme="dark"] attribute on <html> or :root.dark, not separate style sheets. Separate stylesheets require a flash-preventing server-side cookie read; a single stylesheet with attribute selectors is zero-FOUC by default if the attribute is set before paint.--[category]-[concept]-[variant]-[state]. Examples: --color-action-primary-hover, --spacing-component-gap-sm, --radius-card-default. Never use appearance-based names like --color-blue or --space-16 in semantic tokens. Primitives may use appearance names; semantic tokens never do.The Three-Tier Token Architecture
Design tokens exist in three tiers. Tier 1 — Primitive tokens: the complete set of all values the system is allowed to use. --color-blue-500: hsl(217, 91%, 52%). These are referenced only by Tier 2 tokens, never by components. Tier 2 — Semantic tokens: map intent to primitives. --color-action-primary: var(--color-blue-500). These are the tokens components reference. Tier 3 — Component tokens (optional, for large systems): --button-primary-bg: var(--color-action-primary). Tier 3 enables per-component theming without breaking the semantic layer. Theme changes update Tier 2 mappings only. A "dark mode" is a new set of Tier 2 values; the Tier 1 palette and Tier 3 component references are unchanged.
The Accessibility Contract Model
Every component exposes an accessibility contract: a set of ARIA roles, properties, and keyboard interactions it guarantees to implement. This contract is separate from the visual contract. A button's visual contract: background color, border, label. Its accessibility contract: role=button, focusable, activatable with Enter and Space, communicates disabled state via aria-disabled not just opacity: 0.4. When extending or wrapping library components, the accessibility contract of the original must be preserved. Wrapping a <button> in a <div onClick> breaks the contract. The test: can a keyboard-only user and a screen reader user complete every action that a mouse user can?
The Compound Component Pattern
Complex components (Tabs, Accordion, Dropdown Menu, Dialog) expose their internal structure through composed sub-components rather than configuration props. This gives consumers precise control over layout and rendering without prop explosion. The alternative — <Tabs items={[{label: 'A', content: <X />}]} /> — collapses extensibility into an opaque config object. The compound pattern: <Tabs><Tabs.List><Tabs.Trigger value="a">A</Tabs.Trigger></Tabs.List><Tabs.Content value="a"><X /></Tabs.Content></Tabs>. State is shared implicitly via Context. Radix UI uses this model throughout; it is the correct model for any component with 3+ internal interactive parts.
The "Source of Truth" Cascade A healthy design system has a defined cascade: Figma design tokens (source) → Token JSON files (in repo) → CSS custom properties (consumed by components) → Storybook (living documentation) → Production application. A break at any point in this cascade means the system is lying to someone. Common breaks: (1) Figma tokens updated but JSON not regenerated (design and code diverge). (2) CSS variables defined correctly but Storybook stories use hardcoded color values (docs diverge from implementation). (3) Production app overrides CSS variables locally per page (application diverges from system). The cascade must be enforced: tokens are generated from Figma via a plugin (Tokens Studio, Style Dictionary), not hand-updated.
| Term | Precise Meaning |
|---|---|
| Design Token | A named variable storing a single design decision (color, spacing, radius) at the system level. Consumed by all components and platforms. |
| Primitive Token | A token representing a raw value from the design palette (--color-blue-500). Never referenced by components directly. |
| Semantic Token | A token expressing design intent (--color-action-primary). References a primitive. This is the layer components consume. |
| Compound Component | A component pattern where the parent and child sub-components share implicit state via React Context, exposing compositional control to consumers. |
| Headless Component | A component that provides behavior and accessibility with zero visual output. The consumer supplies all rendering via render props or slots. Examples: Radix UI, Headless UI. |
| Controlled Component | A component whose value is entirely managed by the parent via props (value + onChange). No internal state for the controlled value. |
| Uncontrolled Component | A component that manages its own internal state, optionally initialized by defaultValue. The parent queries the DOM ref for the current value if needed. |
| Tree-Shaking | Dead-code elimination during bundling — unused exports are removed from the bundle. Requires ES module syntax (import/export, not require). Ant Design requires babel-plugin-import for reliable tree-shaking. |
| Style Dictionary | An open-source tool (by Amazon) that transforms design token JSON into platform-specific outputs: CSS custom properties, iOS Swift constants, Android XML. The standard for multi-platform token pipelines. |
| Tokens Studio | A Figma plugin that manages design tokens inside Figma files and syncs them to a JSON file in a git repository. The standard for keeping Figma and code tokens in sync. |
| Focus Trap | A pattern that confines keyboard focus within a modal or overlay while it is open. Required for accessible dialogs. Implemented via focus-trap library or Radix's built-in behavior. |
| FOUC | Flash of Unstyled Content. In design systems: the moment before CSS variables are applied, showing default browser styles or wrong theme values. Prevented by setting the theme attribute server-side before HTML is painted. |
Mistake 1: Importing entire component libraries without tree-shaking
import { Button, Table, DatePicker, Form } from 'antd';
Without babel-plugin-import, this imports all of Ant Design (~1.5MB JS).// With babel-plugin-import configured, OR:
import Button from 'antd/es/button';
import Table from 'antd/es/table';
// Each import now resolves only the specific component chunk.
Mistake 2: Using appearance-based names in semantic tokens
:root {
--blue: hsl(217, 91%, 52%);
--red: hsl(4, 86%, 52%);
}
.button-primary { background: var(--blue); }
.error-text { color: var(--red); }
Dark mode: --blue needs to change to a lighter blue, but now every component using --blue needs auditing.:root {
--color-blue-500: hsl(217, 91%, 52%); /* Primitive */
--color-action-primary: var(--color-blue-500); /* Semantic */
--color-feedback-error: var(--color-red-600); /* Semantic */
}
[data-theme="dark"] {
--color-action-primary: var(--color-blue-300); /* Only this changes */
}
Mistake 3: Wrapping a library component in a div and losing accessibility
const MyButton = ({ onClick, children }) => (
<div className="btn" onClick={onClick}>{children}</div>
);
This is not a button. It is not keyboard-focusable, not activated by Enter/Space, not announced as a button by screen readers.<div> to avoid fighting the browser's default button styles.const MyButton = ({ onClick, children }) => (
<button type="button" className="btn" onClick={onClick}>
{children}
</button>
);
Reset browser button styles with CSS (all: unset; display: inline-flex; cursor: pointer). Never replace interactive HTML elements with divs.Mistake 4: Hard-coding theme values in Storybook stories
// story.tsx
export const Primary = () => (
<Button style={{ backgroundColor: '#3B82F6', color: '#fff' }}>Click me</Button>
);
The story documents the wrong thing — a hardcoded value that does not reflect the token system.export const Primary = () => <Button variant="primary">Click me</Button>;
// The visual output is entirely controlled by CSS variables in the token file.
Mistake 5: Applying component-level overrides with !important
.my-modal .ant-modal-content {
border-radius: 16px !important;
}
!important wins the cascade today; breaks silently on Ant Design version upgrades if the internal class name changes.!important as a shortcut.// Ant Design 5.x ConfigProvider
<ConfigProvider theme={{ components: { Modal: { borderRadiusLG: 16 } } }}>
<App />
</ConfigProvider>
Component API Design — Controlled vs Uncontrolled
Bad (mixed, undocumented):
interface InputProps {
value?: string; // controlled
defaultValue?: string; // uncontrolled
onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
}
// No documentation of which mode is active or what happens if both are passed.
Good (explicit, documented):
// Controlled variant — parent owns state
interface ControlledInputProps {
value: string;
onChange: (value: string) => void;
}
// Uncontrolled variant — component owns state
interface UncontrolledInputProps {
defaultValue?: string;
onBlur?: (value: string) => void;
}
type InputProps = ControlledInputProps | UncontrolledInputProps;
// TypeScript enforces the contract: you cannot pass both value and defaultValue.
Theming Override — MUI
Bad:
/* globals.css */
.MuiButton-containedPrimary {
background-color: #7C3AED !important;
border-radius: 8px !important;
}
Overrides break on any MUI update that changes the class naming convention.
Good:
// theme.ts
import { createTheme } from '@mui/material/styles';
const theme = createTheme({
palette: {
primary: {
main: 'hsl(262, 83%, 58%)', // --color-brand-500
},
},
components: {
MuiButton: {
styleOverrides: {
root: {
borderRadius: '8px',
textTransform: 'none',
fontWeight: 600,
},
},
},
},
});
Compound Component Pattern
Bad (opaque config prop):
<Tabs
items={[
{ key: '1', label: 'Profile', children: <Profile /> },
{ key: '2', label: 'Settings', children: <Settings /> },
]}
activeKey={activeTab}
onChange={setActiveTab}
/>
// Consumer cannot inject custom elements between tab triggers or reorder the list structure.
Good (compound pattern with Radix):
<Tabs.Root value={activeTab} onValueChange={setActiveTab}>
<Tabs.List>
<Tabs.Trigger value="profile">Profile</Tabs.Trigger>
<NotificationBadge count={3} /> {/* Consumer injects freely */}
<Tabs.Trigger value="settings">Settings</Tabs.Trigger>
</Tabs.List>
<Tabs.Content value="profile"><Profile /></Tabs.Content>
<Tabs.Content value="settings"><Settings /></Tabs.Content>
</Tabs.Root>
--color-action-primary), never appearance (--color-blue)[data-theme] attribute on <html>@storybook/addon-a11y passes on all stories with zero violations at the "error" severity levelaria-label; modals implement focus trap and restore focus on close<div onClick> or <span onClick> replaces a semantic interactive element anywhere in the codebase!important is absent