Component primitives in-tree: Button / IconButton / Card / Badge / Avatar / Field / Input / Textarea / Select #139

Closed
opened 2026-06-19 17:34:10 +00:00 by james · 0 comments
Owner

Every current UI surface uses inline styles. The Carol DS design package (../carol-design/) is pixel-faithful to a primitive library: Button, IconButton, Card, Badge, Avatar, Field, Input, Textarea, Select on day one (plus Dialog, Toast, Tooltip, Progress, Tabs later). ADR-0012 implicitly punted "component library lands later" — this is the later.

Scope

  • ADR first. Decide and document the library route: in-tree primitives (recommended — matches the no-framework UI ethos and avoids new deps), Radix-skinned, or another headless lib. Capture the why; rejected alternatives in the ADR.
  • Day-one primitives under app/components/ui/, theme-token-driven via the new aliases from the token foundation ticket:
    • Button — variants primary | secondary | ghost; sizes sm | md | lg; leadingIcon / trailingIcon slots; fullWidth.
    • IconButton — same variants/sizes, single icon child, accessible label required.
    • Card — surface + border + radius; elevation="raised" | "hover" | undefined.
    • Badge — tones (neutral | accent | success | warning | danger | info), sizes (sm | md).
    • Avatar — initials, optional image, sizes (sm | md | lg | xl), shapes (circle | square).
    • Field — label + optional hint + error slot, wraps a single control.
    • Input / Textarea / Select — controlled, accessible, focus-ring via --ring.
  • Icons: add lucide-react as a devDep; run lavamoat.allowScripts review per ADR-0010 (lucide has no install scripts today; document the check).
  • Fonts: add Onest + JetBrains Mono via next/font/google self-hosted, weights as the DS calls for (display 600/700, body 400/500, mono 400/500/600).
  • Showcase route: /dev/components (proxy-gated, NODE_ENV !== "production") rendering every primitive variant in both themes for review.

Acceptance criteria

  • Each primitive renders correctly in light + dark on /dev/components.
  • No new lavamoat.allowScripts entry needed (or the allowlist is updated in the PR per ADR-0010).
  • ADR captured.
  • Existing screens still compile; nothing is rewritten to use the primitives yet.

Out of scope

  • Migrating existing screens — happens per-screen.
  • Heavier primitives (Dialog, Toast, Tooltip, Progress, Tabs) — file follow-ups once a screen needs them.

Composes with

Design package, token-foundation ticket above, ADR-0010 (install scripts), ADR-0012 (TanStack — primitives plug into TanStack Form's controlled inputs).

Every current UI surface uses inline styles. The Carol DS design package (`../carol-design/`) is pixel-faithful to a primitive library: `Button`, `IconButton`, `Card`, `Badge`, `Avatar`, `Field`, `Input`, `Textarea`, `Select` on day one (plus `Dialog`, `Toast`, `Tooltip`, `Progress`, `Tabs` later). ADR-0012 implicitly punted "component library lands later" — this is the later. ## Scope - **ADR first.** Decide and document the library route: in-tree primitives (recommended — matches the no-framework UI ethos and avoids new deps), Radix-skinned, or another headless lib. Capture the why; rejected alternatives in the ADR. - **Day-one primitives** under `app/components/ui/`, theme-token-driven via the new aliases from the token foundation ticket: - `Button` — variants `primary | secondary | ghost`; sizes `sm | md | lg`; `leadingIcon` / `trailingIcon` slots; `fullWidth`. - `IconButton` — same variants/sizes, single icon child, accessible label required. - `Card` — surface + border + radius; `elevation="raised" | "hover" | undefined`. - `Badge` — tones (`neutral | accent | success | warning | danger | info`), sizes (`sm | md`). - `Avatar` — initials, optional image, sizes (`sm | md | lg | xl`), shapes (`circle | square`). - `Field` — label + optional hint + error slot, wraps a single control. - `Input` / `Textarea` / `Select` — controlled, accessible, focus-ring via `--ring`. - **Icons:** add `lucide-react` as a devDep; run `lavamoat.allowScripts` review per ADR-0010 (lucide has no install scripts today; document the check). - **Fonts:** add Onest + JetBrains Mono via `next/font/google` self-hosted, weights as the DS calls for (display 600/700, body 400/500, mono 400/500/600). - **Showcase route:** `/dev/components` (proxy-gated, NODE_ENV !== "production") rendering every primitive variant in both themes for review. ## Acceptance criteria - [ ] Each primitive renders correctly in light + dark on `/dev/components`. - [ ] No new `lavamoat.allowScripts` entry needed (or the allowlist is updated in the PR per ADR-0010). - [ ] ADR captured. - [ ] Existing screens still compile; nothing is rewritten to use the primitives yet. ## Out of scope - Migrating existing screens — happens per-screen. - Heavier primitives (`Dialog`, `Toast`, `Tooltip`, `Progress`, `Tabs`) — file follow-ups once a screen needs them. ## Composes with Design package, token-foundation ticket above, ADR-0010 (install scripts), ADR-0012 (TanStack — primitives plug into TanStack Form's controlled inputs).
james closed this issue 2026-06-19 18:30:24 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
james/carol#139
No description provided.