| 1 | /* ------------------------------------------------------------------ */ |
| 2 | /* Tokens and roles — the whale-road */ |
| 3 | /* ------------------------------------------------------------------ */ |
| 4 | /* |
| 5 | * Direction: codewhale-design/DIRECTION.md. Token authority: |
| 6 | * vendor/codewhale-design/tokens.json (1.1.x), generated into ../tokens.css |
| 7 | * as `--gpui-light-*` / `--gpui-dark-*`. This file names the roles every |
| 8 | * page paints with. |
| 9 | * |
| 10 | * The ocean. Dark is the preferred look: deep navy grounds that rise into |
| 11 | * the logo's gradient (#1E8FD8 -> #0B48BB). Light is warm paper above one |
| 12 | * calm horizon. Below the horizon (the footer, the home sea) is always |
| 13 | * the deep water, in both appearances (`.stage`). |
| 14 | * |
| 15 | * Depth is elevation. --panel is raised (nearer the surface, lighter); |
| 16 | * --bg is the floor; --surface is recessed (code, rails, fields). |
| 17 | * |
| 18 | * Light means something. Focus, live work and "needs you" carry a small |
| 19 | * point of light (--glow). Nothing else glows. |
| 20 | * |
| 21 | * State is a mark and a word: --live (working, done, ready), --attention |
| 22 | * (needs you), --danger (failed), --muted (idle). Never colour alone. |
| 23 | * |
| 24 | * Text, accent and state inks are the generated GPUI tokens in both |
| 25 | * appearances. The navy grounds are this site's reach for the ombre; they |
| 26 | * are measured against every ink here (gpui-role-tokens.test.ts checks |
| 27 | * WCAG AA on every ground). |
| 28 | * |
| 29 | * NAMING TRAP: tokens.json `surface` is the RAISED colour (web --panel); |
| 30 | * tokens.json `sidebar` is the RECESSED one (web --surface). |
| 31 | */ |
| 32 | |
| 33 | /* ---------- scheme-independent tokens ---------- */ |
| 34 | :root { |
| 35 | /* The ocean: the navy grounds and the logo gradient. */ |
| 36 | --ocean-abyss: #030a1c; |
| 37 | --ocean-floor: #061431; |
| 38 | --ocean-raised: #0c2152; |
| 39 | --ocean-hover: #11295e; |
| 40 | --ocean-selected: #16316b; |
| 41 | --ocean-line: #1f3a78; |
| 42 | --ocean-line-strong: #6f86b8; |
| 43 | --ocean-sky: #0a1d48; |
| 44 | --brand-light: #1e8fd8; |
| 45 | --brand-deep: #0b48bb; |
| 46 | /* The primary fill: the logo gradient, started where white text on it |
| 47 | still clears 4.5:1 (#1877c9 is 4.65:1; #1e8fd8 itself is 3.5:1). */ |
| 48 | --brand-fill: linear-gradient(135deg, #1877c9, var(--brand-deep)); |
| 49 | --brand-fill-hover: linear-gradient(135deg, #1670c2, #0a3fa6); |
| 50 | --on-brand: #ffffff; |
| 51 | /* The sea, one ramp from the logo's deep blue at the surface to the |
| 52 | floor. It settles within a few rows so text from 3rem down sits on |
| 53 | >= 4.5:1 muted contrast; it is never a bright wall. */ |
| 54 | --sea-surface: var(--brand-deep); |
| 55 | --sea-shallow: #0a3a94; |
| 56 | --sea-mid: #072a6e; |
| 57 | --sea: linear-gradient(180deg, var(--sea-shallow) 0, var(--sea-mid) 5rem, var(--ocean-floor) 22rem); |
| 58 | --sea-band: linear-gradient(180deg, var(--sea-shallow) 0, var(--sea-mid) 40%, var(--ocean-floor) 100%); |
| 59 | /* The tide (the merch's Tide ombré): paper, light aqua, cobalt, then the |
| 60 | deep. Headings start past the logo-deep stop (7rem), where text ink |
| 61 | clears 4.5:1; muted ink only appears past the mid stop (11rem). */ |
| 62 | --tide-aqua: #d3ebf7; |
| 63 | --tide: linear-gradient(180deg, var(--gpui-light-bg) 0, var(--tide-aqua) 2rem, var(--brand-light) 4.5rem, var(--brand-deep) 7rem, var(--sea-mid) 11rem, var(--ocean-floor) 26rem); |
| 64 | /* The horizon: one line of light where the paper meets the sea. */ |
| 65 | --horizon-light: rgb(144 185 255 / 0.85); |
| 66 | --horizon-glow: var(--horizon-light); |
| 67 | |
| 68 | /* Type: Shannon Sans for every role, the system monospace stack for |
| 69 | things a person could type or paste. No serif, no third family. */ |
| 70 | --font-body: var(--font-shannon-latin), var(--font-shannon-ext), var(--gpui-font-fallbacks), |
| 71 | ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif; |
| 72 | --font-display: var(--font-body); |
| 73 | --font-mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Monaco, Consolas, |
| 74 | "Liberation Mono", monospace; |
| 75 | --font-cjk: var(--gpui-font-fallbacks), sans-serif; |
| 76 | |
| 77 | /* Type scale. 12px is the floor. Display sizes are one per page. */ |
| 78 | --text-caption: 0.75rem; /* 12 meta, timestamps, labels */ |
| 79 | --text-mono: var(--gpui-mono-size); /* 13 code, commands */ |
| 80 | --text-ui: 0.875rem; /* 14 controls, rows, panel body */ |
| 81 | --text-body: 1rem; /* 16 short copy, form inputs (no iOS zoom) */ |
| 82 | --text-prose: 1.0625rem; /* 17 reading: docs, legal, answers */ |
| 83 | --leading-prose: 1.6; |
| 84 | --text-heading: 1.0625rem; /* 17 600 group heading */ |
| 85 | --text-title: 1.375rem; /* 22 600 group and prose title */ |
| 86 | --text-section: clamp(1.75rem, 1.2rem + 1.7vw, 2.5rem); /* 28-40 editorial section title */ |
| 87 | --text-display: clamp(2rem, 1.3rem + 2.6vw, 3rem); /* inner-page title */ |
| 88 | --text-display-lg: clamp(2.5rem, 1.4rem + 4.4vw, 4.5rem); /* the home hero */ |
| 89 | --text-xs: var(--text-caption); |
| 90 | --text-sm: var(--text-ui); |
| 91 | --text-base: var(--text-body); |
| 92 | |
| 93 | /* Space: a 4px program. */ |
| 94 | --space-1: 0.25rem; |
| 95 | --space-2: 0.5rem; |
| 96 | --space-3: 0.75rem; |
| 97 | --space-4: 1rem; |
| 98 | --space-5: 1.5rem; |
| 99 | --space-6: 2rem; |
| 100 | --space-7: 3rem; |
| 101 | --space-8: 4rem; |
| 102 | --section-gap: clamp(var(--space-7), 7vw, 5.5rem); |
| 103 | |
| 104 | /* One container, one reading measure. */ |
| 105 | --container: min(100% - var(--gpui-spacing-page), 76rem); |
| 106 | --measure-prose: 46rem; |
| 107 | --measure-text: 68ch; |
| 108 | --hero-pad: clamp(3rem, 7vw, 5.5rem); |
| 109 | --section-pad: clamp(2.75rem, 5vw, 4rem); |
| 110 | |
| 111 | /* Radius: 6 controls, 10 surfaces, 14 sheets, pill for dots. */ |
| 112 | --radius-control: var(--gpui-radius-control); |
| 113 | --radius-surface: var(--gpui-radius-panel); |
| 114 | --radius-sheet: var(--gpui-radius-sheet); |
| 115 | --radius-pill: 999px; |
| 116 | |
| 117 | /* Motion. Nothing bounces: every curve stays inside [0, 1]. */ |
| 118 | --dur-state: 120ms; /* colour, border, opacity on hover and press */ |
| 119 | --dur-arrive: 180ms; /* something new arrives, rise <= 4px */ |
| 120 | --dur-exit: 120ms; /* something closes or leaves */ |
| 121 | --dur-spring: 340ms; /* panes and sheets */ |
| 122 | --dur-spin: 1.2s; /* a working spinner: motion that shows work */ |
| 123 | --ease-out: cubic-bezier(0, 0, 0.2, 1); |
| 124 | --ease-in: cubic-bezier(0.4, 0, 1, 1); |
| 125 | --ease-spring: cubic-bezier(0.2, 0.1, 0.16, 1); |
| 126 | --ease-exit: var(--ease-in); |
| 127 | } |
| 128 | |
| 129 | /* Reduced motion is a still pose: every duration steps. Keyframed motion is |
| 130 | additionally declared only inside `prefers-reduced-motion: |
| 131 | no-preference`, so it never starts. */ |
| 132 | @media (prefers-reduced-motion: reduce) { |
| 133 | :root { |
| 134 | --dur-state: 0ms; |
| 135 | --dur-spring: 0ms; |
| 136 | --dur-arrive: 0ms; |
| 137 | --dur-exit: 0ms; |
| 138 | } |
| 139 | } |
| 140 | |
| 141 | /* ---------- light — warm paper above the horizon ---------- */ |
| 142 | :root { |
| 143 | --bg: var(--gpui-light-bg); |
| 144 | --surface: var(--gpui-light-sidebar); |
| 145 | --panel: var(--gpui-light-panel); |
| 146 | --text: var(--gpui-light-text); |
| 147 | --muted: var(--gpui-light-text-muted); |
| 148 | --line: var(--gpui-light-border); |
| 149 | --line-strong: var(--gpui-light-border-strong); |
| 150 | --accent: var(--gpui-light-primary); |
| 151 | --accent-hover: rgb(var(--gpui-light-primary-rgb) / var(--gpui-primary-hover-opacity)); |
| 152 | --on-accent: var(--gpui-light-on-primary); |
| 153 | --hover: var(--gpui-light-accent); |
| 154 | --selected: var(--gpui-light-list-active); |
| 155 | --selection: rgb(var(--gpui-light-primary-rgb) / var(--gpui-selection-opacity)); |
| 156 | --ring: var(--gpui-light-primary); |
| 157 | --live: var(--gpui-light-live); |
| 158 | --success: var(--gpui-light-live); |
| 159 | --attention: var(--gpui-light-attention); |
| 160 | --danger: var(--gpui-light-danger); |
| 161 | --mark-ink: var(--gpui-light-text); |
| 162 | --ombre-ink: linear-gradient(135deg, var(--brand-light), var(--brand-deep)); |
| 163 | --diff-add: rgb(104 152 119 / 0.14); |
| 164 | --diff-del: rgb(189 119 116 / 0.11); |
| 165 | /* Paper: the plain ground above the horizon. */ |
| 166 | --ground: var(--gpui-light-bg); |
| 167 | /* On paper, light is a quiet ring. */ |
| 168 | --glow: 0 0 0 3px color-mix(in srgb, currentColor 16%, transparent); |
| 169 | --glow-focus: 0 0 0 6px rgb(var(--gpui-light-primary-rgb) / 0.12); |
| 170 | --shadow-popover: 0 4px 8px rgb(0 0 0 / 0.16); |
| 171 | color-scheme: light; |
| 172 | } |
| 173 | |
| 174 | /* ---------- dark — the navy ocean, only when pinned ---------- */ |
| 175 | /* Paper is the site's appearance; the sea lives below the horizon in both. |
| 176 | A reader who pins dark from the theme control gets the navy ground. */ |
| 177 | :root[data-theme="dark"] { |
| 178 | --bg: var(--ocean-floor); |
| 179 | --surface: var(--ocean-abyss); |
| 180 | --panel: var(--ocean-raised); |
| 181 | --text: var(--gpui-dark-text); |
| 182 | --muted: var(--gpui-dark-text-muted); |
| 183 | --line: var(--ocean-line); |
| 184 | --line-strong: var(--ocean-line-strong); |
| 185 | --accent: var(--gpui-dark-primary); |
| 186 | --accent-hover: rgb(var(--gpui-dark-primary-rgb) / var(--gpui-primary-hover-opacity)); |
| 187 | --on-accent: var(--gpui-dark-on-primary); |
| 188 | --hover: var(--ocean-hover); |
| 189 | --selected: var(--ocean-selected); |
| 190 | --selection: rgb(var(--gpui-dark-primary-rgb) / var(--gpui-selection-opacity)); |
| 191 | --ring: var(--gpui-dark-primary); |
| 192 | --live: var(--gpui-dark-live); |
| 193 | --success: var(--gpui-dark-live); |
| 194 | --attention: var(--gpui-dark-attention); |
| 195 | --danger: var(--gpui-dark-danger); |
| 196 | --mark-ink: var(--gpui-dark-text); |
| 197 | --ombre-ink: linear-gradient(135deg, #7cc2f2, var(--brand-light)); |
| 198 | --diff-add: rgb(80 170 140 / 0.16); |
| 199 | --diff-del: rgb(227 154 144 / 0.12); |
| 200 | --ground: radial-gradient(90rem 38rem at 50% -14rem, rgb(30 143 216 / 0.3), transparent 70%), linear-gradient(180deg, var(--ocean-sky) 0, var(--ocean-floor) 34rem); |
| 201 | --glow: 0 0 0 3px color-mix(in srgb, currentColor 20%, transparent), 0 0 12px 1px color-mix(in srgb, currentColor 45%, transparent); |
| 202 | --glow-focus: 0 0 14px 2px rgb(var(--gpui-dark-primary-rgb) / 0.35); |
| 203 | --shadow-popover: 0 4px 8px rgb(0 0 0 / 0.4); |
| 204 | color-scheme: dark; |
| 205 | } |
| 206 | |
| 207 | /* ---------- the stage — below the horizon, in both appearances ---------- */ |
| 208 | /* The footer and the home sea. The ground is the deep water; raised and |
| 209 | recessed plates step up and down from it. Legacy aliases are repeated |
| 210 | here because a custom property that references another is computed where |
| 211 | it is declared. */ |
| 212 | .stage, |
| 213 | .site-footer { |
| 214 | --bg: var(--ocean-floor); |
| 215 | --surface: var(--ocean-abyss); |
| 216 | --panel: var(--ocean-raised); |
| 217 | --text: var(--gpui-dark-text); |
| 218 | --muted: var(--gpui-dark-text-muted); |
| 219 | --line: var(--ocean-line); |
| 220 | --line-strong: var(--ocean-line-strong); |
| 221 | --accent: var(--gpui-dark-primary); |
| 222 | --accent-hover: rgb(var(--gpui-dark-primary-rgb) / var(--gpui-primary-hover-opacity)); |
| 223 | --on-accent: var(--gpui-dark-on-primary); |
| 224 | --hover: var(--ocean-hover); |
| 225 | --selected: var(--ocean-selected); |
| 226 | --selection: rgb(var(--gpui-dark-primary-rgb) / var(--gpui-selection-opacity)); |
| 227 | --ring: var(--gpui-dark-primary); |
| 228 | --live: var(--gpui-dark-live); |
| 229 | --success: var(--gpui-dark-live); |
| 230 | --attention: var(--gpui-dark-attention); |
| 231 | --danger: var(--gpui-dark-danger); |
| 232 | --mark-ink: var(--gpui-dark-text); |
| 233 | --ombre-ink: linear-gradient(135deg, #7cc2f2, var(--brand-light)); |
| 234 | --diff-add: rgb(80 170 140 / 0.16); |
| 235 | --diff-del: rgb(227 154 144 / 0.12); |
| 236 | --glow: 0 0 0 3px color-mix(in srgb, currentColor 20%, transparent), 0 0 12px 1px color-mix(in srgb, currentColor 45%, transparent); |
| 237 | --glow-focus: 0 0 14px 2px rgb(var(--gpui-dark-primary-rgb) / 0.35); |
| 238 | --shadow-popover: 0 4px 8px rgb(0 0 0 / 0.4); |
| 239 | |
| 240 | --paper: var(--bg); |
| 241 | --paper-deep: var(--surface); |
| 242 | --paper-card: var(--panel); |
| 243 | --paper-edge: var(--line); |
| 244 | --paper-line: var(--line); |
| 245 | --paper-line-soft: var(--line); |
| 246 | --hairline: var(--line); |
| 247 | --ink: var(--text); |
| 248 | --ink-soft: var(--muted); |
| 249 | --ink-mute: var(--muted); |
| 250 | --indigo: var(--accent); |
| 251 | --indigo-deep: var(--accent-hover); |
| 252 | --indigo-pale: color-mix(in srgb, var(--accent) 14%, transparent); |
| 253 | --docs-accent: var(--accent); |
| 254 | --code-bg: var(--surface); |
| 255 | --code-fg: var(--text); |
| 256 | color-scheme: dark; |
| 257 | color: var(--text); |
| 258 | } |
| 259 | |
| 260 | /* The page's own ground, resolved here on :root so it survives into regions |
| 261 | (the footer, the stage) that re-declare --bg. */ |
| 262 | :root { --page-ground: var(--bg); } |
| 263 | |
| 264 | /* ---------- legacy names ---------- */ |
| 265 | /* Pages and partials that have not moved to the roles (the docs) read these. |
| 266 | Declared once on :root, they resolve against whichever scheme block wins |
| 267 | there; `.stage` repeats them. Removed in the post-renovation sweep. */ |
| 268 | :root { |
| 269 | --paper: var(--bg); |
| 270 | --paper-deep: var(--surface); |
| 271 | --paper-card: var(--panel); |
| 272 | --paper-edge: var(--line); |
| 273 | --paper-line: var(--line); |
| 274 | --paper-line-soft: var(--line); |
| 275 | --hairline: var(--line); |
| 276 | --ink: var(--text); |
| 277 | --ink-soft: var(--muted); |
| 278 | --ink-mute: var(--muted); |
| 279 | --indigo: var(--accent); |
| 280 | --indigo-deep: var(--accent-hover); |
| 281 | --indigo-pale: color-mix(in srgb, var(--accent) 12%, transparent); |
| 282 | --cobalt: var(--accent); |
| 283 | --cyan: var(--accent); |
| 284 | --ochre: var(--attention); |
| 285 | --signal-gold: var(--attention); |
| 286 | --jade: var(--live); |
| 287 | --seafoam: var(--live); |
| 288 | --ocean-coral: var(--danger); |
| 289 | --docs-accent: var(--accent); |
| 290 | --docs-button-bg: var(--panel); |
| 291 | --docs-button-border: var(--line); |
| 292 | --docs-button-text: var(--text); |
| 293 | --code-bg: var(--surface); |
| 294 | --code-fg: var(--text); |
| 295 | --stage-line: var(--line); |
| 296 | --stage-dim: var(--muted); |
| 297 | } |
| 298 |