# SUB-019 , Sett Design (Design System Website + Component Library) Build Spec
> Status: forward-looking build spec, draft v1. Authored 2026-06-03. Synthesizes 4 research streams (Atlassian site IA, component build stack, token architecture, our docs gap analysis). Source of truth target: this vault file plus eventual Confluence mirror. Constraints baked in: React fixed, 1-2 dev team, no big-bang rewrites, every step shippable.
>
> WEBSITE EFFORT ON HOLD as of 2026-06-03 13:10 IDT. Ishai paused the public-website / hosting track. Focus shifted to working on the components themselves (against the existing /design-system route, which needs no DB or local-backend). The standalone-site scaffold below stays parked, ready to resume. Resume trigger: Ishai says to pick the website back up.
---
0. Status + Today's Progress (2026-06-03)
Decided + done today:- DS work happens on
cosmo-prodpackages/front/. Workflow: per-component branch off fresh main, edit component + showcase, PR for Leor. Synced local main toedc06cc(was 88 commits behind). - Audit of current DS: 23 primitives in
components/ui/, 2-pattern hybrid (A: CSS-class, B: cva+Tailwind, correctly distributed), only 6/23 showcased, ~19 files drift from Gali's syntax standards (React-namespace import +functionkeyword), flat tokens.css (~340 tokens), docs inpackages/front/CLAUDE.md+DESIGN.md(no author-a-component recipe; reference nonexistent.claudescaffolding). - Researched + wrote this full spec (Atlassian IA, Base-UI go-forward stack, 3-tier tokens, phased roadmap).
/design-systemis a normal route in the auth-gated SPA (App.tsx:10), wrapped in(main.tsx:13, Cognito). Already deploys tocosmo-staging.apisett.com/cosmo.apisett.comviadeploy-front.yml(path-filtered onpackages/front/), but a public visitor gets bounced to Cognito login. Not publicly accessible as-is.- Decision: make it a standalone static site (own build, no auth), hosted on Sett AWS (new public S3 + CloudFront), living inside cosmo-prod as
packages/design-system-site/, consuming the prod DS source so no drift. - Built locally as proof (NOT committed, NOT deployed, no PR, Leor not looped):
packages/design-system-site/scaffolded. Shares front's source via@frontalias to../front/src(zero duplication). No AuthProvider/QueryClient/router/backend. Google Fonts for Barlow/Red Hat Text/Rubik (front doesn't bundle them). Theme-init script copied for dark mode. Build clean (0 TS errors, 296kB JS + 159kB CSS). Dev atlocalhost:5175. Pattern B (button/badge) confirmed styled after fixing cross-package Tailwind@sourcescan. - Showcase still shows only the 6 primitives. Making it public-worthy = phases 2-4 below.
:5175 -> commit + PR adding the package -> AWS wiring (public S3 bucket + CloudFront + deploy-ds-site.yml) which is Leor/backend territory (Claude drafts the workflow + resource list, never touches AWS or deploys).
Pivot (current work): components themselves. The /design-system route + the standalone :5175 both render without DB / local Cosmo backend, so component styling work is unblocked even while Ishai lacks staging-DB access in his other session.
---
1. Vision
Sett Design is the single, browsable source of truth for every visual and interactive primitive across the Sett product portfolio (Cosmo V2 today, Apollo and future surfaces next). It is an in-app design-system website that consumes the exact same runtime components the products consume, so the docs can never drift from the code. It grows incrementally from what we have now (a flat token file plus 23 React primitives) into a layered, semantically-named token system and a fully-showcased, lifecycle-tracked component library, with zero flag-day rewrites.
---
2. Website Structure (IA)
Modeled on atlassian.design: a flat, section-first top bar; a per-section grouped left sidebar; a tabbed per-component page that splits design guidance from code.
2.1 Global navigation (top bar)
Left-to-right:
2.2 Sitemap
``
/ Sett Design home (brand, recent changes, jump-in cards)
/get-started install, import-the-package, author-a-component recipe
/foundations overview card grid (name + one-line definition each)
/foundations/tokens searchable token tables (see 2.4)
/foundations/color
/foundations/typography
/foundations/spacing
/foundations/radius
/foundations/elevation
/foundations/border
/foundations/motion
/foundations/iconography
/foundations/accessibility
/components overview card grid, grouped sidebar (see 2.3)
/components/`
2.3 Components sidebar groups
Grouped buckets (maps near 1:1 onto our existing families). Initial population from our 23 primitives, room to grow:
2.4 Per-component page anatomy (the headline structural change)
Each component page is
tabbed, four tabs in fixed order:1.
Examples , H1 (component name) + one-line description + maturity badge, then a horizontal rule, then live interactive demos ordered: Default to Variants (the "Appearance" group, H3 per variant) to States (Disabled / Selected / Loading) to Composition / edge cases. Closes with "Was this page helpful? Yes/No" widget + "Back to top". 2. Usage , design guidance: do/don't, when-to-use, alignment, cross-links to related components. 3. Code , Installation block (how to import from our internal package) + a Props/API table auto-derived from the component's TypeScript types (one [Description, Type] row group per prop). Never hand-maintained.
4. Changelog , version history surfaced from git per component.
2.5 Maturity vocabulary
A small inline badge next to the sidebar entry and page title. Three states only (do not build an alpha/beta/stable/deprecated matrix):
2.6 Token presentation
Tokens render as searchable, filterable tables. Theme-aware rows use side-by-side columns [Token + description, Light value, Dark value (, Wireframe value)]; fixed-scale tokens (type, space, radius, motion) use [Token + description, Value]. The top-bar Theme switcher drives the swatches. Click a token/value to copy.
---
3. Component Build Model
One decision per axis. Sized for 1-2 devs. This is an
evolution of the locked Gali Pruzansky frontend standards, not a replacement; the only net-new strategic call is the headless-layer direction.| Axis | Decision | One-line rationale | |---|---|---| |
Headless layer | Keep Radix for the primitives already working in Cosmo V2 + Apollo; standardize all new primitives on Base UI v1. Treat Radix to Base UI as a gradual planned migration, never an emergency rewrite. | Base UI is stable (Dec 2025), MUI-funded, faster-moving; Radix slowed post-WorkOS. Migration is mechanical (asChild to render prop). |
| Styling | Keep the existing hybrid: Pattern A (canonical CSS classes against semantic CSS-var tokens) for Cosmo-domain primitives; Pattern B (CVA + Tailwind 4 utilities) for low-level interaction primitives. No CSS-in-JS, no vanilla-extract. | A 2-person team should not pay a build/runtime CSS-in-TS tax; CSS custom properties already give runtime theming a static build cannot. |
| Variant API | CVA (base / variants / compoundVariants / defaultVariants), typed via VariantProps. | Type-safe, framework-agnostic, already mandated in the standards. |
| Component API | Compound components (Root/Trigger/Content) + slot/render-prop polymorphism. Drop forwardRef; accept ref as a normal prop (React 19). | React 19 passes ref as a prop; forwardRef is deprecated and slated for removal. |
| Syntax** | Arrow functions, inline ComponentNameProps interface, named exports on the declaration line, named react imports (import * as Radix* only inside ui/*Primitive.tsx), no React.FC, no useMemo/useCallback (React 19 compiler), no arbitrary Tailwind values, tokens only. | Already locked; reaffirmed here so Sett Design obeys its own rules. |
| Architecture | Two layers: components/ui/[Name]Primitive.tsx (base + a11y wiring, never imported at feature level) and components/shared/[Name].tsx (smart wrapper, all styling decisions). One component per file, CamelCase filename = component name. | The locked Apollo/Sett standard; carry it into all Sett Design work. |
| Docs / showcase | The in-app /design-system route (DesignSystemPage.tsx at localhost:5174) is the canonical showcase. Add Ladle only if isolated-component dev friction appears. Defer Storybook until headcount/addons justify it. Skip Histoire. | The showcase already doubles as a real DS consumer; that is the no-drift mechanism. |
| Distribution | Single internal workspace package imported directly by the apps. No npm publish, no release ceremony. | Only consumers are our own apps; instant propagation, one source of truth. Revisit a private shadcn-style registry only when an external team consumes the DS. |
| Versioning | Git history is the changelog. Track per-component maturity with the 3-state label (3.5 above). Adopt Changesets + semver the day the DS becomes a published package. | No versioned boundary exists yet; semver would be ceremony without payoff. |
| Testing | Vitest + Testing Library + jest-axe, focused on stateful interaction primitives (dialog, menu, popover, tabs): open/close, keyboard nav, focus, axe assertions. Wire into the npm run build / CI gate. Defer visual regression. | Minimum-viable suite; eyeball-via-/design-system covers the rest until scale forces Chromatic. |
| Props auto-doc | Derive the Code-tab Props table from TypeScript types (react-docgen-style), single source. | Mirrors Atlassian's Storybook+docgen principle without adopting Gatsby/Storybook; the showcase and API docs share one component source. |
---
4. Token Architecture
Our
tokens.css is already a healthy 2-tier system in disguise (oklch primitive palettes + semantic aliases) with correct per-[data-theme] theming. The move is an incremental rename plus a documented third tier inside the same file. Do NOT adopt Style Dictionary / Terrazzo / DTCG-JSON build steps , pure overhead for a single-platform, single-brand, runtime-themed CSS-var system. tokens.css stays the hand-authored source of truth.
4.1 Target three-tier model (Atlassian-style, documented in the file header)
- TIER 1 , Primitive (palette).
--ink-900, --orange-500, --cream-50. Raw oklch/hex. Never consumed outside tokens.css.
TIER 2 , Semantic / alias. --bg-app, --ink-soft, --accent, --status-success, --role-pg. The only tier components reference and the only tier themes override.
TIER 3 , Component. Rare; only when a component needs a token no semantic covers. Lives near the component's CSS section.
4.2 Conventions
- Naming (CSS vars cannot use dots, so hyphenated):
--color-text-subtle, --color-bg-accent, --status-success-bg, --role-pg. Namespace.usage.role.prominence in spirit.
Prominence ladder for any new role/status/accent tint: subtlest / subtle / (default) / bold. Name new tints consistently (--accent-bold, --accent, --accent-subtle, --accent-subtlest).
Theming invariant (preserve exactly): only TIER-1 palette + TIER-2 semantic tokens get overridden per [data-theme]. --space-*, --text-*, --radius-*, --font-*, --motion/--dur-* are declared once, never themed.
Consume-only-semantic rule (enforced in standards): feature/component code references TIER-2 only; TIER-1 primitives are private to tokens.css. This single discipline is what keeps theming working.
4.3 No-big-bang migration path
1. Document the three tiers in the
tokens.css header comment. Zero code change. Establishes the mental model.
2. **Fix the --chip-* palette first (highest leverage). Replace the 28 hardcoded hex values named-by-color with: TIER-1 primitives (--palette-blue-bg) + TIER-2 semantic chip aliases. This collapses the duplicated dark-mode chip override block into single-line semantic swaps and removes the file's only hardcoded-hex violation.
3. Rename via var() alias bridges, never flag-day. To move --s-on-track toward --status-success, add --status-success: var(--s-on-track), migrate consumers surface-by-surface, then retire the old name. Same for every rename.
4. Standardize the prominence vocabulary now while the set is small.
5. (Optional, low priority) Add composite text-style tokens (--text-style-heading-lg, --text-style-body, --text-style-label, --text-style-mono-sm) that bundle size + family + weight + line-height, so consumers stop re-assembling them. Borrow the DTCG composite-typography idea in spirit, not the JSON format.
---
5. Current State vs Target
| Dimension | Today | Target (this spec) |
|---|---|---|
| Primitives** | 23 React primitives in
components/ui/ | Same set, plus two-layer ui/*Primitive.tsx + shared/*.tsx discipline applied consistently; new ones on Base UI |
| Styling patterns | 2-pattern hybrid (A: CSS classes, B: CVA+Tailwind), real and intentional | Same hybrid kept and documented; default to A for domain primitives |
| Convention drift | ~19 files drift from the locked syntax rules (e.g. cap-bar.tsx uses export function + interface Props, contradicting arrow-fn / ComponentNameProps / named-export rules) | Zero drift; flagship reference example obeys the rules |
| Showcase coverage | 6 / 23 primitives showcased (button, badge, input, segmented-tabs, cap-bar, auditor-identity); 17 missing | 100% coverage, each on a 4-tab page; rule reconciled with reality |
| Tokens | Flat hyphenated names, no tier signal; --chip-* is 28 hardcoded hex named-by-color, duplicated in dark mode; correct per-theme override model already in place | Documented 3-tier model, semantic naming, chip palette fixed, theming invariant preserved |
| Component API | Mixed; some forwardRef, some export function | React 19 ref-as-prop, arrow fns, compound + CVA, uniform |
| Docs | CLAUDE.md + DESIGN.md accurate on edit zones, tokens, primitive table, Pattern A/B; but no author-a-component recipe, references nonexistent .claude scaffolding/skills/agents, frontend-development-standards.md not loadable from inside the repo, no testing/lifecycle/a11y/changelog guidance | Followable recipe, scaffolding marked aspirational or built, standards loadable in-repo, testing + lifecycle + a11y + changelog sections added |
| Site IA | Single stacked /design-system route + design-system/index.html, everything on one page | Top-bar IA, grouped sidebar, 4-tab per-component pages, token tables, theme-aware columns |
| Testing | None (no test/spec/stories files); "verify" = tsc + build + eyeball | Vitest + TL + jest-axe on stateful primitives, in CI gate |
| Lifecycle | No status levels; components.css (3523 lines) has DEPRECATED-2026-04-29 blocks with no removal policy | 3-state maturity (stable/experimental/deprecated), signaled in showcase + primitive table + CSS, with a removal policy |
---
6. What to Add to Our Docs
Concrete edits to
cosmo-prod/packages/front/CLAUDE.md and cosmo-prod/packages/front/DESIGN.md.
6.1
DESIGN.md
- Add a numbered Author-a-new-component recipe (port the workspace
cosmo-design-system.md 6-step flow into the project doc so a dev reading only packages/front has it): (1) tokens first; (2) grep components.css for an existing recipe; (3) write the CSS class (Pattern A) or CVA variants (Pattern B); (4) create components/ui/Name.tsx per syntax conventions; (5) add a showcase entry; (6) verify at the /design-system route; (7) tsc --noEmit + npm run build.
Add a components.css index/map: section name to consuming primitive to status (active/deprecated). Supports the "does a recipe already exist?" survey step against a 3500-line file.
Add a component-lifecycle section: define stable / experimental / deprecated, how deprecation is signaled (CSS comment + primitive-table badge + showcase badge), and the removal policy. Flag the existing DEPRECATED-2026-04-29 blocks for cleanup. Tie to a lightweight CHANGELOG.md in src/design-system (or keep git-as-changelog and say so explicitly).
Add a per-component accessibility checklist (required for non-Radix Pattern A primitives where you get no a11y for free): keyboard nav, focus trap + restore, ARIA roles/labels, contrast across light/dark/wireframe, prefers-reduced-motion honoring the --dur tokens.
Add testing guidance: state the current expectation (tsc + build + manual showcase verification) and the target (Vitest + TL + jest-axe on stateful primitives), naming where tests live.
Pick one canonical Pattern A reference that obeys the syntax rules (arrow fn, ComponentNameProps, named export on the declaration line) and update cap-bar.tsx to match, or re-point the cited reference. Today the flagship example contradicts the ban list.
6.2
CLAUDE.md
- Reconcile the showcase-sync rule with reality: either backfill the 17 missing primitives or soften to "interaction primitives documented via Radix upstream; domain primitives must have a showcase entry," and state current coverage explicitly. Pick the backfill path long-term.
- Fix doc-vs-reality drift on scaffolding: the
.claude/rules, .claude/skills (design-feature, build-component, preflight-check), .claude/agents (ds-auditor, reviewer) and the Vibe-design workflow do not exist under packages/front. Either build them or clearly mark those sections ASPIRATIONAL / NOT-YET-BUILT so no one hunts for absent tooling.
Make frontend-development-standards.md loadable in-repo: copy it into packages/front/.claude/rules/ (or inline its reuse table + syntax rules into CLAUDE.md) so the canonical "use Button not