# Theseus Design System
**`@proper/ds` — Editorial · Dark + Light**

---

## Overview

Theseus is the Proper design system. It descends from the Proper AI Design System v2.1 — Editorial Dark Mode — and extends it with a mechanically inverted light mode, a shadcn-compatible alias layer, and a canonical set of React components built with `class-variance-authority` and Radix UI.

The interface is editorial: Newsreader italic serif headings on a near-black ground (or near-white in light mode), with two reserved accents — **lavender** for structure and **gold** for quantification. Sharp 2px corners signal precision. Cards and surfaces stack by background steps, not shadows.

**Design Principles:**
1. **Editorial tone, dual ground** — Newsreader italic serif headings hold across both modes. Dark is the canonical mode; light is the mechanical inversion of every neutral, with all accents and semantic colors held identical.
2. **Lavender emphasizes, gold quantifies** — Lavender marks section structure, focus rings, and active state labels. Gold marks what matters most: hero numbers and KPI values. Never swap roles.
3. **Surfaces, not shadows** — Depth comes from background color steps (`bg-base` → `bg-surface-1` → `bg-surface-2` → `bg-surface-3`). Shadows are reserved for floating CTAs and the toggle glow.
4. **Sharp corners signal precision** — 2px (`--radius-sm`) is the default. Cards/dialogs lift to 8px. Pills (`--radius-full`) only for avatars and switch glow.
5. **Mono for data, sans for UI, serif italic for editorial** — JetBrains Mono for IDs and timestamps. Inter for all UI. Newsreader italic for titles, metric values, and section numbers.

---

## Color Tokens

All tokens live in `src/tokens/light.css` (`:root`) and `src/tokens/dark.css` (`.dark`). Accents and semantic colors **hold across modes**; only neutrals invert.

### Backgrounds

| Token | Dark | Light | Usage |
|---|---|---|---|
| `--bg-base` | `#0e0e0e` | `#ffffff` | Page / app background |
| `--bg-surface-1` | `#141414` | `#f7f7f7` | Cards, popovers, sidebars, primary panels |
| `--bg-surface-1-transparent` | `#141414a3` | `#f7f7f7a3` | Translucent overlays (mobile header backdrop) |
| `--bg-surface-2` | `#1e2020` | `#ededed` | Active nav items, hover states, tabs ground, switch tracks |
| `--bg-surface-3` | `#1f2020` | `#e4e4e4` | Icon containers, deepest-elevation accents |

> Surfaces follow a strict elevation model — the higher the layer, the more contrast against `bg-base`. Never skip levels.

### Text

| Token | Dark | Light | Usage |
|---|---|---|---|
| `--text-primary` | `#ffffff` | `#0e0e0e` | Headings, body text, active labels, brand logo |
| `--text-secondary` | `#9d9e9e` | `#525252` | Descriptions, secondary labels, table headers |
| `--text-tertiary` | `#ffffff29` | `#0e0e0e29` | Very subtle labels, section overlines |
| `--text-muted` | `#737373` | `#737373` | Timestamps, hints, low-priority metadata (held across modes) |
| `--text-dimmed` | `#525252` | `#9d9e9e` | Footer text, disabled-leaning content |
| `--text-on-accent` | `#1e2021` | `#0e0e0e` | Text on white/light button backgrounds (Primary CTA) |

### Borders

| Token | Dark | Light | Usage |
|---|---|---|---|
| `--border-subtle` | `#48484803` | `#00000003` | Sidebar right edge, very low-contrast dividers |
| `--border-default` | `#48484830` | `#0000000a` | Cards, inputs, dropdowns, general dividers |
| `--border-strong` | `#48484817` | `#00000017` | Hovered inputs, active card borders, scroll thumbs |
| `--border-light` | `#ffffff01` | `#00000001` | Bottom nav top edge, near-invisible separators |
| `--border-sidebar` | `#ffffff03` | `#00000003` | Sidebar right border (tinted to mode) |

### Accent — Lavender

| Token | Hex | Usage |
|---|---|---|
| `--accent-lavender` | `#c3c3ef` | Section heading numbers ("01."), focus rings, active mobile nav labels, active toggle glow |
| `--accent-lavender-muted` | `#b5b5e1` | Resource IDs, code highlights, secondary data references |

> **Usage rule:** Lavender marks structure and focus. Section numbers, ring states, active mobile nav. 2–3 lavender elements per screen, max.

### Accent — Gold

| Token | Hex | Usage |
|---|---|---|
| `--accent-gold` | `#c0a67c` | Hero metric values (MetricCard), KPI numbers, primary statistics |
| `--accent-gold-secondary` | `#b49a71` | Metric units adjacent to gold values (e.g., "USD" beside "$4,820") |

> **Usage rule:** Gold is reserved for quantitative data — the large numbers users scan first. Never use gold for navigation, chrome, or body text.

### Semantic

| Token | Hex | Tint Token | Usage |
|---|---|---|---|
| `--success` | `#22c55e` | `--success-tint` `#22c55e33` | Positive trends, "Paid", "On track", "OK" badges |
| `--error` | `#ef4444` | `--error-tint` `#ef444433` | Errors, destructive actions, "Overdue", sign-out |
| `--warning` | `#f59e0b` | `--warning-tint` `#f59e0b33` | Warnings, paused state, "At risk" |
| `--info` | `#3b82f6` | `--info-tint` `#3b82f633` | Informational state, neutral metadata badges |

> Tints are the same hex as the base color at `33` opacity (20%). Use tint for backgrounds, base for text/icons inside the badge.

### Alias Layer (shadcn compatibility)

Theseus exposes a shadcn-compatible alias layer so consuming apps can use `--background`, `--foreground`, `--primary`, etc. without learning new names.

| Alias | Resolves to |
|---|---|
| `--background` | `var(--bg-base)` |
| `--foreground` | `var(--text-primary)` |
| `--card` | `var(--bg-surface-1)` |
| `--card-foreground` | `var(--text-primary)` |
| `--popover` | `var(--bg-surface-1)` |
| `--popover-foreground` | `var(--text-primary)` |
| `--primary` | `var(--text-primary)` |
| `--primary-foreground` | `var(--text-on-accent)` |
| `--secondary` | `var(--bg-surface-2)` |
| `--secondary-foreground` | `var(--text-primary)` |
| `--muted` | `var(--bg-surface-2)` |
| `--muted-foreground` | `var(--text-secondary)` |
| `--accent` | `var(--bg-surface-2)` |
| `--accent-foreground` | `var(--text-primary)` |
| `--destructive` | `var(--error)` |
| `--destructive-foreground` | `var(--text-primary)` |
| `--border` | `var(--border-default)` |
| `--input` | `var(--border-default)` |
| `--ring` | `var(--accent-lavender)` |
| `--radius` | `var(--radius-sm)` |

---

## Typography

### Font Families

| Token | Value | Usage |
|---|---|---|
| `--font-heading` | `"Newsreader", Georgia, serif` | Brand logo, page titles, section headings, card titles, dialog titles, metric values |
| `--font-body` | `"Inter", system-ui, -apple-system, sans-serif` | All UI text — navigation, labels, descriptions, buttons, table content |

> **Newsreader is always italic.** The italic serif is the editorial signature. Never use Newsreader in upright/normal style.

### Type Scale

| Token | Size | Style | Font | Usage |
|---|---|---|---|---|
| `display` | 48–64px | italic | Newsreader | EditorialHeader h1, MetricCard hero values |
| `heading-lg` | 36px | italic | Newsreader | Mobile page titles, EditorialHeader (mobile) |
| `font-size-2xl` | 24px | italic | Newsreader | SectionHeading (md+), large card titles |
| `font-size-xl` | 20px | italic | Newsreader | SectionHeading (default), brand text, AppShell brand |
| `font-size-lg` | 18px | italic | Newsreader | DialogTitle, SheetTitle, in-card labels |
| `font-size-base` | 16px | normal | Inter | Body copy default |
| `font-size-sm` | 14px | normal | Inter | Buttons, inputs, tabs, table cells, descriptions |
| `font-size-xs` | 12px | normal | Inter | Badge text, MetricCard label (uppercase, tracking 1.2px), Sidebar nav links |
| `mono` | 11px | normal | JetBrains Mono | ResourceId, Timestamp, technical data |

### Tracking & Line Height

| Context | Letter-spacing | Line-height |
|---|---|---|
| EditorialHeader h1 | `-1.2px` | `1.0` |
| Page titles (Newsreader display) | `-1.2px` | `1.0` |
| Card / Dialog / Sheet titles | `-0.3px` to `-0.5px` | `1.0` (leading-none) |
| MetricCard value | `-1.2px` | `1.0` |
| Uppercase nav / labels | `1.2px` | default |
| Mobile uppercase nav | `1.0px` | default |
| Body / UI text | default | `1.5` (relaxed) |

---

## Spacing

Base unit is **4px**. All spacing values are multiples of 4. All tokens hold across themes.

| Token | Value | Common Usage |
|---|---|---|
| `--space-xs` | 4px | Icon gaps, tight internal padding, sidebar nav item gap |
| `--space-sm` | 8px | Badge padding, button gap, card title-to-description gap |
| `--space-md` | 16px | Input gap, ToggleGlow gap, MetricCard row gap, AppShell brand padding |
| `--space-lg` | 24px | Card padding, mobile horizontal margins, content padding |
| `--space-xl` | 32px | Sidebar top/bottom padding, between component groups, desktop content padding |
| `--space-2xl` | 48px | AppShell brand-to-nav spacing, between major sections |

### Layout Dimensions

| Element | Desktop | Mobile |
|---|---|---|
| Sidebar width | 256px (`w-64`) | N/A (bottom nav instead) |
| Sidebar padding | `[32, 16]` (`px-md py-xl`) | N/A |
| Brand-to-nav spacing | 48px (`pb-2xl`) | N/A |
| Header height | N/A | 64px (`h-16`) |
| Header padding | N/A | `[16, 24]` (`px-lg`) |
| Header background | N/A | `var(--bg-surface-1-transparent)` + backdrop blur |
| Content padding (horizontal) | 32px (`md:px-xl`) | 24px (`px-lg`) |
| Content padding (vertical) | 32px (`py-xl`) | 32px (`py-xl`) |
| Bottom nav height | N/A | 64px (`h-16`) |
| Bottom nav padding | N/A | `[0, 24]` (`px-lg`) |
| Account circle | 32×32 | 32×32 |

---

## Border Radius

| Token | Value | Usage |
|---|---|---|
| `--radius-sm` | 2px | Default — badges, switches (offset), tabs triggers, dropdown items, MetricCard, AppShell nav links |
| `--radius-toggle-track` | 4px | Switch track |
| *(Tailwind `rounded-md`)* | 6px | Buttons, inputs, dropdown menu container, tooltips |
| *(Tailwind `rounded-lg`)* | 8px | Cards, dialogs (sm:rounded-lg), AppShell container |
| `--radius-toggle-knob` | 3px | Switch knob |
| `--radius-full` | 9999px | Avatars, scroll thumbs |

> Two anchors: 2px for crisp UI elements, 9999px for circles. Buttons/inputs/popovers sit at 6–8px to feel tactile but never soft.

---

## Elevation & Shadow

| Token | Value | Usage |
|---|---|---|
| `--shadow-cta` | `0 25px 44px -12px rgba(0, 0, 0, 0.25)` | Primary CTA buttons (dramatic lift) |
| `--shadow-cta-secondary` | `0 8px 9px -6px rgba(0, 0, 0, 0.2), 0 20px 22px -5px rgba(0, 0, 0, 0.2)` | Secondary elevated buttons |
| `--shadow-toggle` | `0 0 7px rgba(195, 195, 239, 0.6)` | Active toggle knob lavender glow (Switch `variant="glow"`) |

Cards, surfaces, and overlays do **not** carry CTA shadows. Dialog and Sheet use a subtle `shadow-lg` for depth, but stacking is primarily communicated via background steps and the dim overlay (`bg-black/80`).

---

## Components

<!-- WARNING: manifest contains components without curated specs in COMPONENT_SPECS: NavItem, Logo. Update apps/studio/src/lib/generate-design-md.ts. -->

### Primitives (15)

#### Button
**File:** `src/primitives/Button.tsx`

| Variant | Background | Text | Border | Hover |
|---|---|---|---|---|
| `default` | `--primary` | `--primary-foreground` | none | `bg-primary/90` |
| `destructive` | `--destructive` | `--destructive-foreground` | none | `bg-destructive/90` |
| `outline` | `--background` | `--text-primary` | `1px --input` | `bg-accent` + `text-accent-foreground` |
| `secondary` | `--secondary` | `--secondary-foreground` | none | `bg-secondary/80` |
| `ghost` | transparent | `--text-primary` | none | `bg-accent` + `text-accent-foreground` |
| `link` | transparent | `--primary` | none | underline (offset 4) |

| Size | Height | Padding | Radius |
|---|---|---|---|
| `default` | 40px (`h-10`) | `[8, 16]` | 6px (`rounded-md`) |
| `sm` | 36px (`h-9`) | `[0, 12]` | 6px |
| `lg` | 44px (`h-11`) | `[0, 32]` | 6px |
| `icon` | 40×40 | — | 6px |

Font: 14px Inter `font-medium`. Focus: `ring-2 ring-ring` (`--accent-lavender`) + `ring-offset-2`. Disabled: `opacity-50`. Slot-enabled via `asChild`.

---

#### Card
**File:** `src/primitives/Card.tsx`

```
background:    --card (--bg-surface-1)
border:        1px --border (--border-default)
radius:        8px (rounded-lg)
text:          --card-foreground (--text-primary)
```

**Sub-parts:**
| Part | Style |
|---|---|
| `CardHeader` | `flex-col`, `space-y-1.5`, `p-6` (24px) |
| `CardTitle` | `h3`, **Newsreader italic**, `text-2xl` (28px), `leading-none`, `tracking-tight` |
| `CardDescription` | `p`, 14px Inter, `text-muted-foreground` (`--text-secondary`) |
| `CardContent` | `p-6 pt-0` (preserves header gap) |
| `CardFooter` | `flex items-center`, `p-6 pt-0` |

---

#### Input
**File:** `src/primitives/Input.tsx`

```
height:        40px (h-10)
padding:       [8, 12]
radius:        6px (rounded-md)
background:    --background
border:        1px --input (--border-default)
text:          14px Inter
placeholder:   --muted-foreground (--text-secondary)
focus:         ring-2 --ring (--accent-lavender) + ring-offset-2
disabled:      opacity-50, cursor-not-allowed
```

---

#### Label
**File:** `src/primitives/Label.tsx`

14px Inter `font-medium`. No padding, no background. Reads `peer-disabled` from a sibling input to dim with `opacity-70`.

---

#### Badge
**File:** `src/primitives/Badge.tsx`

| Variant | Background | Text |
|---|---|---|
| `default` | `--primary` | `--primary-foreground` |
| `secondary` | `--secondary` | `--secondary-foreground` |
| `destructive` | `--error` | `--text-primary` |
| `outline` | transparent + `1px --border-strong` | `--text-primary` |
| `success` | `--success-tint` | `--success` |
| `warning` | `--warning-tint` | `--warning` |
| `info` | `--info-tint` | `--info` |

Sizing: `[2, 10]` padding, `rounded-sm` (2px), 12px Inter `font-semibold`. Focus: `ring-2 ring-ring` + offset 2.

---

#### Separator
**File:** `src/primitives/Separator.tsx`

`bg-border` (`--border-default`). Horizontal: `h-[1px] w-full`. Vertical: `h-full w-[1px]`. `shrink-0` to survive flex containers. Decorative by default.

---

#### Avatar
**File:** `src/primitives/Avatar.tsx`

40×40, `rounded-full` (9999px), `overflow-hidden`. Three sub-parts:
- **Avatar** — root container
- **AvatarImage** — `aspect-square h-full w-full`
- **AvatarFallback** — flex-centered, `bg-muted` (`--bg-surface-2`) by default. Holds initials.

---

#### Switch
**File:** `src/primitives/Switch.tsx`

```
track:        44×22, rounded-[4px] (--radius-toggle-track)
              off: --border-default bg
              on:  rgba(255,255,255,0.04) bg
border:       1px --border-default
knob:         18×18, rounded-[3px] (--radius-toggle-knob), white fill
              translate-x: 0 (off) → 22px (on), with ml-[1px] offset
focus:        ring-2 ring-ring (--accent-lavender)
```

**Variant `glow`:** When checked, adds `--shadow-toggle` (lavender 7px blur) on the track.

---

#### Tabs
**File:** `src/primitives/Tabs.tsx`

```
TabsList:      h-10 (40px), p-1 (4px), bg-muted (--bg-surface-2),
               rounded-md (6px), inline-flex
TabsTrigger:   px-3 py-1.5 (12/6), rounded-sm (2px), 14px Inter font-medium
               active:   bg-background (--bg-base), text-foreground (--text-primary)
               inactive: inherits text-muted-foreground (--text-secondary)
TabsContent:   mt-2, focus-visible ring
```

---

#### Tooltip
**File:** `src/primitives/Tooltip.tsx`

```
background:    --popover (--bg-surface-1)
border:        1px --border
radius:        6px (rounded-md)
padding:       [6, 12]
text:          14px Inter, --popover-foreground (--text-primary)
sideOffset:    4px (default)
animation:     fade + zoom on open/close (data-state)
```

Requires `<TooltipProvider>` at app root.

---

#### DropdownMenu
**File:** `src/primitives/DropdownMenu.tsx`

```
content:       min-w-[8rem] (128px), p-1 (4), rounded-md (6),
               --popover bg, 1px --border, z-50, animations
item:          px-2 py-1.5 (8/6), rounded-sm (2), 14px Inter
               focus: bg-accent (--bg-surface-2), text-accent-foreground
               disabled: opacity-50, pointer-events-none
separator:     h-px bg-border, my-1 -mx-1 (negative margin to align with p-1)
```

---

#### ScrollArea
**File:** `src/primitives/ScrollArea.tsx`

```
root:          relative overflow-hidden
viewport:      h-full w-full, rounded-[inherit]
scrollbar:     w-2.5 (10px), p-[1px], border-l-transparent
thumb:         flex-1 rounded-full bg-border (--border-default)
```

---

#### Dialog
**File:** `src/primitives/Dialog.tsx`

```
overlay:       fixed inset-0, bg-black/80, z-50
content:       max-w-lg (32rem), centered via translate
               grid + gap-4 (16), p-6 (24)
               background --background, border --border
               sm:rounded-lg (8px), shadow-lg
               z-50, animate fade+zoom
```

**Sub-parts:**
| Part | Style |
|---|---|
| `DialogTitle` | `h3`, **Newsreader italic**, `text-lg` (18px), `leading-none`, `tracking-tight` |
| `DialogDescription` | `p`, 14px Inter, `text-muted-foreground` |

---

#### Sheet
**File:** `src/primitives/Sheet.tsx`

Side-anchored drawer. `side` prop: `top | bottom | left | right` (default `right`).

```
overlay:       fixed inset-0, bg-black/80, z-50, fade
content:       fixed z-50, p-6, gap-4, --background bg
               border per side (border-l for right, etc.)
               left/right: w-3/4 (75%) sm:max-w-sm (24rem)
               top/bottom: inset-x-0 full width
               animate slide-in/out per side, duration 300/500ms
```

`SheetTitle` mirrors `DialogTitle` styling (Newsreader italic 18px).

---

#### Table
**File:** `src/primitives/Table.tsx`

Wraps `<table>` in a `relative w-full overflow-auto` div for horizontal scroll. 14px Inter throughout.

```
TableHeader:   thead, [&_tr]:border-b
TableHead:     th, h-12 (48px), px-4, font-medium,
               text-muted-foreground (--text-secondary)
TableBody:     tbody, [&_tr:last-child]:border-0
TableRow:      tr, border-b
               hover: bg-muted/50 (--bg-surface-2 at 50%)
               selected: bg-muted (data-state)
TableCell:     td, p-4, align-middle
```

---

### Composites (7)

#### AppShell
**File:** `src/composites/AppShell.tsx`

The full app frame — desktop sidebar + main content + mobile header + bottom nav.

**Desktop (`md:` breakpoint and up):**
```
container:     min-h-screen
sidebar:       fixed inset-y-0 left-0, w-64 (256px),
               px-md py-xl (16/32),
               border-r --border-sidebar
brand:         Newsreader italic, 20px, --text-primary,
               tracking-[-0.5px], leading-[1.556], pb-2xl (48)
nav links:     uppercase 12px Inter font-bold, tracking-[1.2px],
               px-[12px] py-[12px], gap-xs (4) between items
               active:   bg-bg-surface-2, text-text-primary, rounded-sm
               inactive: text-text-secondary, opacity-60
account:       circle 32×32 at sidebar bottom (justify-between)
main:          md:pl-64 offset, px-xl (32) py-xl (32)
```

**Mobile (`md:hidden`):**
```
header:        sticky top-0 z-10, h-16 (64),
               px-lg (24), justify-between,
               bg --bg-surface-1-transparent + backdrop-blur
               left: hamburger / right: account circle
bottom nav:    fixed bottom-0 inset-x-0, h-16,
               px-lg (24), justify-around,
               border-t --border-light
               active:   bg-bg-surface-2, text-accent-lavender, rounded-sm
               inactive: text-text-secondary, opacity-60
               link text: uppercase 10px Inter font-bold, tracking-[1.0px]
```

---

#### MetricCard
**File:** `src/composites/MetricCard.tsx`

```
container:     bg-surface-1, border 1px --border-default, rounded-sm (2),
               p-lg (24), flex-col gap-sm (8)
value:         Newsreader italic, 48px (mobile) / 64px (md+),
               --accent-gold, leading-none, tracking-[-1.2px]
unit:          Newsreader italic, 32px,
               --accent-gold-secondary, ml-[2px] (optional)
label:         12px Inter, uppercase, tracking-[1.2px],
               --text-secondary
```

---

#### EditorialHeader
**File:** `src/composites/EditorialHeader.tsx`

```
layout:        flex-col, gap-sm (8)
h1:            Newsreader italic, 36px (mobile) / 48px (md+),
               leading-none, tracking-[-1.2px], --text-primary
subtitle:      14px Inter, --text-secondary (optional)
```

---

#### SectionHeading
**File:** `src/composites/SectionHeading.tsx`

```
layout:        flex items-baseline, gap-md (16)
number:        Newsreader italic, 20px, --accent-lavender,
               leading-none, zero-padded ("01.", "02.")
title:         Newsreader italic, 20px (mobile) / 24px (md+),
               --text-primary, leading-none
```

---

#### ResourceId
**File:** `src/composites/ResourceId.tsx`

`<code>` element. JetBrains Mono 11px, `--accent-lavender-muted`. No padding, no background. Use inline.

---

#### Timestamp
**File:** `src/composites/Timestamp.tsx`

`<time datetime="...">` element. JetBrains Mono 11px, `--text-muted`. Format defaults via `Intl.DateTimeFormat` → `Apr 14, 2026, 02:30 PM`. Override via `format` prop.

---

#### ToggleGlow
**File:** `src/composites/ToggleGlow.tsx`

```
container:     <label>, flex items-center justify-between, gap-md (16)
text:          14px Inter, --text-primary
switch:        Switch with variant="glow" (always on glow)
```

Auto-generates `id` via `useId` if not provided.

---

## Accent Color Application Guide

### Where lavender appears
- Section heading numbers ("01.", "02.")
- Active mobile bottom nav tab labels
- Focus rings (every primitive: Button, Input, Switch, Tabs, Badge)
- ResourceId text (muted variant)
- Active toggle glow shadow

### Where gold appears
- MetricCard hero values (large 48–64px Newsreader italic)
- KPI numbers in dashboards
- The first quantitative thing the eye lands on

### Where neither appears (stays neutral)
- Primary CTA buttons (`--text-primary` background, `--text-on-accent` text)
- Brand logo text ("Proper" stays `--text-primary`)
- Body copy and descriptions
- Table content (mono lavender-muted is the only exception, for IDs)
- Inactive nav labels (`--text-secondary` with opacity 0.6)

---

## Theme System

Theseus ships two themes via the standard CSS-variable swap:

```css
:root        { /* light tokens */ }
.dark        { /* dark tokens */ }
```

**Activate dark mode** by adding `class="dark"` to `<html>` or any ancestor. All tokens cascade. Components consume only the alias layer (`bg-background`, `text-foreground`, etc.) so they re-skin automatically.

**Mechanical inversion rule:** Light mode is derived from dark by inverting the neutral ramp (`#0e0e0e` ↔ `#ffffff`, surfaces step in matched intervals). Accents (`lavender`, `gold`) and semantic colors (`success`, `error`, `warning`, `info`) **hold identical** across themes — the editorial palette is theme-agnostic.

**App integration:** Consuming apps add a theme toggle of their choice. Theseus does not ship a `ThemeProvider` — that's the app's call (recommended: `next-themes` for React/Next, or a tiny `useTheme` composable elsewhere).

---

## Source of Truth & Regeneration

**Theseus is the canonical source.** Every other artifact is a projection.

| Artifact | Path | Regenerated from |
|---|---|---|
| React component code | `src/primitives/*.tsx`, `src/composites/*.tsx` | — (canonical) |
| Token CSS | `src/tokens/light.css`, `src/tokens/dark.css` | — (canonical) |
| Tailwind preset | `tailwind.preset.ts` | — (canonical) |
| Component manifest | `manifest.json` | `src/index.ts` exports |
| **This document** | `design-system.md` | All of the above |
| Pencil library | `Theseus.lib.pen` (in design tooling repo) | All of the above |
| Consuming-app design.md | `Properverse Design/docs/...` | This document + app-specific overlays |

**When Theseus changes, regenerate downstream:**
1. Edit the .tsx or .css source
2. Update this `design-system.md` (token tables + affected component sections)
3. Update the Pencil lib (variables + reusable components matching the new spec)
4. Notify consuming apps to re-pull the updated theme tokens

**App-level concerns that do NOT belong in Theseus:**
- Specific nav item names ("ARCHIVE", "MODELS", "LEDGER")
- Screen inventories
- Source-of-truth screen mappings
- Brand voice copy

Those live in the consuming app's docs.

---

## File Structure

```
theseus/
  packages/
    ds/                              ← @proper/ds — the canonical source
      src/
        tokens/
          light.css                  ← :root tokens
          dark.css                   ← .dark tokens
        fonts/
          fonts.css                  ← Inter, Newsreader, JetBrains Mono
        primitives/                  ← 16 components
          Button.tsx, Card.tsx, Dialog.tsx, ...
        composites/                  ← 7 patterns
          AppShell.tsx, EditorialHeader.tsx, MetricCard.tsx, ...
        styles.css                   ← imports tokens + fonts + reset
        index.ts                     ← public exports
      tailwind.preset.ts             ← consume in app's tailwind.config
      manifest.json                  ← component map for tooling
  apps/
    studio/                          ← reference app
    accountant/                      ← stub proving @proper/ds lifts cleanly
  design-system.md                   ← This document
```

---

*Theseus — `@proper/ds` — generated 2026-04-22T02:08:16.537Z*
