diff --git a/.claude/skills/ui-components/SKILL.md b/.claude/skills/ui-components/SKILL.md index 0f0ea19c55..ebedcf8f48 100644 --- a/.claude/skills/ui-components/SKILL.md +++ b/.claude/skills/ui-components/SKILL.md @@ -60,6 +60,115 @@ skeleton rules but lives next to its feature. - **No hardcoded user-facing strings** — even in stories/tests. Route through the i18n hook (`useT`); wording conventions are the [`translation`](../translation/SKILL.md) skill. +## Composing pages — use the set, don't hand-roll + +The rules above build primitives; this builds _pages_ from them. **Feature pages and routes +(`services/platform/app/**`) compose design-system components — they do not emit raw layout HTML.** A +raw `
` on a page is the defect this prevents. + +- **Layout primitives** ([`@tale/ui/layout`](../../../packages/ui/src/components/layout/layout.tsx)): + vertical → `Stack`, horizontal → `Row`, responsive grid → `Grid`. They share one `gap` scale and take + `align`/`justify`/`wrap`, plus an `as` prop for semantic elements (``, + ``, ``) and `asChild` to merge onto a single child. `HStack`/`VStack` + are **deprecated aliases** of `Row`/`Stack`. A cluster of action buttons is the semantic `ActionRow`; + a titled section is `PageSection` (or `SettingsSection`). There is **no `Box`** — a neutral wrapper is + ``. +- **One spacing scale** — `gapScale` in + [`layout.tsx`](../../../packages/ui/src/components/layout/layout.tsx). Recommended steps: **`2`** field + group (label → control → hint), **`4`** within a section (the default), **`6`** loose grouping, **`8`** + between sections (the settings rhythm). Never a raw `gap-[…]` or `space-y-*` for layout. +- **Button size by context** + ([`button.tsx`](../../../packages/ui/src/components/primitives/button.tsx)): one height fits all — + **`default`** (`h-9`) for nearly everything (page/dialog/form/CTA actions) · **`sm`** (`h-8`) ONLY + for dense bars/toolbars (page-header action bars, table/card-header toolbars, the chat composer, + filter bars). There is **no `lg`**. Icon-only buttons are the same two heights, square — + **`icon`** (`size-9`) / **`icon-sm`** (`size-8`); prefer + [`IconButton`](../../../packages/ui/src/components/primitives/icon-button.tsx) (forces `aria-label`, + takes the same `size` axis) over a bare `