Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
26 changes: 14 additions & 12 deletions frontend/design-system/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,39 +80,41 @@ Commonly speaks to developers building with AI agents. The voice is **plainly te

### Color

- **One accent**: blue `#2f6feb`. Never blends with another. The accent appears as filled buttons, the active nav row, the active tab underline (2px), the LEAD badge, link text, the focus ring, and the unread counter.
- **One accent**: blue `#2f6feb`. Never blends with another. Filled buttons are **ink** `#111827` (hover `#1f2937`); blue is for links, the mention mark, the active nav row and tab underline (2px), the LEAD badge, the unread counter and the **focus halo**. One accent still — it no longer paints buttons (Sam, 2026-09-03, TASK-122).
- **Neutrals do most of the work**: `#111827` (text), `#4b5563` (secondary), `#7b8494` (tertiary), `#8a93a3` (muted/placeholder).
- **Backgrounds layer subtly**: page `#f8f8fb` → main pane `#ffffff` → tinted card/inspector `#f4f3f8`. The shifts are tiny on purpose.
- **Backgrounds layer as one tint step**: shell `#f1f1f4` behind rail, pods and inspector; the content pane is an inset white card with a 1px `#e5e7eb` ring and 14px radius; inside the card the ground is white. `#f8f8fb` remains the page canvas behind the shell.
- **Semantic** colors stay desaturated: success `#10b981`, warning `#f4a23a`, danger `#ef4444`. Info is `#0891b2` (cyan, deliberately off-axis from accent blue). Always paired with their `*-soft` background tint for badges/chips.
- **Agent role tints** (pink/violet/amber/emerald/sky/rose) are reserved for **avatar backgrounds and role chips** — never for chrome.

### Type

- **SF-first stack**: `"SF Pro Text", -apple-system, BlinkMacSystemFont, "SF Pro", "Helvetica Neue", "Segoe UI", "Inter", Roboto, sans-serif`. San Francisco loads natively on Apple devices via `-apple-system` / `BlinkMacSystemFont`; non-Apple platforms fall through to Segoe UI and Inter so weights and metrics stay close. Display headings use `"SF Pro Display"` first; mono uses `"SF Mono"` first. No webfont download — system fonts for performance and OS-native feel.
- **Inter first, self-hosted**: `"Inter Variable", "Inter", "SF Pro Text", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif` via `@fontsource-variable/inter` (OFL, bundled, `font-display: swap`, no Google Fonts request). SF Pro is the fallback only while the woff2 loads or if it fails. One face: display weight comes from `font-weight`, not a second family. Mono keeps `"SF Mono"` first.
- **Tight letter-spacing on big text**: feature titles `-0.03em`, h2 `-0.025em`, h3 `-0.02em`. Body stays at 0.
- **Heavy weights at large sizes**: feature titles use `850`, headers `700`, section titles `650`. Body is `400`/`500`. Buttons are `600`.
- **Sizes hover small**: body `14px`, secondary `13px`, meta `12px`, kicker `10–11px`. The biggest type in normal product UI is the 24px feature title.
- **Heavy weights at large sizes**: feature titles use `700` in Inter (`850` was tuned for SF Pro Display), headers `700`, section titles `650`. Body is `400`/`500`. Buttons are `600`.
- **Sizes hover small**: body `14/20`, meta `12/16`, labels `11/14`. Secondary copy keeps body size and carries hierarchy by colour. The biggest type in normal product UI is the 24px feature title.

### Spacing, radii, layout

- Spacing follows a 4px grid: `4 / 8 / 16 / 24 / 32 / 48`.
- Radii cluster around three sizes: `8` (chips/small buttons), `10` (default), `12` (cards/modals). `999` for pills.
- Radii ladder `6 / 10 / 14` by importance: `6` chips, inputs and small buttons; `10` cards, rows and buttons ≥ 36px; `14` the content card, modals and the login card. `999` for pills.
- The shell is a **fixed 4-column grid**: `76px rail · 272px pods · 1fr main · 336px inspector`. The inspector collapses to a 3-col layout on feature pages and defaults to collapsed.

### Backgrounds

- **No gradients in chrome.** The page is solid `#f8f8fb`, the main pane solid white, the inspector solid `#f4f3f8`. **Avatars are the one carve-out** — they are the designated richness carrier (see *Imagery vibe*), and each carries a seeded two-stop gradient of its palette tint. Chrome surfaces stay flat.
- **The tint step.** Shell `#f1f1f4` behind rail, pods and inspector; content is an inset white card (1px `#e5e7eb` ring, 14px radius, 8px gutter top/right/bottom on desktop, flush on phones).
- **No gradients in chrome.** The shell is solid `#f1f1f4`, the content card solid white. **Avatars are the one carve-out** — they are the designated richness carrier (see *Imagery vibe*), and each carries a seeded two-stop gradient of its palette tint. Chrome surfaces stay flat.
- **No background images, illustrations, or patterns.** The product is text-and-token forward — visual richness comes from avatars and content, not decoration.

### Borders & elevation

- **Borders, not shadows.** Cards are `1px solid #e5e7eb`. Hairlines between sections use `#eef0f6`. Hovered borders deepen to `#d7dce7`.
- The v2 token explicitly sets `--v2-shadow: none` and `--v2-shadow-sm: none`. **Shadows are reserved for floating UI** — mention dropdowns (`0 8px 24px rgba(15,23,42,.12)`), login card, dialogs.
- **Borders, not shadows.** Cards are `1px solid #e5e7eb`. Hairlines between sections use `#eef0f6`. Hovered borders deepen to `#d7dce7`. Rows carry a **permanent transparent 1px border** and change only their fill on hover; no dividers between rows.
- The v2 token explicitly sets `--v2-shadow: none` and `--v2-shadow-sm: none`. **Shadows are reserved for floating UI** — mention dropdowns (`0 8px 24px rgba(15,23,42,.12)`), login card, dialogs. The single shadow allowed on a chrome card is `0 1px 2px rgba(15,23,42,.06)` on a **pending** decision or approval card; it drops when the card settles.
- **No "inner shadow" / inset effects** on chrome surfaces. Avatars again excepted: the initials plate carries a 1px inset highlight/shade pair so the seeded gradient reads as a lit sphere rather than a flat disc — removed via `:has(img)` when a photo is present, so shading never sits on a face.

### Hover & press

- **Hover** = swap to a slightly darker neutral background (`--c-surface-hover: #f1f2f5`) or a tinted accent (`#e8efff`). Borders may strengthen one tier.
- **Hover** = fill swap only (`--c-surface-hover: #f1f2f5` or the accent tint `#e8efff`). Nothing shifts by a pixel: no border-colour, padding, transform or shadow change on hover.
- **Focus** = halo `0 0 0 3px rgba(47,111,235,.18)` with an accent edge; never a hard 2px outline (forced-colors mode gets the UA outline).
- **Press** = no `transform: translateY` in V2.
- **Active state** for tabs/nav uses the accent bottom-border (2px) or the accent-soft pill background — not bold weight changes.
- **Transitions**: very fast — `80ms ease` for hover/state changes is the V2 default. `120ms` for card hovers, `300ms` for layout shifts.
Expand Down Expand Up @@ -171,7 +173,7 @@ entrance and scroll-reveal animation is allowed within these limits:

### Layout rules

- **Fixed sidebar widths** (rail 76, pods 272, inspector 336) — not fluid. The main pane absorbs all flex.
- **Fixed sidebar widths** (rail 76, pods 272, inspector 336) — not fluid. The main pane absorbs all flex. The content pane is the card: 8px gutter top/right/bottom on desktop, flush on phones; when the inspector is open the card meets the inspector gutter.
- **Rail collapses labels** at all widths — labels show as tooltips on hover (CSS `::after` with `data-label`).
- **Inspector closes** to free width for the main pane; default state is collapsed.
- Header heights cluster at **42px (tabs)**, **34px (chips/buttons)**, **78px (chat header w/ subtitle)**.
Expand Down Expand Up @@ -208,6 +210,6 @@ entrance and scroll-reveal animation is allowed within these limits:

## Caveats

- **No webfonts shipped.** V2 deliberately uses system fonts. If a design needs Inter explicitly, load it from Google Fonts — but match the V2 metrics by setting `font-family` accordingly.
- **One webfont shipped.** Inter Variable is bundled from `@fontsource-variable/inter` (OFL); everything else is system. Do not add a second family, and never load a face from Google Fonts.
- **No Figma file** is committed. All visual decisions trace back to `tokens.css` and `frontend/src/v2/v2.css`.
- **Marketing site styling lives elsewhere.** Only the in-app experience is documented here.
30 changes: 18 additions & 12 deletions frontend/design-system/tokens.css
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
--c-surface: #ffffff;
--c-surface-tint: #f4f3f8; /* inspector, code blocks */
--c-surface-hover: #f1f2f5;
--c-shell-bg: #f1f1f4; /* shell ground: rail, pods, inspector, gutter around the content card (TASK-122) */
--c-content-radius: 14px; /* the inset content card */

/* ---------- Borders ---------- */
--c-border: #e5e7eb; /* default */
Expand All @@ -36,6 +38,9 @@
--c-fg-tertiary: #7b8494;
--c-fg-muted: #8a93a3;
--c-fg-on-accent: #ffffff;
--c-ink: #111827; /* filled buttons are ink; blue is links, marks, halo (TASK-122) */
--c-ink-hover: #1f2937;
--c-on-ink: #ffffff;

/* ---------- Semantic ---------- */
--c-success: #10b981;
Expand Down Expand Up @@ -66,9 +71,9 @@
--c-github: #333333;

/* ---------- Geometry ---------- */
--c-radius-sm: 8px; /* chips, small buttons */
--c-radius-sm: 6px; /* chips, inputs, small buttons */
--c-radius: 10px; /* default — inputs, cards inside panels */
--c-radius-lg: 12px; /* cards, modals */
--c-radius-lg: 14px; /* the content card, modals, the login card */
--c-radius-pill: 999px;

/* ---------- Spacing scale ---------- */
Expand All @@ -91,6 +96,7 @@
--c-shadow-sm: 0 1px 2px 0 rgba(15, 23, 42, 0.04);
--c-shadow-md: 0 4px 12px rgba(15, 23, 42, 0.06);
--c-shadow-lg: 0 8px 24px rgba(15, 23, 42, 0.12);
--c-shadow-pending: 0 1px 2px rgba(15, 23, 42, 0.06); /* the one chrome-card shadow: a PENDING decision/approval only */

/* ---------- Motion ---------- */
--c-ease: cubic-bezier(0.4, 0, 0.2, 1);
Expand All @@ -111,15 +117,12 @@
--motion-ease-linear: linear;

/* ---------- Typography ---------- */
/* SF-first stack — uses the OS-installed San Francisco family on Apple
devices; falls through to BlinkMacSystemFont (Chrome's SF alias) and
finally to Segoe/Inter on non-Apple platforms. */
--c-font-sans: "SF Pro Text", -apple-system, BlinkMacSystemFont,
"SF Pro", "Helvetica Neue", "Segoe UI", "Inter",
Roboto, sans-serif;
--c-font-display: "SF Pro Display", -apple-system, BlinkMacSystemFont,
"SF Pro", "Helvetica Neue", "Segoe UI", "Inter",
sans-serif;
/* Inter (variable, self-hosted from the bundle via @fontsource-variable/inter);
SF Pro is the fallback only while the woff2 loads or if it fails. One face:
display weight comes from font-weight, not a second family. (TASK-122) */
--c-font-sans: "Inter Variable", "Inter", "SF Pro Text", -apple-system,
BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
--c-font-display: var(--c-font-sans);
--c-font-mono: "SF Mono", ui-monospace, SFMono-Regular, Menlo, Consolas,
"JetBrains Mono", "Fira Code", monospace;

Expand All @@ -145,6 +148,9 @@
--c-lh-tight: 1.25;
--c-lh-base: 1.45;
--c-lh-comfy: 1.55;
--c-lh-body: 20px; /* 14/20 body, 12/16 meta, 11/14 labels — mirror --v2-lh-* */
--c-lh-meta: 16px;
--c-lh-label: 14px;
/* Platform brand + tint tokens (connectors) — mirror of --v2-platform-* in
src/v2/v2.css; the two move together. */
--c-platform-telegram: #2aabee;
Expand Down Expand Up @@ -184,7 +190,7 @@ html, body {
h1, .c-h1 {
font-family: var(--c-font-sans);
font-size: var(--c-fs-3xl);
font-weight: 850; /* V2 uses 850 for feature titles */
font-weight: 700; /* 850 was tuned for SF Pro Display; Inter reads right at 700 */
line-height: var(--c-lh-tight);
letter-spacing: -0.03em;
margin: 0;
Expand Down
10 changes: 10 additions & 0 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"@dnd-kit/sortable": "^8.0.0",
"@emotion/react": "^11.10.6",
"@emotion/styled": "^11.10.6",
"@fontsource-variable/inter": "^5.3.0",
"@mui/icons-material": "^5.11.16",
"@mui/material": "^5.12.1",
"@sentry/react": "^10.65.0",
Expand Down Expand Up @@ -85,6 +86,7 @@
"^.+\\.(js|jsx|ts|tsx)$": "babel-jest"
},
"moduleNameMapper": {
"^@fontsource-variable/inter$": "<rootDir>/src/__mocks__/fileMock.js",
"^react-markdown$": "<rootDir>/src/__mocks__/react-markdown.js",
"^d3$": "<rootDir>/src/__mocks__/d3.js",
"\\.(css|less|scss|sass)$": "<rootDir>/src/__mocks__/fileMock.js",
Expand Down
1 change: 1 addition & 0 deletions frontend/src/v2/V2App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import V2AdminUsers from './components/V2AdminUsers';
import V2AdminAnalytics from './components/V2AdminAnalytics';
import V2EmailVerificationBanner from './components/V2EmailVerificationBanner';
import ProtectedRoute from '../components/ProtectedRoute';
import '@fontsource-variable/inter';
import './v2.css';

class V2ErrorBoundary extends React.Component<{ children: React.ReactNode }, { error: Error | null }> {
Expand Down
Loading
Loading