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>
This commit is contained in:
@@ -1,52 +1,174 @@
|
||||
# Component vocabulary
|
||||
|
||||
The named components for <PROJECT NAME>. Match names from `ia.md`
|
||||
where applicable. Do not introduce new components without proposing
|
||||
them here first.
|
||||
The named components for <PROJECT NAME>. Component names match
|
||||
`ia.md` where applicable. **Do not introduce new components without
|
||||
proposing them here first.**
|
||||
|
||||
This file is loaded automatically by the design-system skill — agents
|
||||
generating UI should be aware of every component in this list and
|
||||
prefer them to ad-hoc one-offs.
|
||||
This file is a sidecar of the design-system skill — agents generating
|
||||
UI should be aware of every component in this list and prefer them to
|
||||
ad-hoc one-offs. New patterns belong here, not in mock files.
|
||||
|
||||
## How to read this
|
||||
|
||||
For each component:
|
||||
- **Variants** — distinct visual flavors with different semantic meaning
|
||||
- **Sizes** — usually `sm / md / lg`; pick the same scale across components
|
||||
- **States** — every visible state the component must handle
|
||||
- **Anatomy** — the named slots / regions the component is composed of
|
||||
- **Tokens used** — which design-system tokens this component reads
|
||||
- **A11y notes** — load-bearing accessibility rules for this component
|
||||
|
||||
## Primitives
|
||||
|
||||
### Button
|
||||
- **Variants**: primary | secondary | ghost | destructive | link
|
||||
- **Sizes**: sm | md | lg
|
||||
- **States**: default | hover | active | focus | disabled | loading
|
||||
- **Anatomy**: optional leading icon · label · optional trailing icon
|
||||
- **Motion**: <TBD>
|
||||
|
||||
- **Variants**: `primary` | `secondary` | `ghost` | `destructive` | `link`
|
||||
- **Sizes**: `sm` | `md` | `lg`
|
||||
- **States**: default | hover | active | focus | focus-visible | disabled | loading
|
||||
- **Anatomy**: optional leading icon · label · optional trailing icon · optional loading spinner
|
||||
- **Tokens used**: `color.brand.primary`, `color.semantic.error` (destructive), `color.focus`, `padding.default`, `radius.md`, `motion.duration.fast`, `fontSize.sm/base/lg`, `fontWeight.medium`
|
||||
- **A11y**:
|
||||
- Hit target ≥ `hitTarget.mobile` for `md+` sizes
|
||||
- `aria-busy` while loading; preserve label
|
||||
- `aria-disabled` rather than `disabled` if the action is permission-gated rather than form-invalid
|
||||
- `link` variant uses `<a>`, not `<button>`
|
||||
|
||||
### Input
|
||||
- **Variants**: text | textarea | select | checkbox | radio | toggle
|
||||
- **Sizes**: sm | md | lg
|
||||
- **States**: default | focus | error | disabled | readonly
|
||||
- **Anatomy**: label · field · helper text · error message
|
||||
- **Validation pattern**: <TBD — see interaction-design skill>
|
||||
|
||||
- **Variants**: `text` | `textarea` | `select` | `checkbox` | `radio` | `toggle` | `password` | `number` | `date`
|
||||
- **Sizes**: `sm` | `md` | `lg`
|
||||
- **States**: default | focus | filled | disabled | readonly | error | success *(rare)*
|
||||
- **Anatomy**: label · *(optional)* description · field · *(optional)* helper text · *(error or hint)* message
|
||||
- **Tokens used**: `color.neutral.*`, `color.semantic.error`, `color.focus`, `padding.default`, `radius.sm`, `fontSize.base`
|
||||
- **A11y**:
|
||||
- Label is **always** present (visible or `aria-label`)
|
||||
- Error message is `aria-describedby`-linked to the field
|
||||
- `aria-invalid="true"` while error
|
||||
- Required fields marked in two ways (asterisk + `required` attribute)
|
||||
- `aria-describedby` for helper text
|
||||
|
||||
### Card / Surface
|
||||
- **Variants**: <flat | bordered | elevated — pick or list>
|
||||
- **Anatomy**: <header? · body · footer?>
|
||||
|
||||
- **Variants**: `flat` | `bordered` | `elevated` *(per design-system shadow posture)*
|
||||
- **States**: default | hover *(if interactive)* | focus *(if interactive)* | disabled
|
||||
- **Anatomy**: optional header · body · optional footer · optional media region
|
||||
- **Tokens used**: `color.neutral.*`, `radius.lg`, `shadow.sm` (elevated), `padding.loose`
|
||||
- **A11y**:
|
||||
- If clickable, the entire card is one `<button>` or `<a>` — not nested interactives
|
||||
- If contains interactives, card itself is not clickable
|
||||
|
||||
### Badge / Tag
|
||||
|
||||
- **Variants**: `neutral` | `success` | `warning` | `error` | `info` | `brand`
|
||||
- **Sizes**: `sm` | `md`
|
||||
- **States**: default | *(removable)* with close button
|
||||
- **Anatomy**: optional dot · label · optional close button
|
||||
- **Tokens used**: `color.semantic.*`, `radius.pill`, `fontSize.xs`, `fontWeight.medium`
|
||||
|
||||
### Avatar
|
||||
|
||||
- **Variants**: `image` | `initials` | `placeholder`
|
||||
- **Sizes**: `xs` | `sm` | `md` | `lg` | `xl`
|
||||
- **States**: default | with status indicator
|
||||
- **Anatomy**: image / initials · *(optional)* status dot
|
||||
- **Tokens used**: `radius.full`, `color.neutral.*` (fallback bg), `fontSize.*` (initials sizing)
|
||||
- **A11y**: `alt` text required for image variant; meaningful or empty `alt=""` for purely decorative
|
||||
|
||||
### Tooltip
|
||||
|
||||
- **Variants**: `default` | `error` | `info`
|
||||
- **States**: hidden | shown
|
||||
- **Anatomy**: trigger · floating panel · optional arrow
|
||||
- **Tokens used**: `color.neutral.900` bg, `color.neutral.50` text, `radius.sm`, `shadow.md`, `motion.duration.fast`
|
||||
- **A11y**:
|
||||
- `role="tooltip"`, `aria-describedby` on trigger
|
||||
- Show on hover AND focus (keyboard parity)
|
||||
- Dismissible with `Esc`
|
||||
- Never the only conveyance of critical information
|
||||
|
||||
### Modal / Drawer / Popover
|
||||
|
||||
These are **behavioral patterns** — see the interaction-design skill
|
||||
for *when* to use which. The visual styling is here.
|
||||
|
||||
- **Modal**:
|
||||
- Anatomy: backdrop · panel · optional header · body · optional footer · close button
|
||||
- Tokens: `shadow.xl`, `radius.lg`, `motion.duration.default`, `zIndex.modal`
|
||||
- Always traps focus; restores on close
|
||||
- **Drawer**:
|
||||
- Anatomy: backdrop · panel (side-anchored) · header · body · footer · close button
|
||||
- Tokens: `shadow.xl`, `motion.duration.default`, `zIndex.drawer`
|
||||
- **Popover**:
|
||||
- Anatomy: trigger · floating panel · optional arrow
|
||||
- Tokens: `shadow.md`, `radius.md`, `zIndex.popover`
|
||||
|
||||
### Tabs
|
||||
|
||||
- **Variants**: `underline` | `pills` | `enclosed`
|
||||
- **States**: active | inactive | hover | focus | disabled
|
||||
- **Anatomy**: tab list · tab · panel
|
||||
- **A11y**:
|
||||
- `role="tablist"`, `role="tab"`, `role="tabpanel"`
|
||||
- Arrow-key navigation between tabs (per WAI-ARIA Authoring Practices)
|
||||
- Tab `aria-selected` and `tabindex` managed appropriately
|
||||
|
||||
### Menu / Dropdown
|
||||
|
||||
- **Variants**: `default` | `with-icons` | `with-shortcuts` | `submenu`
|
||||
- **States**: closed | open
|
||||
- **Anatomy**: trigger · floating panel · menu items · separators
|
||||
- **Tokens used**: `shadow.md`, `radius.md`, `padding.tight`, `zIndex.dropdown`
|
||||
- **A11y**:
|
||||
- `role="menu"`, `role="menuitem"`
|
||||
- Arrow-key navigation; `Esc` closes
|
||||
- Type-ahead is encouraged
|
||||
- Focus returns to trigger on close
|
||||
|
||||
### Toast / Notification
|
||||
|
||||
- **Variants**: `default` | `success` | `warning` | `error` | `info`
|
||||
- **Anatomy**: optional icon · title · optional description · optional action button · close button
|
||||
- **Tokens used**: `color.semantic.*`, `radius.md`, `shadow.lg`, `motion.duration.default`, `zIndex.toast`
|
||||
- **A11y**:
|
||||
- `role="status"` for non-critical, `role="alert"` for critical
|
||||
- Dismissible by user; don't auto-dismiss critical errors
|
||||
- Stack region is `aria-live="polite"` (or `assertive` for errors)
|
||||
|
||||
## Project-specific components
|
||||
|
||||
<!--
|
||||
Pulled from ia.md. One block each, with anatomy and any state-specific
|
||||
notes that should always apply when this component is rendered.
|
||||
notes that should always apply when this component is rendered. Match
|
||||
the format of the primitives above.
|
||||
-->
|
||||
|
||||
### <Component name>
|
||||
|
||||
- **Purpose**: <one line>
|
||||
- **Anatomy**: <slots / regions>
|
||||
- **Variants**: <list>
|
||||
- **Sizes**: <list>
|
||||
- **States**: <list>
|
||||
- **Notes**: <anything load-bearing>
|
||||
- **Anatomy**: <slots / regions>
|
||||
- **Tokens used**: <list>
|
||||
- **A11y**: <load-bearing rules>
|
||||
- **Notes**: <anything else>
|
||||
|
||||
## Patterns to avoid
|
||||
|
||||
<!--
|
||||
Things that come up often and should be vetoed. Saves the agent from
|
||||
re-inventing them.
|
||||
-->
|
||||
Things that come up often and should be vetoed by default. Saves the
|
||||
agent from re-inventing them.
|
||||
|
||||
- <TBD: e.g., "no nested cards", "no inline icons in body text", etc.>
|
||||
- **Nested cards** — flatten or use sections; visual hierarchy comes from spacing and dividers, not stacking surfaces
|
||||
- **Inline icons in body text** — icons in body text rarely scale or align well; use them in headers, buttons, badges
|
||||
- **Modal-from-modal** — if a modal needs to launch another modal, the first modal is doing too much
|
||||
- **Tooltip as the only conveyance** — tooltips are supplementary; the primary information must be available without hover
|
||||
- **Color-only meaning** — every color-coded distinction needs a redundant signal (icon, label, position)
|
||||
- **Custom focus rings that don't meet contrast** — use the system focus-ring token
|
||||
- **Animation that violates `prefers-reduced-motion`** — always honor the user preference
|
||||
- **Click targets below 44px on touch devices**
|
||||
|
||||
## Adding a new component
|
||||
|
||||
1. Propose it here first with the same shape as the others (variants, states, anatomy, tokens, a11y).
|
||||
2. Add it to `tokens.ts` if it needs new tokens.
|
||||
3. Add a mock that uses it before writing real-code implementation.
|
||||
4. Council review (Maya for a11y, Marge for end-user) before locking in.
|
||||
|
||||
Reference in New Issue
Block a user