Files
movq 92f005895c feat(composer): expand ui-system from placeholder to full interview
ui-system skill is no longer a placeholder. Full design-system and
interaction-design interviews with branching, defaults, batched
question clusters, and an 'accept default / skip' protocol so users
move through fast when they have taste and patient when they don't.

Design system interview covers: visual posture, color (with WCAG
contrast verification before writing), typography, spacing, radii,
shadows, density, visual motion, components, theming, and
accessibility tokens (focus ring, hit targets).

Interaction design interview covers: input model, state-surface
declarations per action class, motion posture and triggers,
breakpoints, i18n, four state postures (empty/loading/error/offline),
time formatting, keyboard policy, optimistic-update strategy with
rollback and conflict resolution, gestures, focus management,
notification patterns, selection, and drag-and-drop.

Reference banks for "make it look like Linear/Notion/Stripe/etc."
translate well-known products into starting tokens — user still
confirms each value, no silent invention.

Templates expanded to hold the full output:
- design-system/SKILL.md grew sections for accessibility tokens,
  density, dark-mode mapping, contrast verification, theming variants
- design-system/tokens.ts now exports color (light + dark), typography
  (family/size/weight/line-height/letter-spacing), spacing + density-
  aware padding tokens, radii, shadows, motion (durations + easings),
  z-index scale, breakpoints, hit-target minimums
- design-system/components.md gained anatomy/state/a11y coverage for
  10 primitives plus an "adding a new component" workflow and a
  "patterns to avoid" list (nested cards, color-only meaning, etc.)
- interaction-design/SKILL.md gained sections for optimistic updates,
  conflict resolution, gestures, focus management, notification
  patterns, selection/multi-select, and drag-and-drop

Hard rules: never invent on the user's behalf; <TBD> instead. Verify
contrast before writing color tokens. Always declare prefers-reduced-
motion as honored.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 21:10:36 -05:00

217 lines
6.0 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Design tokens for <PROJECT NAME>.
// Generated by Composer's ui-system phase.
// Single source of truth — colors, type, spacing, radii, shadows,
// density, motion, accessibility. Import this in mocks AND in real
// UI code; do not hardcode visual values.
// =============================================================================
// COLOR
// =============================================================================
export const color = {
brand: {
primary: "<TBD: #hex>",
// secondary: "<TBD>",
},
// Neutral scale, tone: <warm | cool | true>
neutral: {
50: "<TBD>",
100: "<TBD>",
200: "<TBD>",
300: "<TBD>",
400: "<TBD>",
500: "<TBD>",
600: "<TBD>",
700: "<TBD>",
800: "<TBD>",
900: "<TBD>",
950: "<TBD>",
},
semantic: {
success: "<TBD>",
warning: "<TBD>",
error: "<TBD>",
info: "<TBD>",
},
// Accessibility tokens
focus: "<TBD>", // focus-ring color
selection: "<TBD>", // text selection background
// Text on each neutral background, pre-verified for contrast
textOnLight: "<TBD>", // body text on neutral.50neutral.200 — must be ≥ 4.5:1
textOnDark: "<TBD>", // body text on neutral.800neutral.950 — must be ≥ 4.5:1
textMuted: "<TBD>", // secondary text — must be ≥ 4.5:1 for body, 3:1 for UI
} as const;
// Dark-mode overrides (only filled in if dark mode = yes/system).
// When dark mode is active, prefer these over `color.*`.
export const colorDark = {
brand: { primary: "<TBD>" },
neutral: { 50: "<TBD>", 100: "<TBD>", /* … */ 950: "<TBD>" },
semantic: { success: "<TBD>", warning: "<TBD>", error: "<TBD>", info: "<TBD>" },
} as const;
// =============================================================================
// TYPOGRAPHY
// =============================================================================
export const fontFamily = {
display: '"<TBD: name>", system-ui, -apple-system, sans-serif',
body: '"<TBD: name>", system-ui, -apple-system, sans-serif',
mono: 'ui-monospace, "SF Mono", Menlo, Consolas, monospace',
} as const;
// Modular scale — base × ratio
export const fontSize = {
xs: "12px",
sm: "14px",
base: "16px", // <TBD: confirm base>
lg: "18px",
xl: "20px",
"2xl": "24px",
"3xl": "30px",
"4xl": "36px",
"5xl": "48px",
} as const;
export const fontWeight = {
regular: 400,
medium: 500,
semibold: 600,
bold: 700,
} as const;
export const lineHeight = {
tight: 1.2, // display
normal: 1.5, // body
relaxed: 1.7, // long-form prose
} as const;
export const letterSpacing = {
tight: "-0.02em",
normal: "0",
wide: "0.04em",
} as const;
// =============================================================================
// SPACING
// =============================================================================
export const spacingBase = 4; // <TBD: 4 or 8>
export const spacing = {
0: 0,
0.5: 0.5 * spacingBase,
1: 1 * spacingBase,
1.5: 1.5 * spacingBase,
2: 2 * spacingBase,
3: 3 * spacingBase,
4: 4 * spacingBase,
6: 6 * spacingBase,
8: 8 * spacingBase,
12: 12 * spacingBase,
16: 16 * spacingBase,
24: 24 * spacingBase,
} as const;
// Padding tokens by density posture — components reference these
// rather than picking spacing values directly. Switch the global
// density by re-pointing `density` (or per-component override).
export const padding = {
tight: { x: spacing[2], y: spacing[1] },
default: { x: spacing[3], y: spacing[2] },
loose: { x: spacing[4], y: spacing[3] },
} as const;
export const density = "default" as keyof typeof padding; // <TBD: "tight" | "default" | "loose">
// =============================================================================
// RADII
// =============================================================================
export const radius = {
none: "0",
sm: "<TBD>",
md: "<TBD>",
lg: "<TBD>",
pill: "9999px",
full: "9999px",
} as const;
// =============================================================================
// SHADOWS
// =============================================================================
export const shadow = {
none: "none",
xs: "<TBD>",
sm: "<TBD>",
md: "<TBD>",
lg: "<TBD>",
xl: "<TBD>",
} as const;
// =============================================================================
// MOTION
// =============================================================================
export const motion = {
duration: {
instant: 0,
fast: 150,
default: 250,
slow: 400,
slower: 600,
},
easing: {
standard: "cubic-bezier(0.2, 0, 0, 1)",
accel: "cubic-bezier(0.4, 0, 1, 1)",
decel: "cubic-bezier(0, 0, 0.2, 1)",
bounce: "cubic-bezier(0.34, 1.56, 0.64, 1)",
},
} as const;
// =============================================================================
// Z-INDEX
// =============================================================================
export const zIndex = {
base: 0,
raised: 10,
dropdown: 100,
sticky: 200,
drawer: 300,
modal: 400,
popover: 500,
toast: 600,
tooltip: 700,
max: 9999,
} as const;
// =============================================================================
// BREAKPOINTS
// =============================================================================
// Note: breakpoint values live here for visual-token coordination, but the
// authoritative *interpretation* (mobile-first, primary target, etc.) is in
// the interaction-design skill.
export const breakpoint = {
sm: "640px",
md: "768px",
lg: "1024px",
xl: "1280px",
"2xl": "1536px",
} as const;
// =============================================================================
// HIT TARGETS (a11y)
// =============================================================================
export const hitTarget = {
mobile: 44, // px — WCAG 2.2 minimum
desktop: 32, // px — pointer-input minimum
} as const;