Add design/ contract extracted from the as-built UI

Inventory the as-built recaps.cc look and distill it into a durable,
vendor-neutral design contract: design/DESIGN.md (nine-section brand
brief) + design/tokens.tokens.json (W3C DTCG tokens), plus brand icon
and provenance notes. Canonical calls reconciled with the owner:
indigo #818cf8 as the single interactive accent, purple #a855f7 for
premium only, the #0a0e1a->#111827->#0f172a surface ladder, and a
normalized type scale. Wire the AGENTS.md Design line and record the
contract-vs-code drift as a cleanup backlog in ROADMAP.md.
This commit is contained in:
Keysat
2026-06-16 23:08:41 -05:00
parent c9ad731860
commit 1741fb11a5
7 changed files with 486 additions and 0 deletions
+6
View File
@@ -5,6 +5,12 @@ YouTube + podcast summarizer + library, served as a single-page app from a Node.
> **Inbox check:** At session start, if `~/Projects/standards/INBOX.md` exists, scan it for
> items tagged `(recap)` and surface them before proposing next steps; triage with `/triage`.
> **Design:** before building or changing any user-facing UI, read `design/DESIGN.md` and
> `design/tokens.tokens.json` and conform to them. Accent is indigo `#818cf8`; purple
> `#a855f7` is premium-only. The same tokens govern three surfaces that must stay in sync:
> the main `public/index.html` stylesheet, its `SHARE_PAGE_CSS` share-export string, and
> `public/auth.html`.
## Stack
- **Server**: Node.js (`type: module`, ES modules). The dev box currently runs `v25.6.1`; container runtime is whatever the `Dockerfile` pins — check before assuming.
+38
View File
@@ -13,6 +13,44 @@ Longer-term backlog for Recaps. Near-term in-flight work and known issues live i
- **Decide the Max tier-quota default.** The relay code default is `max.monthly: null` (unlimited) → cards render "Unlimited" on a fresh install. The operator set `max.monthly: 120` on their box via the Adjust-Tier-Quotas action (so cards show 120 there). Decide whether a metered number (e.g. 120) should be the shipped default in `recap-relay/server/config.js` — note it also enforces the ceiling, not just the card label.
- **Add Gemini 3.5 to model selection.** First have a research agent confirm which stable Gemini model versions are actually available and the correct model id/name before wiring anything. The model list is duplicated server + client (provider config under `server/providers/` + the model picker in `public/index.html`) — add the option in lockstep, like the URL-parser convention. Coordinate with the matching relay-side capture (the relay routes Gemini, so its model list must agree). — captured 2026-06-16
## Design-contract conformance cleanup (from the 2026-06-16 `/design` extract)
The `design/` contract (`design/DESIGN.md` + `design/tokens.tokens.json`) was extracted
from the as-built UI and reconciled with Grant on 2026-06-16. The code is **structurally
aligned** (right surface ladder, accent system, premium-purple, components) but a set of
legacy values survived the reconciliation and now read as off-contract drift. None are
release-blocking; all are mechanical token migrations. `design-checker` found seven
categories (counts approximate, from grep) across the three styling surfaces — the main
`public/index.html` `<style>` block, its `SHARE_PAGE_CSS` string, and `public/auth.html`.
**Edit all three in lockstep** (the SHARE_PAGE_CSS string carries its own copies that a
main-stylesheet grep won't catch). Do NOT do this in the same pass as the contract itself.
- **Legacy accent indigo → `#818cf8`** (~12 hard + ~16 tint). `#6366f1`/`#4f46e5`/`#4338ca`
fills/borders/hovers and `rgba(99,102,241,…)` tints → `#818cf8`/`#a5b4fc` + `rgba(129,140,248,…)`.
Biggest cluster is the activation screen (`index.html:1213-1216`, `:1565,1576,1579`) which
uses all three demoted values in one component; also inline buttons `:6496,7776,8183`,
`.license-block` `:1593,1601`.
- **Blue `#3b82f6` as a primary interactive color → indigo** (auth ×4 + index ×8). `auth.html`
is the worst offender — its primary button + input focus are blue (`auth.html:99,105-117`);
also wallet/sign-up/grant/password buttons (`index.html:5228,5763,8151,8163,7609,12384`).
Blue stays only for info/status + speaker chips.
- **Legacy dark backgrounds → ladder** (~10). `#0a0e17``#0a0e1a` (`:1156,1243,8662`);
`#0b1120``#0a0e1a` (`:1191` + SHARE_PAGE_CSS `:11111`); `#020617``#0f172a` (`:1562`);
and the auth sub-palette `#121828``#111827`, `#1f2942``#1e293b` (`auth.html:47-48,83`,
`index.html:400-401`).
- **Stray heading near-white `#f5f9ff``#f1f5f9`** (×9; 5 in `auth.html:59,63,87,94,96`,
4 in index `:418,660,6335,6343`).
- **Off-scale font sizes → nearest step** (~23). `15→16`, `24→22`, `9→10`, `12.5/11.5→12`,
`10.5→10` (e.g. `index.html:84,899,907`, `auth.html:86,110`).
- **Off-scale weights** (×3): `650→600` (`:1008`, SHARE_PAGE_CSS `:11133`), `680→700`
(SHARE_PAGE_CSS `:11113`).
- **Off-scale radii → snap** (~18). `3→4`, `5→6`, `7→6|8`, `9→8|10`, `11→10|12` (scrollbars,
history/queue action buttons, mobile submit/menu, buy inputs).
- **(stretch) Consolidate inline styles behind CSS custom properties.** ~447 inline `style=`
attrs + duplicated values are why this drift accumulates; a `:root` token block (or
`design/brand/palette.css` generated from the tokens) would make the contract enforceable
instead of advisory. Big diff — schedule deliberately.
## Known debt (P2, from the 2026-06-14 full-eval — `EVALUATION.md`)
Real but not release-blocking for self-host. The P0/P1 findings from the same eval were fixed 2026-06-15 (see git log + `EVALUATION.md`).
+213
View File
@@ -0,0 +1,213 @@
# Recap — Design brief
The durable brand contract for Recap's user-facing UI. Read this and
`design/tokens.tokens.json` before building or changing any UI. This was
**extracted** from the as-built `recaps.cc` interface (no prior guidelines), then
**reconciled** with the owner on 2026-06-16 — so where the live code still
disagrees with a value here, *this file is the intent* and the code is the cleanup
backlog (see `ROADMAP.md`). Provenance: there was no design tool or export; the
source is the shipped `public/index.html`, `public/auth.html`, and the app icon.
---
## 1. Visual theme
A **dense, dark, information-first developer-tool aesthetic.** Restraint over
decoration: small type, tight radii, thin hairline borders, flat panels on a
near-black navy field, with the indigo accent used sparingly to mark what's
interactive or active. It should read like a fast, technical instrument — closer
to a code editor or an ops dashboard than a consumer media app. Calm, quiet, and
legible at high density; never glossy, never playful.
The brand mark (app icon) is a **play-triangle filled with a blue→purple gradient
over four light "transcript" lines**, on the dark navy field — the product in one
glyph: *press play on a video, get the text back.* That blue→purple gradient is
the origin of the whole accent story (§2).
Voice: precise, plain-spoken, unhyped. The UI explains tersely and trusts the
user. What it is **not**: not gradient-heavy, not glassmorphic beyond a light
overlay blur, not rounded-and-bubbly, not light-mode, not framework-flashy.
## 2. Color palette
Built almost entirely on the **Tailwind Slate + Indigo** ramps over a custom
near-black navy base. Canonical roles below; full values in
`tokens.tokens.json`.
**Surface ladder (warm/cool split — the agreed system).**
| Role | Value | Where |
|---|---|---|
| Base | `#0a0e1a` | page, body, sticky top bars, full-height side panels (history sidebar, log drawer). Also the PWA `theme_color`. |
| Card | `#111827` (warm gray-900) | raised cards, modals, popovers, toasts, icon-buttons, pipeline steps, skeletons. |
| Inset | `#0f172a` (cool slate-900) | recessed fields & list rows: settings/key inputs, queue items, subscription items, model buttons, expanded-chunk bg. |
| Raised field | `#1e293b` | the **primary URL input only** — deliberately lighter than the inset so the hero field pops. |
> Legacy darks **`#0a0e17`, `#0b1120`, `#020617`** are near-duplicates that
> leaked in; fold them into the nearest rung (`#0a0e17`/`#0b1120` → Base,
> `#020617` → Inset). Don't add new background darks.
**Accent — indigo (THE single interactive accent).**
- `#818cf8` (indigo-400) — **the accent.** Fills (submit button, active tab/icon,
processing badge) and marks (links, focus ring, active state, timestamps,
expanded-chunk arrow).
- `#a5b4fc` (indigo-300) — accent hover (lighter), active-line emphasis.
- Indigo tints `rgba(129,140,248, .06.20)` — hover washes, active backgrounds,
the `0 0 0 3px …/.15` focus ring, the `0 4px 24px …/.3` submit glow.
- **Demoted:** `#6366f1` (indigo-500) and its hovers `#4f46e5`/`#4338ca` are
legacy dups — migrate them to `#818cf8`; do not introduce new ones.
**Premium — purple (RESERVED for paid/upgrade only).**
- `#a855f7` (purple-500) — upgrade button, highlighted tier, buy badge, primary
buy CTA. `#c084fc` hover; `#9333ea` deep.
- `#c4b5fd`/`#d8b4fe` (purple-300/200) — tier-badge & Pro text.
- Purple appears **nowhere** in non-premium UI.
**Text ramp (slate).**
`#e2e8f0` primary · `#f1f5f9` strong/headings (canonical near-white — fold the
stray `#f5f9ff` into this) · `#cbd5e1` running body copy · `#94a3b8` muted/
secondary · `#64748b` labels & placeholders · `#475569` faint meta/timestamps ·
`#334155` dimmest (doubles as the hover-border). `#fff` only on filled
accent/premium buttons.
**Borders.** `#1e293b` (slate-800) default hairline → `#334155` (slate-700) on
hover/active → `#475569` (slate-600) strongest hover. Borders, not shadows, are
the primary way surfaces are separated.
**Semantic status (consistent, intentional — keep).**
- Success/green: `#22c55e` base · `#4ade80` text · `#86efac` soft · `#16a34a` deep.
- Error/red: `#ef4444` base · `#f87171` text · `#fca5a5` soft · `#dc2626` deep.
- Warning/amber: `#fbbf24` base/dot · `#fcd34d` · `#fde68a` · `#f59e0b` (update btn).
- Info/blue: `#3b82f6` base · `#60a5fa` · `#93c5fd`. **Blue is status/info only**
(and the legacy auth-screen accent, which should migrate to indigo) — never a
primary interactive color in-app.
**Speaker chips (8-hue categorical set — intentional, keep as-is).** A→H:
red/blue/green/amber/purple/sky/pink/slate, each as the *tinted triplet*
(background α≈.18, light-shade text, border α≈.35).
## 3. Typography
- **Sans (everything):** `-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
sans-serif`. System stack — **no web fonts.**
- **Mono (timestamps, license keys, URLs, code):** `ui-monospace, "SF Mono",
Menlo, Consolas, monospace`. One canonical stack — snap the near-variants to it.
- **Scale (px):** `10 · 11 · 12 · 13 · 14 · 16 · 18 · 20 · 22 · 28`.
- 1011 — micro labels, badges, meta, section labels, pills, hints.
- 12 — secondary UI default (buttons, labels, stats, list titles).
- 13 — body default (descriptions, list items, transcript-adjacent).
- 14 — emphasis / larger body, drawer & settings headers.
- 16 — input text, modal `h2`, section heads.
- 18 — sub-headers, logo wordmark, large numerals.
- 2022 — headings, modal/activation titles, tier names.
- 28 — display (price). Snap strays `15→16`, `24→22`, fractionals
`12.5/11.5→12`, `10.5/9→10`.
- **Weights:** 400 normal · 500 medium · **600 semibold (the UI default)** · 700
bold (headings, badges) · 800 extrabold (tier badge, big numerals). Snap
`650→600`, `680→700`.
- **Line-height:** 1.5 body · 1.55 denser reading blocks · 1.31.4 titles · 1.25
large titles · 1 single-line badges.
- **Letter-spacing:** 0 default. Uppercase micro-labels track `0.05em`; badges
`0.04em`; tier/section labels `0.060.08em`; one display title tightens
`-0.01em`.
## 4. Component styling
- **Buttons.** *Primary:* filled `#818cf8`, white text, radius 810px, weight
600, hover lifts `translateY(-1px)` + indigo glow; disabled → `#1e293b` bg /
`#475569` text. *Secondary:* `#1e293b` fill, `#334155` border, `#94a3b8`→
`#cbd5e1` text. *Icon/ghost:* `#111827` (or transparent), `#1e293b` border,
muted glyph; hover lightens bg + border; `.active` → accent fill. *Premium:*
filled `#a855f7`.
- **Inputs.** Inset `#0f172a` (or `#1e293b` for the hero URL input), `#334155`/
`#1e293b` border; focus → accent border + `0 0 0 3px rgba(129,140,248,.15)`
ring. Mono font for keys/codes; placeholders `#64748b`/`#475569`.
- **Cards.** `#111827` bg, `#1e293b` border, radius 1014px; border lightens on
hover; expanded/active → accent border + faint `0 2px 16px …/.06` accent glow.
- **Pills / badges / chips — the "tinted triplet".** Background at α≈.1,
text in the light shade, border at α≈.2.45 of the same hue. Status pills,
tier badges, `queue-from` tags, clip badges, and speaker chips all follow this
one rule.
- **Modals.** `#111827` (settings) / `#0f172a` (buy) bg, radius 16px, sticky
header with `1px #1e293b` bottom border, `slideUp` entrance, scrim
`rgba(0,0,0,.6)` + `backdrop-filter: blur(4px)`.
- **Toasts.** Top-right stack, `#1e293b` bg, `#334155` border, slide-in from
right, auto-fade.
- **Spinner.** 3px ring, `#1e293b` track, `#818cf8` top, `spin 0.8s linear`.
- **Pipeline / tracker.** Stepped pills: idle neutral, `.active` → accent
tint+border, `.done` → green tint+border.
## 5. Layout
- **Single fluid column.** `.container` `max-width:100%`, padding `36px 24px`
(landing) tightening to `16px 24px` (results) and down on mobile.
- **Results = split screen.** `.results-left` 58% (video/player, sticky top) +
`.results-right` (scrolling chunk list), 16px gap; stacks vertically <900px.
- **Persistent left history sidebar**, 320px, *pushes* content on desktop /
overlays on mobile. **Right log drawer**, 440px, slides in.
- **Dense vertical rhythm.** Card margin 14px, chunk margin 6px, gaps 616px.
- **Spacing steps (de-facto):** `4 · 6 · 8 · 10 · 12 · 14 · 16 · 20 · 24` for
controls/gaps, `28 · 32 · 36` for section padding. Not a strict 4/8 grid —
dense and organic by design; prefer these steps over new in-between values.
## 6. Depth / elevation
**Flat by default — separation comes from 1px borders, not shadows.** Shadows are
reserved for things that genuinely float:
- Overlays/drawers/modals: `0 8px 24px …/.3` (toast) · `0 8px 32px …/.4` (video,
side drawers `±8px 0 32px`) · `0 12px 32px …/.5` (menu) · `0 20px 60px …/.5`
and `0 24px 64px …/.5.6` (panel, settings/buy modal). All `rgba(0,0,0,α)`.
- **Accent glows** signal primary/active: submit `0 4px 24px rgba(129,140,248,.3)`;
focus ring `0 0 0 3px rgba(129,140,248,.15)`; expanded chunk `0 2px 16px …/.06`;
premium tier `0 12px 40px rgba(168,85,247,.25)`.
- Overlay scrims use `rgba(0,0,0,.4.65)` + `backdrop-filter: blur(46px)`.
- Drag/drop affordance is an accent line: `box-shadow: 0 ±2px 0 0 #818cf8`.
## 7. Do's and don'ts
**Do**
- Use `#818cf8` as the *single* interactive accent; reserve purple `#a855f7`
strictly for premium/upgrade.
- Separate surfaces with 1px `#1e293b` borders that lighten to `#334155` on hover;
keep shadows for true overlays only.
- Build every status/category chip as the tinted triplet (bg α.1 / light text /
border α.2).
- Keep type dense — 1213px body, 1011px meta — with 600 as the default weight.
- Follow the surface ladder: base `#0a0e1a` → card `#111827` → inset `#0f172a`.
- Use the system font stack; mono only for timestamps / keys / URLs.
**Don't**
- Don't introduce new `#6366f1`/`#4f46e5` indigos — they migrate to `#818cf8`.
- Don't put blue `#3b82f6` on primary interactive elements; blue is info/status
(and legacy auth) only — auth should move to indigo.
- Don't add new background darks (`#0a0e17`/`#0b1120`/`#020617` are legacy dups).
- Don't use purple for non-premium UI, or the accent indigo for premium.
- Don't add fractional font sizes (12.5/11.5/10.5) or off-scale weights (650/680).
- Don't reach for a framework, web font, or heavy drop shadows — vanilla JS,
system fonts, flat-with-borders is the intentional language.
## 8. Responsive behavior
- **Breakpoints:** `900px` (primary: split→stack, sidebar push→overlay), `880px`
(tablet: top breadcrumb swaps to a hoisted mobile copy), `600px` (phone:
icon-only submit, hamburger menu, desktop toolbar/pills hidden), `640px` (share
export). Plus `≤900px landscape` → fullscreen video.
- **Mobile rules:** form inputs forced to `16px` (iOS auto-zoom guard); touch
targets 4448px; sticky top bar; full-width drawers/panels; hover-gated
controls pinned visible (touch has no hover). Use `100dvh` for full-height.
## 9. Agent prompt guide
When building or changing UI in this repo:
- Read `design/tokens.tokens.json` and pull values from it — don't eyeball new hex.
- Match the **dense dark vanilla-JS** style. The frontend is one file,
`public/index.html`, with two `<style>` blocks + ~447 inline `style=` attrs and
a render-into-`innerHTML` loop. No framework, no bundler, no web fonts — keep it
that way.
- Accent is `#818cf8`; purple is premium-only; follow the surface ladder; build
chips as the tinted triplet; system font for text, mono for timestamps/keys.
- **Three surfaces stay in sync:** the main app stylesheet, the `SHARE_PAGE_CSS`
string (the self-contained share export), and `public/auth.html`. A token change
must be reflected in all three. Auth currently uses the legacy blue accent —
new auth work should adopt indigo.
- Sanitize operator-internal strings at error boundaries (per `AGENTS.md`) — they
must never reach cloud users, design surfaces included.
+17
View File
@@ -0,0 +1,17 @@
# Brand assets
- **`icon.png`** — the app icon (1024×1024). A rounded-square (squircle) on the
dark navy field, with a **play-triangle filled by a blue→purple gradient** over
four light "transcript" lines. Used as the PWA icon (`manifest.json`
`/assets/icon.png`, also `assets/icon.png` / `icon.png` at repo root) and the
auth-screen logo. The gradient's blue end and purple end are the source of the
app's blue (info) and purple (premium) accents; the indigo `#818cf8` interactive
accent sits between them.
**Fonts:** none shipped — Recap uses the system font stack (`-apple-system, …`)
and a system monospace stack. No web-font files to vendor here.
**Palette:** the machine-readable source of truth is `../tokens.tokens.json`. No
generated `palette.css` yet — styles live inline in `public/index.html`;
consolidating them behind CSS custom properties is tracked as cleanup in the
repo `ROADMAP.md`.
Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

+18
View File
@@ -0,0 +1,18 @@
# Inspiration / provenance
This is an **extract** run (Case B): Recap had no prior design guidelines and no
design tool was used. There are no external reference images — the *de-facto* look
**is** the source, harvested directly from the shipped code on 2026-06-16:
- `public/index.html` — the whole app + landing (two `<style>` blocks + ~447
inline `style=` attributes).
- `public/index.html``SHARE_PAGE_CSS` — the self-contained share-export styles.
- `public/auth.html` — the magic-link auth screen.
- `public/manifest.json` — PWA name/colors/icons (`theme_color` = `#0a0e1a`).
- `design/brand/icon.png` — the app icon (the only first-class brand asset). Its
blue→purple gradient play-triangle is the origin of the accent story.
The conflict reconciliation (which dark, which indigo, the type scale, the
accent-hue strategy) was decided with the owner and is recorded in `../DESIGN.md`.
If real reference images are gathered later, drop them here with a one-line note
on what's liked about each.
+194
View File
@@ -0,0 +1,194 @@
{
"$schema": "https://tr.designtokens.org/format/",
"$description": "Recap design tokens (W3C DTCG). Extracted from the as-built recaps.cc UI and reconciled 2026-06-16. Canonical roles only; see design/DESIGN.md for usage. Composite shadows and tint scales are documented strings, not strict DTCG primitives.",
"color": {
"$type": "color",
"surface": {
"$description": "Warm/cool elevation ladder over the navy base. Legacy darks #0a0e17/#0b1120 fold into base; #020617 folds into inset.",
"base": { "$value": "#0a0e1a", "$description": "Page, body, sticky bars, full-height side panels. Also PWA theme_color/background_color." },
"card": { "$value": "#111827", "$description": "Warm gray-900. Raised cards, modals, popovers, toasts, icon-buttons, steps, skeletons." },
"inset": { "$value": "#0f172a", "$description": "Cool slate-900. Recessed fields & list rows: inputs, queue/sub items, model buttons, expanded chunk." },
"raised-field": { "$value": "#1e293b", "$description": "Primary URL input only — lighter than inset so the hero field pops." }
},
"border": {
"default": { "$value": "#1e293b", "$description": "Slate-800. Default hairline — primary surface separator." },
"hover": { "$value": "#334155", "$description": "Slate-700. Border on hover/active." },
"strong": { "$value": "#475569", "$description": "Slate-600. Strongest hover border." }
},
"accent": {
"$description": "Indigo — THE single interactive accent.",
"default": { "$value": "#818cf8", "$description": "Indigo-400. Submit/active fills, links, focus ring, timestamps, active marks." },
"hover": { "$value": "#a5b4fc", "$description": "Indigo-300. Accent hover (lighter), active-line emphasis." },
"legacy-500": { "$value": "#6366f1", "$description": "DEMOTED indigo-500 dup — migrate to accent.default. Do not introduce new." },
"legacy-600": { "$value": "#4f46e5", "$description": "DEMOTED legacy hover for #6366f1 — migrate away." }
},
"premium": {
"$description": "Purple — RESERVED for paid/upgrade UI only. Never in non-premium surfaces.",
"default": { "$value": "#a855f7", "$description": "Purple-500. Upgrade button, highlighted tier, buy badge, primary buy CTA." },
"hover": { "$value": "#c084fc", "$description": "Purple-400." },
"deep": { "$value": "#9333ea", "$description": "Purple-600. Pro-CTA hover." },
"text": { "$value": "#c4b5fd", "$description": "Purple-300. Tier-badge & Pro text." },
"text-soft": { "$value": "#d8b4fe", "$description": "Purple-200." }
},
"text": {
"primary": { "$value": "#e2e8f0", "$description": "Slate-200. Primary body/UI text." },
"strong": { "$value": "#f1f5f9", "$description": "Slate-100. Headings/emphasis near-white. Canonical — fold stray #f5f9ff into this." },
"body": { "$value": "#cbd5e1", "$description": "Slate-300. Running body copy (transcript, descriptions, bullets)." },
"muted": { "$value": "#94a3b8", "$description": "Slate-400. Secondary/muted." },
"label": { "$value": "#64748b", "$description": "Slate-500. Labels & placeholders." },
"faint": { "$value": "#475569", "$description": "Slate-600. Faint meta, timestamps." },
"dim": { "$value": "#334155", "$description": "Slate-700. Dimmest text (doubles as border.hover)." },
"on-accent": { "$value": "#ffffff", "$description": "White — only on filled accent/premium buttons." }
},
"status": {
"$description": "Semantic status ramps. Use the tinted-triplet pattern for chips (see tint.*).",
"success": { "$value": "#22c55e" },
"success-text": { "$value": "#4ade80" },
"success-soft": { "$value": "#86efac" },
"success-deep": { "$value": "#16a34a" },
"error": { "$value": "#ef4444" },
"error-text": { "$value": "#f87171" },
"error-soft": { "$value": "#fca5a5" },
"error-deep": { "$value": "#dc2626" },
"warning": { "$value": "#fbbf24" },
"warning-soft": { "$value": "#fcd34d" },
"warning-faint": { "$value": "#fde68a" },
"warning-deep": { "$value": "#f59e0b" },
"info": { "$value": "#3b82f6", "$description": "Blue-500. Info/status only (+ legacy auth accent). Not a primary interactive color in-app." },
"info-mid": { "$value": "#60a5fa" },
"info-soft": { "$value": "#93c5fd" }
},
"speaker": {
"$description": "8-hue categorical set for diarized speaker chips AH. Rendered as the tinted triplet (bg α≈.18 / this text / border α≈.35).",
"a": { "$value": "#fca5a5", "$description": "red — base rgb(239,68,68)" },
"b": { "$value": "#93c5fd", "$description": "blue — base rgb(59,130,246)" },
"c": { "$value": "#86efac", "$description": "green — base rgb(34,197,94)" },
"d": { "$value": "#fcd34d", "$description": "amber — base rgb(245,158,11)" },
"e": { "$value": "#d8b4fe", "$description": "purple — base rgb(168,85,247)" },
"f": { "$value": "#7dd3fc", "$description": "sky — base rgb(14,165,233)" },
"g": { "$value": "#f9a8d4", "$description": "pink — base rgb(236,72,153)" },
"h": { "$value": "#cbd5e1", "$description": "slate — base rgb(100,116,139)" }
}
},
"tint": {
"$type": "color",
"$description": "Recurring translucent washes. Stored as documented rgba strings — they are tints of the named hues, not standalone primitives.",
"accent-wash": { "$value": "rgba(129,140,248,0.06)", "$description": "Hover/active wash of accent.default. Family: .06 .08 .10 .15 .20." },
"accent-active": { "$value": "rgba(129,140,248,0.08)", "$description": "Active background of accent.default." },
"accent-ring": { "$value": "rgba(129,140,248,0.15)", "$description": "Focus-ring color → box-shadow 0 0 0 3px." },
"chip-bg": { "$value": "rgba(34,197,94,0.10)", "$description": "Representative tinted-triplet background (α≈.10) — swap the hue per status/category." },
"chip-border": { "$value": "rgba(34,197,94,0.20)", "$description": "Representative tinted-triplet border (α≈.20)." },
"scrim": { "$value": "rgba(0,0,0,0.6)", "$description": "Overlay scrim behind modals; family .4 .5 .6 .65." }
},
"font": {
"family": {
"$type": "fontFamily",
"sans": { "$value": ["-apple-system", "BlinkMacSystemFont", "Segoe UI", "Roboto", "sans-serif"], "$description": "System stack — no web fonts." },
"mono": { "$value": ["ui-monospace", "SF Mono", "Menlo", "Consolas", "monospace"], "$description": "Timestamps, license keys, URLs, code. Canonical — snap near-variants to this." }
},
"size": {
"$type": "dimension",
"$description": "Normalized scale (px). Snap strays 15→16, 24→22, 12.5/11.5→12, 10.5/9→10.",
"10": { "$value": "10px", "$description": "micro labels, badges, meta, section labels" },
"11": { "$value": "11px", "$description": "small labels, pills, hints, queue meta" },
"12": { "$value": "12px", "$description": "secondary UI default — buttons, labels, stats" },
"13": { "$value": "13px", "$description": "body default — descriptions, list items" },
"14": { "$value": "14px", "$description": "emphasis / larger body, drawer & settings headers" },
"16": { "$value": "16px", "$description": "input text, modal h2, section heads" },
"18": { "$value": "18px", "$description": "sub-headers, logo wordmark, large numerals" },
"20": { "$value": "20px", "$description": "headings" },
"22": { "$value": "22px", "$description": "large headings, modal/activation titles, tier names" },
"28": { "$value": "28px", "$description": "display — price" }
},
"weight": {
"$type": "fontWeight",
"$description": "Snap 650→600, 680→700.",
"normal": { "$value": 400 },
"medium": { "$value": 500 },
"semibold": { "$value": 600, "$description": "The UI default weight." },
"bold": { "$value": 700, "$description": "Headings, badges." },
"extrabold": { "$value": 800, "$description": "Tier badge, big numerals." }
},
"lineHeight": {
"$type": "number",
"body": { "$value": 1.5 },
"reading": { "$value": 1.55, "$description": "Denser reading blocks." },
"title": { "$value": 1.3 },
"title-large": { "$value": 1.25 },
"single": { "$value": 1, "$description": "Single-line badges/controls." }
},
"letterSpacing": {
"$type": "dimension",
"label": { "$value": "0.05em", "$description": "Uppercase micro-labels." },
"badge": { "$value": "0.04em" },
"tracked": { "$value": "0.06em", "$description": "Tier/section labels (up to 0.08em)." },
"tight": { "$value": "-0.01em", "$description": "Large display titles." }
}
},
"radius": {
"$type": "dimension",
"$description": "Snap strays 3→4, 5→6, 7→6|8, 9→8|10, 11→10|12.",
"4": { "$value": "4px", "$description": "tiny chips, badges, drop-zones, skeleton lines" },
"6": { "$value": "6px", "$description": "default small controls — buttons, chips, menu items" },
"8": { "$value": "8px", "$description": "default medium — inputs, buttons, pills" },
"10": { "$value": "10px", "$description": "larger cards/chunks, status bars" },
"12": { "$value": "12px", "$description": "modals, popovers, menus" },
"14": { "$value": "14px", "$description": "big cards, video embed, activation card" },
"16": { "$value": "16px", "$description": "settings / buy modal" },
"18": { "$value": "18px", "$description": "listen panel" },
"pill": { "$value": "999px", "$description": "fully-rounded badges" },
"circle": { "$value": "50%", "$description": "avatars, dots, play controls, spinner" }
},
"space": {
"$type": "dimension",
"$description": "De-facto dense scale (px). Not a strict 4/8 grid; prefer these steps over new in-between values.",
"4": { "$value": "4px" },
"6": { "$value": "6px" },
"8": { "$value": "8px" },
"10": { "$value": "10px" },
"12": { "$value": "12px" },
"14": { "$value": "14px", "$description": "card margin" },
"16": { "$value": "16px", "$description": "split-screen gap" },
"20": { "$value": "20px" },
"24": { "$value": "24px", "$description": "container horizontal padding" },
"28": { "$value": "28px", "$description": "section padding" },
"32": { "$value": "32px" },
"36": { "$value": "36px", "$description": "landing container vertical padding" }
},
"shadow": {
"$type": "shadow",
"$description": "Flat-by-default — shadows only for floating surfaces. Composite/glow values kept as documented strings.",
"toast": { "$value": "0 8px 24px rgba(0,0,0,0.3)" },
"drawer": { "$value": "0 8px 32px rgba(0,0,0,0.4)", "$description": "Video embed; side drawers use ±8px 0 32px." },
"menu": { "$value": "0 12px 32px rgba(0,0,0,0.5)" },
"panel": { "$value": "0 20px 60px rgba(0,0,0,0.5)", "$description": "Listen panel, activation card." },
"modal": { "$value": "0 24px 64px rgba(0,0,0,0.5)", "$description": "Settings/buy modal (up to ...0.6)." },
"glow-accent": { "$value": "0 4px 24px rgba(129,140,248,0.3)", "$description": "Submit button — signals primary." },
"glow-accent-faint": { "$value": "0 2px 16px rgba(129,140,248,0.06)", "$description": "Expanded chunk." },
"ring-accent": { "$value": "0 0 0 3px rgba(129,140,248,0.15)", "$description": "Input focus ring." },
"glow-premium": { "$value": "0 12px 40px rgba(168,85,247,0.25)", "$description": "Highlighted premium tier." }
},
"motion": {
"$description": "Transitions are quick and functional. Durations as documented strings.",
"duration": {
"fast": { "$value": "0.15s", "$description": "Default control transition (transition: all 0.15s)." },
"base": { "$value": "0.2s", "$description": "Cards, layout shifts." },
"slow": { "$value": "0.4s", "$description": "Chunk-body max-height expand." }
},
"overlay-blur": { "$value": "blur(4px)", "$description": "Modal/overlay backdrop-filter; 46px range." }
}
}