|
| 1 | +# Design System — Crème & Bleu de France ⚜️ |
| 2 | + |
| 3 | +> Category: Education · Developer Tools |
| 4 | +> |
| 5 | +> An editorial design language for Typed French. Warm cream paper (papier crème), |
| 6 | +> deep bleu-nuit ink, a single muted bleu-de-France accent, a touch of rouge and |
| 7 | +> a filet doré (gold hairline) — the tricolore *en sourdine*. The same token |
| 8 | +> architecture dresses the whole Typed Grammar family; only the palette changes. |
| 9 | +
|
| 10 | +This document is the source of truth. The canonical token implementation lives in |
| 11 | +[`src/theme.css`](./src/theme.css) (CSS custom properties, light under `:root`, dark |
| 12 | +under `:root[data-theme="dark"]`), mapped into Tailwind v4 with `@theme inline`. |
| 13 | +Authored against the [Open Design](https://github.com/nexu-io/open-design) 9-section schema. |
| 14 | + |
| 15 | +## 1. Color |
| 16 | + |
| 17 | +The palette is three ideas: **crème** (paper) for surfaces, **encre bleu-nuit** |
| 18 | +(ink) for text, and **bleu de France** as the one accent — a muted, editorial |
| 19 | +blue, never the saturated flag. A **rouge** completes the tricolore as a second |
| 20 | +accent (and the error colour), and a **filet doré** adds a discreet gilded note. |
| 21 | + |
| 22 | +| Role | Token | Light | Dark | |
| 23 | +| --- | --- | --- | --- | |
| 24 | +| Page | `--paper` | `#f7f4ec` | `#12161f` | |
| 25 | +| Card surface | `--surface` | `#fffdf8` | `#1a1f2b` | |
| 26 | +| Subtle fill | `--surface-2` | `#eef0f6` | `#232a39` | |
| 27 | +| Hairline | `--border` / `--border-strong` | `#e7e2d4` / `#d8d0bd` | `#2c3340` / `#3d4656` | |
| 28 | +| Text | `--ink-900` → `--ink-300` | `#1c2433` → `#9aa1b1` | `#eef1f7` → `#757d8d` | |
| 29 | +| Accent | `--azur-500` | `#3b5ba5` | `#8fb0e6` | |
| 30 | +| Accent deep / link | `--azur-600` | `#2f4a88` | `#aac4f0` | |
| 31 | +| On-accent text | `--on-accent` | `#ffffff` | `#12161f` | |
| 32 | +| Rouge / Gold | `--rouge` / `--gold` | `#b23a48` / `#b8922f` | `#f0788a` / `#d8b85f` | |
| 33 | +| Success / error | `--ok` / `--err` | `#2f7d5b` / `#b23a48` | `#4cc08c` / `#f0788a` | |
| 34 | + |
| 35 | +The azur ramp (`--azur-50` … `--azur-600`) **inverts** between modes: in light, 50 |
| 36 | +is the faintest wash and 600 the deepest link; in dark, 50 becomes the deepest fill |
| 37 | +and 600 the brightest, with 500 staying the accent in both. This keeps semantic |
| 38 | +usage (`background: --azur-100`, `color: --azur-600`) correct without per-component |
| 39 | +dark rules. |
| 40 | + |
| 41 | +Grammar-category accents (`--cat-*`, used by the composition tree) keep distinct |
| 42 | +hues but ship a lightened/desaturated dark variant so they stay legible on bleu-nuit. |
| 43 | + |
| 44 | +**Always reference tokens, never raw hex.** Text-on-accent uses `--on-accent` (it |
| 45 | +flips to dark ink in dark mode, where the accent is a light azur). Soft status |
| 46 | +backgrounds use `--ok-soft` / `--err-soft`. |
| 47 | + |
| 48 | +## 2. Typography |
| 49 | + |
| 50 | +- **UI** — `--font-ui`: EB Garamond + Noto Serif SC (for 简体中文). Warm, editorial. |
| 51 | +- **French** — `--font-fr`: EB Garamond, applied automatically via `:lang(fr)` and |
| 52 | + `.fr`; example sentences render larger. |
| 53 | +- **Headings** — `--font-heading`: Playfair Display — the Parisian display sheen. |
| 54 | +- **Mono** — `--font-mono`: SF Mono / JetBrains Mono. Inline code (`.tj-code`) and the editor. |
| 55 | + |
| 56 | +Weights: body 400, labels/UI 600–700, titles 800. Section labels 0.72rem, uppercase, |
| 57 | +`letter-spacing: 0.04em`, `--ink-500`. Body line-height ~1.7 for prose. |
| 58 | + |
| 59 | +## 3. Spacing |
| 60 | + |
| 61 | +A soft 4px rhythm (gaps of 6/8/10/12/16/20px). Cards pad 12–20px; page gutters 20px; |
| 62 | +max content width 1280px. Prose columns cap at ~72ch. |
| 63 | + |
| 64 | +## 4. Layout |
| 65 | + |
| 66 | +- App shell: centered column, 1280px max, 20px gutter (`.app`). |
| 67 | +- Course: sticky 268px sidebar + fluid content; collapses to one column < 900px. |
| 68 | +- Analyzer: editor | tree, two panes; opens in a right-side drawer over a `--scrim`. |
| 69 | + |
| 70 | +## 5. Components |
| 71 | + |
| 72 | +Shared primitives live as `.tj-*` utilities in `theme.css`: |
| 73 | + |
| 74 | +- `.tj-card` — surface + hairline + `--shadow-sm`, `--radius` (14px). |
| 75 | +- `.tj-btn` / `.tj-btn--primary` — pill buttons; primary fills `--azur-500` with `--on-accent`. |
| 76 | +- `.tj-chip` — rounded tag, `--surface-2` fill, `--azur-600` text. |
| 77 | +- `.tj-input` / `.tj-select` — focus shows `--azur-400` border + a 3px `--ring` halo. |
| 78 | +- `.tj-label`, `.tj-subtle`, `.tj-result`, `.tj-code`. |
| 79 | + |
| 80 | +Active/selected states fill `--azur-500`. Radii: 14px cards, 9px controls, 999px pills. |
| 81 | + |
| 82 | +## 6. Motion |
| 83 | + |
| 84 | +Restrained and short. Color/border transitions 0.12–0.2s ease. Drawer slides in |
| 85 | +0.26s `cubic-bezier(0.4,0,0.2,1)`. Respect `prefers-reduced-motion`. |
| 86 | + |
| 87 | +## 7. Voice |
| 88 | + |
| 89 | +Calm, precise, encouraging — a good programming tutorial, not a textbook. Bilingual |
| 90 | +EN / 简体中文 at equal depth. Concepts are motivated before mechanics. Let the type |
| 91 | +checker make the claims. |
| 92 | + |
| 93 | +## 8. Brand |
| 94 | + |
| 95 | +The mark is the fleur-de-lis (⚜️). The wordmark "Typed French" / "TypedGrammar" uses |
| 96 | +an azur-600 → azur-400 gradient on the title. The throughline: *grammar you can |
| 97 | +verify* — every sentence is a type the compiler reads. |
| 98 | + |
| 99 | +## 9. Anti-patterns |
| 100 | + |
| 101 | +- ❌ Saturated flag blue/red — reads cheap. Bleu de France is muted, editorial. |
| 102 | +- ❌ Raw hex / `rgba()` in component CSS — breaks dark mode. Use tokens. |
| 103 | +- ❌ White text hardcoded on accents — use `--on-accent`. |
| 104 | +- ❌ Heavy drop shadows or glows — keep elevation soft (`--shadow-sm` / `--shadow-md`). |
| 105 | +- ❌ Theme set after mount (causes a flash) — resolve `data-theme` before first paint. |
0 commit comments