@motir/design-system 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +126 -0
- package/dist/appearance.d.ts +47 -0
- package/dist/appearance.js +3 -0
- package/dist/appearance.js.map +1 -0
- package/dist/components/theme/AppearancePickers.d.ts +97 -0
- package/dist/components/theme/AppearancePickers.js +542 -0
- package/dist/components/theme/AppearancePickers.js.map +1 -0
- package/dist/components/theme/HandDrawnFilter.d.ts +23 -0
- package/dist/components/theme/HandDrawnFilter.js +41 -0
- package/dist/components/theme/HandDrawnFilter.js.map +1 -0
- package/dist/components/theme/ImmersiveTilt.d.ts +3 -0
- package/dist/components/theme/ImmersiveTilt.js +113 -0
- package/dist/components/theme/ImmersiveTilt.js.map +1 -0
- package/dist/components/theme/StyleVignette.d.ts +60 -0
- package/dist/components/theme/StyleVignette.js +382 -0
- package/dist/components/theme/StyleVignette.js.map +1 -0
- package/dist/components/ui/Button.d.ts +32 -0
- package/dist/components/ui/Button.js +92 -0
- package/dist/components/ui/Button.js.map +1 -0
- package/dist/components/ui/Card.d.ts +33 -0
- package/dist/components/ui/Card.js +64 -0
- package/dist/components/ui/Card.js.map +1 -0
- package/dist/components/ui/ColorSwatchPicker.d.ts +19 -0
- package/dist/components/ui/ColorSwatchPicker.js +71 -0
- package/dist/components/ui/ColorSwatchPicker.js.map +1 -0
- package/dist/components/ui/Combobox.d.ts +84 -0
- package/dist/components/ui/Combobox.js +374 -0
- package/dist/components/ui/Combobox.js.map +1 -0
- package/dist/components/ui/EmptyState.d.ts +38 -0
- package/dist/components/ui/EmptyState.js +73 -0
- package/dist/components/ui/EmptyState.js.map +1 -0
- package/dist/components/ui/ErrorState.d.ts +43 -0
- package/dist/components/ui/ErrorState.js +169 -0
- package/dist/components/ui/ErrorState.js.map +1 -0
- package/dist/components/ui/FormField.d.ts +31 -0
- package/dist/components/ui/FormField.js +43 -0
- package/dist/components/ui/FormField.js.map +1 -0
- package/dist/components/ui/Input.d.ts +29 -0
- package/dist/components/ui/Input.js +105 -0
- package/dist/components/ui/Input.js.map +1 -0
- package/dist/components/ui/Modal.d.ts +84 -0
- package/dist/components/ui/Modal.js +153 -0
- package/dist/components/ui/Modal.js.map +1 -0
- package/dist/components/ui/MultiSelectPicker.d.ts +73 -0
- package/dist/components/ui/MultiSelectPicker.js +358 -0
- package/dist/components/ui/MultiSelectPicker.js.map +1 -0
- package/dist/components/ui/Pill.d.ts +49 -0
- package/dist/components/ui/Pill.js +107 -0
- package/dist/components/ui/Pill.js.map +1 -0
- package/dist/components/ui/Popover.d.ts +65 -0
- package/dist/components/ui/Popover.js +57 -0
- package/dist/components/ui/Popover.js.map +1 -0
- package/dist/components/ui/SectionLabel.d.ts +28 -0
- package/dist/components/ui/SectionLabel.js +27 -0
- package/dist/components/ui/SectionLabel.js.map +1 -0
- package/dist/components/ui/Segmented.d.ts +30 -0
- package/dist/components/ui/Segmented.js +65 -0
- package/dist/components/ui/Segmented.js.map +1 -0
- package/dist/components/ui/Spinner.d.ts +22 -0
- package/dist/components/ui/Spinner.js +39 -0
- package/dist/components/ui/Spinner.js.map +1 -0
- package/dist/components/ui/Switch.d.ts +34 -0
- package/dist/components/ui/Switch.js +50 -0
- package/dist/components/ui/Switch.js.map +1 -0
- package/dist/components/ui/Textarea.d.ts +19 -0
- package/dist/components/ui/Textarea.js +72 -0
- package/dist/components/ui/Textarea.js.map +1 -0
- package/dist/components/ui/Toast.d.ts +45 -0
- package/dist/components/ui/Toast.js +116 -0
- package/dist/components/ui/Toast.js.map +1 -0
- package/dist/components/ui/Tooltip.d.ts +25 -0
- package/dist/components/ui/Tooltip.js +43 -0
- package/dist/components/ui/Tooltip.js.map +1 -0
- package/dist/contexts/theme-context.d.ts +95 -0
- package/dist/contexts/theme-context.js +561 -0
- package/dist/contexts/theme-context.js.map +1 -0
- package/dist/index.d.ts +41 -0
- package/dist/index.js +67 -0
- package/dist/specimen/TokensSpecimen.d.ts +13 -0
- package/dist/specimen/TokensSpecimen.js +1090 -0
- package/dist/specimen/TokensSpecimen.js.map +1 -0
- package/dist/theme/appearance-resolution.d.ts +59 -0
- package/dist/theme/appearance-resolution.js +379 -0
- package/dist/theme/appearance-resolution.js.map +1 -0
- package/dist/theme/init-script.d.ts +48 -0
- package/dist/theme/init-script.js +391 -0
- package/dist/theme/init-script.js.map +1 -0
- package/dist/theme/palettes.d.ts +146 -0
- package/dist/theme/palettes.js +85 -0
- package/dist/theme/palettes.js.map +1 -0
- package/dist/theme/styles.d.ts +351 -0
- package/dist/theme/styles.js +264 -0
- package/dist/theme/styles.js.map +1 -0
- package/dist/theme/tilt.d.ts +41 -0
- package/dist/theme/tilt.js +18 -0
- package/dist/theme/tilt.js.map +1 -0
- package/dist/theme/types.d.ts +61 -0
- package/dist/theme/types.js +234 -0
- package/dist/theme/types.js.map +1 -0
- package/dist/theme/typography.d.ts +144 -0
- package/dist/theme/typography.js +57 -0
- package/dist/theme/typography.js.map +1 -0
- package/dist/utils/cn.d.ts +16 -0
- package/dist/utils/cn.js +11 -0
- package/dist/utils/cn.js.map +1 -0
- package/package.json +60 -0
- package/theme.css +3924 -0
package/theme.css
ADDED
|
@@ -0,0 +1,3924 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Motir design system — two-axis theme architecture.
|
|
3
|
+
*
|
|
4
|
+
* Axis 1 — COLOR (the palette axis — registry in lib/theme/palettes.ts)
|
|
5
|
+
* data-theme="light|dark" — light/dark base
|
|
6
|
+
* data-palette="<palette-id>" — a named palette: a per-palette --el-* token
|
|
7
|
+
* override (Tier 3 below). v1 ships `motir`
|
|
8
|
+
* (the Tier-3 default — no override block, like
|
|
9
|
+
* the warm-editorial base style). More palettes
|
|
10
|
+
* each add a `[data-palette='<id>']` block.
|
|
11
|
+
*
|
|
12
|
+
* Axis 2 — STYLE (the named-aesthetic axis — registry in lib/theme/styles.ts)
|
|
13
|
+
* data-style="<style-id>"
|
|
14
|
+
* a named aesthetic that layers per-style SHAPE/FEEL tokens (radius /
|
|
15
|
+
* shadow / spacing / sizing / motion / type) — and, for richer styles,
|
|
16
|
+
* component-variant overrides — over the Tier-0 base scale. The two
|
|
17
|
+
* existing styles are `warm-editorial` (the Tier-0 default) and
|
|
18
|
+
* `soft-playful` (the Tier-2 block below). More styles (Swiss,
|
|
19
|
+
* Neo-Brutalism, Glassmorphism, …) each add a `[data-style='<id>']` block.
|
|
20
|
+
*
|
|
21
|
+
* The axes are INDEPENDENT: a `[data-style]` TOKEN block overrides ONLY
|
|
22
|
+
* shape/feel tokens, NEVER a colour token (`--color-*` / `--el-*`) — colour
|
|
23
|
+
* is the palette axis's job. Keeping them disjoint is what makes
|
|
24
|
+
* "style × palette" a true product of two free choices.
|
|
25
|
+
*
|
|
26
|
+
* SURFACE-MATERIAL styles (glassmorphism, 7.3.35; later cybercore, aurora,
|
|
27
|
+
* neumorphism …) own a richer surface — translucency, a gradient canvas,
|
|
28
|
+
* frosted backdrop-blur, light borders — that the token block cannot express.
|
|
29
|
+
* They add a palette-DERIVED MATERIAL LAYER: STYLE-SCOPED component rules
|
|
30
|
+
* (`[data-style='id'] [data-surface='…'] { … }`) whose colour comes ONLY from
|
|
31
|
+
* `color-mix()`/`var(--color-*|--el-*)` over the ACTIVE palette — never a raw
|
|
32
|
+
* hue. That keeps the two axes disjoint (a palette swap re-tints the glass; a
|
|
33
|
+
* style swap leaves hues untouched) while letting a style own its material.
|
|
34
|
+
* The token-block rule above is unchanged; the material layer is the addition.
|
|
35
|
+
* Both are enforced by tests/theme/styleRegistry.test.ts.
|
|
36
|
+
*
|
|
37
|
+
* Tier structure (top to bottom):
|
|
38
|
+
* Tier 0 — @theme base tokens (light mode defaults = the warm-editorial style)
|
|
39
|
+
* Tier 1 — [data-theme="dark"] overrides color tokens
|
|
40
|
+
* Tier 2 — [data-style="..."] overrides shape/feel tokens (NOT colour)
|
|
41
|
+
* Tier 3 — :root --el-* element tokens reference Tier 0/1 vars;
|
|
42
|
+
* components reference --el-*, never --color-* directly.
|
|
43
|
+
* A `[data-palette='<id>']` block overrides this --el-* layer to
|
|
44
|
+
* re-skin the whole app (the palette axis) — ONLY colour, never a
|
|
45
|
+
* shape/feel token (that is the data-style axis).
|
|
46
|
+
*
|
|
47
|
+
* Inspiration:
|
|
48
|
+
* - Notion's marketing palette (purple primary, pastel feature tints,
|
|
49
|
+
* ink/charcoal/slate text scale, hairline borders). Sourced via
|
|
50
|
+
* `npx getdesign@latest add notion` — full file in docs/inspiration/.
|
|
51
|
+
* - Figma's shape language (pill buttons via --radius-pill, generous
|
|
52
|
+
* letter-spacing, mono caption/eyebrow). Sourced via `add figma`.
|
|
53
|
+
* - Motir's own typography: Inter (sans body) + Source Serif 4 (serif
|
|
54
|
+
* headlines) + JetBrains Mono (mono) loaded via next/font in
|
|
55
|
+
* app/layout.tsx.
|
|
56
|
+
*
|
|
57
|
+
* Architecture pattern mirrors dooooWeb (see /Users/yuezhu/projects/doooo/
|
|
58
|
+
* dooooWeb/src/styles/ for the canonical reference implementation).
|
|
59
|
+
*/
|
|
60
|
+
|
|
61
|
+
@custom-variant dark (&:where([data-theme='dark'], [data-theme='dark'] *));
|
|
62
|
+
|
|
63
|
+
/* ═══════════════════════════════════════════════════════════════════════
|
|
64
|
+
TIER 0 — Base @theme tokens (light mode defaults)
|
|
65
|
+
Tailwind v4 auto-exposes these as utility classes:
|
|
66
|
+
--color-* → bg-*, text-*, border-* utilities
|
|
67
|
+
--radius-* → rounded-* utilities
|
|
68
|
+
--shadow-* → shadow-* utilities
|
|
69
|
+
--spacing-* → m-*, p-* utilities
|
|
70
|
+
--font-* → font-* utilities, text-* utilities
|
|
71
|
+
═══════════════════════════════════════════════════════════════════════ */
|
|
72
|
+
|
|
73
|
+
@theme {
|
|
74
|
+
/* ── Color: brand & primary ─────────────────────────────────────── */
|
|
75
|
+
/* Notion's signature purple — reserved for the dominant CTA only. */
|
|
76
|
+
--color-primary: #5645d4;
|
|
77
|
+
--color-primary-foreground: #ffffff;
|
|
78
|
+
--color-primary-pressed: #4534b3;
|
|
79
|
+
/* Accent FILL behind white text (filled buttons/badges). In light theme it
|
|
80
|
+
equals --color-primary; the dark theme darkens it so white label text
|
|
81
|
+
clears WCAG AA (the brighter --color-primary stays the accent used AS text
|
|
82
|
+
on the page). See [data-theme='dark']. (MOTIR-757) */
|
|
83
|
+
--color-primary-fill: #5645d4;
|
|
84
|
+
|
|
85
|
+
/* Surfaces */
|
|
86
|
+
--color-background: #ffffff; /* canvas */
|
|
87
|
+
--color-foreground: #1a1a1a; /* ink (primary text) */
|
|
88
|
+
--color-surface: #f6f5f4; /* subtle section backgrounds */
|
|
89
|
+
--color-surface-soft: #fafaf9; /* quieter section divisions */
|
|
90
|
+
--color-canvas: #ecebe7; /* recessed board behind raised cards (planning canvas) */
|
|
91
|
+
|
|
92
|
+
/* Text scale (Notion's warm-charcoal hierarchy) */
|
|
93
|
+
--color-ink: #1a1a1a;
|
|
94
|
+
--color-charcoal: #37352f; /* body emphasis */
|
|
95
|
+
--color-slate: #5d5b54; /* secondary text */
|
|
96
|
+
--color-steel: #787671; /* tertiary / footer */
|
|
97
|
+
--color-stone: #a4a097; /* muted labels */
|
|
98
|
+
--color-muted: #f3f4f6;
|
|
99
|
+
--color-muted-foreground: #787671;
|
|
100
|
+
|
|
101
|
+
/* Borders / hairlines */
|
|
102
|
+
--color-hairline: #e5e3df;
|
|
103
|
+
--color-hairline-soft: #ede9e4;
|
|
104
|
+
--color-hairline-strong: #c8c4be;
|
|
105
|
+
--color-border: #e5e3df;
|
|
106
|
+
|
|
107
|
+
/* Accent secondary (used sparingly — pastel tints for feature cards) */
|
|
108
|
+
--color-accent: #ff64c8; /* brand pink */
|
|
109
|
+
--color-accent-orange: #dd5b00;
|
|
110
|
+
--color-accent-teal: #2a9d99;
|
|
111
|
+
--color-accent-green: #1aae39;
|
|
112
|
+
|
|
113
|
+
/* Card tints (pale, for tinted surface variants) */
|
|
114
|
+
--color-tint-peach: #ffe8d4;
|
|
115
|
+
--color-tint-rose: #fde0ec;
|
|
116
|
+
--color-tint-mint: #d9f3e1;
|
|
117
|
+
--color-tint-lavender: #e6e0f5;
|
|
118
|
+
--color-tint-sky: #dcecfa;
|
|
119
|
+
--color-tint-yellow: #fef7d6;
|
|
120
|
+
|
|
121
|
+
/* Semantic */
|
|
122
|
+
--color-success: #1aae39;
|
|
123
|
+
--color-warning: #dd5b00;
|
|
124
|
+
--color-destructive: #e03131;
|
|
125
|
+
--color-destructive-foreground: #ffffff;
|
|
126
|
+
--color-info: #0075de;
|
|
127
|
+
|
|
128
|
+
/* Link blue (NOT primary purple — distinct role). Tuned to clear WCAG AA
|
|
129
|
+
(4.5:1) on BOTH the white page (#ffffff → 4.94:1) AND the slightly darker
|
|
130
|
+
--color-surface-soft (#fafaf9 → 4.73:1) that hovered rows paint behind it,
|
|
131
|
+
so a link on a hovered soft-surface row stays accessible
|
|
132
|
+
(bug-filters-directory-name-link-hover-aa-contrast — #0075de was 4.37:1
|
|
133
|
+
on the soft surface). */
|
|
134
|
+
--color-link: #0070d2;
|
|
135
|
+
--color-link-pressed: #005bab;
|
|
136
|
+
|
|
137
|
+
/* ── Typography ─────────────────────────────────────────────────── */
|
|
138
|
+
/* CSS variables set by next/font in app/layout.tsx. */
|
|
139
|
+
--font-sans:
|
|
140
|
+
var(--font-sans-source, -apple-system), BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
141
|
+
--font-serif: var(--font-serif-source, Georgia), serif;
|
|
142
|
+
--font-mono: var(--font-mono-source, ui-monospace), 'SF Mono', Menlo, monospace;
|
|
143
|
+
|
|
144
|
+
/* Type scale — Notion-ish hierarchy.
|
|
145
|
+
Headings (xl and up) default to serif; body defaults to sans. */
|
|
146
|
+
--font-size-xs: 0.75rem; /* 12px — micro labels */
|
|
147
|
+
--font-size-sm: 0.875rem; /* 14px — small body, buttons */
|
|
148
|
+
--font-size-base: 1rem; /* 16px — primary body */
|
|
149
|
+
--font-size-lg: 1.25rem; /* 20px — h4 / subtitle */
|
|
150
|
+
--font-size-xl: 1.5rem; /* 24px — h3 / card titles */
|
|
151
|
+
--font-size-2xl: 2rem; /* 32px — h2 */
|
|
152
|
+
--font-size-3xl: 3rem; /* 48px — h1 */
|
|
153
|
+
--font-size-display: 5rem; /* 80px — hero display */
|
|
154
|
+
|
|
155
|
+
/* ── Shape (the warm-editorial base style) ─────────────────────── */
|
|
156
|
+
/* Notion uses sober rectangles (8px buttons); Figma uses pills (50px).
|
|
157
|
+
The base scale IS the `warm-editorial` style (sober rectangles); the
|
|
158
|
+
`soft-playful` style adopts Figma's pills in its [data-style] block. */
|
|
159
|
+
--radius-xs: 4px;
|
|
160
|
+
--radius-sm: 6px;
|
|
161
|
+
--radius-md: 8px;
|
|
162
|
+
--radius-lg: 12px;
|
|
163
|
+
--radius-xl: 16px;
|
|
164
|
+
--radius-pill: 9999px;
|
|
165
|
+
|
|
166
|
+
/* Semantic shape tokens — components reference these, not --radius-md
|
|
167
|
+
directly. Lets a [data-style] block override these without renaming. */
|
|
168
|
+
--radius-btn: 8px; /* Notion-style sober button */
|
|
169
|
+
--radius-card: 12px;
|
|
170
|
+
--radius-input: 8px;
|
|
171
|
+
--radius-modal: 12px;
|
|
172
|
+
--radius-badge: 9999px;
|
|
173
|
+
--radius-control: 6px; /* small interactive affordances — menu/list rows, icon & close buttons, tooltips, code chips */
|
|
174
|
+
--radius-kbd: 4px; /* keyboard-hint chips */
|
|
175
|
+
|
|
176
|
+
/* ── Elevation ──────────────────────────────────────────────────── */
|
|
177
|
+
--shadow-flat: none;
|
|
178
|
+
--shadow-subtle: 0 1px 2px rgba(15, 15, 15, 0.04);
|
|
179
|
+
--shadow-card: 0 4px 12px rgba(15, 15, 15, 0.08);
|
|
180
|
+
--shadow-elevated: 0 8px 24px rgba(15, 15, 15, 0.12);
|
|
181
|
+
--shadow-modal: 0 16px 48px -8px rgba(15, 15, 15, 0.16);
|
|
182
|
+
--shadow-hero-mockup: 0 24px 48px -8px rgba(15, 15, 15, 0.2);
|
|
183
|
+
|
|
184
|
+
/* ── Spacing ────────────────────────────────────────────────────── */
|
|
185
|
+
--spacing-xxs: 4px;
|
|
186
|
+
--spacing-xs: 8px;
|
|
187
|
+
--spacing-sm: 12px;
|
|
188
|
+
--spacing-md: 16px;
|
|
189
|
+
--spacing-lg: 20px;
|
|
190
|
+
--spacing-xl: 24px;
|
|
191
|
+
--spacing-2xl: 32px;
|
|
192
|
+
--spacing-3xl: 40px;
|
|
193
|
+
--spacing-section: 64px;
|
|
194
|
+
--spacing-section-lg: 96px;
|
|
195
|
+
--spacing-hero: 120px;
|
|
196
|
+
|
|
197
|
+
/* Component-specific spacing */
|
|
198
|
+
--spacing-btn-x: 18px;
|
|
199
|
+
--spacing-btn-y: 10px;
|
|
200
|
+
--spacing-btn-x-sm: 12px;
|
|
201
|
+
--spacing-btn-y-sm: 8px;
|
|
202
|
+
--spacing-input-x: 16px;
|
|
203
|
+
--spacing-input-y: 12px;
|
|
204
|
+
--spacing-card-padding: 24px;
|
|
205
|
+
|
|
206
|
+
/* Control-level spacing — small affordances. Flip with the active style so a
|
|
207
|
+
getdesign swap can redefine control DENSITY, not just radius. */
|
|
208
|
+
--spacing-control-x: 10px; /* menu/list rows, combobox trigger & search, sidebar rows */
|
|
209
|
+
--spacing-control-y: 6px;
|
|
210
|
+
--spacing-icon-btn: 4px; /* square icon / close buttons */
|
|
211
|
+
--spacing-chip-x: 10px; /* badge / pill chips */
|
|
212
|
+
--spacing-chip-y: 2px;
|
|
213
|
+
--spacing-kbd-x: 6px; /* <kbd> keyboard-hint chips */
|
|
214
|
+
--spacing-kbd-y: 2px;
|
|
215
|
+
--spacing-tooltip-x: 8px; /* tooltip & inline code blocks */
|
|
216
|
+
--spacing-tooltip-y: 4px;
|
|
217
|
+
|
|
218
|
+
/* Heights */
|
|
219
|
+
--height-btn-sm: 32px;
|
|
220
|
+
--height-btn-md: 40px;
|
|
221
|
+
--height-btn-lg: 48px;
|
|
222
|
+
--height-input: 44px;
|
|
223
|
+
--height-control: 36px; /* single-line / square small control */
|
|
224
|
+
|
|
225
|
+
/* ── Interaction ────────────────────────────────────────────────── */
|
|
226
|
+
--transition-duration: 150ms;
|
|
227
|
+
--transition-fast: 100ms;
|
|
228
|
+
--transition-slow: 250ms;
|
|
229
|
+
--hover-scale: 1;
|
|
230
|
+
--active-scale: 0.98;
|
|
231
|
+
--focus-ring-width: 2px;
|
|
232
|
+
--focus-ring-offset: 2px;
|
|
233
|
+
--focus-ring-color: var(--color-primary);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/* ═══════════════════════════════════════════════════════════════════════
|
|
237
|
+
TIER 1 — Dark mode color overrides
|
|
238
|
+
Activated when [data-theme="dark"] is set on <html> (by the init
|
|
239
|
+
script + ThemeProvider).
|
|
240
|
+
═══════════════════════════════════════════════════════════════════════ */
|
|
241
|
+
|
|
242
|
+
[data-theme='dark'] {
|
|
243
|
+
color-scheme: dark;
|
|
244
|
+
|
|
245
|
+
--color-background: #0f0f0f;
|
|
246
|
+
--color-foreground: #f3f4f6;
|
|
247
|
+
--color-surface: #1a1a1a;
|
|
248
|
+
--color-surface-soft: #161616;
|
|
249
|
+
--color-canvas: #0e0e0e; /* recessed board behind raised cards (planning canvas) */
|
|
250
|
+
|
|
251
|
+
--color-ink: #f3f4f6;
|
|
252
|
+
--color-charcoal: #e5e5e5;
|
|
253
|
+
--color-slate: #a4a097;
|
|
254
|
+
--color-steel: #787671;
|
|
255
|
+
--color-stone: #5d5b54;
|
|
256
|
+
--color-muted: #1a1a1a;
|
|
257
|
+
--color-muted-foreground: #a4a097;
|
|
258
|
+
|
|
259
|
+
--color-hairline: #2a2a2a;
|
|
260
|
+
--color-hairline-soft: #1f1f1f;
|
|
261
|
+
--color-hairline-strong: #3a3a3a;
|
|
262
|
+
--color-border: #2a2a2a;
|
|
263
|
+
|
|
264
|
+
/* Primary stays close to brand; brightened from the light theme so the accent
|
|
265
|
+
reads as TEXT/icon on the near-black canvas (#7b6ce5 on #0f0f0f ≈ 4.67:1, AA
|
|
266
|
+
text). But white text ON this as a FILL is only 4.1:1 — so filled accent
|
|
267
|
+
buttons use the darker --color-primary-fill below instead (MOTIR-757). */
|
|
268
|
+
--color-primary: #7b6ce5;
|
|
269
|
+
--color-primary-foreground: #ffffff;
|
|
270
|
+
--color-primary-pressed: #5645d4;
|
|
271
|
+
/* Darker accent fill for dark theme: white label text on #6c5cdd is 4.99:1
|
|
272
|
+
(AA), while #6c5cdd still clears 3:1 as a fill against the dark canvas. */
|
|
273
|
+
--color-primary-fill: #6c5cdd;
|
|
274
|
+
|
|
275
|
+
/* Pastel tints dimmed for dark backgrounds */
|
|
276
|
+
--color-tint-peach: #3a2a1a;
|
|
277
|
+
--color-tint-rose: #3a1f2a;
|
|
278
|
+
--color-tint-mint: #1a2f25;
|
|
279
|
+
--color-tint-lavender: #2a253a;
|
|
280
|
+
--color-tint-sky: #1a2a3a;
|
|
281
|
+
--color-tint-yellow: #3a341a;
|
|
282
|
+
|
|
283
|
+
--color-link: #58a6ff;
|
|
284
|
+
--color-link-pressed: #79b8ff;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/* Explicit LIGHT re-assertion. Light is the implicit base (the @theme defaults
|
|
288
|
+
above), so <html> needs no `data-theme='light'` — it omits the attribute. This
|
|
289
|
+
block exists so a NESTED subtree can force light back ON even when an ancestor
|
|
290
|
+
(<html>) is dark — the symmetric counterpart to [data-theme='dark'], mirroring
|
|
291
|
+
how [data-palette]/[data-style]/[data-type] already re-skin a subtree. The
|
|
292
|
+
onboarding design step (MOTIR-1040) uses it to preview the user's project in
|
|
293
|
+
light regardless of Motir's own theme. Values mirror the @theme light base. */
|
|
294
|
+
[data-theme='light'] {
|
|
295
|
+
color-scheme: light;
|
|
296
|
+
|
|
297
|
+
--color-background: #ffffff;
|
|
298
|
+
--color-foreground: #1a1a1a;
|
|
299
|
+
--color-surface: #f6f5f4;
|
|
300
|
+
--color-surface-soft: #fafaf9;
|
|
301
|
+
|
|
302
|
+
--color-ink: #1a1a1a;
|
|
303
|
+
--color-charcoal: #37352f;
|
|
304
|
+
--color-slate: #5d5b54;
|
|
305
|
+
--color-steel: #787671;
|
|
306
|
+
--color-stone: #a4a097;
|
|
307
|
+
--color-muted: #f3f4f6;
|
|
308
|
+
--color-muted-foreground: #787671;
|
|
309
|
+
|
|
310
|
+
--color-hairline: #e5e3df;
|
|
311
|
+
--color-hairline-soft: #ede9e4;
|
|
312
|
+
--color-hairline-strong: #c8c4be;
|
|
313
|
+
--color-border: #e5e3df;
|
|
314
|
+
|
|
315
|
+
--color-primary: #5645d4;
|
|
316
|
+
--color-primary-foreground: #ffffff;
|
|
317
|
+
--color-primary-pressed: #4534b3;
|
|
318
|
+
--color-primary-fill: #5645d4;
|
|
319
|
+
|
|
320
|
+
--color-tint-peach: #ffe8d4;
|
|
321
|
+
--color-tint-rose: #fde0ec;
|
|
322
|
+
--color-tint-mint: #d9f3e1;
|
|
323
|
+
--color-tint-lavender: #e6e0f5;
|
|
324
|
+
--color-tint-sky: #dcecfa;
|
|
325
|
+
--color-tint-yellow: #fef7d6;
|
|
326
|
+
|
|
327
|
+
--color-link: #0070d2;
|
|
328
|
+
--color-link-pressed: #005bab;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/* ═══════════════════════════════════════════════════════════════════════
|
|
332
|
+
TIER 2 — Named-style overrides ([data-style], Axis 2)
|
|
333
|
+
`warm-editorial` is the base (the Tier-0 defaults above — Notion-sober
|
|
334
|
+
rectangles), so it needs no override block.
|
|
335
|
+
`soft-playful` adopts Figma's pill buttons + larger radius + softer shadows
|
|
336
|
+
+ roomier spacing + a gentler motion feel. Each later style (Swiss,
|
|
337
|
+
Neo-Brutalism, Glassmorphism, …) adds its own `[data-style='<id>']` block
|
|
338
|
+
here, overriding ONLY shape/feel tokens (radius / shadow / spacing / sizing /
|
|
339
|
+
motion / type) — NEVER a colour token (that is the data-palette axis).
|
|
340
|
+
═══════════════════════════════════════════════════════════════════════ */
|
|
341
|
+
|
|
342
|
+
[data-style='soft-playful'] {
|
|
343
|
+
/* Figma's pill geometry */
|
|
344
|
+
--radius-btn: 9999px; /* pill */
|
|
345
|
+
--radius-input: 24px;
|
|
346
|
+
--radius-card: 24px;
|
|
347
|
+
--radius-modal: 32px;
|
|
348
|
+
--radius-badge: 9999px;
|
|
349
|
+
--radius-control: 12px; /* small affordances round up with the rest */
|
|
350
|
+
--radius-kbd: 8px;
|
|
351
|
+
|
|
352
|
+
/* Softer, more diffused shadows */
|
|
353
|
+
--shadow-card: 0 2px 8px rgba(15, 15, 15, 0.06);
|
|
354
|
+
--shadow-elevated: 0 6px 20px rgba(15, 15, 15, 0.1);
|
|
355
|
+
--shadow-modal: 0 12px 40px rgba(15, 15, 15, 0.15);
|
|
356
|
+
|
|
357
|
+
/* Roomier spacing */
|
|
358
|
+
--spacing-btn-x: 20px;
|
|
359
|
+
--spacing-btn-y: 12px;
|
|
360
|
+
--spacing-input-x: 18px;
|
|
361
|
+
--spacing-input-y: 14px;
|
|
362
|
+
--spacing-card-padding: 28px;
|
|
363
|
+
--spacing-control-x: 14px;
|
|
364
|
+
--spacing-control-y: 8px;
|
|
365
|
+
--spacing-icon-btn: 8px;
|
|
366
|
+
--spacing-chip-x: 14px;
|
|
367
|
+
--spacing-chip-y: 4px;
|
|
368
|
+
--spacing-kbd-x: 8px;
|
|
369
|
+
--spacing-kbd-y: 3px;
|
|
370
|
+
--spacing-tooltip-x: 12px;
|
|
371
|
+
--spacing-tooltip-y: 6px;
|
|
372
|
+
--height-control: 40px;
|
|
373
|
+
|
|
374
|
+
/* Slightly slower transitions for softer feel */
|
|
375
|
+
--transition-duration: 200ms;
|
|
376
|
+
--active-scale: 0.97;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/* ─────────────────────────────────────────────────────────────────────────
|
|
380
|
+
Swiss / Minimal-Flat — the International-Typographic style (7.3.33).
|
|
381
|
+
Structural, calm, gridded: hard right angles (sharp 2px corners), every
|
|
382
|
+
shadow removed (truly flat — depth is carried by hairline rules +
|
|
383
|
+
whitespace), a restrained neo-grotesque sans for headlines too (the serif
|
|
384
|
+
is swapped to the sans stack — a type token, not colour), tight/crisp
|
|
385
|
+
control density against generous card whitespace, and minimal motion (fast,
|
|
386
|
+
no press-scale). Overrides ONLY shape/feel tokens — never a colour token
|
|
387
|
+
(that is the data-palette axis; this style wears whatever palette is active).
|
|
388
|
+
The differentiator vs the planned Neo-Brutalism style: Swiss is REFINED and
|
|
389
|
+
calm (hairline borders, no shadow), not harsh (thick borders, hard offset
|
|
390
|
+
shadows). See docs/styles/swiss-minimal-flat.md. ───────────────────────── */
|
|
391
|
+
[data-style='swiss-minimal-flat'] {
|
|
392
|
+
/* Silhouette — hard right angles. Sharp 2px on every surface; --radius-pill
|
|
393
|
+
is left untouched so genuinely-circular affordances (avatars, dots) stay
|
|
394
|
+
round. */
|
|
395
|
+
--radius-xs: 1px;
|
|
396
|
+
--radius-sm: 2px;
|
|
397
|
+
--radius-md: 2px;
|
|
398
|
+
--radius-lg: 2px;
|
|
399
|
+
--radius-xl: 3px;
|
|
400
|
+
--radius-btn: 2px;
|
|
401
|
+
--radius-input: 2px;
|
|
402
|
+
--radius-card: 2px;
|
|
403
|
+
--radius-modal: 2px;
|
|
404
|
+
--radius-badge: 2px; /* rectangular status chips — NOT pills (the Swiss silhouette) */
|
|
405
|
+
--radius-control: 2px;
|
|
406
|
+
--radius-kbd: 2px;
|
|
407
|
+
|
|
408
|
+
/* Elevation — flat. Every surface drops its shadow; structure is drawn with
|
|
409
|
+
hairline borders + whitespace, never lift. */
|
|
410
|
+
--shadow-subtle: none;
|
|
411
|
+
--shadow-card: none;
|
|
412
|
+
--shadow-elevated: none;
|
|
413
|
+
--shadow-modal: none;
|
|
414
|
+
--shadow-hero-mockup: none;
|
|
415
|
+
|
|
416
|
+
/* Typography is the data-type axis now — Swiss ships `defaultTypeId:
|
|
417
|
+
'motir-sans'` (sans headlines), applied via the [data-type='motir-sans']
|
|
418
|
+
block below. A [data-style] block sets NO --font-* token (disjointness). */
|
|
419
|
+
|
|
420
|
+
/* Density — tight, gridded controls; generous card whitespace. */
|
|
421
|
+
--spacing-btn-x: 16px;
|
|
422
|
+
--spacing-btn-y: 9px;
|
|
423
|
+
--spacing-input-x: 14px;
|
|
424
|
+
--spacing-input-y: 11px;
|
|
425
|
+
--spacing-card-padding: 28px;
|
|
426
|
+
--spacing-control-x: 10px;
|
|
427
|
+
--spacing-control-y: 6px;
|
|
428
|
+
--height-control: 34px;
|
|
429
|
+
--height-input: 42px;
|
|
430
|
+
|
|
431
|
+
/* Motion — minimal and calm. Fast transitions, NO press-scale bounce. */
|
|
432
|
+
--transition-duration: 100ms;
|
|
433
|
+
--transition-fast: 80ms;
|
|
434
|
+
--transition-slow: 160ms;
|
|
435
|
+
--hover-scale: 1;
|
|
436
|
+
--active-scale: 1;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/* ─────────────────────────────────────────────────────────────────────────
|
|
440
|
+
Glassmorphism — translucent frosted glass over a vibrant gradient (7.3.35).
|
|
441
|
+
The FIRST surface-MATERIAL style: where Swiss / Soft-Playful only re-shape
|
|
442
|
+
the silhouette, glass re-shapes the SURFACE itself — frosted backdrop-blur
|
|
443
|
+
panels, a soft palette-derived gradient canvas, light 1px borders, layered
|
|
444
|
+
diffuse depth, a rounded silhouette, and gentle motion.
|
|
445
|
+
|
|
446
|
+
It ships in TWO parts (the style contract was extended for this — see
|
|
447
|
+
docs/styles/glassmorphism.md + DESIGN.md):
|
|
448
|
+
1. This [data-style] TOKEN BLOCK — shape/feel tokens ONLY (radius / shadow /
|
|
449
|
+
motion / density) PLUS palette-AGNOSTIC material SCALARS (blur radius,
|
|
450
|
+
surface/border ALPHAs, wash strength). No hue lives here, so it stays
|
|
451
|
+
disjoint from the palette axis — still enforced by styleRegistry.test.ts.
|
|
452
|
+
2. A palette-DERIVED MATERIAL LAYER (the [data-surface] rules just below)
|
|
453
|
+
that turns those scalars into frosted translucency + the gradient canvas
|
|
454
|
+
via color-mix()/alpha OVER the active palette tokens, introducing NO new
|
|
455
|
+
hue — so "style x palette" stays orthogonal (swap the palette and the
|
|
456
|
+
glass re-tints; swap the style and the hues are untouched). This is the
|
|
457
|
+
surface-MATERIAL axis Swiss / Soft-Playful never needed.
|
|
458
|
+
───────────────────────────────────────────────────────────────────────── */
|
|
459
|
+
[data-style='glassmorphism'] {
|
|
460
|
+
/* Silhouette — rounded, soft (not pill). Generous radii read as "glass tile". */
|
|
461
|
+
--radius-xs: 6px;
|
|
462
|
+
--radius-sm: 8px;
|
|
463
|
+
--radius-md: 12px;
|
|
464
|
+
--radius-lg: 16px;
|
|
465
|
+
--radius-xl: 22px;
|
|
466
|
+
--radius-btn: 12px;
|
|
467
|
+
--radius-input: 12px;
|
|
468
|
+
--radius-card: 18px;
|
|
469
|
+
--radius-modal: 22px;
|
|
470
|
+
--radius-badge: 9999px; /* chips stay pills — soft is the glass register */
|
|
471
|
+
--radius-control: 10px;
|
|
472
|
+
--radius-kbd: 6px;
|
|
473
|
+
|
|
474
|
+
/* Elevation — layered, diffuse, soft. Glass FLOATS: bigger, softer, lower-
|
|
475
|
+
opacity shadows than the base, so panels read as hovering sheets. (rgba
|
|
476
|
+
literals are neutral shadow ink, not a palette hue.) */
|
|
477
|
+
--shadow-subtle: 0 1px 2px rgba(15, 15, 15, 0.05);
|
|
478
|
+
--shadow-card: 0 8px 32px -8px rgba(15, 15, 15, 0.18);
|
|
479
|
+
--shadow-elevated: 0 16px 48px -12px rgba(15, 15, 15, 0.24);
|
|
480
|
+
--shadow-modal: 0 28px 70px -16px rgba(15, 15, 15, 0.34);
|
|
481
|
+
--shadow-hero-mockup: 0 32px 80px -16px rgba(15, 15, 15, 0.38);
|
|
482
|
+
|
|
483
|
+
/* Density — comfortable, a touch roomy (glass wants air around its tiles). */
|
|
484
|
+
--spacing-btn-x: 20px;
|
|
485
|
+
--spacing-btn-y: 11px;
|
|
486
|
+
--spacing-card-padding: 26px;
|
|
487
|
+
--spacing-control-x: 12px;
|
|
488
|
+
--spacing-control-y: 7px;
|
|
489
|
+
--height-control: 38px;
|
|
490
|
+
|
|
491
|
+
/* Motion — gentle, smooth (glass slides; never snaps). */
|
|
492
|
+
--transition-duration: 220ms;
|
|
493
|
+
--transition-slow: 320ms;
|
|
494
|
+
--active-scale: 0.98;
|
|
495
|
+
|
|
496
|
+
/* ── Material SCALARS — palette-AGNOSTIC (no hue, so no colour token here).
|
|
497
|
+
Consumed only by the [data-surface] material layer below; they are the
|
|
498
|
+
"glass recipe": how far to blur, and how much of the palette surface to
|
|
499
|
+
keep opaque (the rest lets the blurred gradient show through). ── */
|
|
500
|
+
--glass-blur: 18px; /* frosted-panel backdrop blur */
|
|
501
|
+
--glass-blur-strong: 30px; /* modal / overlay blur — more separation */
|
|
502
|
+
/* Lower opacity than before: real glass is TRANSLUCENT, so the vibrant canvas
|
|
503
|
+
shows THROUGH the panel (the old 64% read as near-solid white = "flat with a
|
|
504
|
+
little blur"). */
|
|
505
|
+
--glass-surface-alpha: 50%; /* opacity of a frosted card / popover */
|
|
506
|
+
--glass-modal-alpha: 56%; /* the modal panel is GLASS too — translucent enough
|
|
507
|
+
that the frosted page shows through it (only a
|
|
508
|
+
touch more opaque than a card, for its body text) */
|
|
509
|
+
--glass-chrome-alpha: 52%; /* sidebar / chrome translucency */
|
|
510
|
+
--glass-input-alpha: 44%; /* form-control fill translucency */
|
|
511
|
+
--glass-border-alpha: 72%; /* the glass RIM — a more visible lit edge */
|
|
512
|
+
--glass-wash: 42%; /* MUCH stronger canvas tint — glass needs vibrant colour
|
|
513
|
+
behind it to refract; a near-white canvas looks flat */
|
|
514
|
+
--glass-saturate: 180%; /* punchier refraction (was a fixed 150%) */
|
|
515
|
+
/* The specular SHEEN + RIM light — white, palette-agnostic (glass catches
|
|
516
|
+
light regardless of hue); used by the material layer, never as a fill. */
|
|
517
|
+
--glass-sheen: rgba(255, 255, 255, 0.22);
|
|
518
|
+
--glass-rim: rgba(255, 255, 255, 0.55);
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
/* ─────────────────────────────────────────────────────────────────────────
|
|
522
|
+
Glassmorphism MATERIAL LAYER — palette-DERIVED surface treatment (7.3.35).
|
|
523
|
+
The style-contract EXTENSION: a style may own its surface MATERIAL in these
|
|
524
|
+
STYLE-SCOPED component rules as long as every colour is DERIVED from the
|
|
525
|
+
active palette via color-mix()/var() (never a raw hue) — so the style axis
|
|
526
|
+
stays disjoint from the palette axis. Surfaces are tagged with the
|
|
527
|
+
`data-surface` hook the shared primitives now emit (Card / Modal / Popover /
|
|
528
|
+
Sidebar / Input). Enforced by the "surface-material layer" test in
|
|
529
|
+
tests/theme/styleRegistry.test.ts. These are descendant-scoped selectors, so
|
|
530
|
+
they are NOT the bare [data-style] token block and never carry a hue token
|
|
531
|
+
there — the disjoint-axis token-block test still passes. ───────────────── */
|
|
532
|
+
|
|
533
|
+
/* The canvas: the palette's page bg + three soft corner washes. The hue sources
|
|
534
|
+
are the `--el-*` accent roles (NOT Tier-0 `--color-*`), so the washes re-tint
|
|
535
|
+
with the active palette — the `data-palette` axis (7.3.48 / MOTIR-1065)
|
|
536
|
+
overrides `--el-*`, never `--color-*`, so reading `--el-*` here is what keeps
|
|
537
|
+
the glass orthogonal to the palette. The gradient is what the frosted panels
|
|
538
|
+
blur — without it, the blur is invisible. */
|
|
539
|
+
[data-style='glassmorphism'] body {
|
|
540
|
+
background-image:
|
|
541
|
+
radial-gradient(
|
|
542
|
+
85% 80% at 6% -12%,
|
|
543
|
+
color-mix(in srgb, var(--el-accent) var(--glass-wash), transparent),
|
|
544
|
+
transparent 62%
|
|
545
|
+
),
|
|
546
|
+
radial-gradient(
|
|
547
|
+
80% 75% at 110% 4%,
|
|
548
|
+
color-mix(in srgb, var(--el-highlight) var(--glass-wash), transparent),
|
|
549
|
+
transparent 60%
|
|
550
|
+
),
|
|
551
|
+
radial-gradient(
|
|
552
|
+
95% 90% at 50% 120%,
|
|
553
|
+
color-mix(in srgb, var(--el-link) var(--glass-wash), transparent),
|
|
554
|
+
transparent 62%
|
|
555
|
+
),
|
|
556
|
+
radial-gradient(
|
|
557
|
+
60% 60% at 85% 95%,
|
|
558
|
+
color-mix(in srgb, var(--el-accent) var(--glass-wash), transparent),
|
|
559
|
+
transparent 60%
|
|
560
|
+
);
|
|
561
|
+
background-attachment: fixed;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
/* Frosted container surfaces — cards & popovers float as translucent sheets. */
|
|
565
|
+
[data-style='glassmorphism'] [data-surface='card'],
|
|
566
|
+
[data-style='glassmorphism'] [data-surface='popover'] {
|
|
567
|
+
/* The translucent fill lets the vibrant canvas through; the linear sheen is
|
|
568
|
+
the light raking across the glass (top-left → fading), the inset RIM is the
|
|
569
|
+
lit glass edge, and the soft drop makes the sheet FLOAT. White light is
|
|
570
|
+
palette-agnostic; the colour still comes only from the palette-derived
|
|
571
|
+
fill/border, so the axes stay disjoint. */
|
|
572
|
+
background-color: color-mix(in srgb, var(--el-page-bg) var(--glass-surface-alpha), transparent);
|
|
573
|
+
background-image: linear-gradient(150deg, var(--glass-sheen), transparent 55%);
|
|
574
|
+
border-color: color-mix(in srgb, var(--el-border) var(--glass-border-alpha), transparent);
|
|
575
|
+
box-shadow:
|
|
576
|
+
inset 0 1px 0 0 var(--glass-rim),
|
|
577
|
+
inset 0 0 0 1px rgba(255, 255, 255, 0.06),
|
|
578
|
+
0 8px 32px -8px rgba(15, 15, 15, 0.18);
|
|
579
|
+
-webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
|
|
580
|
+
backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
/* Modal panel — a touch more opaque + a stronger blur (body-text AA + lift). */
|
|
584
|
+
[data-style='glassmorphism'] [data-surface='modal'] {
|
|
585
|
+
background-color: color-mix(in srgb, var(--el-page-bg) var(--glass-modal-alpha), transparent);
|
|
586
|
+
background-image: linear-gradient(150deg, var(--glass-sheen), transparent 55%);
|
|
587
|
+
border-color: color-mix(in srgb, var(--el-border) var(--glass-border-alpha), transparent);
|
|
588
|
+
box-shadow:
|
|
589
|
+
inset 0 1px 0 0 var(--glass-rim),
|
|
590
|
+
0 28px 70px -16px rgba(15, 15, 15, 0.34);
|
|
591
|
+
-webkit-backdrop-filter: blur(var(--glass-blur-strong)) saturate(var(--glass-saturate));
|
|
592
|
+
backdrop-filter: blur(var(--glass-blur-strong)) saturate(var(--glass-saturate));
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
/* The sidebar rail — frosted chrome over the canvas. */
|
|
596
|
+
[data-style='glassmorphism'] [data-surface='sidebar'] {
|
|
597
|
+
background-color: color-mix(in srgb, var(--el-sidebar-bg) var(--glass-chrome-alpha), transparent);
|
|
598
|
+
-webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
|
|
599
|
+
backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
/* Form controls — a translucent fill so inputs read as glass too. */
|
|
603
|
+
[data-style='glassmorphism'] [data-surface='input'] {
|
|
604
|
+
background-color: color-mix(in srgb, var(--el-page-bg) var(--glass-input-alpha), transparent);
|
|
605
|
+
-webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
|
|
606
|
+
backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
/* The MODAL / command-palette BACKDROP — the most important glass surface, since
|
|
610
|
+
a modal floats over real page content. Under glass it is NOT a dark scrim: a
|
|
611
|
+
light, palette-derived veil + a page BLUR, so the whole page behind frosts and
|
|
612
|
+
the (also-frosted) modal panel refracts it — the proper glass-modal look.
|
|
613
|
+
Overrides the primitives' `bg-black/40` utility. Palette-derived (no hue). */
|
|
614
|
+
[data-style='glassmorphism'] [data-surface='overlay'] {
|
|
615
|
+
background-color: color-mix(in srgb, var(--el-page-bg) 24%, transparent);
|
|
616
|
+
-webkit-backdrop-filter: blur(8px) saturate(var(--glass-saturate));
|
|
617
|
+
backdrop-filter: blur(8px) saturate(var(--glass-saturate));
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
/* ─────────────────────────────────────────────────────────────────────────
|
|
621
|
+
Neo-Brutalism — the raw, punchy, utilitarian style (7.3.34).
|
|
622
|
+
The LOUD end of the angular family — where Swiss is refined and calm, this is
|
|
623
|
+
harsh on purpose: hard 0px corners on every surface, heavy 2px solid borders
|
|
624
|
+
doing the structural work, and HARD-OFFSET drop shadows with ZERO blur (a
|
|
625
|
+
solid block of shadow, not a soft lift). Tight utilitarian density, raw
|
|
626
|
+
monospace headlines, and snappy mechanical motion (near-instant, no
|
|
627
|
+
press-scale). Overrides ONLY shape/feel tokens — never a colour token (that is
|
|
628
|
+
the data-palette axis; this style wears whatever palette is active, so the
|
|
629
|
+
active palette's AA contrast is preserved by construction). The heavy-border
|
|
630
|
+
axis has no Tier-0 token (components draw a 1px Tailwind `border`), so it is
|
|
631
|
+
carried by the style-scoped component-variant block that FOLLOWS this token
|
|
632
|
+
block. See docs/styles/neo-brutalism.md. ─────────────────────────────────── */
|
|
633
|
+
[data-style='neo-brutalism'] {
|
|
634
|
+
/* Silhouette — zero radius. Hard 0px corners on every surface; --radius-pill
|
|
635
|
+
is left untouched so genuinely-circular affordances (avatars, status dots,
|
|
636
|
+
the spinner) stay round. */
|
|
637
|
+
--radius-xs: 0;
|
|
638
|
+
--radius-sm: 0;
|
|
639
|
+
--radius-md: 0;
|
|
640
|
+
--radius-lg: 0;
|
|
641
|
+
--radius-xl: 0;
|
|
642
|
+
--radius-btn: 0;
|
|
643
|
+
--radius-input: 0;
|
|
644
|
+
--radius-card: 0;
|
|
645
|
+
--radius-modal: 0;
|
|
646
|
+
--radius-badge: 0; /* rectangular (non-pill) status chips — the brutalist block */
|
|
647
|
+
--radius-control: 0;
|
|
648
|
+
--radius-kbd: 0;
|
|
649
|
+
|
|
650
|
+
/* Elevation — HARD-OFFSET shadows, zero blur. A solid offset block of shadow
|
|
651
|
+
(Xpx Ypx 0 0) instead of a soft diffuse lift; the chunky frame is the whole
|
|
652
|
+
point. The shadow colour is a literal near-ink (matching how the base
|
|
653
|
+
shadows already hard-code their rgba) — NOT a palette token, so the colour
|
|
654
|
+
axis stays untouched. */
|
|
655
|
+
--shadow-subtle: 2px 2px 0 0 rgba(15, 15, 15, 1);
|
|
656
|
+
--shadow-card: 4px 4px 0 0 rgba(15, 15, 15, 1);
|
|
657
|
+
--shadow-elevated: 6px 6px 0 0 rgba(15, 15, 15, 1);
|
|
658
|
+
--shadow-modal: 8px 8px 0 0 rgba(15, 15, 15, 1);
|
|
659
|
+
--shadow-hero-mockup: 12px 12px 0 0 rgba(15, 15, 15, 1);
|
|
660
|
+
|
|
661
|
+
/* Typography is the data-type axis now — Neo-Brutalism ships `defaultTypeId:
|
|
662
|
+
'motir-mono'` (mono headlines), applied via the [data-type='motir-mono']
|
|
663
|
+
block below. A [data-style] block sets NO --font-* token (disjointness). */
|
|
664
|
+
|
|
665
|
+
/* Density — tight, utilitarian. Chunky controls, compact card padding, no
|
|
666
|
+
wasted space. */
|
|
667
|
+
--spacing-btn-x: 16px;
|
|
668
|
+
--spacing-btn-y: 10px;
|
|
669
|
+
--spacing-input-x: 14px;
|
|
670
|
+
--spacing-input-y: 11px;
|
|
671
|
+
--spacing-card-padding: 20px;
|
|
672
|
+
--spacing-control-x: 10px;
|
|
673
|
+
--spacing-control-y: 6px;
|
|
674
|
+
--height-control: 36px;
|
|
675
|
+
--height-input: 44px;
|
|
676
|
+
|
|
677
|
+
/* Motion — snappy, mechanical. Near-instant transitions and NO press-scale;
|
|
678
|
+
the UI reacts, it does not ease. */
|
|
679
|
+
--transition-duration: 60ms;
|
|
680
|
+
--transition-fast: 40ms;
|
|
681
|
+
--transition-slow: 100ms;
|
|
682
|
+
--hover-scale: 1;
|
|
683
|
+
--active-scale: 1;
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
/* Neo-Brutalism — component-variant override: HEAVY borders (the deliverable's
|
|
687
|
+
"component-variant overrides" axis). There is no Tier-0 border-WIDTH token —
|
|
688
|
+
every primitive (Button outline, Card, Input, Modal, Pill, Popover, …) draws
|
|
689
|
+
a 1px Tailwind `.border`. This style-scoped rule thickens that full box border
|
|
690
|
+
to a loud 2px on every framed surface WITHOUT touching a colour token or any
|
|
691
|
+
component file, so the heaviness is real but stays entirely inside the
|
|
692
|
+
neo-brutalism scope (no other style is affected). It is intentionally a
|
|
693
|
+
descendant rule, not a bare token block, so the disjoint-axis test (which
|
|
694
|
+
inspects token blocks) does not treat it as one — and it sets no colour
|
|
695
|
+
anyway, so the two axes stay disjoint regardless. */
|
|
696
|
+
[data-style='neo-brutalism'] .border {
|
|
697
|
+
border-width: 2px;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
/* ─────────────────────────────────────────────────────────────────────────
|
|
701
|
+
Cybercore / Y2K — the neon-on-dark retro-tech style (7.3.36). Dark-first.
|
|
702
|
+
The identity is GLOW + GRID + MONO: surfaces are lit by an outward halo
|
|
703
|
+
(neon glow elevation) instead of a drop shadow, a faint tech grid textures
|
|
704
|
+
the canvas (see the body rule below), headlines render in mono for a
|
|
705
|
+
terminal read, corners are hard HUD frames, and motion is fast/electric.
|
|
706
|
+
Overrides ONLY shape/feel tokens — never a colour token (that is the
|
|
707
|
+
data-palette axis; the neon hue comes from the active palette / dark theme,
|
|
708
|
+
and this style amplifies it into a halo). The glow + grid are built from
|
|
709
|
+
`currentColor`, so they pick up whatever hue the palette provides and never
|
|
710
|
+
pin one — keeping the style/palette axes disjoint AND preserving the
|
|
711
|
+
palette's AA by construction (no text/background pair is touched). The
|
|
712
|
+
differentiator vs Swiss / Minimal-Flat: Swiss is flat + calm, Cybercore is
|
|
713
|
+
LIT + electric (glow rings, grid texture, mono headlines, snappy press).
|
|
714
|
+
See docs/styles/cybercore-y2k.md. ────────────────────────────────────── */
|
|
715
|
+
[data-style='cybercore-y2k'] {
|
|
716
|
+
/* Silhouette — hard HUD/terminal frames. Sharp small corners on every
|
|
717
|
+
surface; --radius-pill stays untouched so circular affordances (avatars,
|
|
718
|
+
status dots, the spinner) stay round. */
|
|
719
|
+
--radius-xs: 1px;
|
|
720
|
+
--radius-sm: 2px;
|
|
721
|
+
--radius-md: 3px;
|
|
722
|
+
--radius-lg: 4px;
|
|
723
|
+
--radius-xl: 4px;
|
|
724
|
+
--radius-btn: 3px;
|
|
725
|
+
--radius-input: 3px;
|
|
726
|
+
--radius-card: 4px;
|
|
727
|
+
--radius-modal: 4px;
|
|
728
|
+
--radius-badge: 2px; /* rectangular HUD chips — not pills */
|
|
729
|
+
--radius-control: 2px;
|
|
730
|
+
--radius-kbd: 2px;
|
|
731
|
+
|
|
732
|
+
/* Elevation — NEON GLOW, not drop-shadow. Every surface emits an outward
|
|
733
|
+
halo built from currentColor (the active hue), so it reads as lit rather
|
|
734
|
+
than lifted; the 1px ring carries the "stroke" (lit edge) too. No colour
|
|
735
|
+
token is referenced — only currentColor, so the palette stays in charge of
|
|
736
|
+
the hue and AA is untouched. */
|
|
737
|
+
--shadow-subtle: 0 0 0 1px color-mix(in srgb, currentColor 9%, transparent);
|
|
738
|
+
--shadow-card:
|
|
739
|
+
0 0 0 1px color-mix(in srgb, currentColor 12%, transparent),
|
|
740
|
+
0 0 14px -3px color-mix(in srgb, currentColor 24%, transparent);
|
|
741
|
+
--shadow-elevated:
|
|
742
|
+
0 0 0 1px color-mix(in srgb, currentColor 16%, transparent),
|
|
743
|
+
0 0 22px -4px color-mix(in srgb, currentColor 32%, transparent);
|
|
744
|
+
--shadow-modal:
|
|
745
|
+
0 0 0 1px color-mix(in srgb, currentColor 20%, transparent),
|
|
746
|
+
0 0 40px -6px color-mix(in srgb, currentColor 40%, transparent);
|
|
747
|
+
--shadow-hero-mockup:
|
|
748
|
+
0 0 0 1px color-mix(in srgb, currentColor 22%, transparent),
|
|
749
|
+
0 0 56px -8px color-mix(in srgb, currentColor 45%, transparent);
|
|
750
|
+
|
|
751
|
+
/* Typography is the data-type axis now — Cybercore ships `defaultTypeId:
|
|
752
|
+
'motir-mono'` (mono headlines), applied via the [data-type='motir-mono']
|
|
753
|
+
block below. A [data-style] block sets NO --font-* token (disjointness). */
|
|
754
|
+
|
|
755
|
+
/* Density — tight, technical HUD rhythm: compact controls, snug card pad. */
|
|
756
|
+
--spacing-btn-x: 18px;
|
|
757
|
+
--spacing-btn-y: 9px;
|
|
758
|
+
--spacing-input-x: 14px;
|
|
759
|
+
--spacing-input-y: 11px;
|
|
760
|
+
--spacing-card-padding: 20px;
|
|
761
|
+
--spacing-control-x: 10px;
|
|
762
|
+
--spacing-control-y: 6px;
|
|
763
|
+
--height-control: 34px;
|
|
764
|
+
--height-input: 42px;
|
|
765
|
+
|
|
766
|
+
/* Motion — snappy and electric. Very fast transitions, crisp press-scale. */
|
|
767
|
+
--transition-duration: 90ms;
|
|
768
|
+
--transition-fast: 60ms;
|
|
769
|
+
--transition-slow: 140ms;
|
|
770
|
+
--hover-scale: 1;
|
|
771
|
+
--active-scale: 0.97;
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
/* Cybercore surface texture — the tech grid (the "surface treatment" feel
|
|
775
|
+
axis). A component-variant override, NOT a [data-style] token block, so it
|
|
776
|
+
can carry the faint grid lines: built from currentColor at a very low alpha
|
|
777
|
+
so it adapts to the active palette and sits BEHIND content (text never
|
|
778
|
+
renders on it), keeping AA intact. Dark-first — on the dark theme this reads
|
|
779
|
+
as a Tron grid over the near-black canvas. */
|
|
780
|
+
[data-style='cybercore-y2k'] body {
|
|
781
|
+
background-image:
|
|
782
|
+
linear-gradient(color-mix(in srgb, currentColor 6%, transparent) 1px, transparent 1px),
|
|
783
|
+
linear-gradient(90deg, color-mix(in srgb, currentColor 6%, transparent) 1px, transparent 1px);
|
|
784
|
+
background-size: 32px 32px;
|
|
785
|
+
background-position: center top;
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
/* ─────────────────────────────────────────────────────────────────────────
|
|
789
|
+
Aurora — soft, DRIFTING aurora light behind fluid, gently-glowing surfaces
|
|
790
|
+
(7.3.38). The third surface-MATERIAL style (after glassmorphism + cybercore):
|
|
791
|
+
where glass re-shapes the surface into frosted TRANSLUCENCY and cybercore lits
|
|
792
|
+
it with a sharp neon ring, Aurora keeps surfaces OPAQUE and FLUID and moves the
|
|
793
|
+
identity onto (a) a slowly-animated, palette-derived aurora gradient canvas —
|
|
794
|
+
ribbons of light that drift like the northern lights — and (b) a gentle,
|
|
795
|
+
palette-derived COLOUR glow halo on each panel, so surfaces feel lit from
|
|
796
|
+
within rather than lifted by a hard shadow.
|
|
797
|
+
|
|
798
|
+
It ships in TWO parts, the same surface-material contract glassmorphism
|
|
799
|
+
established (see docs/styles/aurora.md + DESIGN.md):
|
|
800
|
+
1. This [data-style] TOKEN BLOCK — shape/feel tokens ONLY (fluid radii, soft
|
|
801
|
+
NEUTRAL shadows, smooth slow motion, roomy density) PLUS palette-AGNOSTIC
|
|
802
|
+
material SCALARS (wash strengths, surface sheen, the drift duration). No
|
|
803
|
+
hue lives here, so it stays disjoint from the palette axis — still
|
|
804
|
+
enforced by the token-block test in styleRegistry.test.ts.
|
|
805
|
+
2. A palette-DERIVED MATERIAL LAYER (the rules below) that paints the animated
|
|
806
|
+
aurora canvas + the per-surface colour glow via color-mix() OVER the
|
|
807
|
+
active palette tokens (--el-*), introducing NO new hue — so "style x
|
|
808
|
+
palette" stays orthogonal (swap the palette and the aurora re-tints; swap
|
|
809
|
+
the style and the hues are untouched). Surfaces opt in via the same
|
|
810
|
+
`data-surface` hook the shared primitives emit.
|
|
811
|
+
───────────────────────────────────────────────────────────────────────── */
|
|
812
|
+
[data-style='aurora'] {
|
|
813
|
+
/* Silhouette — fluid, soft. A touch larger/rounder than glass so panels read
|
|
814
|
+
as organic, flowing forms. --radius-pill stays untouched so circular
|
|
815
|
+
affordances (avatars, status dots, the spinner) stay round. */
|
|
816
|
+
--radius-xs: 7px;
|
|
817
|
+
--radius-sm: 10px;
|
|
818
|
+
--radius-md: 14px;
|
|
819
|
+
--radius-lg: 18px;
|
|
820
|
+
--radius-xl: 24px;
|
|
821
|
+
--radius-btn: 14px;
|
|
822
|
+
--radius-input: 14px;
|
|
823
|
+
--radius-card: 20px;
|
|
824
|
+
--radius-modal: 26px;
|
|
825
|
+
--radius-badge: 9999px; /* chips stay pills — soft is the aurora register */
|
|
826
|
+
--radius-control: 12px;
|
|
827
|
+
--radius-kbd: 7px;
|
|
828
|
+
|
|
829
|
+
/* Elevation — a soft NEUTRAL lift only. The colour glow halo is palette-DERIVED
|
|
830
|
+
and so lives in the material layer below (not here, where no hue may appear);
|
|
831
|
+
these neutral rgba shadows are the diffuse base lift the glow sits on top of.
|
|
832
|
+
(rgba literals are neutral shadow ink — a cool near-black — not a palette
|
|
833
|
+
hue, so the token block stays colour-free.) */
|
|
834
|
+
--shadow-subtle: 0 1px 2px rgba(20, 20, 45, 0.05);
|
|
835
|
+
--shadow-card: 0 8px 30px -12px rgba(20, 20, 45, 0.16);
|
|
836
|
+
--shadow-elevated: 0 16px 44px -14px rgba(20, 20, 45, 0.22);
|
|
837
|
+
--shadow-modal: 0 26px 64px -18px rgba(20, 20, 45, 0.3);
|
|
838
|
+
--shadow-hero-mockup: 0 32px 78px -18px rgba(20, 20, 45, 0.34);
|
|
839
|
+
|
|
840
|
+
/* Density — comfortable, a touch roomy (the light wants room to breathe). */
|
|
841
|
+
--spacing-btn-x: 20px;
|
|
842
|
+
--spacing-btn-y: 11px;
|
|
843
|
+
--spacing-card-padding: 26px;
|
|
844
|
+
--spacing-control-x: 12px;
|
|
845
|
+
--spacing-control-y: 7px;
|
|
846
|
+
--height-control: 38px;
|
|
847
|
+
|
|
848
|
+
/* Motion — smooth and slow. Transitions glide; nothing snaps. */
|
|
849
|
+
--transition-duration: 260ms;
|
|
850
|
+
--transition-slow: 380ms;
|
|
851
|
+
--active-scale: 0.985;
|
|
852
|
+
|
|
853
|
+
/* ── Material SCALARS — palette-AGNOSTIC (no hue, so no colour token here).
|
|
854
|
+
Consumed only by the [data-style='aurora'] material rules below: how strong
|
|
855
|
+
each aurora ribbon tints the canvas, the faint top-sheen on a surface, the
|
|
856
|
+
glow-halo strengths, and how slowly the canvas drifts. ── */
|
|
857
|
+
--aurora-wash: 22%; /* primary ribbon tint over the page canvas */
|
|
858
|
+
--aurora-wash-soft: 15%; /* the cooler secondary ribbon (green/teal band) */
|
|
859
|
+
--aurora-sheen: 7%; /* faint lit-from-above sheen on an opaque surface */
|
|
860
|
+
--aurora-glow: 22%; /* colour-glow halo strength on a card / popover */
|
|
861
|
+
--aurora-glow-strong: 30%; /* the modal's slightly deeper halo */
|
|
862
|
+
--aurora-drift-duration: 28s; /* one slow breath of the drifting canvas */
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
/* ─────────────────────────────────────────────────────────────────────────
|
|
866
|
+
Aurora MATERIAL LAYER — palette-DERIVED surface treatment (7.3.38).
|
|
867
|
+
The same style-contract extension glassmorphism uses: a style may own its
|
|
868
|
+
surface material in STYLE-SCOPED component rules as long as every colour is
|
|
869
|
+
DERIVED from the active palette via color-mix()/var() (never a raw hue) — so
|
|
870
|
+
the style axis stays disjoint from the palette axis. These descendant-scoped
|
|
871
|
+
selectors are NOT the bare token block, so they never trip the colour-free
|
|
872
|
+
token-block test; they ARE read as material rules and must be palette-derived
|
|
873
|
+
(enforced by the "surface-material layer" test in styleRegistry.test.ts).
|
|
874
|
+
───────────────────────────────────────────────────────────────────────── */
|
|
875
|
+
|
|
876
|
+
/* The canvas: the palette's page bg + four soft aurora ribbons that slowly
|
|
877
|
+
DRIFT. Hues come from the `--el-*` accent roles (NOT Tier-0 `--color-*`), so
|
|
878
|
+
the ribbons re-tint with the active palette — the `data-palette` axis overrides
|
|
879
|
+
`--el-*`, never `--color-*`, which is what keeps the aurora orthogonal to the
|
|
880
|
+
palette. background-size > 100% gives the gradient room to drift; the whole
|
|
881
|
+
field eases back and forth over `--aurora-drift-duration`. */
|
|
882
|
+
[data-style='aurora'] body {
|
|
883
|
+
background-image:
|
|
884
|
+
radial-gradient(
|
|
885
|
+
58% 75% at 12% 0%,
|
|
886
|
+
color-mix(in srgb, var(--el-accent) var(--aurora-wash), transparent),
|
|
887
|
+
transparent 60%
|
|
888
|
+
),
|
|
889
|
+
radial-gradient(
|
|
890
|
+
54% 70% at 92% 8%,
|
|
891
|
+
color-mix(in srgb, var(--el-highlight) var(--aurora-wash), transparent),
|
|
892
|
+
transparent 58%
|
|
893
|
+
),
|
|
894
|
+
radial-gradient(
|
|
895
|
+
68% 82% at 62% 104%,
|
|
896
|
+
color-mix(in srgb, var(--el-link) var(--aurora-wash), transparent),
|
|
897
|
+
transparent 62%
|
|
898
|
+
),
|
|
899
|
+
radial-gradient(
|
|
900
|
+
48% 64% at 34% 58%,
|
|
901
|
+
color-mix(in srgb, var(--el-info) var(--aurora-wash-soft), transparent),
|
|
902
|
+
transparent 55%
|
|
903
|
+
);
|
|
904
|
+
background-size: 180% 180%;
|
|
905
|
+
background-attachment: fixed;
|
|
906
|
+
animation: aurora-drift var(--aurora-drift-duration) ease-in-out infinite alternate;
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
@keyframes aurora-drift {
|
|
910
|
+
from {
|
|
911
|
+
background-position:
|
|
912
|
+
0% 0%,
|
|
913
|
+
100% 0%,
|
|
914
|
+
50% 100%,
|
|
915
|
+
0% 60%;
|
|
916
|
+
}
|
|
917
|
+
to {
|
|
918
|
+
background-position:
|
|
919
|
+
100% 40%,
|
|
920
|
+
0% 60%,
|
|
921
|
+
40% 0%,
|
|
922
|
+
100% 100%;
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
/* Respect a reduced-motion preference — the aurora settles to a still gradient
|
|
927
|
+
(the identity holds; only the drift stops). */
|
|
928
|
+
@media (prefers-reduced-motion: reduce) {
|
|
929
|
+
[data-style='aurora'] body {
|
|
930
|
+
animation: none;
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
/* Fluid container surfaces — OPAQUE (text AA is preserved against the base
|
|
935
|
+
surface the palette already tuned) with a faint lit-from-above aurora sheen and
|
|
936
|
+
a gentle palette-derived COLOUR glow halo, so panels feel lit from within
|
|
937
|
+
rather than lifted by a hard shadow. The sheen sits on background-image over
|
|
938
|
+
the opaque background-color, so it never pulls the surface far enough from its
|
|
939
|
+
base tone to threaten contrast. */
|
|
940
|
+
[data-style='aurora'] [data-surface='card'],
|
|
941
|
+
[data-style='aurora'] [data-surface='popover'] {
|
|
942
|
+
background-color: var(--el-surface);
|
|
943
|
+
background-image: linear-gradient(
|
|
944
|
+
180deg,
|
|
945
|
+
color-mix(in srgb, var(--el-highlight) var(--aurora-sheen), transparent),
|
|
946
|
+
transparent 42%
|
|
947
|
+
);
|
|
948
|
+
box-shadow:
|
|
949
|
+
var(--shadow-card),
|
|
950
|
+
0 0 26px -8px color-mix(in srgb, var(--el-accent) var(--aurora-glow), transparent),
|
|
951
|
+
0 0 40px -14px color-mix(in srgb, var(--el-link) var(--aurora-glow), transparent);
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
/* Modal panel — opaque, with a deeper glow so the dialog separates cleanly from
|
|
955
|
+
the drifting canvas behind it (and body text stays on the solid surface). */
|
|
956
|
+
[data-style='aurora'] [data-surface='modal'] {
|
|
957
|
+
background-color: var(--el-surface);
|
|
958
|
+
background-image: linear-gradient(
|
|
959
|
+
180deg,
|
|
960
|
+
color-mix(in srgb, var(--el-highlight) var(--aurora-sheen), transparent),
|
|
961
|
+
transparent 38%
|
|
962
|
+
);
|
|
963
|
+
box-shadow:
|
|
964
|
+
var(--shadow-modal),
|
|
965
|
+
0 0 60px -16px color-mix(in srgb, var(--el-accent) var(--aurora-glow-strong), transparent);
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
/* The sidebar rail — opaque chrome carrying the faint aurora sheen so it reads as
|
|
969
|
+
part of the lit surface family, not a flat slab. */
|
|
970
|
+
[data-style='aurora'] [data-surface='sidebar'] {
|
|
971
|
+
background-color: var(--el-sidebar-bg);
|
|
972
|
+
background-image: linear-gradient(
|
|
973
|
+
180deg,
|
|
974
|
+
color-mix(in srgb, var(--el-accent) var(--aurora-sheen), transparent),
|
|
975
|
+
transparent 30%
|
|
976
|
+
);
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
/* ─────────────────────────────────────────────────────────────────────────
|
|
980
|
+
3D / Immersive — the depth + perspective style (7.3.39, DEFERRED follow-up).
|
|
981
|
+
EXPERIMENTAL. The identity is DEPTH + the standard "3D card" interaction:
|
|
982
|
+
deep, multi-layer shadows (specular highlight + contact + ambient + far key
|
|
983
|
+
light) float every surface dramatically off the canvas, and the ImmersiveTilt
|
|
984
|
+
shell engine tips `[data-tilt]` tiles TOWARD THE CURSOR (rotateX/rotateY over a
|
|
985
|
+
perspective — the vanilla-tilt effect; see the @media block below).
|
|
986
|
+
Overrides ONLY shape/feel tokens — never a colour token (that is the
|
|
987
|
+
data-palette axis; this style wears whatever palette is active, so the active
|
|
988
|
+
palette's AA contrast is preserved by construction). The shadow light/ink is
|
|
989
|
+
literal rgba (white specular highlight + near-ink shadow), NOT a palette token,
|
|
990
|
+
so the colour axis stays untouched. The differentiator vs Glassmorphism: glass
|
|
991
|
+
is a translucent MATERIAL over a gradient; 3D is OPAQUE depth — real layered
|
|
992
|
+
light + a cursor-tracking tilt, no blur, no tint. "Performance + accessibility
|
|
993
|
+
heavy" (the card's caveat), so the tilt is gated behind `prefers-reduced-motion`
|
|
994
|
+
(BOTH the engine and the @media block below) — reduced-motion users keep the
|
|
995
|
+
static deep-shadow depth. See docs/styles/3d-immersive.md. ────────────────── */
|
|
996
|
+
[data-style='3d-immersive'] {
|
|
997
|
+
/* Silhouette — generously rounded dimensional tiles. --radius-pill is left
|
|
998
|
+
untouched so genuinely-circular affordances (avatars, status dots, the
|
|
999
|
+
spinner) stay round. */
|
|
1000
|
+
--radius-xs: 6px;
|
|
1001
|
+
--radius-sm: 8px;
|
|
1002
|
+
--radius-md: 12px;
|
|
1003
|
+
--radius-lg: 18px;
|
|
1004
|
+
--radius-xl: 24px;
|
|
1005
|
+
--radius-btn: 14px;
|
|
1006
|
+
--radius-input: 14px;
|
|
1007
|
+
--radius-card: 20px;
|
|
1008
|
+
--radius-modal: 28px;
|
|
1009
|
+
--radius-badge: 9999px; /* soft, dimensional status pills */
|
|
1010
|
+
--radius-control: 12px;
|
|
1011
|
+
--radius-kbd: 8px;
|
|
1012
|
+
|
|
1013
|
+
/* Elevation — THE signature. Every shadow token becomes a DEEP, multi-layer
|
|
1014
|
+
drop: a top specular highlight (inset, a lit edge where light catches the
|
|
1015
|
+
raised tile) + a tight contact shadow + a mid ambient + a wide, soft key
|
|
1016
|
+
light far below, so each tile floats dramatically off the canvas and reads
|
|
1017
|
+
as a physical object — not the timid lift a single shadow gives. Scales from
|
|
1018
|
+
--shadow-subtle up to --shadow-hero-mockup. Pure rgba light/ink (white
|
|
1019
|
+
highlight + near-ink shadow), no palette token — the colour axis is
|
|
1020
|
+
untouched (a white specular highlight reads as "lit" on any palette). */
|
|
1021
|
+
--shadow-subtle:
|
|
1022
|
+
inset 0 1px 0 0 rgba(255, 255, 255, 0.5), 0 1px 2px rgba(15, 15, 15, 0.06),
|
|
1023
|
+
0 5px 12px -2px rgba(15, 15, 15, 0.13);
|
|
1024
|
+
--shadow-card:
|
|
1025
|
+
inset 0 1px 0 0 rgba(255, 255, 255, 0.6), 0 2px 4px rgba(15, 15, 15, 0.05),
|
|
1026
|
+
0 12px 24px -6px rgba(15, 15, 15, 0.16), 0 28px 56px -12px rgba(15, 15, 15, 0.3);
|
|
1027
|
+
--shadow-elevated:
|
|
1028
|
+
inset 0 1px 0 0 rgba(255, 255, 255, 0.65), 0 4px 8px rgba(15, 15, 15, 0.06),
|
|
1029
|
+
0 20px 40px -8px rgba(15, 15, 15, 0.22), 0 44px 80px -16px rgba(15, 15, 15, 0.38);
|
|
1030
|
+
--shadow-modal:
|
|
1031
|
+
inset 0 1px 0 0 rgba(255, 255, 255, 0.7), 0 8px 16px rgba(15, 15, 15, 0.08),
|
|
1032
|
+
0 32px 64px -12px rgba(15, 15, 15, 0.28), 0 72px 120px -24px rgba(15, 15, 15, 0.46);
|
|
1033
|
+
--shadow-hero-mockup:
|
|
1034
|
+
inset 0 1px 0 0 rgba(255, 255, 255, 0.7), 0 16px 32px rgba(15, 15, 15, 0.1),
|
|
1035
|
+
0 96px 160px -32px rgba(15, 15, 15, 0.5);
|
|
1036
|
+
|
|
1037
|
+
/* Typography is the data-type axis now — 3D / Immersive ships
|
|
1038
|
+
`defaultTypeId: 'motir'` (the base editorial pairing); the personality is
|
|
1039
|
+
depth + light, not type. A [data-style] block sets NO --font-* token. */
|
|
1040
|
+
|
|
1041
|
+
/* Density — roomy and immersive; depth wants air around each tile. */
|
|
1042
|
+
--spacing-btn-x: 22px;
|
|
1043
|
+
--spacing-btn-y: 12px;
|
|
1044
|
+
--spacing-input-x: 18px;
|
|
1045
|
+
--spacing-input-y: 13px;
|
|
1046
|
+
--spacing-card-padding: 28px;
|
|
1047
|
+
--spacing-control-x: 14px;
|
|
1048
|
+
--spacing-control-y: 8px;
|
|
1049
|
+
--height-control: 40px;
|
|
1050
|
+
--height-input: 46px;
|
|
1051
|
+
|
|
1052
|
+
/* Motion — smooth and floaty. A slower ease for the depth to feel weighty.
|
|
1053
|
+
The signature INTERACTION is the pointer-parallax TILT below (the standard
|
|
1054
|
+
"3D card" effect — cards tip toward the cursor), driven by the
|
|
1055
|
+
ImmersiveTilt shell engine. */
|
|
1056
|
+
--transition-duration: 260ms;
|
|
1057
|
+
--transition-fast: 180ms;
|
|
1058
|
+
--transition-slow: 360ms;
|
|
1059
|
+
--active-scale: 0.98;
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
/* 3D / Immersive — the IMMERSIVE BACKGROUND (depth atmosphere on EVERY page,
|
|
1063
|
+
not just card surfaces) + the resting FLOAT (every tile lifts off that
|
|
1064
|
+
canvas). Both are STATIC depth, so they sit OUTSIDE the reduced-motion gate —
|
|
1065
|
+
a reduced-motion user still gets the full immersive depth, only the tilt is
|
|
1066
|
+
suppressed. The background is palette-DERIVED (color-mix over --el tokens,
|
|
1067
|
+
never a raw hue), so the style/palette axes stay disjoint and the material-
|
|
1068
|
+
rule test passes: a palette swap re-tints the atmosphere, a style swap leaves
|
|
1069
|
+
hues alone. This is what makes the table/list/board pages (no card surfaces of
|
|
1070
|
+
their own) read as immersive too. */
|
|
1071
|
+
[data-style='3d-immersive'] body {
|
|
1072
|
+
background-image:
|
|
1073
|
+
radial-gradient(
|
|
1074
|
+
130% 85% at 50% -15%,
|
|
1075
|
+
color-mix(in srgb, var(--el-accent) 9%, transparent),
|
|
1076
|
+
transparent 60%
|
|
1077
|
+
),
|
|
1078
|
+
radial-gradient(
|
|
1079
|
+
95% 75% at 108% 112%,
|
|
1080
|
+
color-mix(in srgb, var(--el-link) 8%, transparent),
|
|
1081
|
+
transparent 55%
|
|
1082
|
+
),
|
|
1083
|
+
radial-gradient(
|
|
1084
|
+
150% 130% at 50% 42%,
|
|
1085
|
+
transparent 52%,
|
|
1086
|
+
color-mix(in srgb, var(--el-text) 8%, transparent)
|
|
1087
|
+
);
|
|
1088
|
+
background-attachment: fixed;
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
/* EVERY PANEL FLOATS — systematic depth across the WHOLE app, not just the
|
|
1092
|
+
handful of surfaces that use the <Card> primitive. A "panel" is any element
|
|
1093
|
+
with the card radius (`rounded-(--radius-card)` → the compiled class
|
|
1094
|
+
`.rounded-\(--radius-card\)`): cards, popovers, callouts, list/table panels,
|
|
1095
|
+
widgets, the hand-rolled `<div className="rounded-(--radius-card) border …">`
|
|
1096
|
+
surfaces every screen builds. They all get the deep resting float shadow so no
|
|
1097
|
+
screen is left flat. This is the same global-utility-targeting approach
|
|
1098
|
+
neo-brutalism uses for `.border`. Static depth (NOT motion-gated). box-shadow
|
|
1099
|
+
is not a colour-painting property, so the material-rule test skips it.
|
|
1100
|
+
Per-surface exceptions (the board column's clip-safe shadow, modals) override
|
|
1101
|
+
below by source order. Modals carry their own deeper shadow via the modal
|
|
1102
|
+
radius (next rule). */
|
|
1103
|
+
[data-style='3d-immersive'] .rounded-\(--radius-card\) {
|
|
1104
|
+
box-shadow: var(--shadow-card);
|
|
1105
|
+
}
|
|
1106
|
+
[data-style='3d-immersive'] .rounded-\(--radius-modal\) {
|
|
1107
|
+
box-shadow: var(--shadow-modal);
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
/* 3D / Immersive — the POINTER-PARALLAX TILT (the standard "3D card" effect,
|
|
1111
|
+
the interactive half of the identity). The ImmersiveTilt shell engine
|
|
1112
|
+
(components/theme/ImmersiveTilt.tsx) — active ONLY when data-style is this
|
|
1113
|
+
style and the user has NOT requested reduced motion — reads the cursor
|
|
1114
|
+
position over a `[data-tilt]` tile and writes per-tile `--tilt-rx` / `--tilt-ry`
|
|
1115
|
+
rotation vars, so the tile tips toward the pointer (rotateX/rotateY over a
|
|
1116
|
+
perspective) and lifts; on leave the engine zeroes the vars and the tile eases
|
|
1117
|
+
back flat. The whole block is GATED behind `prefers-reduced-motion:
|
|
1118
|
+
no-preference` (the card flags this style accessibility-heavy) — a
|
|
1119
|
+
reduced-motion user gets NO transform at all and keeps the static deep-shadow
|
|
1120
|
+
depth. It sets NO colour (transform + box-shadow only; box-shadow draws the
|
|
1121
|
+
existing near-ink token), so the style/palette axes stay disjoint and
|
|
1122
|
+
styleRegistry.test.ts's material-rule check (which inspects only
|
|
1123
|
+
colour-painting rules) skips it. Tiles opt in with the `data-tilt` hook
|
|
1124
|
+
(Card, BoardCard). */
|
|
1125
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
1126
|
+
/* The transition lives on the resting tile so BOTH the tilt-in and the
|
|
1127
|
+
settle-back-flat animate. Crucially, NO `transform` is set at rest — only
|
|
1128
|
+
the actively-hovered tile (data-tilt-active) gets one, so a resting card
|
|
1129
|
+
never establishes a transform stacking context that could reposition a
|
|
1130
|
+
non-portaled fixed/absolute descendant. On leave the engine drops the
|
|
1131
|
+
attribute and the transform eases back to none over 400ms. */
|
|
1132
|
+
[data-style='3d-immersive'] [data-tilt] {
|
|
1133
|
+
transition:
|
|
1134
|
+
transform 400ms cubic-bezier(0.22, 1, 0.36, 1),
|
|
1135
|
+
box-shadow 260ms ease-out;
|
|
1136
|
+
}
|
|
1137
|
+
/* While the pointer is over the tile the engine sets data-tilt-active='1':
|
|
1138
|
+
tip it toward the cursor (the per-tile --tilt-rx/ry the engine writes),
|
|
1139
|
+
drop the transform transition so it tracks the cursor in real time, deepen
|
|
1140
|
+
the shadow so the lifted tile casts further, and — the proper-3D bit —
|
|
1141
|
+
establish a 3D context (`preserve-3d`) so the card's slots can sit on
|
|
1142
|
+
separate translateZ planes and PARALLAX (a flat-plane tilt without this is
|
|
1143
|
+
the "halfway / flat-3D mix" tell). `position: relative` anchors the glare. */
|
|
1144
|
+
[data-style='3d-immersive'] [data-tilt][data-tilt-active='1'] {
|
|
1145
|
+
transform: perspective(var(--tilt-perspective, 900px)) rotateX(var(--tilt-rx, 0deg))
|
|
1146
|
+
rotateY(var(--tilt-ry, 0deg));
|
|
1147
|
+
transform-style: preserve-3d;
|
|
1148
|
+
transition:
|
|
1149
|
+
transform 80ms linear,
|
|
1150
|
+
box-shadow 260ms ease-out;
|
|
1151
|
+
box-shadow: var(--shadow-elevated);
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
/* DEPTH PLANES — the card's slots lift onto different translateZ planes so
|
|
1155
|
+
they parallax above the card face as it tilts (the title/header pops most,
|
|
1156
|
+
the footer sits nearer the face; the body stays ON the face so arbitrary
|
|
1157
|
+
content never renders skewed). Only while actively tilting, so a resting
|
|
1158
|
+
card is perfectly flat. This is the standard layered-parallax technique
|
|
1159
|
+
(vanilla-tilt / Atropos): perspective + preserve-3d + per-layer translateZ. */
|
|
1160
|
+
[data-style='3d-immersive'] [data-tilt][data-tilt-active='1'] [data-tilt-layer='front'] {
|
|
1161
|
+
transform: translateZ(42px);
|
|
1162
|
+
transition: transform 80ms linear;
|
|
1163
|
+
}
|
|
1164
|
+
[data-style='3d-immersive'] [data-tilt][data-tilt-active='1'] [data-tilt-layer='back'] {
|
|
1165
|
+
transform: translateZ(14px);
|
|
1166
|
+
transition: transform 80ms linear;
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
/* GLARE — a cursor-tracked specular sweep across the face (the light catching
|
|
1170
|
+
a tilted surface). Palette-DERIVED (color-mix over --el-page-bg, never a raw
|
|
1171
|
+
hue → passes the material-rule test; a palette swap re-tints the sheen). It
|
|
1172
|
+
rides a high translateZ so it reads as light on the front glass, fades in
|
|
1173
|
+
only while active, and never eats pointer events. */
|
|
1174
|
+
[data-style='3d-immersive'] [data-tilt]::after {
|
|
1175
|
+
content: '';
|
|
1176
|
+
position: absolute;
|
|
1177
|
+
inset: 0;
|
|
1178
|
+
border-radius: inherit;
|
|
1179
|
+
background: radial-gradient(
|
|
1180
|
+
circle at var(--tilt-glare-x, 50%) var(--tilt-glare-y, 50%),
|
|
1181
|
+
color-mix(in srgb, var(--el-page-bg) 65%, transparent),
|
|
1182
|
+
transparent 55%
|
|
1183
|
+
);
|
|
1184
|
+
opacity: 0;
|
|
1185
|
+
transition: opacity 200ms ease-out;
|
|
1186
|
+
pointer-events: none;
|
|
1187
|
+
}
|
|
1188
|
+
[data-style='3d-immersive'] [data-tilt][data-tilt-active='1']::after {
|
|
1189
|
+
opacity: 0.45;
|
|
1190
|
+
/* Just above the card FACE but BELOW the popped header/footer planes, so it
|
|
1191
|
+
lights the surface without washing the floating title. */
|
|
1192
|
+
transform: translateZ(2px);
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
/* `position: relative` anchors the glare ::after to each tile. Static (outside
|
|
1197
|
+
the motion gate) so the anchor is stable; the glare itself only shows while
|
|
1198
|
+
tilting (above). */
|
|
1199
|
+
[data-style='3d-immersive'] [data-tilt] {
|
|
1200
|
+
position: relative;
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
/* ── 3D / Immersive — BOARD COLUMNS ─────────────────────────────────────────
|
|
1204
|
+
A board column is a TALL panel inside an `overflow-x-auto` scroll row, which
|
|
1205
|
+
forces overflow-y to clip — so the wide --shadow-card gets cut off at the
|
|
1206
|
+
bottom and adjacent opaque columns occlude each other's side shadows, making
|
|
1207
|
+
the board read as one backing slab. Instead each column gets a TIGHTER,
|
|
1208
|
+
clip/occlusion-safe float shadow (small horizontal bleed, modest drop), and
|
|
1209
|
+
the scroll row widens its gap + adds bottom room so the shadow shows. Result:
|
|
1210
|
+
each column floats as its own distinct card. */
|
|
1211
|
+
[data-style='3d-immersive'] [data-board-col-panel] {
|
|
1212
|
+
box-shadow:
|
|
1213
|
+
inset 0 1px 0 0 rgba(255, 255, 255, 0.55),
|
|
1214
|
+
0 1px 3px rgba(15, 15, 15, 0.08),
|
|
1215
|
+
0 8px 18px -8px rgba(15, 15, 15, 0.22);
|
|
1216
|
+
/* The column is `h-[calc(100dvh-12rem)]` — sized to fill the board area. In
|
|
1217
|
+
the `overflow-x-auto` row that exactly-filling height means the column's
|
|
1218
|
+
rounded BOTTOM corner + its drop shadow sit at the overflow-clip boundary
|
|
1219
|
+
and get sheared flat (the reported "sharp bottom corner"). Trim the height
|
|
1220
|
+
so the rounded corner + shadow clear the clip and the column reads as a
|
|
1221
|
+
fully-rounded floating card. Also clip the column's OWN children to its
|
|
1222
|
+
radius so a flush inner surface can't square a corner — the board cards pop
|
|
1223
|
+
on hover but the DragOverlay clone is portaled, so nothing 3D is lost. */
|
|
1224
|
+
height: calc(100dvh - 14.5rem);
|
|
1225
|
+
overflow: hidden;
|
|
1226
|
+
}
|
|
1227
|
+
[data-style='3d-immersive'] [data-board-scroll] {
|
|
1228
|
+
gap: 1.5rem;
|
|
1229
|
+
padding-bottom: 2.25rem;
|
|
1230
|
+
padding-top: 0.25rem;
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
/* ── 3D / Immersive — PHYSICAL BUTTONS ──────────────────────────────────────
|
|
1234
|
+
In a 3D design a button is not flat: it has visible THICKNESS (a solid base
|
|
1235
|
+
edge) and presses DOWN onto it on click (translateY + the base compresses) —
|
|
1236
|
+
the standard 3D/skeuomorphic button. This must cover EVERY button, not just
|
|
1237
|
+
the <Button> primitive: the ~35 hand-rolled `rounded-(--radius-btn)` <button>s
|
|
1238
|
+
(the /issues toolbar triggers — Filter / Advanced / Saved / the Tree-List
|
|
1239
|
+
switcher — links-styled-as-buttons, etc.) were flat. So the GLOBAL rule on the
|
|
1240
|
+
button-radius class is the baseline (a neutral `--el-border-strong` edge, which
|
|
1241
|
+
suits the outline/toolbar buttons); FILLED variants override it with a
|
|
1242
|
+
colour-matched edge (primary → `--el-accent-pressed`, danger → a darkened
|
|
1243
|
+
`--el-danger` — palette-derived, so the axes stay disjoint); ghost stays flat
|
|
1244
|
+
(the quiet button). box-shadow is not a colour-painting property, so the
|
|
1245
|
+
material-rule test skips these. The press overrides the base `active:scale`
|
|
1246
|
+
with a more physical translateY. */
|
|
1247
|
+
/* `button.rounded-(--radius-input)` catches DROPDOWN-TRIGGER buttons styled as an
|
|
1248
|
+
input (the board switcher, combobox / picker triggers) so they're physical
|
|
1249
|
+
keys too — scoped to `button` so a real <input>/<textarea> text field (same
|
|
1250
|
+
radius) is NEVER raised. */
|
|
1251
|
+
[data-style='3d-immersive'] .rounded-\(--radius-btn\),
|
|
1252
|
+
[data-style='3d-immersive'] button.rounded-\(--radius-input\) {
|
|
1253
|
+
box-shadow:
|
|
1254
|
+
0 3px 0 0 var(--el-border-strong),
|
|
1255
|
+
0 5px 10px -3px rgba(15, 15, 15, 0.18);
|
|
1256
|
+
transition:
|
|
1257
|
+
transform 90ms ease-out,
|
|
1258
|
+
box-shadow 90ms ease-out,
|
|
1259
|
+
background-color var(--transition-duration);
|
|
1260
|
+
}
|
|
1261
|
+
[data-style='3d-immersive'] [data-variant='primary'] {
|
|
1262
|
+
box-shadow:
|
|
1263
|
+
0 4px 0 0 var(--el-accent-pressed),
|
|
1264
|
+
0 7px 12px -3px rgba(15, 15, 15, 0.32);
|
|
1265
|
+
}
|
|
1266
|
+
[data-style='3d-immersive'] [data-variant='danger'] {
|
|
1267
|
+
box-shadow:
|
|
1268
|
+
0 4px 0 0 color-mix(in srgb, var(--el-danger), black 32%),
|
|
1269
|
+
0 7px 12px -3px rgba(15, 15, 15, 0.32);
|
|
1270
|
+
}
|
|
1271
|
+
/* Ghost — the deliberately-flat quiet button; no thickness. */
|
|
1272
|
+
[data-style='3d-immersive'] [data-variant='ghost'] {
|
|
1273
|
+
box-shadow: none;
|
|
1274
|
+
}
|
|
1275
|
+
/* Press DOWN onto the base — the button drops by its thickness and the base
|
|
1276
|
+
edge compresses, like a real key. Overrides the base `active:scale`. */
|
|
1277
|
+
[data-style='3d-immersive'] .rounded-\(--radius-btn\):active:not(:disabled),
|
|
1278
|
+
[data-style='3d-immersive'] button.rounded-\(--radius-input\):active:not(:disabled) {
|
|
1279
|
+
transform: translateY(3px);
|
|
1280
|
+
box-shadow:
|
|
1281
|
+
0 1px 0 0 var(--el-border-strong),
|
|
1282
|
+
0 2px 4px rgba(15, 15, 15, 0.14);
|
|
1283
|
+
}
|
|
1284
|
+
[data-style='3d-immersive'] [data-variant='primary']:active:not(:disabled) {
|
|
1285
|
+
box-shadow:
|
|
1286
|
+
0 1px 0 0 var(--el-accent-pressed),
|
|
1287
|
+
0 2px 5px rgba(15, 15, 15, 0.22);
|
|
1288
|
+
}
|
|
1289
|
+
[data-style='3d-immersive'] [data-variant='danger']:active:not(:disabled) {
|
|
1290
|
+
box-shadow:
|
|
1291
|
+
0 1px 0 0 color-mix(in srgb, var(--el-danger), black 32%),
|
|
1292
|
+
0 2px 5px rgba(15, 15, 15, 0.22);
|
|
1293
|
+
}
|
|
1294
|
+
[data-style='3d-immersive'] [data-variant='ghost']:active:not(:disabled) {
|
|
1295
|
+
box-shadow: none;
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
/* ── 3D / Immersive — DROPDOWNS / POPOVERS / MENUS unfold in 3D ──────────────
|
|
1299
|
+
A flat fade-in is the "still not 3D" tell on overlays. The standard 3D
|
|
1300
|
+
dropdown UNFOLDS: it swings open from its top edge over a perspective
|
|
1301
|
+
(transform-origin top + rotateX, folded → flat). Every floating overlay in the
|
|
1302
|
+
app shares `shadow-(--shadow-elevated)` (popovers, comboboxes, multi-selects,
|
|
1303
|
+
menus, tooltips, toasts), so one global rule on the compiled
|
|
1304
|
+
`.shadow-\(--shadow-elevated\)` class gives them all the 3D open — and restores
|
|
1305
|
+
the deeper elevated shadow (the global card-float rule above would otherwise
|
|
1306
|
+
flatten them to --shadow-card). gated by reduced-motion. */
|
|
1307
|
+
@keyframes immersive-menu-open {
|
|
1308
|
+
from {
|
|
1309
|
+
opacity: 0;
|
|
1310
|
+
transform: perspective(900px) rotateX(-26deg) translateY(-6px);
|
|
1311
|
+
}
|
|
1312
|
+
to {
|
|
1313
|
+
opacity: 1;
|
|
1314
|
+
transform: none;
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
/* Restore the deeper ELEVATED shadow on every overlay (the global card-float
|
|
1318
|
+
rule above would otherwise flatten any `shadow-(--shadow-elevated)` surface to
|
|
1319
|
+
--shadow-card). Global + static so menus, tooltips, drag-overlays all keep the
|
|
1320
|
+
right depth. */
|
|
1321
|
+
[data-style='3d-immersive'] .shadow-\(--shadow-elevated\) {
|
|
1322
|
+
box-shadow: var(--shadow-elevated);
|
|
1323
|
+
}
|
|
1324
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
1325
|
+
/* The 3D UNFOLD is scoped to actual MENU surfaces (the Popover primitive +
|
|
1326
|
+
the `data-menu-surface` hook on Combobox / MultiSelect / BoardSwitcher
|
|
1327
|
+
menus) — NOT every elevated surface, so a drag-overlay or toast doesn't
|
|
1328
|
+
unexpectedly fold open. */
|
|
1329
|
+
[data-style='3d-immersive'] [data-surface='popover'],
|
|
1330
|
+
[data-style='3d-immersive'] [data-menu-surface] {
|
|
1331
|
+
transform-origin: top center;
|
|
1332
|
+
animation: immersive-menu-open 200ms cubic-bezier(0.22, 1, 0.36, 1);
|
|
1333
|
+
}
|
|
1334
|
+
/* A Radix popover that flips ABOVE its trigger folds from the bottom edge. */
|
|
1335
|
+
[data-style='3d-immersive'] [data-surface='popover'][data-side='top'] {
|
|
1336
|
+
transform-origin: bottom center;
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1340
|
+
/* ── Hand-Drawn / Indie (data-style="hand-drawn-indie") — Subtask 7.3.41 ─────
|
|
1341
|
+
DEFERRED follow-up. A sketchy, friendly, zine-like aesthetic. TWO moves make
|
|
1342
|
+
it read as genuinely hand-drawn, not just "softly rounded":
|
|
1343
|
+
(1) WONKY corners — every framed surface gets a strongly asymmetric
|
|
1344
|
+
multi-value radius (one corner near-sharp, the opposite generously round,
|
|
1345
|
+
no two alike) so a card/button/input reads as a DRAWN box; and
|
|
1346
|
+
(2) ROUGH edges — a `feTurbulence`+`feDisplacementMap` filter (`#hd-rough`,
|
|
1347
|
+
defined once by <HandDrawnFilter/> in the root layout) warps the OUTLINE
|
|
1348
|
+
into an ink-like wobble. Asymmetric radius alone only bends the corners;
|
|
1349
|
+
the displacement is what makes the LINES wavy (the Excalidraw / rough.js
|
|
1350
|
+
look). It is applied to a CONTENT-SAFE `::after` pseudo-border so only the
|
|
1351
|
+
outline is displaced — the text stays crisp.
|
|
1352
|
+
Plus a soft hand-placed offset shadow, a roomy rhythm, and a springy
|
|
1353
|
+
hover/press bounce. Colour stays the palette axis: the token block sets no
|
|
1354
|
+
`--color-` / `--el-` colour token (AA preserved), and the rough-border rule
|
|
1355
|
+
takes its ink from the palette (var(--el-border-strong)). Out-of-the-box type
|
|
1356
|
+
is the characterful Editorial (Fraunces) pairing via `defaultTypeId`. See
|
|
1357
|
+
docs/styles/hand-drawn-indie.md. */
|
|
1358
|
+
[data-style='hand-drawn-indie'] {
|
|
1359
|
+
/* Silhouette — WONKY hand-drawn corners. The slash splits horizontal /
|
|
1360
|
+
vertical corner radii, and the four values per side differ widely, so each
|
|
1361
|
+
corner of a box is a different curve (the "drawn rectangle" tell). Generic
|
|
1362
|
+
scale stays soft + single-valued; the SEMANTIC surfaces carry the wonk. */
|
|
1363
|
+
--radius-xs: 6px;
|
|
1364
|
+
--radius-sm: 9px;
|
|
1365
|
+
--radius-md: 13px;
|
|
1366
|
+
--radius-lg: 18px;
|
|
1367
|
+
--radius-xl: 24px;
|
|
1368
|
+
--radius-btn: 14px 40px 12px 36px / 36px 14px 38px 16px;
|
|
1369
|
+
--radius-input: 16px 48px 14px 42px / 42px 16px 46px 18px;
|
|
1370
|
+
--radius-card: 18px 58px 22px 48px / 48px 20px 54px 24px;
|
|
1371
|
+
--radius-modal: 22px 66px 20px 56px / 56px 22px 60px 26px;
|
|
1372
|
+
--radius-badge: 9px 26px 11px 22px / 22px 11px 24px 9px;
|
|
1373
|
+
--radius-control: 10px 26px 12px 22px / 22px 12px 24px 10px;
|
|
1374
|
+
--radius-kbd: 6px 18px 8px 15px / 15px 8px 16px 6px;
|
|
1375
|
+
|
|
1376
|
+
/* Elevation — soft, hand-placed offset shadows (a doodled drop, nudged
|
|
1377
|
+
down-right); friendly + low, never a hard brutalist block. Warm ink. */
|
|
1378
|
+
--shadow-subtle: 1px 2px 4px -1px rgba(38, 30, 22, 0.1);
|
|
1379
|
+
--shadow-card: 2px 4px 9px -3px rgba(38, 30, 22, 0.14);
|
|
1380
|
+
--shadow-elevated: 3px 6px 16px -4px rgba(38, 30, 22, 0.17);
|
|
1381
|
+
--shadow-modal: 5px 9px 30px -8px rgba(38, 30, 22, 0.22);
|
|
1382
|
+
--shadow-hero-mockup: 6px 12px 40px -10px rgba(38, 30, 22, 0.22);
|
|
1383
|
+
|
|
1384
|
+
/* Density — roomy + relaxed; the sketch wants margins around it. */
|
|
1385
|
+
--spacing-btn-x: 20px;
|
|
1386
|
+
--spacing-btn-y: 12px;
|
|
1387
|
+
--spacing-input-x: 16px;
|
|
1388
|
+
--spacing-input-y: 12px;
|
|
1389
|
+
--spacing-card-padding: 26px;
|
|
1390
|
+
--spacing-control-x: 12px;
|
|
1391
|
+
--spacing-control-y: 8px;
|
|
1392
|
+
--height-control: 38px;
|
|
1393
|
+
--height-input: 44px;
|
|
1394
|
+
|
|
1395
|
+
/* Motion — springy + playful: a bouncy hover lift and a deeper press squish. */
|
|
1396
|
+
--transition-duration: 200ms;
|
|
1397
|
+
--transition-fast: 130ms;
|
|
1398
|
+
--transition-slow: 320ms;
|
|
1399
|
+
--hover-scale: 1.02;
|
|
1400
|
+
--active-scale: 0.97;
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1403
|
+
/* The ROUGH, hand-inked edge. A wavy ink line is drawn on a `::after` overlay
|
|
1404
|
+
warped by the `#hd-rough` turbulence filter — the move that makes lines look
|
|
1405
|
+
DRAWN, not machine-straight. The overlay is `pointer-events: none` and only it
|
|
1406
|
+
is filtered, so the surface's own text/content stays CRISP (filtering the
|
|
1407
|
+
element itself would smear the text). The real border is KEPT (2px) as the
|
|
1408
|
+
structural fallback. The ink is palette-DERIVED (var(--el-border-strong)), so
|
|
1409
|
+
the colour axis stays disjoint and it adapts to dark mode.
|
|
1410
|
+
|
|
1411
|
+
The overlay needs a positioned host. We add `position: relative` ONLY to
|
|
1412
|
+
STATIC elements: a `fixed`/`absolute` element (a centered modal / command
|
|
1413
|
+
palette — both `.fixed .border`) or a `sticky` header is ALREADY a containing
|
|
1414
|
+
block, so overriding its position would break it (overriding `.fixed` is what
|
|
1415
|
+
knocked the modals off-centre). Those positioned elements still get the `::after`
|
|
1416
|
+
overlay (they're already a containing block), just not the position override.
|
|
1417
|
+
|
|
1418
|
+
Two line kinds are roughened so the frame AND the inner grid match (no straight
|
|
1419
|
+
dividers inside a wavy card): the full-box `.border`, and the single-side
|
|
1420
|
+
dividers `.border-b` / `.border-t` (table rows, section rules) — the latter
|
|
1421
|
+
scoped INSIDE `[data-surface]` panels to bound the count to real surfaces (and
|
|
1422
|
+
their rows are virtualised, so only ~viewport-many overlays ever render). */
|
|
1423
|
+
[data-style='hand-drawn-indie'] .border {
|
|
1424
|
+
border-width: 2px;
|
|
1425
|
+
}
|
|
1426
|
+
[data-style='hand-drawn-indie'] .border:not(.fixed):not(.absolute):not(.sticky) {
|
|
1427
|
+
position: relative;
|
|
1428
|
+
}
|
|
1429
|
+
[data-style='hand-drawn-indie'] .border::after {
|
|
1430
|
+
content: '';
|
|
1431
|
+
position: absolute;
|
|
1432
|
+
inset: -1px;
|
|
1433
|
+
pointer-events: none;
|
|
1434
|
+
border: 2px solid var(--el-border-strong);
|
|
1435
|
+
border-radius: inherit;
|
|
1436
|
+
filter: url(#hd-rough);
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1439
|
+
/* Internal grid dividers — make the inner rules as rough as the frame. Scoped
|
|
1440
|
+
inside framed surfaces; `:not(.border)` so a full-box element isn't handled
|
|
1441
|
+
twice; `:not(:last-child)` mirrors the common `last:border-b-0` strip so the
|
|
1442
|
+
final row gets no phantom line. */
|
|
1443
|
+
[data-style='hand-drawn-indie']
|
|
1444
|
+
[data-surface]
|
|
1445
|
+
:is(.border-b, .border-t):not(.border):not(.fixed):not(.absolute):not(.sticky) {
|
|
1446
|
+
position: relative;
|
|
1447
|
+
}
|
|
1448
|
+
[data-style='hand-drawn-indie'] [data-surface] .border-b:not(.border)::after {
|
|
1449
|
+
content: '';
|
|
1450
|
+
position: absolute;
|
|
1451
|
+
left: 0;
|
|
1452
|
+
right: 0;
|
|
1453
|
+
bottom: -1px;
|
|
1454
|
+
height: 0;
|
|
1455
|
+
pointer-events: none;
|
|
1456
|
+
border-bottom: 2px solid var(--el-border-strong);
|
|
1457
|
+
filter: url(#hd-rough);
|
|
1458
|
+
}
|
|
1459
|
+
[data-style='hand-drawn-indie'] [data-surface] .border-t:not(.border)::after {
|
|
1460
|
+
content: '';
|
|
1461
|
+
position: absolute;
|
|
1462
|
+
left: 0;
|
|
1463
|
+
right: 0;
|
|
1464
|
+
top: -1px;
|
|
1465
|
+
height: 0;
|
|
1466
|
+
pointer-events: none;
|
|
1467
|
+
border-top: 2px solid var(--el-border-strong);
|
|
1468
|
+
filter: url(#hd-rough);
|
|
1469
|
+
}
|
|
1470
|
+
/* Suppress the overlay only where the REAL divider is removed: the last row of a
|
|
1471
|
+
fully-rendered list strips its border via `last:border-b-0`. (We can't use
|
|
1472
|
+
`:not(:last-child)` on the draw rule — a STICKY table header sits alone in its
|
|
1473
|
+
own `role="rowgroup"`, so it is itself a `:last-child` and would be wrongly
|
|
1474
|
+
skipped, which left the work-item table header's divider straight.) Same
|
|
1475
|
+
specificity as the draw rule, placed after, so it wins on the real last row. */
|
|
1476
|
+
[data-style='hand-drawn-indie'] [data-surface] .last\:border-b-0:last-child::after {
|
|
1477
|
+
content: none;
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
/* The APP SHELL FRAME — the sidebar rail's right edge + the top bar's bottom edge
|
|
1481
|
+
(MOTIR-1315). These use DIRECTIONAL borders (border-r / border-b) the full-box
|
|
1482
|
+
`.border` rule doesn't match, and the rail/header sit OUTSIDE the [data-surface]
|
|
1483
|
+
panels the divider rules scope to — so the shell frame stayed machine-straight
|
|
1484
|
+
while every card looked hand-drawn. Draw the rough ink directly on the
|
|
1485
|
+
data-surface host: the rail (data-surface="sidebar") gets a wavy RIGHT edge, the
|
|
1486
|
+
header (data-surface="header") a wavy BOTTOM edge. Palette-DERIVED ink
|
|
1487
|
+
(var(--el-border-strong)), so the colour axis stays disjoint. Add `position`
|
|
1488
|
+
ONLY to a STATIC host (the rail) — never the STICKY header, which is already a
|
|
1489
|
+
containing block (overriding it would break the sticky), mirroring the `.border`
|
|
1490
|
+
rule's guard. */
|
|
1491
|
+
[data-style='hand-drawn-indie']
|
|
1492
|
+
:is([data-surface='sidebar'], [data-surface='header']):not(.fixed):not(.absolute):not(.sticky) {
|
|
1493
|
+
position: relative;
|
|
1494
|
+
}
|
|
1495
|
+
[data-style='hand-drawn-indie'] [data-surface='sidebar']::after {
|
|
1496
|
+
content: '';
|
|
1497
|
+
position: absolute;
|
|
1498
|
+
top: 0;
|
|
1499
|
+
bottom: 0;
|
|
1500
|
+
right: -1px;
|
|
1501
|
+
width: 0;
|
|
1502
|
+
pointer-events: none;
|
|
1503
|
+
border-right: 2px solid var(--el-border-strong);
|
|
1504
|
+
filter: url(#hd-rough);
|
|
1505
|
+
}
|
|
1506
|
+
[data-style='hand-drawn-indie'] [data-surface='header']::after {
|
|
1507
|
+
content: '';
|
|
1508
|
+
position: absolute;
|
|
1509
|
+
left: 0;
|
|
1510
|
+
right: 0;
|
|
1511
|
+
bottom: -1px;
|
|
1512
|
+
height: 0;
|
|
1513
|
+
pointer-events: none;
|
|
1514
|
+
border-bottom: 2px solid var(--el-border-strong);
|
|
1515
|
+
filter: url(#hd-rough);
|
|
1516
|
+
}
|
|
1517
|
+
|
|
1518
|
+
/* ─────────────────────────────────────────────────────────────────────────
|
|
1519
|
+
Neumorphism — the soft-extruded "soft UI" style (7.3.42, DEFERRED follow-up).
|
|
1520
|
+
EXPERIMENTAL. The identity is a SINGLE MONOCHROME FIELD out of which every
|
|
1521
|
+
surface is moulded by a DUAL soft shadow — a light highlight (top-left) paired
|
|
1522
|
+
with a palette-derived dark shadow (bottom-right) — so a card swells OUT of the
|
|
1523
|
+
canvas (raised) and a form field sinks INTO it (an inset well). No translucency,
|
|
1524
|
+
no gradient, no glow: just continuous opaque material + paired soft shadows.
|
|
1525
|
+
Ships in two parts, like the other surface-material styles (glass / aurora):
|
|
1526
|
+
1. This [data-style] TOKEN BLOCK — shape/feel tokens ONLY (soft radii, soft
|
|
1527
|
+
NEUTRAL shadows, gentle motion, roomy density) PLUS palette-AGNOSTIC
|
|
1528
|
+
material SCALARS (the extrusion distance + blur, the white highlight, and
|
|
1529
|
+
the dark-shadow strength %). No hue lives here, so it stays disjoint from
|
|
1530
|
+
the palette axis — enforced by the token-block test in styleRegistry.test.ts.
|
|
1531
|
+
2. A palette-DERIVED MATERIAL LAYER (the rules below) that paints the
|
|
1532
|
+
monochrome field + the dual raised/inset shadows. The dark shadow is
|
|
1533
|
+
color-mix() over the active palette's `--el-text` ink (NO new hue); the
|
|
1534
|
+
light highlight is palette-AGNOSTIC white (like glass's --glass-rim), so a
|
|
1535
|
+
palette swap re-tones the moulded surface and a style swap leaves hues be.
|
|
1536
|
+
── Accessibility (the card's "accessibility-hard, gate carefully" caveat) ──
|
|
1537
|
+
Soft-UI's classic failure is low contrast — borderless same-colour surfaces and
|
|
1538
|
+
text that washes out. Gated by construction here: surfaces stay OPAQUE at the
|
|
1539
|
+
palette's own AA-tuned `--el-surface` (text contrast preserved, like aurora), a
|
|
1540
|
+
faint hairline border is KEPT (not removed) so structure never relies on shadow
|
|
1541
|
+
alone, and a `prefers-contrast: more` / `forced-colors: active` fallback below
|
|
1542
|
+
restores a solid border and flattens the shadow for users who need it.
|
|
1543
|
+
See docs/styles/neumorphism.md. ──────────────────────────────────────────── */
|
|
1544
|
+
[data-style='neumorphism'] {
|
|
1545
|
+
/* Silhouette — soft, generously rounded pebbles; the rounding lets the paired
|
|
1546
|
+
shadow wrap each surface. --radius-pill / circular affordances untouched. */
|
|
1547
|
+
--radius-xs: 6px;
|
|
1548
|
+
--radius-sm: 8px;
|
|
1549
|
+
--radius-md: 12px;
|
|
1550
|
+
--radius-lg: 16px;
|
|
1551
|
+
--radius-xl: 20px;
|
|
1552
|
+
--radius-btn: 12px;
|
|
1553
|
+
--radius-input: 12px;
|
|
1554
|
+
--radius-card: 18px;
|
|
1555
|
+
--radius-modal: 22px;
|
|
1556
|
+
--radius-badge: 9999px; /* chips stay pills — soft is the neumorphic register */
|
|
1557
|
+
--radius-control: 10px;
|
|
1558
|
+
--radius-kbd: 6px;
|
|
1559
|
+
|
|
1560
|
+
/* Elevation — soft, diffuse NEUTRAL base lift for any non-data-surface elevated
|
|
1561
|
+
element (tooltips, bare dropdowns). The dual raised/inset MATERIAL shadow is
|
|
1562
|
+
palette-derived and lives in the material layer below (not here, where no hue
|
|
1563
|
+
may appear). rgba literals are neutral shadow ink, not a palette hue. */
|
|
1564
|
+
--shadow-subtle: 0 1px 2px rgba(20, 20, 30, 0.05);
|
|
1565
|
+
--shadow-card: 0 6px 18px -10px rgba(20, 20, 30, 0.14);
|
|
1566
|
+
--shadow-elevated: 0 12px 30px -14px rgba(20, 20, 30, 0.18);
|
|
1567
|
+
--shadow-modal: 0 22px 50px -18px rgba(20, 20, 30, 0.24);
|
|
1568
|
+
--shadow-hero-mockup: 0 26px 60px -18px rgba(20, 20, 30, 0.28);
|
|
1569
|
+
|
|
1570
|
+
/* Density — comfortable, roomy; the soft shadows need air to read as extruded. */
|
|
1571
|
+
--spacing-btn-x: 20px;
|
|
1572
|
+
--spacing-btn-y: 11px;
|
|
1573
|
+
--spacing-card-padding: 26px;
|
|
1574
|
+
--spacing-control-x: 12px;
|
|
1575
|
+
--spacing-control-y: 7px;
|
|
1576
|
+
--height-control: 38px;
|
|
1577
|
+
|
|
1578
|
+
/* Motion — gentle; a soft press-scale, nothing snaps. */
|
|
1579
|
+
--transition-duration: 200ms;
|
|
1580
|
+
--transition-slow: 300ms;
|
|
1581
|
+
--active-scale: 0.98;
|
|
1582
|
+
|
|
1583
|
+
/* ── Material SCALARS — palette-AGNOSTIC (no hue, so no colour token here).
|
|
1584
|
+
Consumed only by the [data-style='neumorphism'] material rules below: how far
|
|
1585
|
+
the paired shadow is offset, how soft it blurs, and the two shadow inks (the
|
|
1586
|
+
top-left highlight + the bottom-right dark). Both inks are NEUTRAL rgba (white
|
|
1587
|
+
light + near-black shade) — like glass's --glass-rim / aurora's --shadow-*:
|
|
1588
|
+
they are shadow ink, not a palette hue, so the token block stays colour-free.
|
|
1589
|
+
A neumorphic mould needs a CONSTANT light/dark pairing (highlight always
|
|
1590
|
+
lighter, shadow always darker than the surface) — deriving the dark ink from
|
|
1591
|
+
a palette text token would INVERT it in dark mode (the ink goes light), so the
|
|
1592
|
+
ink stays neutral and the [data-theme='dark'] block below re-balances it. ── */
|
|
1593
|
+
--neu-distance: 6px; /* offset of the paired shadow on a card / panel */
|
|
1594
|
+
--neu-blur: 16px; /* how soft the paired shadow blurs (the "swell") */
|
|
1595
|
+
--neu-distance-sm: 3px; /* tighter offset for an inset input well */
|
|
1596
|
+
--neu-blur-sm: 8px; /* tighter blur for the inset well */
|
|
1597
|
+
--neu-light: rgba(255, 255, 255, 0.6); /* top-left raised HIGHLIGHT (light ink) */
|
|
1598
|
+
--neu-shadow: rgba(20, 20, 30, 0.16); /* bottom-right dark shadow (shade ink) */
|
|
1599
|
+
}
|
|
1600
|
+
|
|
1601
|
+
/* Dark theme re-balances the two inks: on a dark surface a 0.6 white halo is far
|
|
1602
|
+
too loud and a 0.16 near-black shade barely shows, so the highlight drops to a
|
|
1603
|
+
faint lighten and the shade deepens to a real black — keeping the constant
|
|
1604
|
+
light-above / dark-below mould readable in both themes. Scoped to BOTH axis
|
|
1605
|
+
attributes on <html> (NOT a bare [data-style] token block, so the disjoint-axis
|
|
1606
|
+
test does not read it); sets only neutral rgba scalars, no hue. */
|
|
1607
|
+
[data-style='neumorphism'][data-theme='dark'] {
|
|
1608
|
+
--neu-light: rgba(255, 255, 255, 0.05);
|
|
1609
|
+
--neu-shadow: rgba(0, 0, 0, 0.44);
|
|
1610
|
+
}
|
|
1611
|
+
|
|
1612
|
+
/* ─────────────────────────────────────────────────────────────────────────
|
|
1613
|
+
Neumorphism MATERIAL LAYER — palette-DERIVED surface treatment (7.3.42).
|
|
1614
|
+
The same style-contract extension glass / aurora use: a style may own its
|
|
1615
|
+
surface material in STYLE-SCOPED component rules as long as every colour is
|
|
1616
|
+
DERIVED from the active palette via color-mix()/var() (the dark shadow + the
|
|
1617
|
+
monochrome field) — never a raw hue (the white highlight is palette-agnostic
|
|
1618
|
+
light, not a hue). These descendant-scoped selectors are NOT the bare token
|
|
1619
|
+
block, so they never trip the colour-free token-block test; they ARE read as
|
|
1620
|
+
material rules and are palette-derived (enforced by the "surface-material
|
|
1621
|
+
layer" test in styleRegistry.test.ts). ──────────────────────────────────── */
|
|
1622
|
+
|
|
1623
|
+
/* The monochrome field: the page canvas adopts the palette's opaque surface so
|
|
1624
|
+
panels look MOULDED OUT OF the same material rather than placed on top. Both
|
|
1625
|
+
the canvas and the surfaces below share `var(--el-surface)`, which is the whole
|
|
1626
|
+
neumorphic illusion. Palette-derived (no hue of its own). */
|
|
1627
|
+
[data-style='neumorphism'] body {
|
|
1628
|
+
background-color: var(--el-surface);
|
|
1629
|
+
}
|
|
1630
|
+
|
|
1631
|
+
/* Raised container surfaces — cards & popovers swell OUT of the field on a paired
|
|
1632
|
+
soft shadow: a light highlight top-left + a dark shadow bottom-right. Both inks
|
|
1633
|
+
are neutral (palette-agnostic) shadow ink; the SURFACE + border are
|
|
1634
|
+
palette-derived, so the mould re-tones per palette and the style stays disjoint
|
|
1635
|
+
from hue. The faint hairline is kept as the accessibility fallback so structure
|
|
1636
|
+
doesn't depend on the shadow alone. Opaque `--el-surface` keeps text on the
|
|
1637
|
+
palette's AA-tuned surface. */
|
|
1638
|
+
[data-style='neumorphism'] [data-surface='card'],
|
|
1639
|
+
[data-style='neumorphism'] [data-surface='popover'] {
|
|
1640
|
+
background-color: var(--el-surface);
|
|
1641
|
+
border-color: color-mix(in srgb, var(--el-border) 45%, transparent);
|
|
1642
|
+
box-shadow:
|
|
1643
|
+
var(--neu-distance) var(--neu-distance) var(--neu-blur) var(--neu-shadow),
|
|
1644
|
+
calc(-1 * var(--neu-distance)) calc(-1 * var(--neu-distance)) var(--neu-blur) var(--neu-light);
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1647
|
+
/* Modal panel — the same raised mould, a touch deeper so the dialog separates
|
|
1648
|
+
from the field behind it (body text stays on the opaque surface). */
|
|
1649
|
+
[data-style='neumorphism'] [data-surface='modal'] {
|
|
1650
|
+
background-color: var(--el-surface);
|
|
1651
|
+
border-color: color-mix(in srgb, var(--el-border) 45%, transparent);
|
|
1652
|
+
box-shadow:
|
|
1653
|
+
calc(var(--neu-distance) + 2px) calc(var(--neu-distance) + 2px) calc(var(--neu-blur) + 8px)
|
|
1654
|
+
var(--neu-shadow),
|
|
1655
|
+
calc(-1 * var(--neu-distance)) calc(-1 * var(--neu-distance)) calc(var(--neu-blur) + 4px)
|
|
1656
|
+
var(--neu-light);
|
|
1657
|
+
}
|
|
1658
|
+
|
|
1659
|
+
/* The sidebar rail — moulded from the same field as the chrome, gently raised so
|
|
1660
|
+
it reads as part of the one continuous surface. */
|
|
1661
|
+
[data-style='neumorphism'] [data-surface='sidebar'] {
|
|
1662
|
+
background-color: var(--el-surface);
|
|
1663
|
+
box-shadow:
|
|
1664
|
+
var(--neu-distance) 0 var(--neu-blur) var(--neu-shadow),
|
|
1665
|
+
calc(-1 * var(--neu-distance)) 0 var(--neu-blur) var(--neu-light);
|
|
1666
|
+
}
|
|
1667
|
+
|
|
1668
|
+
/* Form controls — the inverse mould: a CONCAVE INSET well pressed INTO the field
|
|
1669
|
+
(the soft-UI input look), the paired shadow flipped to `inset`. */
|
|
1670
|
+
[data-style='neumorphism'] [data-surface='input'] {
|
|
1671
|
+
background-color: var(--el-surface);
|
|
1672
|
+
border-color: color-mix(in srgb, var(--el-border) 45%, transparent);
|
|
1673
|
+
box-shadow:
|
|
1674
|
+
inset var(--neu-distance-sm) var(--neu-distance-sm) var(--neu-blur-sm) var(--neu-shadow),
|
|
1675
|
+
inset calc(-1 * var(--neu-distance-sm)) calc(-1 * var(--neu-distance-sm)) var(--neu-blur-sm)
|
|
1676
|
+
var(--neu-light);
|
|
1677
|
+
}
|
|
1678
|
+
|
|
1679
|
+
/* Accessibility fallback — when a user asks for more contrast (or forced colours),
|
|
1680
|
+
structure must NOT rest on the soft shadow. Restore a solid hairline on every
|
|
1681
|
+
moulded surface and flatten the paired shadow to a single readable lift.
|
|
1682
|
+
Palette-derived (var(--el-border)); no hue. */
|
|
1683
|
+
@media (prefers-contrast: more) {
|
|
1684
|
+
[data-style='neumorphism'] [data-surface='card'],
|
|
1685
|
+
[data-style='neumorphism'] [data-surface='popover'],
|
|
1686
|
+
[data-style='neumorphism'] [data-surface='modal'],
|
|
1687
|
+
[data-style='neumorphism'] [data-surface='input'],
|
|
1688
|
+
[data-style='neumorphism'] [data-surface='sidebar'] {
|
|
1689
|
+
border-color: var(--el-border-strong);
|
|
1690
|
+
box-shadow: var(--shadow-card);
|
|
1691
|
+
}
|
|
1692
|
+
}
|
|
1693
|
+
@media (forced-colors: active) {
|
|
1694
|
+
[data-style='neumorphism'] [data-surface='card'],
|
|
1695
|
+
[data-style='neumorphism'] [data-surface='popover'],
|
|
1696
|
+
[data-style='neumorphism'] [data-surface='modal'],
|
|
1697
|
+
[data-style='neumorphism'] [data-surface='input'],
|
|
1698
|
+
[data-style='neumorphism'] [data-surface='sidebar'] {
|
|
1699
|
+
border-color: var(--el-border-strong);
|
|
1700
|
+
box-shadow: none;
|
|
1701
|
+
}
|
|
1702
|
+
}
|
|
1703
|
+
|
|
1704
|
+
/* ─────────────────────────────────────────────────────────────────────────
|
|
1705
|
+
Retrofuturism — chrome + gradient sweeps + retro display (7.3.40, DEFERRED
|
|
1706
|
+
follow-up). The 70s/80s sci-fi / Outrun look: a synthwave HORIZON gradient
|
|
1707
|
+
sweeps the canvas, and every surface wears a CHROME BEVEL (a bright top
|
|
1708
|
+
highlight fading to a dark base) plus a diagonal reflective sheen, so panels
|
|
1709
|
+
read as machined chrome rather than flat cards. The retro display read is the
|
|
1710
|
+
`grotesk` type pairing (the style's defaultTypeId — wide geometric headlines),
|
|
1711
|
+
NOT a `--font-*` override here (type is the independent data-type axis, 7.3.53).
|
|
1712
|
+
Overrides ONLY shape/feel tokens — never a colour token (the data-palette
|
|
1713
|
+
axis; the style wears whatever palette is active, so AA contrast is preserved
|
|
1714
|
+
by construction). The metallic bevel/sheen lives in the MATERIAL LAYER below,
|
|
1715
|
+
palette-DERIVED: every colour comes from color-mix()/var(--el-*) over the
|
|
1716
|
+
active palette, or from the achromatic white/black KEYWORDS that make the
|
|
1717
|
+
chrome read as lightness — never a raw hex hue (so the style stays disjoint
|
|
1718
|
+
from the palette; enforced by tests/theme/styleRegistry.test.ts). The
|
|
1719
|
+
differentiator vs Cybercore/Y2K: cybercore GLOWS neon on dark behind a tech
|
|
1720
|
+
grid; retrofuturism is REFLECTIVE chrome over a sunset horizon. See
|
|
1721
|
+
docs/styles/retrofuturism.md. ──────────────────────────────────────────── */
|
|
1722
|
+
[data-style='retrofuturism'] {
|
|
1723
|
+
/* Silhouette — streamlined chrome capsules. --radius-pill stays untouched so
|
|
1724
|
+
genuinely-circular affordances (avatars, status dots, the spinner) stay
|
|
1725
|
+
round. Buttons + chips go FULL pill (chrome lozenges); panels stay softly
|
|
1726
|
+
rounded. */
|
|
1727
|
+
--radius-xs: 6px;
|
|
1728
|
+
--radius-sm: 9px;
|
|
1729
|
+
--radius-md: 12px;
|
|
1730
|
+
--radius-lg: 16px;
|
|
1731
|
+
--radius-xl: 22px;
|
|
1732
|
+
--radius-btn: 9999px; /* chrome-capsule buttons */
|
|
1733
|
+
--radius-input: 12px;
|
|
1734
|
+
--radius-card: 16px;
|
|
1735
|
+
--radius-modal: 22px;
|
|
1736
|
+
--radius-badge: 9999px; /* status chips stay pills — the streamlined register */
|
|
1737
|
+
--radius-control: 10px;
|
|
1738
|
+
--radius-kbd: 6px;
|
|
1739
|
+
|
|
1740
|
+
/* Elevation — a soft NEUTRAL lift only. The chrome bevel highlight + the
|
|
1741
|
+
palette-derived colour glow are painted in the material layer below (no hue
|
|
1742
|
+
may appear here); these neutral rgba shadows are the diffuse base the chrome
|
|
1743
|
+
sits on. (rgba literals are neutral shadow ink — a cool near-black — not a
|
|
1744
|
+
palette hue, so the token block stays colour-free.) */
|
|
1745
|
+
--shadow-subtle: 0 1px 2px rgba(10, 12, 30, 0.06);
|
|
1746
|
+
--shadow-card: 0 10px 26px -12px rgba(10, 12, 30, 0.22);
|
|
1747
|
+
--shadow-elevated: 0 18px 40px -14px rgba(10, 12, 30, 0.28);
|
|
1748
|
+
--shadow-modal: 0 28px 60px -18px rgba(10, 12, 30, 0.34);
|
|
1749
|
+
--shadow-hero-mockup: 0 34px 74px -18px rgba(10, 12, 30, 0.4);
|
|
1750
|
+
|
|
1751
|
+
/* Density — confident and roomy; the chrome wants room to catch the light. */
|
|
1752
|
+
--spacing-btn-x: 22px;
|
|
1753
|
+
--spacing-btn-y: 11px;
|
|
1754
|
+
--spacing-card-padding: 24px;
|
|
1755
|
+
--spacing-control-x: 12px;
|
|
1756
|
+
--spacing-control-y: 7px;
|
|
1757
|
+
--height-control: 38px;
|
|
1758
|
+
|
|
1759
|
+
/* Motion — smooth and gliding; transitions ease, with a confident press. */
|
|
1760
|
+
--transition-duration: 200ms;
|
|
1761
|
+
--transition-slow: 320ms;
|
|
1762
|
+
--active-scale: 0.97;
|
|
1763
|
+
|
|
1764
|
+
/* ── Material SCALARS — palette-AGNOSTIC (no hue, so no colour token here).
|
|
1765
|
+
Consumed only by the [data-style='retrofuturism'] material rules below: the
|
|
1766
|
+
synthwave SKY / HORIZON / GROUND band strengths + the neon GRID line alpha
|
|
1767
|
+
and cell size (the Outrun floor), the chrome SPECULAR streak + bevel
|
|
1768
|
+
highlight/shade (mixed from the achromatic white/black keywords), and the
|
|
1769
|
+
palette-derived glow. Bold by design — this style is dark-FIRST (the neon
|
|
1770
|
+
horizon + grid read strongest on a dark palette), so the strengths run high.
|
|
1771
|
+
── */
|
|
1772
|
+
--retro-sky: 24%; /* the upper-sky wash (cool, from --el-link) */
|
|
1773
|
+
--retro-horizon: 72%; /* the bright sun/horizon GLOW band — the signature */
|
|
1774
|
+
--retro-ground: 26%; /* the warm ground wash rising from the bottom */
|
|
1775
|
+
--retro-grid: 52%; /* the neon perspective-grid line alpha */
|
|
1776
|
+
--retro-grid-size: 60px; /* the grid cell size before the perspective tilt */
|
|
1777
|
+
--retro-spec: 44%; /* the bright diagonal SPECULAR streak across chrome */
|
|
1778
|
+
--retro-bevel-light: 44%; /* the bright top metallic bevel highlight (white mix) */
|
|
1779
|
+
--retro-bevel-shade: 28%; /* the dark base of the bevel (black mix) */
|
|
1780
|
+
--retro-sheen: 18%; /* the quieter chrome edge tint (rail / input) */
|
|
1781
|
+
--retro-glow: 32%; /* palette-derived colour glow on a card / popover */
|
|
1782
|
+
--retro-glow-strong: 44%; /* the modal's / primary button's deeper glow */
|
|
1783
|
+
}
|
|
1784
|
+
|
|
1785
|
+
/* ─────────────────────────────────────────────────────────────────────────
|
|
1786
|
+
Retrofuturism MATERIAL LAYER — palette-DERIVED chrome + horizon (7.3.40).
|
|
1787
|
+
The same style-contract extension glassmorphism / aurora use: a style may own
|
|
1788
|
+
its surface material in STYLE-SCOPED component rules as long as every colour
|
|
1789
|
+
is DERIVED from the active palette via color-mix()/var() (or the achromatic
|
|
1790
|
+
white/black keywords for pure metallic lightness) — never a raw hex hue — so
|
|
1791
|
+
the style axis stays disjoint from the palette axis. These descendant-scoped
|
|
1792
|
+
selectors are NOT the bare token block, so they never trip the colour-free
|
|
1793
|
+
token-block test; they ARE read as material rules and must be palette-derived
|
|
1794
|
+
(enforced by the "surface-material layer" test in styleRegistry.test.ts).
|
|
1795
|
+
───────────────────────────────────────────────────────────────────────── */
|
|
1796
|
+
|
|
1797
|
+
/* The canvas: the palette's page bg + a SYNTHWAVE SKY — a cool upper-sky wash, a
|
|
1798
|
+
bright sun/HORIZON GLOW band ~62% down, and a warm ground wash rising from the
|
|
1799
|
+
bottom. Every hue comes from the `--el-*` accent roles (NOT Tier-0
|
|
1800
|
+
`--color-*`), so the sky re-tints with the active palette — the `data-palette`
|
|
1801
|
+
axis overrides `--el-*`, never `--color-*`, which keeps the horizon orthogonal
|
|
1802
|
+
to the palette. Painted as the body's own background (always behind content),
|
|
1803
|
+
the proven 3D/Immersive atmosphere approach. */
|
|
1804
|
+
[data-style='retrofuturism'] body {
|
|
1805
|
+
background-image:
|
|
1806
|
+
linear-gradient(
|
|
1807
|
+
180deg,
|
|
1808
|
+
transparent 54%,
|
|
1809
|
+
color-mix(in srgb, var(--el-highlight) var(--retro-horizon), transparent) 60.5%,
|
|
1810
|
+
color-mix(in srgb, var(--el-accent) var(--retro-horizon), transparent) 62.5%,
|
|
1811
|
+
transparent 69%
|
|
1812
|
+
),
|
|
1813
|
+
linear-gradient(
|
|
1814
|
+
180deg,
|
|
1815
|
+
color-mix(in srgb, var(--el-link) var(--retro-sky), transparent),
|
|
1816
|
+
transparent 46%
|
|
1817
|
+
),
|
|
1818
|
+
linear-gradient(
|
|
1819
|
+
0deg,
|
|
1820
|
+
color-mix(in srgb, var(--el-accent) var(--retro-ground), transparent),
|
|
1821
|
+
transparent 42%
|
|
1822
|
+
);
|
|
1823
|
+
background-size: 100% 100%;
|
|
1824
|
+
background-attachment: fixed;
|
|
1825
|
+
}
|
|
1826
|
+
|
|
1827
|
+
/* The Outrun GRID FLOOR — a receding neon grid below the horizon. A separate
|
|
1828
|
+
fixed layer (`body::after`) so it can be perspective-tilted and masked to fade
|
|
1829
|
+
into the horizon; `z-index: -1` keeps it BEHIND all app content (above only the
|
|
1830
|
+
body's own sky background), so it textures the floor without ever covering the
|
|
1831
|
+
UI. Lines are `color-mix()` over `--el-*` accents (palette-derived, no hue of
|
|
1832
|
+
their own); the mask uses the achromatic `black` keyword (no hex). The grid is
|
|
1833
|
+
STATIC depth — like 3D/Immersive's atmosphere it carries the identity, so it is
|
|
1834
|
+
not motion-gated (there is no animation to suppress). */
|
|
1835
|
+
[data-style='retrofuturism'] body::after {
|
|
1836
|
+
content: '';
|
|
1837
|
+
position: fixed;
|
|
1838
|
+
left: -30%;
|
|
1839
|
+
right: -30%;
|
|
1840
|
+
bottom: 0;
|
|
1841
|
+
height: 42vh;
|
|
1842
|
+
background-image:
|
|
1843
|
+
repeating-linear-gradient(
|
|
1844
|
+
0deg,
|
|
1845
|
+
color-mix(in srgb, var(--el-highlight) var(--retro-grid), transparent) 0 2px,
|
|
1846
|
+
transparent 2px var(--retro-grid-size)
|
|
1847
|
+
),
|
|
1848
|
+
repeating-linear-gradient(
|
|
1849
|
+
90deg,
|
|
1850
|
+
color-mix(in srgb, var(--el-accent) var(--retro-grid), transparent) 0 2px,
|
|
1851
|
+
transparent 2px var(--retro-grid-size)
|
|
1852
|
+
);
|
|
1853
|
+
background-size: var(--retro-grid-size) var(--retro-grid-size);
|
|
1854
|
+
transform: perspective(300px) rotateX(75deg);
|
|
1855
|
+
transform-origin: bottom center;
|
|
1856
|
+
-webkit-mask-image: linear-gradient(to top, black 2%, transparent 80%);
|
|
1857
|
+
mask-image: linear-gradient(to top, black 2%, transparent 80%);
|
|
1858
|
+
pointer-events: none;
|
|
1859
|
+
z-index: -1;
|
|
1860
|
+
}
|
|
1861
|
+
|
|
1862
|
+
/* Chrome container surfaces — OPAQUE (text AA is preserved against the base
|
|
1863
|
+
surface the palette already tuned) carrying a diagonal SPECULAR streak (a
|
|
1864
|
+
bright `white` light-bar swept across the panel, the reflective-metal read) AND
|
|
1865
|
+
a vertical CHROME BEVEL (bright `white` top → dark `black` base — both
|
|
1866
|
+
achromatic lightness, no hue), over a soft lift + a palette-derived colour
|
|
1867
|
+
glow. The streak/bevel ride background-image over the opaque background-color,
|
|
1868
|
+
so they never pull a surface far from its base tone; the inset top/bottom lines
|
|
1869
|
+
are the lit chrome rim. Applied to EVERY panel via the global card-radius
|
|
1870
|
+
utility (the 3D/Immersive coverage approach), not just the <Card> primitive. */
|
|
1871
|
+
[data-style='retrofuturism'] [data-surface='card'],
|
|
1872
|
+
[data-style='retrofuturism'] [data-surface='popover'],
|
|
1873
|
+
[data-style='retrofuturism'] .rounded-\(--radius-card\) {
|
|
1874
|
+
background-color: var(--el-surface);
|
|
1875
|
+
background-image:
|
|
1876
|
+
linear-gradient(
|
|
1877
|
+
158deg,
|
|
1878
|
+
transparent 36%,
|
|
1879
|
+
color-mix(in srgb, white var(--retro-spec), transparent) 49%,
|
|
1880
|
+
transparent 62%
|
|
1881
|
+
),
|
|
1882
|
+
linear-gradient(
|
|
1883
|
+
180deg,
|
|
1884
|
+
color-mix(in srgb, white var(--retro-bevel-light), transparent) 0%,
|
|
1885
|
+
transparent 16%,
|
|
1886
|
+
transparent 78%,
|
|
1887
|
+
color-mix(in srgb, black var(--retro-bevel-shade), transparent) 100%
|
|
1888
|
+
);
|
|
1889
|
+
box-shadow:
|
|
1890
|
+
var(--shadow-card),
|
|
1891
|
+
inset 0 1px 0 color-mix(in srgb, white 60%, transparent),
|
|
1892
|
+
inset 0 -1px 0 color-mix(in srgb, black 24%, transparent),
|
|
1893
|
+
0 0 28px -8px color-mix(in srgb, var(--el-accent) var(--retro-glow), transparent);
|
|
1894
|
+
}
|
|
1895
|
+
|
|
1896
|
+
/* Modal panel — the same chrome, with a deeper palette glow so the dialog
|
|
1897
|
+
separates cleanly from the grid behind it (and body text stays on the solid
|
|
1898
|
+
surface). Overrides the card-radius rule above for the modal radius. */
|
|
1899
|
+
[data-style='retrofuturism'] [data-surface='modal'] {
|
|
1900
|
+
background-color: var(--el-surface);
|
|
1901
|
+
background-image:
|
|
1902
|
+
linear-gradient(
|
|
1903
|
+
158deg,
|
|
1904
|
+
transparent 34%,
|
|
1905
|
+
color-mix(in srgb, white var(--retro-spec), transparent) 49%,
|
|
1906
|
+
transparent 64%
|
|
1907
|
+
),
|
|
1908
|
+
linear-gradient(
|
|
1909
|
+
180deg,
|
|
1910
|
+
color-mix(in srgb, white var(--retro-bevel-light), transparent) 0%,
|
|
1911
|
+
transparent 14%,
|
|
1912
|
+
transparent 80%,
|
|
1913
|
+
color-mix(in srgb, black var(--retro-bevel-shade), transparent) 100%
|
|
1914
|
+
);
|
|
1915
|
+
box-shadow:
|
|
1916
|
+
var(--shadow-modal),
|
|
1917
|
+
inset 0 1px 0 color-mix(in srgb, white 60%, transparent),
|
|
1918
|
+
0 0 56px -14px color-mix(in srgb, var(--el-highlight) var(--retro-glow-strong), transparent);
|
|
1919
|
+
}
|
|
1920
|
+
|
|
1921
|
+
/* The sidebar rail — opaque chrome with a quieter specular + bevel so it reads as
|
|
1922
|
+
part of the polished-metal family, not a flat slab. */
|
|
1923
|
+
[data-style='retrofuturism'] [data-surface='sidebar'] {
|
|
1924
|
+
background-color: var(--el-sidebar-bg);
|
|
1925
|
+
background-image:
|
|
1926
|
+
linear-gradient(
|
|
1927
|
+
158deg,
|
|
1928
|
+
transparent 40%,
|
|
1929
|
+
color-mix(in srgb, white var(--retro-sheen), transparent) 50%,
|
|
1930
|
+
transparent 60%
|
|
1931
|
+
),
|
|
1932
|
+
linear-gradient(
|
|
1933
|
+
180deg,
|
|
1934
|
+
color-mix(in srgb, white var(--retro-sheen), transparent),
|
|
1935
|
+
transparent 24%,
|
|
1936
|
+
color-mix(in srgb, black var(--retro-bevel-shade), transparent) 100%
|
|
1937
|
+
);
|
|
1938
|
+
}
|
|
1939
|
+
|
|
1940
|
+
/* Text inputs (a TYPING field) — a RECESSED chrome well: the bevel inverts (a
|
|
1941
|
+
dark lip at the top) + an inset shadow, so the field reads as stamped INTO the
|
|
1942
|
+
metal. Clickable controls (dropdown / select / picker triggers + buttons) get
|
|
1943
|
+
the RAISED chrome below instead — matching the CTA — so a typing field stays
|
|
1944
|
+
recessed while everything you click reads as a raised chrome control. */
|
|
1945
|
+
[data-style='retrofuturism'] [data-surface='input'] {
|
|
1946
|
+
background-color: var(--el-page-bg);
|
|
1947
|
+
background-image: linear-gradient(
|
|
1948
|
+
180deg,
|
|
1949
|
+
color-mix(in srgb, black var(--retro-sheen), transparent) 0%,
|
|
1950
|
+
transparent 34%
|
|
1951
|
+
);
|
|
1952
|
+
box-shadow: inset 0 1px 3px color-mix(in srgb, black var(--retro-glow), transparent);
|
|
1953
|
+
}
|
|
1954
|
+
|
|
1955
|
+
/* ── EVERY clickable control reads as RETRO CHROME (7.3.40) ──────────────────
|
|
1956
|
+
The whole point of the style: not just the `Button` primitive, but every
|
|
1957
|
+
button-SHAPED control gets a machined-chrome treatment, so nothing sits flat.
|
|
1958
|
+
Buttons render in THREE shapes across the app, so keying on `data-variant`
|
|
1959
|
+
alone (only the `Button` primitive sets it) misses most of them — these rules
|
|
1960
|
+
instead key on the UTILITY CLASSES the design system emits
|
|
1961
|
+
(`.rounded-(--radius-btn)` + the fill / `.border` class), which are
|
|
1962
|
+
tag-agnostic and catch all of:
|
|
1963
|
+
• the `Button` primitive (`<button>` with `data-variant`),
|
|
1964
|
+
• `<a>` / `<Link>` styled via `buttonVariants()` (NO `data-variant` — e.g.
|
|
1965
|
+
the "View all issues" / empty-state links),
|
|
1966
|
+
• hand-rolled `<button>` / `<a>` with `rounded-(--radius-btn) border` (the
|
|
1967
|
+
/issues filter bar: Filter / Advanced / Saved / the Tree·List switcher),
|
|
1968
|
+
• input-styled dropdown / select / picker triggers
|
|
1969
|
+
(`button.rounded-(--radius-input)`),
|
|
1970
|
+
• the SEGMENTED control (`components/ui/Segmented.tsx` — the board "Group by"
|
|
1971
|
+
switch & friends: a `[role='group']` track + `aria-pressed` segments).
|
|
1972
|
+
Two families: a FILLED CTA (accent / danger) keeps its hue under a metallic
|
|
1973
|
+
sweep; everything else is NEUTRAL GUNMETAL steel. All palette-derived
|
|
1974
|
+
(color-mix / var(--el-*)); the light/shade are the achromatic white/black
|
|
1975
|
+
keywords (no hex). The gunmetal base is `--el-surface` DARKENED — on a light
|
|
1976
|
+
palette the bare surface ≈ the page, so a white-highlight bevel over it is
|
|
1977
|
+
invisible (flat white); a mid-grey gives the highlight AND the shade contrast,
|
|
1978
|
+
so the metal reads on light AND dark. ───────────────────────────────────── */
|
|
1979
|
+
|
|
1980
|
+
/* FILLED CTAs — accent (primary) keeps its hue under a diagonal specular glint +
|
|
1981
|
+
a vertical metallic sweep + a palette glow. Keyed on the fill class so it
|
|
1982
|
+
covers the `Button` primitive AND `buttonVariants()` anchors alike. */
|
|
1983
|
+
[data-style='retrofuturism'] .rounded-\(--radius-btn\).bg-\(--el-accent\) {
|
|
1984
|
+
background-image:
|
|
1985
|
+
linear-gradient(
|
|
1986
|
+
158deg,
|
|
1987
|
+
transparent 38%,
|
|
1988
|
+
color-mix(in srgb, white var(--retro-spec), transparent) 50%,
|
|
1989
|
+
transparent 62%
|
|
1990
|
+
),
|
|
1991
|
+
linear-gradient(
|
|
1992
|
+
180deg,
|
|
1993
|
+
color-mix(in srgb, white var(--retro-spec), var(--el-accent)) 0%,
|
|
1994
|
+
var(--el-accent) 46%,
|
|
1995
|
+
color-mix(in srgb, black var(--retro-bevel-shade), var(--el-accent)) 100%
|
|
1996
|
+
);
|
|
1997
|
+
box-shadow:
|
|
1998
|
+
var(--shadow-subtle),
|
|
1999
|
+
inset 0 1px 0 color-mix(in srgb, white 60%, transparent),
|
|
2000
|
+
0 0 22px -6px color-mix(in srgb, var(--el-accent) var(--retro-glow-strong), transparent);
|
|
2001
|
+
}
|
|
2002
|
+
|
|
2003
|
+
/* Danger CTAs — the same machined sweep, in the danger hue (so a destructive
|
|
2004
|
+
button reads as red chrome, not a flat red, matching the accent CTA). */
|
|
2005
|
+
[data-style='retrofuturism'] .rounded-\(--radius-btn\).bg-\(--el-danger\) {
|
|
2006
|
+
background-image:
|
|
2007
|
+
linear-gradient(
|
|
2008
|
+
158deg,
|
|
2009
|
+
transparent 38%,
|
|
2010
|
+
color-mix(in srgb, white var(--retro-spec), transparent) 50%,
|
|
2011
|
+
transparent 62%
|
|
2012
|
+
),
|
|
2013
|
+
linear-gradient(
|
|
2014
|
+
180deg,
|
|
2015
|
+
color-mix(in srgb, white var(--retro-spec), var(--el-danger)) 0%,
|
|
2016
|
+
var(--el-danger) 46%,
|
|
2017
|
+
color-mix(in srgb, black var(--retro-bevel-shade), var(--el-danger)) 100%
|
|
2018
|
+
);
|
|
2019
|
+
box-shadow:
|
|
2020
|
+
var(--shadow-subtle),
|
|
2021
|
+
inset 0 1px 0 color-mix(in srgb, white 60%, transparent),
|
|
2022
|
+
0 0 22px -6px color-mix(in srgb, var(--el-danger) var(--retro-glow-strong), transparent);
|
|
2023
|
+
}
|
|
2024
|
+
|
|
2025
|
+
/* NEUTRAL STEEL chrome — every OTHER clickable control: bordered buttons + links
|
|
2026
|
+
(secondary `Button`, secondary anchors, the hand-rolled filter-bar buttons, the
|
|
2027
|
+
"View all" links) and the dropdown / select / picker triggers.
|
|
2028
|
+
`:not([role='group'])` excludes the segmented TRACK (its own rule below); the
|
|
2029
|
+
FILLED CTAs are excluded automatically (they carry a fill class, not `.border`).
|
|
2030
|
+
The gunmetal base is set here because secondary controls ship `bg-transparent`. */
|
|
2031
|
+
[data-style='retrofuturism'] .rounded-\(--radius-btn\).border:not([role='group']),
|
|
2032
|
+
[data-style='retrofuturism'] button.rounded-\(--radius-input\) {
|
|
2033
|
+
background-color: color-mix(in srgb, black 24%, var(--el-surface));
|
|
2034
|
+
background-image:
|
|
2035
|
+
linear-gradient(
|
|
2036
|
+
158deg,
|
|
2037
|
+
transparent 36%,
|
|
2038
|
+
color-mix(in srgb, white var(--retro-spec), transparent) 50%,
|
|
2039
|
+
transparent 64%
|
|
2040
|
+
),
|
|
2041
|
+
linear-gradient(
|
|
2042
|
+
180deg,
|
|
2043
|
+
color-mix(in srgb, white var(--retro-bevel-light), transparent) 0%,
|
|
2044
|
+
transparent 42%,
|
|
2045
|
+
transparent 60%,
|
|
2046
|
+
color-mix(in srgb, black var(--retro-bevel-shade), transparent) 100%
|
|
2047
|
+
);
|
|
2048
|
+
box-shadow:
|
|
2049
|
+
var(--shadow-subtle),
|
|
2050
|
+
inset 0 1px 0 color-mix(in srgb, white 60%, transparent),
|
|
2051
|
+
inset 0 -1px 0 color-mix(in srgb, black 24%, transparent),
|
|
2052
|
+
0 0 18px -8px color-mix(in srgb, var(--el-accent) var(--retro-glow), transparent);
|
|
2053
|
+
}
|
|
2054
|
+
|
|
2055
|
+
/* SEGMENTED control (the board "Group by" switch + every other `Segmented` use):
|
|
2056
|
+
the classic metal segmented control — a RECESSED gunmetal TRACK (the
|
|
2057
|
+
`[role='group']` wrapper) with the ACTIVE segment (`aria-pressed='true'`) raised
|
|
2058
|
+
out of it as a chrome chip. Inactive segments stay transparent on the track. */
|
|
2059
|
+
[data-style='retrofuturism'] [role='group'].rounded-\(--radius-btn\) {
|
|
2060
|
+
background-color: color-mix(in srgb, black 15%, var(--el-surface));
|
|
2061
|
+
box-shadow: inset 0 1px 3px color-mix(in srgb, black var(--retro-glow), transparent);
|
|
2062
|
+
}
|
|
2063
|
+
[data-style='retrofuturism'] [role='group'] [aria-pressed='true'] {
|
|
2064
|
+
background-color: color-mix(in srgb, black 24%, var(--el-surface));
|
|
2065
|
+
background-image:
|
|
2066
|
+
linear-gradient(
|
|
2067
|
+
158deg,
|
|
2068
|
+
transparent 36%,
|
|
2069
|
+
color-mix(in srgb, white var(--retro-spec), transparent) 50%,
|
|
2070
|
+
transparent 64%
|
|
2071
|
+
),
|
|
2072
|
+
linear-gradient(
|
|
2073
|
+
180deg,
|
|
2074
|
+
color-mix(in srgb, white var(--retro-bevel-light), transparent) 0%,
|
|
2075
|
+
transparent 42%,
|
|
2076
|
+
transparent 60%,
|
|
2077
|
+
color-mix(in srgb, black var(--retro-bevel-shade), transparent) 100%
|
|
2078
|
+
);
|
|
2079
|
+
box-shadow:
|
|
2080
|
+
var(--shadow-subtle),
|
|
2081
|
+
inset 0 1px 0 color-mix(in srgb, white 60%, transparent),
|
|
2082
|
+
inset 0 -1px 0 color-mix(in srgb, black 24%, transparent);
|
|
2083
|
+
}
|
|
2084
|
+
|
|
2085
|
+
/* Hover — brighten the steel a touch (the controls' own `hover:bg-*` utilities
|
|
2086
|
+
are out-specified by the rules above, so restore a clear hover signal). */
|
|
2087
|
+
[data-style='retrofuturism']
|
|
2088
|
+
.rounded-\(--radius-btn\).border:not([role='group']):hover:not(:disabled),
|
|
2089
|
+
[data-style='retrofuturism'] button.rounded-\(--radius-input\):hover:not(:disabled),
|
|
2090
|
+
[data-style='retrofuturism'] [role='group'] [aria-pressed='false']:hover:not(:disabled) {
|
|
2091
|
+
background-color: color-mix(in srgb, black 14%, var(--el-surface));
|
|
2092
|
+
}
|
|
2093
|
+
|
|
2094
|
+
/* ─────────────────────────────────────────────────────────────────────────
|
|
2095
|
+
Style-preview vignette material canvas (7.3.37 / MOTIR-1050).
|
|
2096
|
+
The StyleVignette preview specimen carries its OWN axis attribute on a
|
|
2097
|
+
nested wrapper (`.style-vignette[data-style='…']`), so it cannot lean on the
|
|
2098
|
+
`body`-scoped material canvas above — a glassmorphism / cybercore vignette
|
|
2099
|
+
would otherwise float its frosted/glow surfaces over a flat fill. These rules
|
|
2100
|
+
paint the same canvas onto the vignette's `.sv-canvas` layer, scoped to a
|
|
2101
|
+
DIRECT child of the marked wrapper so an ancestor `<html data-style>` can't
|
|
2102
|
+
bleed the wrong canvas into a differently-scoped vignette. Palette-DERIVED
|
|
2103
|
+
(color-mix / currentColor over the active palette, never a raw hue) and
|
|
2104
|
+
descendant-scoped — so styleRegistry.test.ts reads them as material rules,
|
|
2105
|
+
not bare token blocks. */
|
|
2106
|
+
.style-vignette[data-style='glassmorphism'] > .sv-canvas {
|
|
2107
|
+
background-image:
|
|
2108
|
+
radial-gradient(
|
|
2109
|
+
62% 70% at 12% -5%,
|
|
2110
|
+
color-mix(in srgb, var(--el-accent) var(--glass-wash), transparent),
|
|
2111
|
+
transparent 70%
|
|
2112
|
+
),
|
|
2113
|
+
radial-gradient(
|
|
2114
|
+
58% 65% at 105% 6%,
|
|
2115
|
+
color-mix(in srgb, var(--el-highlight) var(--glass-wash), transparent),
|
|
2116
|
+
transparent 68%
|
|
2117
|
+
),
|
|
2118
|
+
radial-gradient(
|
|
2119
|
+
72% 78% at 50% 112%,
|
|
2120
|
+
color-mix(in srgb, var(--el-link) var(--glass-wash), transparent),
|
|
2121
|
+
transparent 72%
|
|
2122
|
+
);
|
|
2123
|
+
}
|
|
2124
|
+
.style-vignette[data-style='cybercore-y2k'] > .sv-canvas {
|
|
2125
|
+
background-color: var(--el-page-bg);
|
|
2126
|
+
background-image:
|
|
2127
|
+
linear-gradient(color-mix(in srgb, currentColor 6%, transparent) 1px, transparent 1px),
|
|
2128
|
+
linear-gradient(90deg, color-mix(in srgb, currentColor 6%, transparent) 1px, transparent 1px);
|
|
2129
|
+
background-size: 26px 26px;
|
|
2130
|
+
}
|
|
2131
|
+
/* Aurora's drifting ribbons, painted statically onto the preview swatch (the
|
|
2132
|
+
gallery shows many vignettes at once, so the swatch holds a still frame of the
|
|
2133
|
+
canvas — the live drift is reserved for the full `body`). Palette-derived. */
|
|
2134
|
+
.style-vignette[data-style='aurora'] > .sv-canvas {
|
|
2135
|
+
background-color: var(--el-page-bg);
|
|
2136
|
+
background-image:
|
|
2137
|
+
radial-gradient(
|
|
2138
|
+
58% 75% at 12% 0%,
|
|
2139
|
+
color-mix(in srgb, var(--el-accent) var(--aurora-wash), transparent),
|
|
2140
|
+
transparent 60%
|
|
2141
|
+
),
|
|
2142
|
+
radial-gradient(
|
|
2143
|
+
54% 70% at 92% 8%,
|
|
2144
|
+
color-mix(in srgb, var(--el-highlight) var(--aurora-wash), transparent),
|
|
2145
|
+
transparent 58%
|
|
2146
|
+
),
|
|
2147
|
+
radial-gradient(
|
|
2148
|
+
68% 82% at 62% 104%,
|
|
2149
|
+
color-mix(in srgb, var(--el-link) var(--aurora-wash), transparent),
|
|
2150
|
+
transparent 62%
|
|
2151
|
+
),
|
|
2152
|
+
radial-gradient(
|
|
2153
|
+
48% 64% at 34% 58%,
|
|
2154
|
+
color-mix(in srgb, var(--el-info) var(--aurora-wash-soft), transparent),
|
|
2155
|
+
transparent 55%
|
|
2156
|
+
);
|
|
2157
|
+
}
|
|
2158
|
+
|
|
2159
|
+
/* Retrofuturism's synthwave sky + horizon glow + a hint of the neon grid,
|
|
2160
|
+
painted statically onto the preview swatch (the gallery shows many vignettes
|
|
2161
|
+
at once, so the swatch holds a still frame — the live grid floor is reserved
|
|
2162
|
+
for the full `body`). Palette-derived. */
|
|
2163
|
+
.style-vignette[data-style='retrofuturism'] > .sv-canvas {
|
|
2164
|
+
background-color: var(--el-page-bg);
|
|
2165
|
+
background-image:
|
|
2166
|
+
repeating-linear-gradient(
|
|
2167
|
+
90deg,
|
|
2168
|
+
color-mix(in srgb, var(--el-accent) var(--retro-grid), transparent) 0 1px,
|
|
2169
|
+
transparent 1px 18px
|
|
2170
|
+
),
|
|
2171
|
+
linear-gradient(
|
|
2172
|
+
180deg,
|
|
2173
|
+
transparent 56%,
|
|
2174
|
+
color-mix(in srgb, var(--el-highlight) var(--retro-horizon), transparent) 64%,
|
|
2175
|
+
transparent 72%
|
|
2176
|
+
),
|
|
2177
|
+
linear-gradient(
|
|
2178
|
+
180deg,
|
|
2179
|
+
color-mix(in srgb, var(--el-link) var(--retro-sky), transparent),
|
|
2180
|
+
transparent 50%
|
|
2181
|
+
),
|
|
2182
|
+
linear-gradient(
|
|
2183
|
+
0deg,
|
|
2184
|
+
color-mix(in srgb, var(--el-accent) var(--retro-ground), transparent),
|
|
2185
|
+
transparent 46%
|
|
2186
|
+
);
|
|
2187
|
+
background-size:
|
|
2188
|
+
18px 100%,
|
|
2189
|
+
100% 100%,
|
|
2190
|
+
100% 100%,
|
|
2191
|
+
100% 100%;
|
|
2192
|
+
background-position: center bottom;
|
|
2193
|
+
}
|
|
2194
|
+
|
|
2195
|
+
/* ═══════════════════════════════════════════════════════════════════════
|
|
2196
|
+
TIER 3 — Element tokens (--el-*)
|
|
2197
|
+
Components reference these, NEVER --color-* directly. This is the
|
|
2198
|
+
layer future palettes (data-palette="...") will override to swap the
|
|
2199
|
+
look without touching component code.
|
|
2200
|
+
|
|
2201
|
+
The COMPLETE semantic element-token layer (finding #54). Every colour a
|
|
2202
|
+
component renders flows through one of these — never a Tier-0 --color-*
|
|
2203
|
+
nor an auto-generated --color-* utility (text-foreground / bg-surface /
|
|
2204
|
+
text-muted-foreground), which bypass this swap layer. When a surface needs
|
|
2205
|
+
a colour not exposed here, ADD the --el-* token (→ --color-*) and consume
|
|
2206
|
+
that (the per-component growth pattern, notes.html mistake #20). All tokens
|
|
2207
|
+
reference Tier-0 vars, so the Tier-1 dark block re-skins them automatically;
|
|
2208
|
+
only concrete hexes (the sidebar hover lift) carry their own dark value.
|
|
2209
|
+
═══════════════════════════════════════════════════════════════════════ */
|
|
2210
|
+
|
|
2211
|
+
/* `[data-appearance-scope]` re-emits this whole Tier-3 layer onto a NESTED
|
|
2212
|
+
element too — not just :root. `--el-*` reference `--color-*` and var() resolves
|
|
2213
|
+
at the DECLARING element, so a subtree that overrides `--color-*` (via a nested
|
|
2214
|
+
data-theme/-palette/-style) needs the `--el-*` re-declared locally to pick those
|
|
2215
|
+
overrides up. Put `data-appearance-scope` on an element alongside its data-*
|
|
2216
|
+
axes to theme that subtree independently of <html> (the onboarding design step,
|
|
2217
|
+
MOTIR-1040, uses it to preview the user's PROJECT design without touching Motir).
|
|
2218
|
+
Keep this selector list in sync if a new `--el-*` token is added below. */
|
|
2219
|
+
:root,
|
|
2220
|
+
[data-appearance-scope] {
|
|
2221
|
+
/* ── Page ──────────────────────────────────────────────────────── */
|
|
2222
|
+
--el-page-bg: var(--color-background);
|
|
2223
|
+
--el-page-text: var(--color-foreground);
|
|
2224
|
+
--el-page-text-muted: var(--color-muted-foreground);
|
|
2225
|
+
|
|
2226
|
+
/* ── Text scale (the warm-charcoal hierarchy, named by ROLE) ────── */
|
|
2227
|
+
--el-text: var(--color-foreground); /* primary ink */
|
|
2228
|
+
--el-text-strong: var(--color-charcoal); /* emphasis / AA-safe text on tints */
|
|
2229
|
+
--el-text-secondary: var(--color-slate); /* secondary copy */
|
|
2230
|
+
--el-text-muted: var(--color-muted-foreground); /* muted / captions */
|
|
2231
|
+
--el-text-tertiary: var(--color-steel); /* tertiary / footers */
|
|
2232
|
+
--el-text-faint: var(--color-stone); /* faint labels */
|
|
2233
|
+
--el-text-inverted: var(--color-background); /* text on an ink / accent fill */
|
|
2234
|
+
|
|
2235
|
+
/* ── Accent (active CTA identity) + brand highlight ─────────────── */
|
|
2236
|
+
/* --el-accent is the FILL behind --el-accent-text (white): filled buttons,
|
|
2237
|
+
badges, toggles. --el-accent-on-surface is the accent used AS text/icon on
|
|
2238
|
+
a page surface. They diverge in dark theme (fill darkens for white-text AA;
|
|
2239
|
+
text stays brand-bright for AA on the near-black canvas) — MOTIR-757. */
|
|
2240
|
+
--el-accent: var(--color-primary-fill);
|
|
2241
|
+
--el-accent-text: var(--color-primary-foreground);
|
|
2242
|
+
--el-accent-on-surface: var(--color-primary);
|
|
2243
|
+
--el-accent-pressed: var(--color-primary-pressed);
|
|
2244
|
+
--el-highlight: var(--color-accent); /* brand pink — decorative accents */
|
|
2245
|
+
|
|
2246
|
+
/* ── Surfaces ──────────────────────────────────────────────────── */
|
|
2247
|
+
--el-surface: var(--color-surface);
|
|
2248
|
+
--el-surface-soft: var(--color-surface-soft);
|
|
2249
|
+
--el-canvas: var(--color-canvas); /* recessed board behind raised cards (planning canvas) */
|
|
2250
|
+
--el-surface-text: var(--color-foreground);
|
|
2251
|
+
--el-muted: var(--color-muted); /* faint fill — chips, code, inverted text */
|
|
2252
|
+
|
|
2253
|
+
/* ── Borders ───────────────────────────────────────────────────── */
|
|
2254
|
+
--el-border: var(--color-border); /* default hairline */
|
|
2255
|
+
--el-border-soft: var(--color-hairline-soft);
|
|
2256
|
+
--el-border-strong: var(--color-hairline-strong);
|
|
2257
|
+
|
|
2258
|
+
/* ── Links ─────────────────────────────────────────────────────── */
|
|
2259
|
+
--el-link: var(--color-link);
|
|
2260
|
+
--el-link-pressed: var(--color-link-pressed);
|
|
2261
|
+
|
|
2262
|
+
/* ── Semantic status ───────────────────────────────────────────── */
|
|
2263
|
+
--el-danger: var(--color-destructive);
|
|
2264
|
+
--el-danger-text: var(--color-destructive-foreground);
|
|
2265
|
+
--el-success: var(--color-success);
|
|
2266
|
+
--el-warning: var(--color-warning);
|
|
2267
|
+
--el-info: var(--color-info);
|
|
2268
|
+
|
|
2269
|
+
/* ── Pastel tints (Pill tones, feature surfaces) ───────────────── */
|
|
2270
|
+
--el-tint-peach: var(--color-tint-peach);
|
|
2271
|
+
--el-tint-rose: var(--color-tint-rose);
|
|
2272
|
+
--el-tint-mint: var(--color-tint-mint);
|
|
2273
|
+
--el-tint-lavender: var(--color-tint-lavender);
|
|
2274
|
+
--el-tint-sky: var(--color-tint-sky);
|
|
2275
|
+
--el-tint-yellow: var(--color-tint-yellow);
|
|
2276
|
+
|
|
2277
|
+
/* ── Status hues (MOTIR-1273 · 1266.2 — un-collapse the workflow statuses) ──
|
|
2278
|
+
Each workflow status renders its dot/icon in its OWN hue, so in_review /
|
|
2279
|
+
blocked / cancelled differentiate from the three lifecycle CATEGORIES the
|
|
2280
|
+
filter dots used to collapse them onto. Maps to a Tier-0 --color-* (the
|
|
2281
|
+
gold-standard --el-chart-* / --el-type-* pattern), so every palette re-skins
|
|
2282
|
+
them automatically through its --color-* overrides — no per-palette block
|
|
2283
|
+
needed (the card's "+ 10 palette blocks" is satisfied by that indirection;
|
|
2284
|
+
verified slate ≠ stone and info/warning/destructive/success/primary are all
|
|
2285
|
+
palette-defined across all 10). The status DOT uses the hue at full strength;
|
|
2286
|
+
a status CHIP bg = color-mix(var(--el-status-X) 14%, var(--el-surface)) with
|
|
2287
|
+
--el-text-strong (design/design-system/design-notes.md §A). Keys grounded in
|
|
2288
|
+
lib/workflows/defaultWorkflow.ts. */
|
|
2289
|
+
--el-status-todo: var(--color-stone); /* neutral grey — not started */
|
|
2290
|
+
--el-status-in-progress: var(--color-info); /* blue — active */
|
|
2291
|
+
--el-status-in-review: var(--color-primary); /* brand — differentiate from in-progress */
|
|
2292
|
+
--el-status-done: var(--color-success); /* green — resolved */
|
|
2293
|
+
--el-status-blocked: var(--color-warning); /* amber — can't proceed (gap fixed) */
|
|
2294
|
+
--el-status-cancelled: var(--color-steel); /* terminal grey — not red (cancel ≠ error) */
|
|
2295
|
+
|
|
2296
|
+
/* ── Priority hues (MOTIR-1273 · 1266.2 — a 5-step diverging ramp) ──
|
|
2297
|
+
Un-collapses lib/issues/priorityMeta.ts, where `medium` AND `lowest` were
|
|
2298
|
+
both neutral grey (the exact "can't differentiate" complaint). red → orange
|
|
2299
|
+
→ mid-slate → blue → faint-stone: two DISTINCT greys for medium / lowest,
|
|
2300
|
+
kept legible by the redundant ArrowUp / Minus / ArrowDown icon (AA, finding
|
|
2301
|
+
#35). The Pill `priority` tone tints these over --el-surface
|
|
2302
|
+
(design/design-system/design-notes.md §B). */
|
|
2303
|
+
--el-priority-highest: var(--color-destructive); /* red */
|
|
2304
|
+
--el-priority-high: var(--color-warning); /* orange */
|
|
2305
|
+
--el-priority-medium: var(--color-slate); /* mid slate (un-collapsed) */
|
|
2306
|
+
--el-priority-low: var(--color-info); /* blue */
|
|
2307
|
+
--el-priority-lowest: var(--color-stone); /* faint stone (un-collapsed) */
|
|
2308
|
+
|
|
2309
|
+
/* ── Semantic SURFACES (MOTIR-1273 · 1266.2 — banner / callout fills) ──
|
|
2310
|
+
Names the banner/callout fills components reached for as raw --el-tint-*
|
|
2311
|
+
(the FormField error box, the public danger / warning banners). Each = a
|
|
2312
|
+
tint base + a strong-ink text token, AA-safe (charcoal-on-tint clears
|
|
2313
|
+
~10:1 both themes, finding #35). notice-info / callout / success are
|
|
2314
|
+
defined AHEAD of their dedicated surfaces (the spec's "NEW (no impl)"
|
|
2315
|
+
pattern; design/design-system/design-notes.md §C). */
|
|
2316
|
+
--el-danger-surface: var(--color-tint-rose);
|
|
2317
|
+
--el-danger-surface-text: var(--color-charcoal);
|
|
2318
|
+
--el-warning-surface: var(--color-tint-peach);
|
|
2319
|
+
--el-warning-text: var(--color-charcoal); /* ink on the warning surface */
|
|
2320
|
+
--el-success-surface: var(--color-tint-mint);
|
|
2321
|
+
--el-notice-info-bg: var(--color-tint-sky);
|
|
2322
|
+
--el-notice-info-border: var(--color-info);
|
|
2323
|
+
--el-callout-bg: var(--color-tint-lavender);
|
|
2324
|
+
--el-callout-text: var(--color-charcoal);
|
|
2325
|
+
|
|
2326
|
+
/* ── Identity hues (MOTIR-1274 · 1266.3 — un-collapse the shared --el-tint-* pool) ──
|
|
2327
|
+
Roles / org-roles / privacy / labels / avatars each got a DEDICATED token so a
|
|
2328
|
+
palette can tune them apart (today they all pulled from the same six --el-tint-*,
|
|
2329
|
+
so a label, a role and an avatar could not diverge). Every token DEFAULTS to
|
|
2330
|
+
today's exact tint → zero visual change in the motir base; each maps to a Tier-0
|
|
2331
|
+
--color-* so palettes + the dark block re-skin them for free (no per-palette
|
|
2332
|
+
block — the gold-standard --el-chart-* / --el-type-* indirection). This block
|
|
2333
|
+
also DECOUPLES the --el-type-* MISUSE: NotificationRow event badges, the
|
|
2334
|
+
ProjectAvatar mono tile and the OrgUsageClient model dots were borrowing
|
|
2335
|
+
work-item-KIND hues for non-type meaning; they get their own --el-notif-* /
|
|
2336
|
+
--el-avatar-fallback / --el-model-* tokens at the SAME hues (still zero change).
|
|
2337
|
+
design/design-system/design-notes.md §D. */
|
|
2338
|
+
/* Roles / org-roles / privacy — Pill memberRole/orgRole/tone (today hardcoded in
|
|
2339
|
+
Pill.tsx CVA). admin→lavender, member→sky, viewer→mint; owner→lavender; private→
|
|
2340
|
+
lavender, public→sky (public defined AHEAD — no rendered "public" badge yet). */
|
|
2341
|
+
--el-role-admin: var(--color-tint-lavender);
|
|
2342
|
+
--el-role-member: var(--color-tint-sky);
|
|
2343
|
+
--el-role-viewer: var(--color-tint-mint);
|
|
2344
|
+
--el-org-role-owner: var(--color-tint-lavender);
|
|
2345
|
+
--el-org-role-admin: var(--color-tint-sky);
|
|
2346
|
+
--el-org-role-member: var(--color-tint-mint);
|
|
2347
|
+
--el-privacy-private: var(--color-tint-lavender);
|
|
2348
|
+
--el-privacy-public: var(--color-tint-sky);
|
|
2349
|
+
/* Label ramp — lib/labels/labelTint.ts hashes the name → 1..6 in the fixed order
|
|
2350
|
+
peach,rose,mint,lavender,sky,yellow (the seed-loader family; matches shipped). */
|
|
2351
|
+
--el-label-1: var(--color-tint-peach);
|
|
2352
|
+
--el-label-2: var(--color-tint-rose);
|
|
2353
|
+
--el-label-3: var(--color-tint-mint);
|
|
2354
|
+
--el-label-4: var(--color-tint-lavender);
|
|
2355
|
+
--el-label-5: var(--color-tint-sky);
|
|
2356
|
+
--el-label-6: var(--color-tint-yellow);
|
|
2357
|
+
/* Avatar ramp — KEEP the named keys (peach…yellow): lib/projects/avatar.ts persists
|
|
2358
|
+
project.avatarColor ∈ these strings, so numbering them would break stored rows
|
|
2359
|
+
(spec §7.1, rung-2 migration safety). The fallback fixes --el-type-* misuse #2 —
|
|
2360
|
+
the mono initials tile keeps its blue but stops borrowing --el-type-task. */
|
|
2361
|
+
--el-avatar-peach: var(--color-tint-peach);
|
|
2362
|
+
--el-avatar-rose: var(--color-tint-rose);
|
|
2363
|
+
--el-avatar-mint: var(--color-tint-mint);
|
|
2364
|
+
--el-avatar-lavender: var(--color-tint-lavender);
|
|
2365
|
+
--el-avatar-sky: var(--color-tint-sky);
|
|
2366
|
+
--el-avatar-yellow: var(--color-tint-yellow);
|
|
2367
|
+
--el-avatar-fallback: var(--color-info); /* mono tile — was --el-type-task (= --color-info) */
|
|
2368
|
+
/* Notification event badges (decouple --el-type-* misuse #2; same hues → zero change).
|
|
2369
|
+
mentioned keeps the accent fill it already used — --el-accent resolves to
|
|
2370
|
+
--color-primary-fill (purple), NOT --color-accent (pink); the spec's base column
|
|
2371
|
+
read --color-accent but its note said "alias for consistency", so per §2
|
|
2372
|
+
zero-visual-change this maps to the SHIPPED value. */
|
|
2373
|
+
--el-notif-mentioned: var(--color-primary-fill); /* the accent fill (matches prior --el-accent) */
|
|
2374
|
+
--el-notif-commented: var(--color-info); /* was --el-type-task */
|
|
2375
|
+
--el-notif-assigned: var(--color-accent-green); /* was --el-type-story */
|
|
2376
|
+
--el-notif-transitioned: var(--color-accent-teal); /* was --el-type-subtask */
|
|
2377
|
+
/* AI model dots (promote to a named family; deepseek was borrowing --el-type-subtask).
|
|
2378
|
+
opus keeps the accent fill, same --color-primary-fill reasoning as mentioned. */
|
|
2379
|
+
--el-model-opus: var(--color-primary-fill); /* the accent fill (matches prior --el-accent) */
|
|
2380
|
+
--el-model-sonnet: var(--color-info);
|
|
2381
|
+
--el-model-haiku: var(--color-success);
|
|
2382
|
+
--el-model-deepseek: var(--color-accent-teal); /* was --el-type-subtask */
|
|
2383
|
+
|
|
2384
|
+
/* ── Icon + text roles (MOTIR-1275 · 1266.4 — split the --el-text-muted /
|
|
2385
|
+
--el-text triple-duty) ──
|
|
2386
|
+
Icons and overlines/subtitles were borrowing the body-copy text tokens
|
|
2387
|
+
(--el-text-muted for every icon + caption, --el-text for the tooltip fill),
|
|
2388
|
+
so a palette could not tune an icon apart from prose. These give each role
|
|
2389
|
+
its own token mapped to the SAME Tier-0 neutral it resolves to today, so
|
|
2390
|
+
every palette + the dark block re-skin them for free (the gold-standard
|
|
2391
|
+
--el-chart-* / --el-type-* indirection — no per-palette block). icon-btn /
|
|
2392
|
+
icon-heading are defined AHEAD of an explicit consumer (the spec's
|
|
2393
|
+
"NEW (no impl)" pattern; buttons inherit currentColor today).
|
|
2394
|
+
design/design-system/design-notes.md §E. */
|
|
2395
|
+
--el-icon-muted: var(--color-muted-foreground); /* inactive nav / chevron / close */
|
|
2396
|
+
--el-icon-active: var(--color-primary); /* active nav icon (was --el-accent-on-surface) */
|
|
2397
|
+
--el-icon-btn: var(--color-foreground); /* explicit button-icon ink (usually currentColor) */
|
|
2398
|
+
--el-icon-heading: var(--color-charcoal); /* an icon beside a heading */
|
|
2399
|
+
--el-icon-field: var(--color-muted-foreground); /* search / chevron / calendar in inputs */
|
|
2400
|
+
--el-text-eyebrow: var(--color-muted-foreground); /* uppercase-mono overline */
|
|
2401
|
+
--el-text-subtitle: var(--color-slate); /* lead paragraph (Modal / EmptyState desc) */
|
|
2402
|
+
--el-text-helper: var(--color-muted-foreground); /* form hint (was --el-text-muted) */
|
|
2403
|
+
--el-text-identifier: var(--color-slate); /* monospace MOTIR-123 keys (was --el-text-secondary) */
|
|
2404
|
+
|
|
2405
|
+
/* ── Component-surface primitives (MOTIR-1275 · 1266.4) ──
|
|
2406
|
+
Names the control surfaces that were overloading --el-surface (option
|
|
2407
|
+
highlight / chip fill / switch knob), --el-border(-strong) (control vs
|
|
2408
|
+
panel edge) and --el-text (tooltip fill). Each defaults to the exact
|
|
2409
|
+
Tier-0 --color-* the component renders today → zero visual change in the
|
|
2410
|
+
motir base; a palette can now tune each apart. Two deliberate
|
|
2411
|
+
spec-vs-shipped reconciliations (rung-2), documented in §F of the notes:
|
|
2412
|
+
(1) --el-count-bg maps to --color-muted, NOT the spec's tentative
|
|
2413
|
+
--color-surface — board count badges render ON an --el-surface column, so
|
|
2414
|
+
surface would erase them; muted is the shipped fill (and identical to
|
|
2415
|
+
surface in dark). (2) --el-overlay-scrim is the lone concrete-value token
|
|
2416
|
+
(no --color-* base), so it carries an explicit dark companion below; light
|
|
2417
|
+
== today's bg-black/40, dark DEEPENS to ~65% for a legible scrim.
|
|
2418
|
+
design/design-system/design-notes.md §F. */
|
|
2419
|
+
--el-tooltip-bg: var(--color-foreground); /* inverted tooltip fill (was --el-text) */
|
|
2420
|
+
--el-tooltip-text: var(--color-background); /* text on the tooltip (was --el-text-inverted) */
|
|
2421
|
+
--el-switch-on: var(--color-primary-fill); /* checked track (was --el-accent) */
|
|
2422
|
+
--el-switch-knob: var(--color-surface); /* the thumb (was --el-surface) */
|
|
2423
|
+
--el-option-active-bg: var(--color-muted); /* highlighted listbox option (was --el-surface) */
|
|
2424
|
+
--el-overlay-scrim: #00000066; /* modal/dialog scrim — concrete (light); dark companion below */
|
|
2425
|
+
--el-chip-bg: var(--color-surface); /* neutral chip fill (tinted chips keep their tint) */
|
|
2426
|
+
--el-chip-border: var(--color-border); /* neutral chip edge */
|
|
2427
|
+
--el-card: var(--color-background); /* untinted card surface — fixes the bg-card orphan */
|
|
2428
|
+
--el-input-border: var(--color-hairline-strong); /* input outline (was --el-border-strong) */
|
|
2429
|
+
--el-button-border: var(--color-hairline-strong); /* secondary-button outline */
|
|
2430
|
+
--el-count-bg: var(--color-muted); /* count-badge fill (shipped value; see note above) */
|
|
2431
|
+
--el-count-text: var(--color-slate); /* count-badge ink (was --el-text-secondary) */
|
|
2432
|
+
|
|
2433
|
+
/* ── Issue-type hues (work-item kind identity — finding #54) ────── */
|
|
2434
|
+
/* The colour the UI was missing: each issue type renders its icon in its
|
|
2435
|
+
own hue, matching ISSUE_TYPE_META.colorToken. */
|
|
2436
|
+
--el-type-epic: var(--color-accent); /* pink */
|
|
2437
|
+
--el-type-story: var(--color-accent-green);
|
|
2438
|
+
--el-type-task: var(--color-info);
|
|
2439
|
+
--el-type-bug: var(--color-destructive);
|
|
2440
|
+
--el-type-subtask: var(--color-accent-teal);
|
|
2441
|
+
|
|
2442
|
+
/* ── Work-item TYPE hues (the work-NATURE palette, Story 2.7 · 2.7.4) ────
|
|
2443
|
+
Parallel to the per-KIND hues above: the ten fixed `WorkItemType` members
|
|
2444
|
+
each render their icon/chip in their own hue (matches
|
|
2445
|
+
lib/issues/workItemTypeMeta.ts). NO new Tier-0 colour needed — the seven
|
|
2446
|
+
"produces an artifact" types take the seven saturated hues; the three
|
|
2447
|
+
"meta / admin" types (decision, manual, chore) take the three greys
|
|
2448
|
+
(desaturation is semantically apt). Each maps to a Tier-0 var, so the
|
|
2449
|
+
[data-theme="dark"] block re-skins them automatically. The chip puts the
|
|
2450
|
+
hue in a color-mix tint BACKGROUND with --el-text-strong text (AA,
|
|
2451
|
+
finding #35); never a page-level tint. */
|
|
2452
|
+
--el-type-code: var(--color-info); /* blue — implementation */
|
|
2453
|
+
--el-type-design: var(--color-accent); /* pink — UI/UX design */
|
|
2454
|
+
--el-type-test: var(--color-success); /* green — automated tests */
|
|
2455
|
+
--el-type-content: var(--color-accent-teal); /* teal — copy / docs / translate */
|
|
2456
|
+
--el-type-research: var(--color-primary); /* purple — spike / investigation */
|
|
2457
|
+
--el-type-review: var(--color-accent-orange); /* orange — QA / review */
|
|
2458
|
+
--el-type-deploy: var(--color-destructive); /* red — infra / ops / release */
|
|
2459
|
+
--el-type-decision: var(--color-charcoal); /* ink — decision record */
|
|
2460
|
+
--el-type-manual: var(--color-stone); /* stone — human provisioning */
|
|
2461
|
+
--el-type-chore: var(--color-slate); /* slate — housekeeping */
|
|
2462
|
+
|
|
2463
|
+
/* ── Chart series (Story 4.6 · the reusable viz Story 6.3 reuses) ──
|
|
2464
|
+
Every chart colour routes through one of these, NEVER a raw --color-*
|
|
2465
|
+
in chart code (the per-component growth pattern). They map to Tier-0
|
|
2466
|
+
vars, so the Tier-1 dark block re-skins them automatically. The pair
|
|
2467
|
+
committed/completed is distinguished by TEXT label too, never colour
|
|
2468
|
+
alone (finding #35). */
|
|
2469
|
+
--el-chart-guideline: var(--color-stone); /* burndown ideal line (dashed) */
|
|
2470
|
+
--el-chart-actual: var(--color-primary); /* burndown actual remaining */
|
|
2471
|
+
--el-chart-scope: var(--color-warning); /* scope-change marker */
|
|
2472
|
+
--el-chart-committed: var(--color-info); /* velocity committed bar */
|
|
2473
|
+
--el-chart-completed: var(--color-success); /* velocity completed bar */
|
|
2474
|
+
--el-chart-average: var(--color-charcoal); /* velocity average (dashed ref) */
|
|
2475
|
+
--el-chart-plot: var(--color-surface-soft); /* plot background fill */
|
|
2476
|
+
--el-chart-grid: var(--color-border); /* gridlines */
|
|
2477
|
+
--el-chart-axis: var(--color-steel); /* axis line + tick labels */
|
|
2478
|
+
/* Story 6.3 dashboards (6.3.4) — the categorical donut ramp + the
|
|
2479
|
+
created/resolved series + the deficit/surplus difference fills. Same
|
|
2480
|
+
growth pattern: map to existing Tier-0 vars, so the dark block re-skins
|
|
2481
|
+
them automatically. The donut cycles cat-1..7; "None"/unset is always
|
|
2482
|
+
cat-none (neutral grey); created/resolved read by line + legend label,
|
|
2483
|
+
never colour alone (finding #35). */
|
|
2484
|
+
--el-chart-cat-1: var(--color-primary); /* distribution donut segment 1 */
|
|
2485
|
+
--el-chart-cat-2: var(--color-info); /* segment 2 */
|
|
2486
|
+
--el-chart-cat-3: var(--color-success); /* segment 3 */
|
|
2487
|
+
--el-chart-cat-4: var(--color-warning); /* segment 4 */
|
|
2488
|
+
--el-chart-cat-5: var(--color-accent-teal); /* segment 5 */
|
|
2489
|
+
--el-chart-cat-6: var(--color-accent); /* segment 6 */
|
|
2490
|
+
--el-chart-cat-7: var(--color-charcoal); /* segment 7 (ramp length) */
|
|
2491
|
+
--el-chart-cat-none: var(--color-stone); /* the "None" / unset group */
|
|
2492
|
+
--el-chart-created: var(--color-info); /* created-vs-resolved: created line */
|
|
2493
|
+
--el-chart-resolved: var(--color-success); /* created-vs-resolved: resolved line */
|
|
2494
|
+
--el-chart-deficit: var(--color-destructive); /* fill where created > resolved */
|
|
2495
|
+
--el-chart-surplus: var(--color-success); /* fill where resolved > created */
|
|
2496
|
+
--el-chart-age: var(--color-warning); /* average-age bar (8.8.13 — aging backlog) */
|
|
2497
|
+
--el-chart-resolution: var(--color-info); /* resolution-time bar (8.8.13 — throughput) */
|
|
2498
|
+
/* Sprint CYCLE GRAPH series (Story 8.14 — Linear scope-vs-completed). A
|
|
2499
|
+
collision-free --el-chart-cycle-* family: the bare --el-chart-completed /
|
|
2500
|
+
-started / -target names are taken (velocity / old burndown), so reusing
|
|
2501
|
+
them would silently re-skin those charts. The --el-chart-plot/grid/axis
|
|
2502
|
+
scaffold is reused unchanged. */
|
|
2503
|
+
--el-chart-cycle-scope: var(--color-stone); /* live total scope (gray) */
|
|
2504
|
+
--el-chart-cycle-started: var(--color-warning); /* started / in progress (amber) */
|
|
2505
|
+
--el-chart-cycle-completed: var(--color-info); /* completed (solid blue) */
|
|
2506
|
+
--el-chart-cycle-target: var(--color-info); /* ideal remaining (blue, dotted) */
|
|
2507
|
+
|
|
2508
|
+
/* ── Public projects (Story 6.12 · public read-only view) ──────────
|
|
2509
|
+
New --el-* roles 6.12.4/6.12.6/6.12.7 add here, each mapped to an
|
|
2510
|
+
existing Tier-0 value (the per-component token-growth pattern). The
|
|
2511
|
+
[data-theme="dark"] block re-skins them automatically because every
|
|
2512
|
+
mapping is a --color-* var the Tier-1 block already flips (no concrete
|
|
2513
|
+
hex). Per design/public-projects/design-notes.md "Colour roles". */
|
|
2514
|
+
--el-public-banner-bg: var(--color-tint-sky); /* "you're viewing a public project" framing */
|
|
2515
|
+
--el-public-banner-text: var(--color-charcoal); /* AA ~10:1 on the sky tint */
|
|
2516
|
+
--el-vote-bg: var(--color-tint-lavender); /* upvote control resting fill (6.12.7) */
|
|
2517
|
+
--el-vote-active-bg: var(--color-primary); /* voted: accent fill */
|
|
2518
|
+
--el-vote-active-text: var(--color-primary-foreground);
|
|
2519
|
+
--el-roadmap-submitted: var(--color-tint-peach);
|
|
2520
|
+
--el-roadmap-planned: var(--color-tint-lavender);
|
|
2521
|
+
--el-roadmap-progress: var(--color-tint-sky);
|
|
2522
|
+
--el-roadmap-done: var(--color-tint-mint);
|
|
2523
|
+
/* ── Onboarding / canvas (Story 1266.6 · MOTIR-1277) ───────────────
|
|
2524
|
+
Per-element hues for the onboarding canvas: revision-diff kind chips
|
|
2525
|
+
(RevisionDiff), the discovery chat bubbles (DiscoveryChatRail), the
|
|
2526
|
+
planning-canvas edges (PlanningCanvas), and the station tier tile
|
|
2527
|
+
(StationNode). Each maps to the exact Tier-0 --color-* it renders
|
|
2528
|
+
today (zero visual change vs the borrowed --el-tint-* / --el-accent /
|
|
2529
|
+
--el-border), so a palette re-skins them via its --color-* override
|
|
2530
|
+
and [data-theme="dark"] flips them — no per-palette / dark entry.
|
|
2531
|
+
Per design/design-system/design-notes.md §H. NB: the card's "wire
|
|
2532
|
+
--el-roadmap-* into StationNode" is corrected there — roadmap stays
|
|
2533
|
+
scoped to the PUBLIC roadmap; the canvas gets its own
|
|
2534
|
+
--el-station-tier-* family (a different semantic concept). */
|
|
2535
|
+
--el-diff-added: var(--color-tint-mint);
|
|
2536
|
+
--el-diff-removed: var(--color-tint-rose);
|
|
2537
|
+
--el-diff-moved: var(--color-tint-sky);
|
|
2538
|
+
--el-chat-bubble-user: var(--color-primary-fill); /* text stays --el-accent-text */
|
|
2539
|
+
--el-chat-bubble-ai: var(--color-surface-soft); /* text stays --el-text */
|
|
2540
|
+
--el-canvas-edge-pending: var(--color-border); /* dashed, not-yet-committed edge */
|
|
2541
|
+
--el-canvas-edge-committed: var(--color-hairline-strong); /* solid, settled edge */
|
|
2542
|
+
--el-station-tier-discovery: var(--color-tint-sky);
|
|
2543
|
+
--el-station-tier-vision: var(--color-tint-lavender);
|
|
2544
|
+
--el-station-tier-feasibility: var(--color-tint-mint);
|
|
2545
|
+
--el-station-tier-validation: var(--color-tint-peach);
|
|
2546
|
+
/* the Overview hero's soft corner washes (decorative tints only; all text
|
|
2547
|
+
sits on --el-page-bg, never on the wash, so AA holds — finding #35) */
|
|
2548
|
+
--el-hero-wash-a: var(--color-tint-lavender);
|
|
2549
|
+
--el-hero-wash-b: var(--color-tint-sky);
|
|
2550
|
+
/* "Building in public" (Story 6.17.2/6.17.4) — the reframed `public` access
|
|
2551
|
+
option glyph + the "Live"/status badge. Tier-0 flips (--color-tint-lavender
|
|
2552
|
+
and --color-charcoal both re-skin under [data-theme="dark"]) re-skin these
|
|
2553
|
+
automatically, so they live in Tier 3 once with no dark-block entry. */
|
|
2554
|
+
--el-build-bg: var(--color-tint-lavender); /* badge / reframed-option tint */
|
|
2555
|
+
--el-build-text: var(--color-charcoal); /* AA ~10:1 on the lavender tint, both themes */
|
|
2556
|
+
--el-build-glyph: var(--color-primary); /* the megaphone accent glyph */
|
|
2557
|
+
|
|
2558
|
+
/* ── Interaction / agile surfaces (MOTIR-1276 · 1266.5) ──────────────
|
|
2559
|
+
Un-collapses the dnd / selection / overdue / accent meanings that all
|
|
2560
|
+
rode --el-tint-lavender / --el-accent / --el-text, so a palette can tune a
|
|
2561
|
+
drop target apart from a selected row apart from an overdue date. Each
|
|
2562
|
+
maps to a --color-* → zero base-palette change AND every palette + dark
|
|
2563
|
+
re-skins for free (§1; the chart/type ramp pattern). The previously
|
|
2564
|
+
undifferentiated meanings (selection vs drop; overdue vs a future date)
|
|
2565
|
+
now read distinctly; the rest default to today's exact value.
|
|
2566
|
+
design/design-system/design-notes.md §G. */
|
|
2567
|
+
--el-selection-bg: var(--color-tint-sky); /* selected row/card — sky, NOT the drop lavender */
|
|
2568
|
+
--el-droptarget-bg: var(--color-tint-lavender); /* dnd drop-zone fill (shipped) */
|
|
2569
|
+
--el-board-column-accent: var(--color-primary); /* drop ring / column border */
|
|
2570
|
+
--el-overdue: var(--color-destructive); /* past-due date */
|
|
2571
|
+
--el-due-soon: var(--color-warning); /* due today / within 3 days */
|
|
2572
|
+
--el-sprint-accent: var(--color-tint-lavender); /* sprint-header emphasis (shipped) */
|
|
2573
|
+
--el-epic-accent: var(--color-accent); /* pink epic identity (aliases --el-type-epic) */
|
|
2574
|
+
--el-archived-pill-bg: var(--color-muted); /* inactive "Archived" badge fill */
|
|
2575
|
+
--el-archived-pill-text: var(--color-slate); /* AA-safe ink on the muted fill */
|
|
2576
|
+
--el-auth-wash: var(--color-tint-sky); /* sign-in background wash (text on the card) */
|
|
2577
|
+
--el-tabnav-track: var(--color-surface); /* tab-bar track (Segmented / PublicTabNav) */
|
|
2578
|
+
--el-tabnav-active: var(--color-primary); /* active-tab glyph (== --el-accent-on-surface) */
|
|
2579
|
+
--el-card-icon-bg: var(--color-muted); /* hub/settings card icon tile */
|
|
2580
|
+
--el-card-icon-fg: var(--color-primary); /* the icon inside that tile */
|
|
2581
|
+
|
|
2582
|
+
/* ── Code/mono (used by /tokens reference route) ───────────────── */
|
|
2583
|
+
--el-code-bg: var(--color-muted);
|
|
2584
|
+
--el-code-text: var(--color-charcoal);
|
|
2585
|
+
|
|
2586
|
+
/* ── App shell — sidebar (Subtask 1.5.2). The shell mockup paints the
|
|
2587
|
+
sidebar rail on $--surface and the content area on $--background; the
|
|
2588
|
+
active nav row inverts that (a $--background row against the $--surface
|
|
2589
|
+
rail) so the current page reads as inset into the page canvas. ──── */
|
|
2590
|
+
--el-sidebar-bg: var(--color-surface); /* the rail — surface, distinct from the page canvas */
|
|
2591
|
+
--el-sidebar-border: var(--color-hairline); /* the 1px seam between rail and content */
|
|
2592
|
+
--el-sidebar-item-bg-hover: #eeede9; /* subtle hover lift — one shade darker than surface (light) */
|
|
2593
|
+
--el-sidebar-item-bg-active: var(
|
|
2594
|
+
--color-background
|
|
2595
|
+
); /* active row = canvas inset into the rail */
|
|
2596
|
+
}
|
|
2597
|
+
|
|
2598
|
+
/* Dark-mode lift for the sidebar hover token. The other three --el-sidebar-*
|
|
2599
|
+
tokens resolve through --color-* vars the Tier-1 dark block already flips;
|
|
2600
|
+
only the hover shade is a concrete hex, so it carries its own dark value —
|
|
2601
|
+
one shade *lighter* than the dark surface, mirroring the light recipe's
|
|
2602
|
+
one-shade-darker. */
|
|
2603
|
+
[data-theme='dark'] {
|
|
2604
|
+
--el-sidebar-item-bg-hover: #222222;
|
|
2605
|
+
/* The modal scrim deepens in dark (MOTIR-1275 · 1266.4): on a near-black
|
|
2606
|
+
canvas a 40% scrim barely separates the dialog, so dark uses ~65% — the
|
|
2607
|
+
other concrete-hex --el-* alongside the sidebar hover. Light value (40%,
|
|
2608
|
+
== the prior bg-black/40) lives in the :root block above. */
|
|
2609
|
+
--el-overlay-scrim: #000000a6;
|
|
2610
|
+
}
|
|
2611
|
+
|
|
2612
|
+
/* ═══════════════════════════════════════════════════════════════════════
|
|
2613
|
+
AXIS 1 (COLOUR) — data-palette overrides (Subtask 7.3.48)
|
|
2614
|
+
Registry: lib/theme/palettes.ts. A named palette re-skins the app by
|
|
2615
|
+
OVERRIDING the Tier-3 --el-* layer above — and ONLY colour (--el-* /
|
|
2616
|
+
--color-*), NEVER a shape/feel token (radius / spacing / shadow / sizing /
|
|
2617
|
+
motion / type). That is the data-style axis's job; keeping the two disjoint
|
|
2618
|
+
is what makes "style × palette" a product of two independent choices.
|
|
2619
|
+
|
|
2620
|
+
v1 ships exactly one palette — `motir` (Yue, 2026-06-17: "use Motir's colour
|
|
2621
|
+
palette for the v1"). It IS the Tier-3 base, so — exactly like the
|
|
2622
|
+
`warm-editorial` base style — it needs NO override block: setting
|
|
2623
|
+
data-palette="motir" simply leaves the base --el-* tokens in force. Each
|
|
2624
|
+
later "Palette: …" subtask ADDS its `[data-palette='<id>']` block here,
|
|
2625
|
+
overriding --el-* (light + dark via [data-palette='<id>'][data-theme='dark']
|
|
2626
|
+
where the palette diverges from the base flip), and authors its
|
|
2627
|
+
docs/palettes/<id>.md — mirroring how the per-style subtasks add a
|
|
2628
|
+
[data-style] block on the other axis.
|
|
2629
|
+
═══════════════════════════════════════════════════════════════════════ */
|
|
2630
|
+
|
|
2631
|
+
/* `motir` needs no block — it is the Tier-3 base above. Each later palette
|
|
2632
|
+
ADDS its [data-palette='<id>'] override here (light + dark companion). */
|
|
2633
|
+
|
|
2634
|
+
/* ── Palette: Cobalt (data-palette="cobalt") — Subtask 7.3.49 ───────────────
|
|
2635
|
+
A cool, calm, trustworthy INSTITUTIONAL-BLUE re-skin: slate-cool surfaces, a
|
|
2636
|
+
confident cobalt primary/accent, a cool cyan highlight, cooled pastel tints.
|
|
2637
|
+
Mood anchored in getdesign.md's Coinbase (clean institutional blue) / IBM
|
|
2638
|
+
(structured blue) analysis; the actual light/dark ramps + UI-state steps are
|
|
2639
|
+
built from Radix Colors (Blue / Indigo / Slate / Red / Jade / Amber) — the
|
|
2640
|
+
accessibility-first 12-step scales designed for UI states.
|
|
2641
|
+
|
|
2642
|
+
This block re-skins by overriding the Tier-0 --color-* SOURCE the Tier-3
|
|
2643
|
+
--el-* layer references (exactly how the [data-theme='dark'] block works), so
|
|
2644
|
+
every --el-* token — surfaces, ink, accent, links, semantic, tints, and
|
|
2645
|
+
work-item type hues — re-skins coherently with no per-token churn. It sets
|
|
2646
|
+
ONLY colour tokens (--color-* / --el-*), NEVER a shape/feel token (radius /
|
|
2647
|
+
spacing / shadow / sizing / motion / type) — that is the independent
|
|
2648
|
+
data-style axis (paletteRegistry.test.ts enforces the disjointness).
|
|
2649
|
+
|
|
2650
|
+
Accessibility: every text-on-surface, white-on-fill, link, and chip-tint
|
|
2651
|
+
pairing clears WCAG AA (≥4.5; ≥3.0 for icon/UI hues) in BOTH light and dark —
|
|
2652
|
+
verified numerically + by rendered specimen, never eyeballed. See
|
|
2653
|
+
docs/palettes/cobalt.md for the colour-role reference. */
|
|
2654
|
+
[data-palette='cobalt'] {
|
|
2655
|
+
/* brand & primary — confident cobalt (Radix Indigo family) */
|
|
2656
|
+
--color-primary: #3a57cf; /* cobalt AS text/icon on a surface (AA on white/surface) */
|
|
2657
|
+
--color-primary-foreground: #ffffff;
|
|
2658
|
+
--color-primary-pressed: #2b44ad;
|
|
2659
|
+
--color-primary-fill: #3650c2; /* fill behind white labels (white-on AA) */
|
|
2660
|
+
|
|
2661
|
+
/* surfaces — white canvas over cool slate sections */
|
|
2662
|
+
--color-background: #ffffff;
|
|
2663
|
+
--color-foreground: #0f1b2e; /* cool navy ink */
|
|
2664
|
+
--color-surface: #eef2f8; /* cool slate section background */
|
|
2665
|
+
--color-surface-soft: #f7f9fc; /* quieter cool division */
|
|
2666
|
+
|
|
2667
|
+
/* text scale — cool slate hierarchy */
|
|
2668
|
+
--color-ink: #0f1b2e;
|
|
2669
|
+
--color-charcoal: #1e2c40; /* body emphasis / AA-safe text on tints */
|
|
2670
|
+
--color-slate: #46556b; /* secondary text */
|
|
2671
|
+
--color-steel: #586a82; /* tertiary / footer */
|
|
2672
|
+
--color-stone: #8493a6; /* faint labels */
|
|
2673
|
+
--color-muted: #eef2f8;
|
|
2674
|
+
--color-muted-foreground: #5b6a7e; /* captions — AA on white + surfaces */
|
|
2675
|
+
|
|
2676
|
+
/* borders — cool slate hairlines */
|
|
2677
|
+
--color-hairline: #d8e0ea;
|
|
2678
|
+
--color-hairline-soft: #e6ebf2;
|
|
2679
|
+
--color-hairline-strong: #bcc7d6;
|
|
2680
|
+
--color-border: #d8e0ea;
|
|
2681
|
+
|
|
2682
|
+
/* accent secondary — a cool cyan highlight (replaces the warm brand pink) */
|
|
2683
|
+
--color-accent: #0e93a8; /* cool cyan — decorative / epic & design type hue */
|
|
2684
|
+
--color-accent-orange: #c2410c;
|
|
2685
|
+
--color-accent-teal: #0e8f86;
|
|
2686
|
+
--color-accent-green: #18874c; /* cooled green */
|
|
2687
|
+
|
|
2688
|
+
/* card tints — cooled pastel washes (6 distinct feature hues) */
|
|
2689
|
+
--color-tint-peach: #fde7d4;
|
|
2690
|
+
--color-tint-rose: #fbe0ea;
|
|
2691
|
+
--color-tint-mint: #d6f0e2;
|
|
2692
|
+
--color-tint-lavender: #e4e8fb; /* cool indigo tint */
|
|
2693
|
+
--color-tint-sky: #dbeafb; /* cool blue tint — the most-used wash */
|
|
2694
|
+
--color-tint-yellow: #fbf2d2;
|
|
2695
|
+
|
|
2696
|
+
/* semantic */
|
|
2697
|
+
--color-success: #18874c;
|
|
2698
|
+
--color-warning: #c2410c;
|
|
2699
|
+
--color-destructive: #d3322f; /* white-on AA */
|
|
2700
|
+
--color-destructive-foreground: #ffffff;
|
|
2701
|
+
--color-info: #0d63b8;
|
|
2702
|
+
|
|
2703
|
+
/* links — a cleaner sky-blue, distinct from the cobalt primary (AA on the
|
|
2704
|
+
soft surface a hovered row paints, too) */
|
|
2705
|
+
--color-link: #0d63b8;
|
|
2706
|
+
--color-link-pressed: #0a4f95;
|
|
2707
|
+
|
|
2708
|
+
/* sidebar hover is a concrete hex in Tier 3 (not a --color-* ref), so the
|
|
2709
|
+
palette overrides it directly — one shade darker than the cool surface. */
|
|
2710
|
+
--el-sidebar-item-bg-hover: #e4e9f1;
|
|
2711
|
+
}
|
|
2712
|
+
|
|
2713
|
+
/* Cobalt — dark companion. A cool navy canvas (not the base near-black);
|
|
2714
|
+
brightened cobalt/link hues so they read AS text on the dark surface, and a
|
|
2715
|
+
darker accent fill so white labels still clear AA. */
|
|
2716
|
+
[data-palette='cobalt'][data-theme='dark'] {
|
|
2717
|
+
--color-background: #0b111d; /* deep cool navy canvas */
|
|
2718
|
+
--color-foreground: #e7edf6;
|
|
2719
|
+
--color-surface: #131d2c;
|
|
2720
|
+
--color-surface-soft: #0f1825;
|
|
2721
|
+
|
|
2722
|
+
--color-ink: #e7edf6;
|
|
2723
|
+
--color-charcoal: #d3deec;
|
|
2724
|
+
--color-slate: #a0b1c6;
|
|
2725
|
+
--color-steel: #7e90a6;
|
|
2726
|
+
--color-stone: #5f6f84;
|
|
2727
|
+
--color-muted: #131d2c;
|
|
2728
|
+
--color-muted-foreground: #94a4ba;
|
|
2729
|
+
|
|
2730
|
+
--color-hairline: #243245;
|
|
2731
|
+
--color-hairline-soft: #1a2433;
|
|
2732
|
+
--color-hairline-strong: #324258;
|
|
2733
|
+
--color-border: #243245;
|
|
2734
|
+
|
|
2735
|
+
/* cobalt — brightened as text on navy; fill darkened for white-label AA */
|
|
2736
|
+
--color-primary: #7e98f2;
|
|
2737
|
+
--color-primary-foreground: #ffffff;
|
|
2738
|
+
--color-primary-pressed: #3a51b8;
|
|
2739
|
+
--color-primary-fill: #4a63cf;
|
|
2740
|
+
|
|
2741
|
+
/* cooled tints, dimmed for the navy canvas */
|
|
2742
|
+
--color-tint-peach: #2f2317;
|
|
2743
|
+
--color-tint-rose: #301a25;
|
|
2744
|
+
--color-tint-mint: #15281f;
|
|
2745
|
+
--color-tint-lavender: #1c2238;
|
|
2746
|
+
--color-tint-sky: #13243a;
|
|
2747
|
+
--color-tint-yellow: #2b2814;
|
|
2748
|
+
|
|
2749
|
+
--color-accent: #2ec3d6; /* bright cool cyan highlight on dark */
|
|
2750
|
+
--color-accent-orange: #f08a4b;
|
|
2751
|
+
--color-accent-teal: #2db6a8;
|
|
2752
|
+
--color-accent-green: #34c578;
|
|
2753
|
+
|
|
2754
|
+
--color-success: #34c578;
|
|
2755
|
+
--color-warning: #f08a4b;
|
|
2756
|
+
--color-destructive: #d83847; /* white-on AA on the navy canvas */
|
|
2757
|
+
--color-destructive-foreground: #ffffff;
|
|
2758
|
+
--color-info: #6fb3ff;
|
|
2759
|
+
|
|
2760
|
+
--color-link: #6fb3ff;
|
|
2761
|
+
--color-link-pressed: #93c5ff;
|
|
2762
|
+
|
|
2763
|
+
/* sidebar hover — one shade lighter than the dark cool surface */
|
|
2764
|
+
--el-sidebar-item-bg-hover: #1c2840;
|
|
2765
|
+
}
|
|
2766
|
+
|
|
2767
|
+
/* ── Palette: Graphite (data-palette="graphite") — Subtask 7.3.51 ───────────
|
|
2768
|
+
A stark, editorial, COOL-NEUTRAL MONOCHROME re-skin — the cool foil to
|
|
2769
|
+
Motir's warmth: true greyscale surfaces + ink on a clean Radix Slate scale,
|
|
2770
|
+
a single restrained accent. The accent is expressed two ways, exactly as
|
|
2771
|
+
Vercel / Linear do it: the primary CTA is a high-contrast INK fill (near-black
|
|
2772
|
+
on light, near-white on dark — the monochrome statement), while the one
|
|
2773
|
+
chromatic accent — a restrained cool blue — carries links, active/selected
|
|
2774
|
+
states, focus, and the decorative highlight. Surfaces, ink, and borders are
|
|
2775
|
+
pure cool greyscale; semantic (danger/success/warning/info) and work-item
|
|
2776
|
+
type hues stay chromatic-but-cooled so status & kind remain legible (the
|
|
2777
|
+
finding-#54 guard — never collapse the UI to grey + one colour).
|
|
2778
|
+
|
|
2779
|
+
Mood + accent hue anchored in getdesign.md's Vercel (black-and-white
|
|
2780
|
+
precision) / Linear (ultra-minimal) analysis; the actual light/dark ramps +
|
|
2781
|
+
UI-state steps are built from Radix Colors (Slate / Blue / Red / Grass /
|
|
2782
|
+
Amber / Teal) — the accessibility-first 12-step scales designed for UI
|
|
2783
|
+
states. Slate is the cool neutral closest to the brand anchor.
|
|
2784
|
+
|
|
2785
|
+
This block re-skins by overriding the Tier-0 --color-* SOURCE the Tier-3
|
|
2786
|
+
--el-* layer references (exactly how the [data-theme='dark'] block works), so
|
|
2787
|
+
every --el-* token — surfaces, ink, accent, links, semantic, tints, and
|
|
2788
|
+
work-item type hues — re-skins coherently with no per-token churn. It sets
|
|
2789
|
+
ONLY colour tokens (--color-* / --el-*), NEVER a shape/feel token (radius /
|
|
2790
|
+
spacing / shadow / sizing / motion / type) — that is the independent
|
|
2791
|
+
data-style axis (paletteRegistry.test.ts enforces the disjointness).
|
|
2792
|
+
|
|
2793
|
+
Accessibility: every text-on-surface, white-on-fill, link, and chip-tint
|
|
2794
|
+
pairing clears WCAG AA (≥4.5; ≥3.0 for icon/UI hues) in BOTH light and dark —
|
|
2795
|
+
verified numerically + by rendered specimen, never eyeballed. See
|
|
2796
|
+
docs/palettes/graphite.md for the colour-role reference. */
|
|
2797
|
+
[data-palette='graphite'] {
|
|
2798
|
+
/* brand & primary — the high-contrast INK CTA + the single cool-blue accent.
|
|
2799
|
+
--color-primary is the accent AS text/icon/focus on a surface (the cool
|
|
2800
|
+
blue, AA on white & the slate surface); --color-primary-fill is the CTA
|
|
2801
|
+
FILL behind white labels — near-black ink, the monochrome statement. */
|
|
2802
|
+
--color-primary: #155bc4; /* cool-blue accent — links / active / focus / icon */
|
|
2803
|
+
--color-primary-foreground: #ffffff; /* white labels on the ink fill */
|
|
2804
|
+
--color-primary-pressed: #2b2f35; /* pressed ink CTA (one step off the fill) */
|
|
2805
|
+
--color-primary-fill: #1a1d21; /* near-black graphite CTA fill (white-on AA) */
|
|
2806
|
+
|
|
2807
|
+
/* surfaces — pure white canvas over cool slate sections (Radix Slate) */
|
|
2808
|
+
--color-background: #ffffff; /* stark white canvas */
|
|
2809
|
+
--color-foreground: #16191d; /* cool near-black ink */
|
|
2810
|
+
--color-surface: #eef0f3; /* cool slate section background */
|
|
2811
|
+
--color-surface-soft: #f8f9fa; /* quieter cool division */
|
|
2812
|
+
|
|
2813
|
+
/* text scale — cool slate hierarchy (Radix Slate 11→9 below body) */
|
|
2814
|
+
--color-ink: #16191d;
|
|
2815
|
+
--color-charcoal: #20242a; /* body emphasis / AA-safe text on tints */
|
|
2816
|
+
--color-slate: #565c64; /* secondary text — AA on white + surfaces */
|
|
2817
|
+
--color-steel: #6b7079; /* tertiary / footer */
|
|
2818
|
+
--color-stone: #8a8f98; /* faint labels (decorative, sub-AA — as the base) */
|
|
2819
|
+
--color-muted: #eef0f3;
|
|
2820
|
+
--color-muted-foreground: #5d636b; /* captions — AA on white + surfaces */
|
|
2821
|
+
|
|
2822
|
+
/* borders — cool slate hairlines */
|
|
2823
|
+
--color-hairline: #e2e4e9;
|
|
2824
|
+
--color-hairline-soft: #ebedf0;
|
|
2825
|
+
--color-hairline-strong: #c8ccd3;
|
|
2826
|
+
--color-border: #e2e4e9;
|
|
2827
|
+
|
|
2828
|
+
/* accent secondary — the cool-blue highlight replaces the warm brand pink */
|
|
2829
|
+
--color-accent: #2563c9; /* cool-blue decorative highlight / epic & design hue */
|
|
2830
|
+
--color-accent-orange: #c2410c; /* review hue / warning — cooled */
|
|
2831
|
+
--color-accent-teal: #0e8f86; /* subtask / content hue */
|
|
2832
|
+
--color-accent-green: #18804a; /* story hue / success — cooled */
|
|
2833
|
+
|
|
2834
|
+
/* card tints — cooled pastel washes (6 distinct feature hues; charcoal text
|
|
2835
|
+
on them clears AA in both themes) */
|
|
2836
|
+
--color-tint-peach: #f7e6d6;
|
|
2837
|
+
--color-tint-rose: #f6dfe8;
|
|
2838
|
+
--color-tint-mint: #d9efe1;
|
|
2839
|
+
--color-tint-lavender: #e4e6f3; /* cool indigo wash */
|
|
2840
|
+
--color-tint-sky: #dde9f6; /* cool blue wash — the accent tint */
|
|
2841
|
+
--color-tint-yellow: #f6f1d6;
|
|
2842
|
+
|
|
2843
|
+
/* semantic */
|
|
2844
|
+
--color-success: #18804a;
|
|
2845
|
+
--color-warning: #c2410c;
|
|
2846
|
+
--color-destructive: #c92a2a; /* white-on AA */
|
|
2847
|
+
--color-destructive-foreground: #ffffff;
|
|
2848
|
+
--color-info: #155bc4; /* shares the accent blue (coherent in mono) */
|
|
2849
|
+
|
|
2850
|
+
/* links — the accent blue (AA on white AND the soft surface a hovered row
|
|
2851
|
+
paints behind it) */
|
|
2852
|
+
--color-link: #155bc4;
|
|
2853
|
+
--color-link-pressed: #114a9e;
|
|
2854
|
+
|
|
2855
|
+
/* sidebar hover is a concrete hex in Tier 3 (not a --color-* ref), so the
|
|
2856
|
+
palette overrides it directly — one shade darker than the cool surface. */
|
|
2857
|
+
--el-sidebar-item-bg-hover: #e6e8ec;
|
|
2858
|
+
}
|
|
2859
|
+
|
|
2860
|
+
/* Graphite — dark companion. A cool near-black slate canvas; the CTA fill
|
|
2861
|
+
inverts to near-WHITE ink (Vercel's dark white-button), the accent blue
|
|
2862
|
+
brightens so it reads AS text on the dark canvas, and semantic/type hues
|
|
2863
|
+
brighten to clear AA on near-black. */
|
|
2864
|
+
[data-palette='graphite'][data-theme='dark'] {
|
|
2865
|
+
--color-background: #0c0d0f; /* stark cool near-black canvas */
|
|
2866
|
+
--color-foreground: #edeef0;
|
|
2867
|
+
--color-surface: #18191c; /* cool slate section */
|
|
2868
|
+
--color-surface-soft: #141517; /* quieter cool division */
|
|
2869
|
+
|
|
2870
|
+
--color-ink: #edeef0;
|
|
2871
|
+
--color-charcoal: #d7d9dd; /* emphasis / AA-safe on tints */
|
|
2872
|
+
--color-slate: #a9adb5; /* secondary — AA on the dark canvas */
|
|
2873
|
+
--color-steel: #888d96; /* tertiary / footer */
|
|
2874
|
+
--color-stone: #696e77; /* faint labels */
|
|
2875
|
+
--color-muted: #18191c;
|
|
2876
|
+
--color-muted-foreground: #a0a4ac; /* captions — AA on dark */
|
|
2877
|
+
|
|
2878
|
+
--color-hairline: #282a2e;
|
|
2879
|
+
--color-hairline-soft: #1d1f22;
|
|
2880
|
+
--color-hairline-strong: #3a3d42;
|
|
2881
|
+
--color-border: #282a2e;
|
|
2882
|
+
|
|
2883
|
+
/* accent — blue brightened AS text on near-black; CTA fill inverts to
|
|
2884
|
+
near-white ink with near-black labels (the dark monochrome statement) */
|
|
2885
|
+
--color-primary: #7db1ff; /* bright cool-blue accent — links / active / focus */
|
|
2886
|
+
--color-primary-foreground: #0c0d0f; /* near-black labels on the white fill */
|
|
2887
|
+
--color-primary-pressed: #d3d5d9; /* pressed white-ink CTA */
|
|
2888
|
+
--color-primary-fill: #edeef0; /* near-white ink CTA fill (black-on AA) */
|
|
2889
|
+
|
|
2890
|
+
/* cooled tints, dimmed for the near-black canvas */
|
|
2891
|
+
--color-tint-peach: #2e2418;
|
|
2892
|
+
--color-tint-rose: #2f1c26;
|
|
2893
|
+
--color-tint-mint: #15271e;
|
|
2894
|
+
--color-tint-lavender: #1f2236;
|
|
2895
|
+
--color-tint-sky: #15233a;
|
|
2896
|
+
--color-tint-yellow: #2a2716;
|
|
2897
|
+
|
|
2898
|
+
--color-accent: #5b9dff; /* bright cool-blue highlight on dark */
|
|
2899
|
+
--color-accent-orange: #f08a4b;
|
|
2900
|
+
--color-accent-teal: #2db6a8;
|
|
2901
|
+
--color-accent-green: #34b86e;
|
|
2902
|
+
|
|
2903
|
+
--color-success: #34b86e;
|
|
2904
|
+
--color-warning: #f08a4b;
|
|
2905
|
+
--color-destructive: #d83847; /* white-on AA on the dark canvas */
|
|
2906
|
+
--color-destructive-foreground: #ffffff;
|
|
2907
|
+
--color-info: #7db1ff;
|
|
2908
|
+
|
|
2909
|
+
--color-link: #7db1ff;
|
|
2910
|
+
--color-link-pressed: #a3c8ff;
|
|
2911
|
+
|
|
2912
|
+
/* sidebar hover — one shade lighter than the dark cool surface */
|
|
2913
|
+
--el-sidebar-item-bg-hover: #202226;
|
|
2914
|
+
}
|
|
2915
|
+
|
|
2916
|
+
/* ── Palette: Evergreen (data-palette="evergreen") — Subtask 7.3.50 ──────────
|
|
2917
|
+
A fresh, technical emerald scheme — Supabase (dark emerald) / Spotify
|
|
2918
|
+
(vibrant green) / MongoDB (spring green), built on Radix green/jade ramps.
|
|
2919
|
+
See docs/palettes/evergreen.md.
|
|
2920
|
+
|
|
2921
|
+
Like every palette block, this re-skins by overriding the Tier-0 --color-*
|
|
2922
|
+
layer (COLOUR only — never a shape/feel token, which is the data-style axis):
|
|
2923
|
+
because the Tier-3 --el-* element tokens reference --color-*, this cascades a
|
|
2924
|
+
COORDINATED re-skin through every surface, ink step, accent/CTA, link,
|
|
2925
|
+
semantic, tint, work-item-type hue, and chart colour at once — no per-`--el-*`
|
|
2926
|
+
enumeration, no drift from the element layer. The work-item type / chart hues
|
|
2927
|
+
stay a distinguishable rainbow (a `code` chip must still read distinct from a
|
|
2928
|
+
`test` chip); only the palette's MOOD shifts emerald. Every text-on-surface,
|
|
2929
|
+
CTA, link, and chip pairing passes WCAG AA in light AND dark (verified by
|
|
2930
|
+
render specimen; see the doc).
|
|
2931
|
+
|
|
2932
|
+
Concrete-hex --el-* tokens that do NOT resolve through --color-*
|
|
2933
|
+
(--el-sidebar-item-bg-hover) are re-skinned explicitly below. */
|
|
2934
|
+
[data-palette='evergreen'] {
|
|
2935
|
+
/* Brand / primary — emerald CTA (white label text clears AA on the fill) */
|
|
2936
|
+
--color-primary: #0c7a52;
|
|
2937
|
+
--color-primary-foreground: #ffffff;
|
|
2938
|
+
--color-primary-pressed: #095f40;
|
|
2939
|
+
--color-primary-fill: #0c7a52;
|
|
2940
|
+
|
|
2941
|
+
/* Surfaces — lightly cooled (green-leaning) neutrals over a white canvas */
|
|
2942
|
+
--color-background: #ffffff;
|
|
2943
|
+
--color-foreground: #14201b;
|
|
2944
|
+
--color-surface: #eef3f0;
|
|
2945
|
+
--color-surface-soft: #f6faf8;
|
|
2946
|
+
|
|
2947
|
+
/* Text scale — cooled forest-charcoal ink hierarchy */
|
|
2948
|
+
--color-ink: #14201b;
|
|
2949
|
+
--color-charcoal: #1d2c25;
|
|
2950
|
+
--color-slate: #42534a;
|
|
2951
|
+
--color-steel: #5e6f66;
|
|
2952
|
+
--color-stone: #8f9f96;
|
|
2953
|
+
--color-muted: #eef2f0;
|
|
2954
|
+
--color-muted-foreground: #5e6f66;
|
|
2955
|
+
|
|
2956
|
+
/* Borders / hairlines — cool green-grey */
|
|
2957
|
+
--color-hairline: #d9e2dc;
|
|
2958
|
+
--color-hairline-soft: #e6ece9;
|
|
2959
|
+
--color-hairline-strong: #c2d0c8;
|
|
2960
|
+
--color-border: #d9e2dc;
|
|
2961
|
+
|
|
2962
|
+
/* Secondary accents — a harmonized-but-distinct rainbow for type/chart hues.
|
|
2963
|
+
Violet replaces the house pink as the complementary accent (epic/design),
|
|
2964
|
+
so those chips stay distinct from the emerald greens. */
|
|
2965
|
+
--color-accent: #8257e6; /* violet — epic / design / highlight */
|
|
2966
|
+
--color-accent-orange: #c2410c; /* review */
|
|
2967
|
+
--color-accent-teal: #0d8a86; /* subtask / content */
|
|
2968
|
+
--color-accent-green: #157f3a; /* story — fresh grass green */
|
|
2969
|
+
|
|
2970
|
+
/* Card tints — cool/green-leaning washes (lavender shifts toward the violet
|
|
2971
|
+
accent; carry charcoal text at AA) */
|
|
2972
|
+
--color-tint-peach: #ffe9d6;
|
|
2973
|
+
--color-tint-rose: #f6dced;
|
|
2974
|
+
--color-tint-mint: #d5f1e1;
|
|
2975
|
+
--color-tint-lavender: #e7e0fa;
|
|
2976
|
+
--color-tint-sky: #d8ecf6;
|
|
2977
|
+
--color-tint-yellow: #fbf3d2;
|
|
2978
|
+
|
|
2979
|
+
/* Semantic — green-leaning success; the rest kept distinct for legibility */
|
|
2980
|
+
--color-success: #127c3e; /* grassier than the emerald primary, so they read apart */
|
|
2981
|
+
--color-warning: #b45309;
|
|
2982
|
+
--color-destructive: #d63d3d;
|
|
2983
|
+
--color-destructive-foreground: #ffffff;
|
|
2984
|
+
--color-info: #1366c4; /* a clear blue — keeps task/code apart from teal subtask/content */
|
|
2985
|
+
|
|
2986
|
+
/* Links — blue affordance (distinct from the emerald accent), AA on both the
|
|
2987
|
+
page and the soft hovered-row surface */
|
|
2988
|
+
--color-link: #0a6ebd;
|
|
2989
|
+
--color-link-pressed: #08537f;
|
|
2990
|
+
|
|
2991
|
+
/* Concrete-hex --el token (does not resolve through --color-*) — cool hover
|
|
2992
|
+
lift one shade darker than the cooled light surface */
|
|
2993
|
+
--el-sidebar-item-bg-hover: #e4ebe7;
|
|
2994
|
+
}
|
|
2995
|
+
|
|
2996
|
+
/* Evergreen — dark companion. Bright emerald on a deep-forest canvas
|
|
2997
|
+
(Supabase-style: the bright accent FILL carries DARK label text). Mirrors the
|
|
2998
|
+
Tier-1 dark flip for the cooled neutrals. */
|
|
2999
|
+
[data-palette='evergreen'][data-theme='dark'] {
|
|
3000
|
+
--color-primary: #3ddc97;
|
|
3001
|
+
--color-primary-foreground: #06140d; /* dark ink on the bright emerald fill */
|
|
3002
|
+
--color-primary-pressed: #2bbd7f;
|
|
3003
|
+
--color-primary-fill: #3ddc97;
|
|
3004
|
+
|
|
3005
|
+
--color-background: #0c130f;
|
|
3006
|
+
--color-foreground: #e8efe9;
|
|
3007
|
+
--color-surface: #141d18;
|
|
3008
|
+
--color-surface-soft: #101713;
|
|
3009
|
+
|
|
3010
|
+
--color-ink: #e8efe9;
|
|
3011
|
+
--color-charcoal: #d6e0d9;
|
|
3012
|
+
--color-slate: #9aaaa1;
|
|
3013
|
+
--color-steel: #6f8077;
|
|
3014
|
+
--color-stone: #5a6b62;
|
|
3015
|
+
--color-muted: #141d18;
|
|
3016
|
+
--color-muted-foreground: #9aaaa1;
|
|
3017
|
+
|
|
3018
|
+
--color-hairline: #25322b;
|
|
3019
|
+
--color-hairline-soft: #1b251f;
|
|
3020
|
+
--color-hairline-strong: #35453c;
|
|
3021
|
+
--color-border: #25322b;
|
|
3022
|
+
|
|
3023
|
+
--color-accent: #a78bfa; /* violet */
|
|
3024
|
+
--color-accent-orange: #fb923c;
|
|
3025
|
+
--color-accent-teal: #2dd4bf;
|
|
3026
|
+
--color-accent-green: #34d27a;
|
|
3027
|
+
|
|
3028
|
+
/* Pastel tints dimmed for the dark canvas */
|
|
3029
|
+
--color-tint-peach: #2f2114;
|
|
3030
|
+
--color-tint-rose: #2e1822;
|
|
3031
|
+
--color-tint-mint: #11271c;
|
|
3032
|
+
--color-tint-lavender: #211b33;
|
|
3033
|
+
--color-tint-sky: #122430;
|
|
3034
|
+
--color-tint-yellow: #2b2712;
|
|
3035
|
+
|
|
3036
|
+
--color-success: #34d27a;
|
|
3037
|
+
--color-warning: #fb923c;
|
|
3038
|
+
--color-destructive: #f87171;
|
|
3039
|
+
--color-destructive-foreground: #06140d; /* dark ink on the bright red */
|
|
3040
|
+
--color-info: #5aa8f5;
|
|
3041
|
+
|
|
3042
|
+
--color-link: #56b6f5;
|
|
3043
|
+
--color-link-pressed: #7cc7f8;
|
|
3044
|
+
|
|
3045
|
+
/* hover lift one shade *lighter* than the dark surface (mirrors base recipe) */
|
|
3046
|
+
--el-sidebar-item-bg-hover: #1e2a23;
|
|
3047
|
+
}
|
|
3048
|
+
|
|
3049
|
+
/* ═══════════════════════════════════════════════════════════════════════
|
|
3050
|
+
AXIS 3 (TYPE) — data-type overrides (Subtask 7.3.53)
|
|
3051
|
+
Registry: lib/theme/typography.ts. A named type pairing re-points the
|
|
3052
|
+
`--font-*` ROLE tokens (sans / serif / mono) — and ONLY font tokens, NEVER a
|
|
3053
|
+
colour (--el / --color) or shape (--radius / --spacing / --shadow) token. The
|
|
3054
|
+
shared --font-size scale stays common (sizes are layout, not brand).
|
|
3055
|
+
|
|
3056
|
+
v1 ships three pairings built from the THREE already-loaded next/font faces
|
|
3057
|
+
(Inter · Source Serif 4 · JetBrains Mono) — ZERO new payload:
|
|
3058
|
+
- `motir` — the BASE (editorial serif headlines); it IS the Tier-0
|
|
3059
|
+
--font-* defaults, so — like the `motir` palette — it needs
|
|
3060
|
+
NO override block.
|
|
3061
|
+
- `motir-sans` — all-sans: the serif headline face re-points to the sans
|
|
3062
|
+
stack (reproduces the old Swiss `--font-serif` override).
|
|
3063
|
+
- `motir-mono` — mono headlines: re-points to the mono stack (reproduces
|
|
3064
|
+
the old Neo-Brutalism / Cybercore override).
|
|
3065
|
+
A STYLE declares a `defaultTypeId` (lib/theme/styles.ts) — the pairing applied
|
|
3066
|
+
when the user hasn't pinned a `data-type` — so the out-of-the-box look is
|
|
3067
|
+
UNCHANGED (swiss → motir-sans, neo/cybercore → motir-mono). Each later
|
|
3068
|
+
"Type: …" subtask (7.3.54+) ADDS its `[data-type='<id>']` block + faces here:
|
|
3069
|
+
- `grotesk` (7.3.54) — Space Grotesk on the headline role over Inter.
|
|
3070
|
+
- `mono-technical` (7.3.56) — IBM Plex Mono on the headline + meta/code
|
|
3071
|
+
roles over the Inter body; loads one new face (in layout.tsx).
|
|
3072
|
+
═══════════════════════════════════════════════════════════════════════ */
|
|
3073
|
+
|
|
3074
|
+
[data-type='motir-sans'] {
|
|
3075
|
+
/* Sans headlines — re-point the editorial serif role at the sans stack. */
|
|
3076
|
+
--font-serif:
|
|
3077
|
+
var(--font-sans-source, -apple-system), BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
3078
|
+
}
|
|
3079
|
+
|
|
3080
|
+
[data-type='motir-mono'] {
|
|
3081
|
+
/* Mono throughout — re-point BOTH the headline (serif) role AND the body/UI
|
|
3082
|
+
(sans) role at the mono stack, so the WHOLE app (nav, header, buttons,
|
|
3083
|
+
content) re-types to a code-native monospace voice, not just headlines (Yue,
|
|
3084
|
+
2026-06-19: the type axis must re-type the whole UI, chrome included). Meta
|
|
3085
|
+
already is mono. ONLY --font-* tokens (the disjointness guard). */
|
|
3086
|
+
--font-serif: var(--font-mono-source, ui-monospace), 'SF Mono', Menlo, monospace;
|
|
3087
|
+
--font-sans: var(--font-mono-source, ui-monospace), 'SF Mono', Menlo, monospace;
|
|
3088
|
+
}
|
|
3089
|
+
|
|
3090
|
+
/* ── Palette: Spectrum (data-palette="spectrum") — Subtask 7.3.52 ───────────
|
|
3091
|
+
A vibrant, playful, energetic MULTI-ACCENT re-skin: crisp cool-white
|
|
3092
|
+
surfaces, a bright violet primary, a candy-magenta highlight, and a
|
|
3093
|
+
saturated, well-separated rainbow tint/accent set — friendly, never garish.
|
|
3094
|
+
Visibly distinct from Motir (warm cream → crisp cool surfaces; muted pastels
|
|
3095
|
+
→ candy-bright tints), not just an accent-hue swap. Mood anchored in
|
|
3096
|
+
getdesign.md's Figma (vibrant multi-colour) / Airtable (colourful, friendly)
|
|
3097
|
+
analysis; the actual light/dark ramps + UI-state steps are built from Radix
|
|
3098
|
+
Colors (Violet / Iris / Pink / Blue / Jade / Amber / Red) — the
|
|
3099
|
+
accessibility-first 12-step scales designed for UI states.
|
|
3100
|
+
|
|
3101
|
+
This block re-skins by overriding the Tier-0 --color-* SOURCE the Tier-3
|
|
3102
|
+
--el-* layer references (exactly how the [data-theme='dark'] block works), so
|
|
3103
|
+
every --el-* token — surfaces, ink, accent, links, semantic, tints, and
|
|
3104
|
+
work-item type hues — re-skins coherently with no per-token churn. It sets
|
|
3105
|
+
ONLY colour tokens (--color-* / --el-*), NEVER a shape/feel token (radius /
|
|
3106
|
+
spacing / shadow / sizing / motion / type) — that is the independent
|
|
3107
|
+
data-style axis (paletteRegistry.test.ts enforces the disjointness).
|
|
3108
|
+
|
|
3109
|
+
Accessibility: every text-on-surface, white-on-fill, link, and chip-tint
|
|
3110
|
+
pairing clears WCAG AA (≥4.5 text; ≥3.0 icon/UI hue) in BOTH light and dark —
|
|
3111
|
+
verified numerically + by rendered specimen, never eyeballed. See
|
|
3112
|
+
docs/palettes/spectrum.md for the colour-role reference. */
|
|
3113
|
+
[data-palette='spectrum'] {
|
|
3114
|
+
/* brand & primary — bright violet (Radix Violet/Iris family) */
|
|
3115
|
+
--color-primary: #6440d6; /* violet AS text/icon on a surface (AA ~6.5:1 on white) */
|
|
3116
|
+
--color-primary-foreground: #ffffff;
|
|
3117
|
+
--color-primary-pressed: #5331b8;
|
|
3118
|
+
--color-primary-fill: #5a37c9; /* fill behind white labels (white-on AA ~7.4:1) */
|
|
3119
|
+
|
|
3120
|
+
/* surfaces — crisp cool white over faintly violet-tinted sections */
|
|
3121
|
+
--color-background: #ffffff;
|
|
3122
|
+
--color-foreground: #1a1626; /* cool near-black ink with a faint violet cast */
|
|
3123
|
+
--color-surface: #f4f2fb; /* cool violet-tinted section background */
|
|
3124
|
+
--color-surface-soft: #faf9fe; /* quieter cool division */
|
|
3125
|
+
|
|
3126
|
+
/* text scale — cool violet-neutral hierarchy */
|
|
3127
|
+
--color-ink: #1a1626;
|
|
3128
|
+
--color-charcoal: #2a2440; /* body emphasis / AA-safe text on tints */
|
|
3129
|
+
--color-slate: #524d66; /* secondary text */
|
|
3130
|
+
--color-steel: #6b6680; /* tertiary / footer */
|
|
3131
|
+
--color-stone: #938da6; /* faint labels */
|
|
3132
|
+
--color-muted: #f4f2fb;
|
|
3133
|
+
--color-muted-foreground: #5e5975; /* captions — AA ~6.6:1 on white + surfaces */
|
|
3134
|
+
|
|
3135
|
+
/* borders — cool violet hairlines */
|
|
3136
|
+
--color-hairline: #e6e2f3;
|
|
3137
|
+
--color-hairline-soft: #efecf8;
|
|
3138
|
+
--color-hairline-strong: #cfc8e6;
|
|
3139
|
+
--color-border: #e6e2f3;
|
|
3140
|
+
|
|
3141
|
+
/* accent secondary — a candy magenta-pink highlight (the playful brand spark;
|
|
3142
|
+
also the epic & design type hue) + a vivid, well-separated accent trio */
|
|
3143
|
+
--color-accent: #e23d97; /* vivid magenta-pink — decorative / epic & design hue */
|
|
3144
|
+
--color-accent-orange: #ea580c;
|
|
3145
|
+
--color-accent-teal: #0d9488;
|
|
3146
|
+
--color-accent-green: #16a34a;
|
|
3147
|
+
|
|
3148
|
+
/* card tints — candy-bright washes (6 distinct, saturated feature hues; each
|
|
3149
|
+
light enough that --el-text-strong charcoal clears AA ≥10:1 on it) */
|
|
3150
|
+
--color-tint-peach: #ffe1c2;
|
|
3151
|
+
--color-tint-rose: #ffd9e8;
|
|
3152
|
+
--color-tint-mint: #c7f5d9;
|
|
3153
|
+
--color-tint-lavender: #e6ddff;
|
|
3154
|
+
--color-tint-sky: #d2e7ff;
|
|
3155
|
+
--color-tint-yellow: #fff0b8;
|
|
3156
|
+
|
|
3157
|
+
/* semantic */
|
|
3158
|
+
--color-success: #16a34a;
|
|
3159
|
+
--color-warning: #ea580c;
|
|
3160
|
+
--color-destructive: #d92e2b; /* white-on AA */
|
|
3161
|
+
--color-destructive-foreground: #ffffff;
|
|
3162
|
+
--color-info: #2563eb;
|
|
3163
|
+
|
|
3164
|
+
/* links — a clean vivid blue, distinct from the violet primary (AA on the
|
|
3165
|
+
soft surface a hovered row paints, too) */
|
|
3166
|
+
--color-link: #2563eb;
|
|
3167
|
+
--color-link-pressed: #1d4ed8;
|
|
3168
|
+
|
|
3169
|
+
/* sidebar hover is a concrete hex in Tier 3 (not a --color-* ref), so the
|
|
3170
|
+
palette overrides it directly — one shade darker than the cool surface. */
|
|
3171
|
+
--el-sidebar-item-bg-hover: #ece8f8;
|
|
3172
|
+
}
|
|
3173
|
+
|
|
3174
|
+
/* Spectrum — dark companion. A deep cool-violet canvas (not the base
|
|
3175
|
+
near-black); brightened violet/pink/link hues so they read AS text on the
|
|
3176
|
+
dark surface, and a darker accent fill so white labels still clear AA. */
|
|
3177
|
+
[data-palette='spectrum'][data-theme='dark'] {
|
|
3178
|
+
--color-background: #14111f; /* deep cool-violet canvas */
|
|
3179
|
+
--color-foreground: #ece9f6;
|
|
3180
|
+
--color-surface: #1d1830;
|
|
3181
|
+
--color-surface-soft: #181426;
|
|
3182
|
+
|
|
3183
|
+
--color-ink: #ece9f6;
|
|
3184
|
+
--color-charcoal: #ddd8ee;
|
|
3185
|
+
--color-slate: #aaa3c2;
|
|
3186
|
+
--color-steel: #8a82a3;
|
|
3187
|
+
--color-stone: #6b6485;
|
|
3188
|
+
--color-muted: #1d1830;
|
|
3189
|
+
--color-muted-foreground: #a39bbd;
|
|
3190
|
+
|
|
3191
|
+
--color-hairline: #2b2440;
|
|
3192
|
+
--color-hairline-soft: #211b33;
|
|
3193
|
+
--color-hairline-strong: #3a3257;
|
|
3194
|
+
--color-border: #2b2440;
|
|
3195
|
+
|
|
3196
|
+
/* violet — brightened as text on the violet canvas; fill darkened for
|
|
3197
|
+
white-label AA */
|
|
3198
|
+
--color-primary: #a78bfa;
|
|
3199
|
+
--color-primary-foreground: #ffffff;
|
|
3200
|
+
--color-primary-pressed: #5331b8;
|
|
3201
|
+
--color-primary-fill: #6d4fd6;
|
|
3202
|
+
|
|
3203
|
+
/* candy tints, dimmed for the dark canvas (light --el-text-strong on them) */
|
|
3204
|
+
--color-tint-peach: #34261a;
|
|
3205
|
+
--color-tint-rose: #351c28;
|
|
3206
|
+
--color-tint-mint: #16301f;
|
|
3207
|
+
--color-tint-lavender: #251f3c;
|
|
3208
|
+
--color-tint-sky: #16263f;
|
|
3209
|
+
--color-tint-yellow: #322c16;
|
|
3210
|
+
|
|
3211
|
+
--color-accent: #ff5fb0; /* bright candy magenta-pink on dark */
|
|
3212
|
+
--color-accent-orange: #fb8b4c;
|
|
3213
|
+
--color-accent-teal: #2dc4b8;
|
|
3214
|
+
--color-accent-green: #36c977;
|
|
3215
|
+
|
|
3216
|
+
--color-success: #36c977;
|
|
3217
|
+
--color-warning: #fb8b4c;
|
|
3218
|
+
--color-destructive: #f0555f; /* reads AS text on the dark canvas (AA ~5.5:1) */
|
|
3219
|
+
--color-destructive-foreground: #ffffff;
|
|
3220
|
+
--color-info: #6fa8ff;
|
|
3221
|
+
|
|
3222
|
+
--color-link: #8fb8ff;
|
|
3223
|
+
--color-link-pressed: #b3d0ff;
|
|
3224
|
+
|
|
3225
|
+
/* sidebar hover — one shade lighter than the dark cool surface */
|
|
3226
|
+
--el-sidebar-item-bg-hover: #261f3d;
|
|
3227
|
+
}
|
|
3228
|
+
|
|
3229
|
+
/* ── Palette: Amber (data-palette="amber") — Subtask 8.8.34 (MOTIR-1252) ─────
|
|
3230
|
+
BINANCE's whole palette: the bold trading-floor GOLD over the exchange's
|
|
3231
|
+
cool graphite near-blacks. Transcribed from Binance's documented system —
|
|
3232
|
+
the warm gold primary IS what makes this a "warm" palette; the neutrals are
|
|
3233
|
+
Binance's own cool greys (the whole-palette directive: a warm primary + the
|
|
3234
|
+
brand's real neutrals). Brand tokens: Binance Gold #F0B90B (primary) /
|
|
3235
|
+
brighter #FCD535 (on-dark accent), trading-floor blacks #0B0E11 / #181A20 /
|
|
3236
|
+
"Shark" #1E2329 / #2B3139, greys #EAECEF / #B7BDC6 / #848E9C / #5E6673 /
|
|
3237
|
+
#474D57, buy-green #0ECB81, sell-red #F6465D. (Binance ships no documented
|
|
3238
|
+
light theme, so the light surfaces/greys are derived from its cool grey
|
|
3239
|
+
family + the gold; the dark theme is the authentic trading floor.)
|
|
3240
|
+
|
|
3241
|
+
⚠️ THE YELLOW TRAP: the gold FILL cannot carry white text at AA, so the
|
|
3242
|
+
accent-text (--color-primary-foreground) is Binance's near-black #181A20,
|
|
3243
|
+
not #fff (dark-on-gold ≥9:1) — Binance's own black-on-yellow treatment.
|
|
3244
|
+
|
|
3245
|
+
Re-skins by overriding the Tier-0 --color-* SOURCE (as [data-theme='dark']
|
|
3246
|
+
does), so every --el-* token re-skins coherently. ONLY colour tokens
|
|
3247
|
+
(--color-* / --el-*), NEVER a shape/feel token (the independent data-style
|
|
3248
|
+
axis; paletteRegistry.test.ts enforces the disjointness). Every --el-* role
|
|
3249
|
+
clears WCAG AA (≥4.5 text; ≥3.0 icon/UI) in BOTH themes, verified
|
|
3250
|
+
numerically. See docs/palettes/amber.md. */
|
|
3251
|
+
[data-palette='amber'] {
|
|
3252
|
+
/* brand & primary — Binance Gold. The FILL carries DARK ink labels (yellow
|
|
3253
|
+
trap); --color-primary is a darkened gold used AS text/icon on a surface. */
|
|
3254
|
+
--color-primary: #8a6a00; /* darkened gold as text/icon (AA ~5:1 on white) */
|
|
3255
|
+
--color-primary-foreground: #181a20; /* Binance near-black on the gold fill (AA ~9.6:1) */
|
|
3256
|
+
--color-primary-pressed: #d4a30a;
|
|
3257
|
+
--color-primary-fill: #f0b90b; /* Binance Gold — the signature; dark-on AA */
|
|
3258
|
+
|
|
3259
|
+
/* surfaces — white canvas over cool graphite sections (derived light) */
|
|
3260
|
+
--color-background: #ffffff;
|
|
3261
|
+
--color-foreground: #181a20; /* Binance near-black ink */
|
|
3262
|
+
--color-surface: #f4f5f7; /* cool graphite section background */
|
|
3263
|
+
--color-surface-soft: #fafbfc; /* quieter cool division */
|
|
3264
|
+
|
|
3265
|
+
/* text scale — Binance cool-grey hierarchy */
|
|
3266
|
+
--color-ink: #181a20;
|
|
3267
|
+
--color-charcoal: #1e2329; /* Binance "Shark" — emphasis / AA-safe on tints */
|
|
3268
|
+
--color-slate: #4a5159; /* secondary text */
|
|
3269
|
+
--color-steel: #5e6673; /* tertiary / footer */
|
|
3270
|
+
--color-stone: #848e9c; /* Binance signature grey — faint labels */
|
|
3271
|
+
--color-muted: #f4f5f7;
|
|
3272
|
+
--color-muted-foreground: #5e6673; /* captions — AA on white + surfaces */
|
|
3273
|
+
|
|
3274
|
+
/* borders — cool graphite hairlines */
|
|
3275
|
+
--color-hairline: #e6e8eb;
|
|
3276
|
+
--color-hairline-soft: #eef0f2;
|
|
3277
|
+
--color-hairline-strong: #c9ced4;
|
|
3278
|
+
--color-border: #e6e8eb;
|
|
3279
|
+
|
|
3280
|
+
/* accent secondary — a deep amber-gold highlight (epic & design hue),
|
|
3281
|
+
distinct from the bright primary gold */
|
|
3282
|
+
--color-accent: #97751a;
|
|
3283
|
+
--color-accent-orange: #c2410c;
|
|
3284
|
+
--color-accent-teal: #0e8f86;
|
|
3285
|
+
--color-accent-green: #0a8f57; /* darkened Binance buy-green for light AA */
|
|
3286
|
+
|
|
3287
|
+
/* card tints — gold-leaning warm washes + cool feature hues */
|
|
3288
|
+
--color-tint-peach: #fbe7c2;
|
|
3289
|
+
--color-tint-rose: #f7dde2;
|
|
3290
|
+
--color-tint-mint: #d6ecdf;
|
|
3291
|
+
--color-tint-lavender: #e2e4ee;
|
|
3292
|
+
--color-tint-sky: #dbe7f5;
|
|
3293
|
+
--color-tint-yellow: #fbeec0; /* gold wash — the most-used here */
|
|
3294
|
+
|
|
3295
|
+
/* semantic — Binance buy-green / sell-red, darkened for white-on / light AA */
|
|
3296
|
+
--color-success: #0a8f57;
|
|
3297
|
+
--color-warning: #b45309;
|
|
3298
|
+
--color-destructive: #d4283f; /* Binance sell-red, darkened (white-on AA) */
|
|
3299
|
+
--color-destructive-foreground: #ffffff;
|
|
3300
|
+
--color-info: #1366c4;
|
|
3301
|
+
|
|
3302
|
+
/* links — a clear blue, distinct from the gold primary */
|
|
3303
|
+
--color-link: #1366c4;
|
|
3304
|
+
--color-link-pressed: #0f4f9c;
|
|
3305
|
+
|
|
3306
|
+
/* sidebar hover — one shade darker than the cool surface */
|
|
3307
|
+
--el-sidebar-item-bg-hover: #eceef1;
|
|
3308
|
+
}
|
|
3309
|
+
|
|
3310
|
+
/* Amber — dark companion: the AUTHENTIC Binance trading floor. Cool near-black
|
|
3311
|
+
canvas (#0B0E11) + "Shark" cards (#1E2329); the gold brightens to #FCD535 and
|
|
3312
|
+
the CTA fill carries dark labels; semantics are Binance's real buy-green
|
|
3313
|
+
#0ECB81 / sell-red #F6465D. */
|
|
3314
|
+
[data-palette='amber'][data-theme='dark'] {
|
|
3315
|
+
--color-background: #0b0e11; /* Binance deepest trading-floor black */
|
|
3316
|
+
--color-foreground: #eaecef;
|
|
3317
|
+
--color-surface: #1e2329; /* Binance "Shark" card surface */
|
|
3318
|
+
--color-surface-soft: #181a20; /* Binance app background — quieter division */
|
|
3319
|
+
|
|
3320
|
+
--color-ink: #eaecef;
|
|
3321
|
+
--color-charcoal: #d1d6dd;
|
|
3322
|
+
--color-slate: #b7bdc6;
|
|
3323
|
+
--color-steel: #848e9c;
|
|
3324
|
+
--color-stone: #5e6673;
|
|
3325
|
+
--color-muted: #1e2329;
|
|
3326
|
+
--color-muted-foreground: #8a94a0;
|
|
3327
|
+
|
|
3328
|
+
--color-hairline: #2b3139;
|
|
3329
|
+
--color-hairline-soft: #1e2329;
|
|
3330
|
+
--color-hairline-strong: #474d57;
|
|
3331
|
+
--color-border: #2b3139;
|
|
3332
|
+
|
|
3333
|
+
/* gold brightened to Binance #FCD535 on the dark floor; fill carries dark
|
|
3334
|
+
labels (AA ~13.5:1) */
|
|
3335
|
+
--color-primary: #fcd535;
|
|
3336
|
+
--color-primary-foreground: #0b0e11;
|
|
3337
|
+
--color-primary-pressed: #d4a30a;
|
|
3338
|
+
--color-primary-fill: #fcd535;
|
|
3339
|
+
|
|
3340
|
+
/* tints dimmed for the trading-floor black */
|
|
3341
|
+
--color-tint-peach: #2f2814;
|
|
3342
|
+
--color-tint-rose: #2f1a22;
|
|
3343
|
+
--color-tint-mint: #102a1e;
|
|
3344
|
+
--color-tint-lavender: #1c2030;
|
|
3345
|
+
--color-tint-sky: #122436;
|
|
3346
|
+
--color-tint-yellow: #2e2810;
|
|
3347
|
+
|
|
3348
|
+
--color-accent: #f0b90b; /* Binance Gold highlight on dark */
|
|
3349
|
+
--color-accent-orange: #fb923c;
|
|
3350
|
+
--color-accent-teal: #2dd4bf;
|
|
3351
|
+
--color-accent-green: #0ecb81; /* Binance buy-green */
|
|
3352
|
+
|
|
3353
|
+
--color-success: #0ecb81; /* Binance buy-green */
|
|
3354
|
+
--color-warning: #fbab4c;
|
|
3355
|
+
--color-destructive: #f6465d; /* Binance sell-red */
|
|
3356
|
+
--color-destructive-foreground: #0b0e11; /* dark ink on the bright sell-red */
|
|
3357
|
+
--color-info: #6fb3ff;
|
|
3358
|
+
|
|
3359
|
+
--color-link: #6fb3ff;
|
|
3360
|
+
--color-link-pressed: #a3ccff;
|
|
3361
|
+
|
|
3362
|
+
/* sidebar hover — one shade lighter than the dark "Shark" surface */
|
|
3363
|
+
--el-sidebar-item-bg-hover: #262d35;
|
|
3364
|
+
}
|
|
3365
|
+
|
|
3366
|
+
/* ── Palette: Sienna (data-palette="sienna") — Subtask 8.8.34 (MOTIR-1252) ───
|
|
3367
|
+
MISTRAL AI's whole palette: the flame-orange over Mistral's Mediterranean
|
|
3368
|
+
warm-cream surfaces. Transcribed from Mistral's documented system
|
|
3369
|
+
(getdesign.md/mistral.ai): Mistral Orange #fa520f (primary) / Flame #fb6424,
|
|
3370
|
+
the Block gradient (yellow→amber→flame→orange), the Sunshine family
|
|
3371
|
+
(#ff8a00/#ffa110/#ffb83e/#ffd06a), Block Gold #ffe295, Warm-Ivory cream
|
|
3372
|
+
surfaces (#fffaeb → #fff0c2), Mistral-Black #1f1f1f ink. Mistral ships NO
|
|
3373
|
+
semantic colours, so success/warning/danger/info are added warm-consistent.
|
|
3374
|
+
|
|
3375
|
+
⚠️ THE YELLOW TRAP (as Amber): the bright flame FILL cannot carry white at
|
|
3376
|
+
AA, so the accent-text is a DARK warm ink in BOTH themes — Mistral's own
|
|
3377
|
+
dark-on-orange treatment (dark-on-flame ~5.3:1 light / ~6.8:1 dark).
|
|
3378
|
+
|
|
3379
|
+
Re-skins by overriding the Tier-0 --color-* SOURCE; ONLY colour tokens, never
|
|
3380
|
+
a shape/feel token (paletteRegistry.test.ts enforces it). Every --el-* role
|
|
3381
|
+
clears WCAG AA in BOTH themes, verified numerically. See
|
|
3382
|
+
docs/palettes/sienna.md. */
|
|
3383
|
+
[data-palette='sienna'] {
|
|
3384
|
+
/* brand & primary — Mistral's flame. The FILL is Mistral Orange #fa520f with
|
|
3385
|
+
DARK ink labels; --color-primary is a darkened flame used AS text/icon. */
|
|
3386
|
+
--color-primary: #c4400a; /* darkened Mistral flame as text/icon (AA ~4.9:1 on the cream) */
|
|
3387
|
+
--color-primary-foreground: #2a1205; /* DARK warm ink on the flame fill (AA ~5.3:1) */
|
|
3388
|
+
--color-primary-pressed: #cc3a05; /* Mistral primary-deep */
|
|
3389
|
+
--color-primary-fill: #fa520f; /* Mistral Orange — the signature flame; dark-on AA */
|
|
3390
|
+
|
|
3391
|
+
/* surfaces — Mistral's Mediterranean warm creams (never pure white) */
|
|
3392
|
+
--color-background: #fffaeb; /* Mistral "Warm Ivory" page background */
|
|
3393
|
+
--color-foreground: #1f1f1f; /* Mistral Black ink */
|
|
3394
|
+
--color-surface: #fbf1d3; /* warm cream section background (Mistral cream ramp) */
|
|
3395
|
+
--color-surface-soft: #fdf6e0; /* quieter warm cream division */
|
|
3396
|
+
|
|
3397
|
+
/* text scale — Mistral warm-charcoal hierarchy */
|
|
3398
|
+
--color-ink: #1f1f1f;
|
|
3399
|
+
--color-charcoal: #2c2c2c; /* emphasis / AA-safe text on tints */
|
|
3400
|
+
--color-slate: #4a4a4a; /* secondary text */
|
|
3401
|
+
--color-steel: #6a6a6a; /* tertiary / footer */
|
|
3402
|
+
--color-stone: #8a8a8a; /* faint labels */
|
|
3403
|
+
--color-muted: #fbf1d3;
|
|
3404
|
+
--color-muted-foreground: #5f5f5f; /* captions — AA on the cream + surfaces */
|
|
3405
|
+
|
|
3406
|
+
/* borders — warm beige hairlines (Mistral beige-deep family) */
|
|
3407
|
+
--color-hairline: #ece0c4;
|
|
3408
|
+
--color-hairline-soft: #f3ebd6;
|
|
3409
|
+
--color-hairline-strong: #ddcca6;
|
|
3410
|
+
--color-border: #ece0c4;
|
|
3411
|
+
|
|
3412
|
+
/* accent secondary — a darkened Mistral "Sunshine" amber (epic & design hue) */
|
|
3413
|
+
--color-accent: #b5660a;
|
|
3414
|
+
--color-accent-orange: #c2410c;
|
|
3415
|
+
--color-accent-teal: #0e8f86;
|
|
3416
|
+
--color-accent-green: #18874c;
|
|
3417
|
+
|
|
3418
|
+
/* card tints — Mistral's golden washes (yellow = Block Gold #ffe295) + warm
|
|
3419
|
+
feature hues; charcoal text clears AA ≥11:1 on each */
|
|
3420
|
+
--color-tint-peach: #ffe0c6;
|
|
3421
|
+
--color-tint-rose: #fbdfd2;
|
|
3422
|
+
--color-tint-mint: #e3ead0;
|
|
3423
|
+
--color-tint-lavender: #ece6d6;
|
|
3424
|
+
--color-tint-sky: #e6ebda;
|
|
3425
|
+
--color-tint-yellow: #ffe295; /* Mistral Block Gold */
|
|
3426
|
+
|
|
3427
|
+
/* semantic — Mistral documents none; added warm-consistent + AA-safe */
|
|
3428
|
+
--color-success: #18804a;
|
|
3429
|
+
--color-warning: #b45309;
|
|
3430
|
+
--color-destructive: #c92a2a;
|
|
3431
|
+
--color-destructive-foreground: #ffffff;
|
|
3432
|
+
--color-info: #1366c4;
|
|
3433
|
+
|
|
3434
|
+
/* links — Mistral uses the brand orange for links; a darkened flame (AA) */
|
|
3435
|
+
--color-link: #c4400a;
|
|
3436
|
+
--color-link-pressed: #a8350a;
|
|
3437
|
+
|
|
3438
|
+
/* sidebar hover — one shade deeper than the cream surface */
|
|
3439
|
+
--el-sidebar-item-bg-hover: #f5e9c4;
|
|
3440
|
+
}
|
|
3441
|
+
|
|
3442
|
+
/* Sienna — dark companion. Mistral is light-dominant (few dark tokens: Mistral
|
|
3443
|
+
Black #1f1f1f); a warm near-black canvas built from it, the flame brightened
|
|
3444
|
+
to read as text, the fill a bright flame with dark-ink labels. */
|
|
3445
|
+
[data-palette='sienna'][data-theme='dark'] {
|
|
3446
|
+
--color-background: #15120d; /* warm near-black canvas (from Mistral Black) */
|
|
3447
|
+
--color-foreground: #f5efe2;
|
|
3448
|
+
--color-surface: #1f1d17;
|
|
3449
|
+
--color-surface-soft: #1a1712;
|
|
3450
|
+
|
|
3451
|
+
--color-ink: #f5efe2;
|
|
3452
|
+
--color-charcoal: #e6ddcb;
|
|
3453
|
+
--color-slate: #b3a892;
|
|
3454
|
+
--color-steel: #8a8069;
|
|
3455
|
+
--color-stone: #6b6250;
|
|
3456
|
+
--color-muted: #1f1d17;
|
|
3457
|
+
--color-muted-foreground: #a89d86;
|
|
3458
|
+
|
|
3459
|
+
--color-hairline: #322c1d;
|
|
3460
|
+
--color-hairline-soft: #241f15;
|
|
3461
|
+
--color-hairline-strong: #4a4129;
|
|
3462
|
+
--color-border: #322c1d;
|
|
3463
|
+
|
|
3464
|
+
/* flame brightened as text; fill a bright flame with DARK ink labels (~6.8:1) */
|
|
3465
|
+
--color-primary: #ff8a4c;
|
|
3466
|
+
--color-primary-foreground: #2a1205;
|
|
3467
|
+
--color-primary-pressed: #d2530a;
|
|
3468
|
+
--color-primary-fill: #ff7a38;
|
|
3469
|
+
|
|
3470
|
+
--color-tint-peach: #33240f;
|
|
3471
|
+
--color-tint-rose: #321c16;
|
|
3472
|
+
--color-tint-mint: #23271a;
|
|
3473
|
+
--color-tint-lavender: #272318;
|
|
3474
|
+
--color-tint-sky: #22251a;
|
|
3475
|
+
--color-tint-yellow: #332b12;
|
|
3476
|
+
|
|
3477
|
+
--color-accent: #ffa110; /* Mistral Sunshine-700 amber on dark */
|
|
3478
|
+
--color-accent-orange: #fb923c;
|
|
3479
|
+
--color-accent-teal: #2dd4bf;
|
|
3480
|
+
--color-accent-green: #34d27a;
|
|
3481
|
+
|
|
3482
|
+
--color-success: #34d27a;
|
|
3483
|
+
--color-warning: #fbab4c;
|
|
3484
|
+
--color-destructive: #e0586a;
|
|
3485
|
+
--color-destructive-foreground: #15120d; /* dark ink on the bright danger fill */
|
|
3486
|
+
--color-info: #6fb3ff;
|
|
3487
|
+
|
|
3488
|
+
--color-link: #ff9a5c; /* warm orange link on dark (Mistral) */
|
|
3489
|
+
--color-link-pressed: #ffb37e;
|
|
3490
|
+
|
|
3491
|
+
/* sidebar hover — one shade lighter than the warm-dark surface */
|
|
3492
|
+
--el-sidebar-item-bg-hover: #292619;
|
|
3493
|
+
}
|
|
3494
|
+
|
|
3495
|
+
/* ── Palette: Garnet (data-palette="garnet") — Subtask 8.8.34 (MOTIR-1252) ───
|
|
3496
|
+
PINTEREST's whole palette: the Pushpin RED over Pinterest's neutral greys.
|
|
3497
|
+
Transcribed from Pinterest's documented brand + Gestalt design system: Pushpin
|
|
3498
|
+
red #E60023 (primary, deepened to #c30021 for white-on AA) / darker #BD081C,
|
|
3499
|
+
Cod-Gray #111111 ink, greys #767676 / #EFEFEF, Gestalt "Skycicle" blue for
|
|
3500
|
+
links/info. The warm red primary makes this a "warm" palette; Pinterest's
|
|
3501
|
+
neutrals are true-neutral greys (the whole-palette directive). The danger hue
|
|
3502
|
+
is kept a distinct orange-red so it never blurs into the red brand.
|
|
3503
|
+
|
|
3504
|
+
Re-skins by overriding the Tier-0 --color-* SOURCE; ONLY colour tokens, never
|
|
3505
|
+
a shape/feel token (paletteRegistry.test.ts enforces it). The deep-red FILL
|
|
3506
|
+
carries WHITE labels at AA in both themes. Every --el-* role clears WCAG AA in
|
|
3507
|
+
BOTH themes, verified numerically. See docs/palettes/garnet.md. */
|
|
3508
|
+
[data-palette='garnet'] {
|
|
3509
|
+
/* brand & primary — Pinterest Pushpin red, deepened for white-on AA */
|
|
3510
|
+
--color-primary: #c30021; /* Pushpin red as text/icon on a surface (AA ~6.3:1 on white) */
|
|
3511
|
+
--color-primary-foreground: #ffffff;
|
|
3512
|
+
--color-primary-pressed: #9e0419; /* darker (Pinterest #BD081C family) */
|
|
3513
|
+
--color-primary-fill: #c30021; /* deep Pushpin red — white-on AA ~6.3:1 */
|
|
3514
|
+
|
|
3515
|
+
/* surfaces — white canvas over Pinterest neutral greys */
|
|
3516
|
+
--color-background: #ffffff;
|
|
3517
|
+
--color-foreground: #111111; /* Pinterest "Cod Gray" ink */
|
|
3518
|
+
--color-surface: #efefef; /* Pinterest light-gray section background */
|
|
3519
|
+
--color-surface-soft: #f7f7f7; /* quieter neutral division */
|
|
3520
|
+
|
|
3521
|
+
/* text scale — Pinterest neutral-grey hierarchy */
|
|
3522
|
+
--color-ink: #111111;
|
|
3523
|
+
--color-charcoal: #1a1a1a; /* emphasis / AA-safe text on tints */
|
|
3524
|
+
--color-slate: #444444; /* secondary text */
|
|
3525
|
+
--color-steel: #5f5f5f; /* tertiary / footer */
|
|
3526
|
+
--color-stone: #767676; /* Pinterest grey — faint labels */
|
|
3527
|
+
--color-muted: #efefef;
|
|
3528
|
+
--color-muted-foreground: #5f5f5f; /* captions — AA on white + surfaces */
|
|
3529
|
+
|
|
3530
|
+
/* borders — neutral hairlines */
|
|
3531
|
+
--color-hairline: #e0e0e0;
|
|
3532
|
+
--color-hairline-soft: #ececec;
|
|
3533
|
+
--color-hairline-strong: #cdcdcd;
|
|
3534
|
+
--color-border: #e0e0e0;
|
|
3535
|
+
|
|
3536
|
+
/* accent secondary — a warm magenta-plum highlight (epic & design hue),
|
|
3537
|
+
distinct from both the Pushpin red primary and the orange-red danger */
|
|
3538
|
+
--color-accent: #9c2d7f;
|
|
3539
|
+
--color-accent-orange: #c2410c;
|
|
3540
|
+
--color-accent-teal: #0e8f86;
|
|
3541
|
+
--color-accent-green: #0a7d4f;
|
|
3542
|
+
|
|
3543
|
+
/* card tints — a Pinterest-red rose wash + clean feature hues */
|
|
3544
|
+
--color-tint-peach: #ffe2d4;
|
|
3545
|
+
--color-tint-rose: #ffe0e4; /* Pushpin-red rose wash */
|
|
3546
|
+
--color-tint-mint: #d9efe1;
|
|
3547
|
+
--color-tint-lavender: #ece0f2;
|
|
3548
|
+
--color-tint-sky: #d8e8fb;
|
|
3549
|
+
--color-tint-yellow: #faedc8;
|
|
3550
|
+
|
|
3551
|
+
/* semantic — danger held distinct (orange-red) from the red brand */
|
|
3552
|
+
--color-success: #0a7d4f;
|
|
3553
|
+
--color-warning: #b45309;
|
|
3554
|
+
--color-destructive: #e0300f;
|
|
3555
|
+
--color-destructive-foreground: #ffffff;
|
|
3556
|
+
--color-info: #0a66c2; /* Gestalt "Skycicle" blue family */
|
|
3557
|
+
|
|
3558
|
+
/* links — Gestalt Skycicle blue, distinct from the red primary */
|
|
3559
|
+
--color-link: #0a66c2;
|
|
3560
|
+
--color-link-pressed: #084e95;
|
|
3561
|
+
|
|
3562
|
+
/* sidebar hover — one shade darker than the neutral surface */
|
|
3563
|
+
--el-sidebar-item-bg-hover: #e6e6e6;
|
|
3564
|
+
}
|
|
3565
|
+
|
|
3566
|
+
/* Garnet — dark companion. Pinterest's Cod-Gray near-black canvas; the red
|
|
3567
|
+
brightens to read as text, the CTA fill stays the deep Pushpin red carrying
|
|
3568
|
+
WHITE labels, semantics brighten to clear AA. */
|
|
3569
|
+
[data-palette='garnet'][data-theme='dark'] {
|
|
3570
|
+
--color-background: #0e0e0e; /* Cod-Gray near-black canvas */
|
|
3571
|
+
--color-foreground: #efefef;
|
|
3572
|
+
--color-surface: #1a1a1a;
|
|
3573
|
+
--color-surface-soft: #161616;
|
|
3574
|
+
|
|
3575
|
+
--color-ink: #efefef;
|
|
3576
|
+
--color-charcoal: #e0e0e0;
|
|
3577
|
+
--color-slate: #b5b5b5;
|
|
3578
|
+
--color-steel: #8c8c8c;
|
|
3579
|
+
--color-stone: #6e6e6e;
|
|
3580
|
+
--color-muted: #1a1a1a;
|
|
3581
|
+
--color-muted-foreground: #9a9a9a;
|
|
3582
|
+
|
|
3583
|
+
--color-hairline: #2e2e2e;
|
|
3584
|
+
--color-hairline-soft: #1f1f1f;
|
|
3585
|
+
--color-hairline-strong: #444444;
|
|
3586
|
+
--color-border: #2e2e2e;
|
|
3587
|
+
|
|
3588
|
+
/* red brightened as text on the dark canvas; fill stays deep Pushpin red with
|
|
3589
|
+
WHITE labels (AA ~6.3:1) */
|
|
3590
|
+
--color-primary: #ff5a76;
|
|
3591
|
+
--color-primary-foreground: #ffffff;
|
|
3592
|
+
--color-primary-pressed: #9e0419;
|
|
3593
|
+
--color-primary-fill: #c30021;
|
|
3594
|
+
|
|
3595
|
+
--color-tint-peach: #33231a;
|
|
3596
|
+
--color-tint-rose: #33161a;
|
|
3597
|
+
--color-tint-mint: #15281e;
|
|
3598
|
+
--color-tint-lavender: #271d33;
|
|
3599
|
+
--color-tint-sky: #14233a;
|
|
3600
|
+
--color-tint-yellow: #2e2613;
|
|
3601
|
+
|
|
3602
|
+
--color-accent: #e35bb8; /* bright magenta-plum highlight on dark */
|
|
3603
|
+
--color-accent-orange: #fb923c;
|
|
3604
|
+
--color-accent-teal: #2dd4bf;
|
|
3605
|
+
--color-accent-green: #34d27a;
|
|
3606
|
+
|
|
3607
|
+
--color-success: #2ec27e;
|
|
3608
|
+
--color-warning: #fbab4c;
|
|
3609
|
+
--color-destructive: #ff6b6b;
|
|
3610
|
+
--color-destructive-foreground: #0e0e0e; /* dark ink on the bright danger fill */
|
|
3611
|
+
--color-info: #5aa0ff;
|
|
3612
|
+
|
|
3613
|
+
--color-link: #5aa0ff;
|
|
3614
|
+
--color-link-pressed: #8cc0ff;
|
|
3615
|
+
|
|
3616
|
+
/* sidebar hover — one shade lighter than the dark surface */
|
|
3617
|
+
--el-sidebar-item-bg-hover: #232323;
|
|
3618
|
+
}
|
|
3619
|
+
|
|
3620
|
+
/* ── Palette: Citrine (data-palette="citrine") — Subtask 8.8.34 (MOTIR-1252) ──
|
|
3621
|
+
MIRO's whole palette, transcribed verbatim from its Mirotone design system
|
|
3622
|
+
(npm mirotone@5.3.2). EVERY value here is a documented Mirotone primitive
|
|
3623
|
+
step or Miro brand token — nothing is invented or hand-darkened; where a role
|
|
3624
|
+
needs a darker shade for AA, a documented deeper RAMP STEP is used (e.g.
|
|
3625
|
+
yellow-700 #746019 for on-surface gold text). Miro is a 5-hue system
|
|
3626
|
+
(yellow / blue / green / red / gray): the warm Sunglow YELLOW (#ffd02f =
|
|
3627
|
+
yellow-500, the brand/logo colour) is the primary; the action-BLUE #3859ff
|
|
3628
|
+
(blue-500) is the accent; ink is Stratos #050038; semantics are green-500
|
|
3629
|
+
#1c8f00 / red-500 #d8182c. The warm yellow primary is what makes this a "warm"
|
|
3630
|
+
palette; the neutrals are Mirotone's own cool gray ramp.
|
|
3631
|
+
|
|
3632
|
+
⚠️ THE YELLOW TRAP: the Sunglow FILL cannot carry white at AA, so the
|
|
3633
|
+
accent-text (--color-primary-foreground) is Miro's Stratos ink #050038, not
|
|
3634
|
+
#fff (dark-on-Sunglow ~13.5:1) — Miro's own dark-on-yellow treatment.
|
|
3635
|
+
|
|
3636
|
+
Re-skins by overriding the Tier-0 --color-* SOURCE; ONLY colour tokens, never
|
|
3637
|
+
a shape/feel token (paletteRegistry.test.ts enforces it). Every --el-* role
|
|
3638
|
+
clears WCAG AA in BOTH themes, verified numerically. See
|
|
3639
|
+
docs/palettes/citrine.md. */
|
|
3640
|
+
[data-palette='citrine'] {
|
|
3641
|
+
/* brand & primary — Miro Sunglow yellow (yellow-500). FILL carries DARK
|
|
3642
|
+
Stratos-ink labels (yellow trap); --color-primary is a documented dark
|
|
3643
|
+
yellow step (yellow-700) used AS text/icon on a surface. */
|
|
3644
|
+
--color-primary: #746019; /* Mirotone yellow-700 — gold text/icon (AA ~6.1:1 on white) */
|
|
3645
|
+
--color-primary-foreground: #050038; /* Miro Stratos ink on the Sunglow fill (AA ~13.5:1) */
|
|
3646
|
+
--color-primary-pressed: #d7b029; /* Mirotone yellow-550 */
|
|
3647
|
+
--color-primary-fill: #ffd02f; /* Miro Sunglow — yellow-500, the brand/logo colour */
|
|
3648
|
+
|
|
3649
|
+
/* surfaces — white over Mirotone gray-50/100 (documented) */
|
|
3650
|
+
--color-background: #ffffff;
|
|
3651
|
+
--color-foreground: #050038; /* Miro Stratos navy ink */
|
|
3652
|
+
--color-surface: #f3f4f6; /* Mirotone gray-100 section background */
|
|
3653
|
+
--color-surface-soft: #f6f7f9; /* Mirotone gray-50 — quieter division */
|
|
3654
|
+
|
|
3655
|
+
/* text scale — Mirotone gray ramp */
|
|
3656
|
+
--color-ink: #050038;
|
|
3657
|
+
--color-charcoal: #1a1b1e; /* gray-900 — emphasis / AA-safe on tints */
|
|
3658
|
+
--color-slate: #4a4e5e; /* gray-600 — secondary text */
|
|
3659
|
+
--color-steel: #656b81; /* gray-500 — tertiary / footer */
|
|
3660
|
+
--color-stone: #7f8497; /* gray-450 — faint labels */
|
|
3661
|
+
--color-muted: #f3f4f6;
|
|
3662
|
+
--color-muted-foreground: #575c6f; /* gray-550 — captions, AA on white + surfaces */
|
|
3663
|
+
|
|
3664
|
+
/* borders — Mirotone gray hairlines */
|
|
3665
|
+
--color-hairline: #e0e1e6; /* gray-200 */
|
|
3666
|
+
--color-hairline-soft: #ebecf0; /* gray-150 */
|
|
3667
|
+
--color-hairline-strong: #c1c3cd; /* gray-300 */
|
|
3668
|
+
--color-border: #e0e1e6;
|
|
3669
|
+
|
|
3670
|
+
/* accent secondary — Miro's action-BLUE (blue-500) as the epic/design hue;
|
|
3671
|
+
the deep-blue + dark-gold below cover the other type hues with documented
|
|
3672
|
+
Mirotone steps (Miro has no orange/teal primitive) */
|
|
3673
|
+
--color-accent: #3859ff; /* Mirotone blue-500 — Miro action blue */
|
|
3674
|
+
--color-accent-orange: #91771e; /* yellow-650 (Miro has no orange ramp) */
|
|
3675
|
+
--color-accent-teal: #243797; /* blue-650 (Miro has no teal ramp) */
|
|
3676
|
+
--color-accent-green: #1c8f00; /* green-500 */
|
|
3677
|
+
|
|
3678
|
+
/* card tints — Mirotone light ramp steps (charcoal text clears AA ≥13:1) */
|
|
3679
|
+
--color-tint-peach: #ffefb9; /* yellow-250 */
|
|
3680
|
+
--color-tint-rose: #fbe6e8; /* red-150 */
|
|
3681
|
+
--color-tint-mint: #dff1da; /* green-150 */
|
|
3682
|
+
--color-tint-lavender: #d9dffc; /* blue-200 */
|
|
3683
|
+
--color-tint-sky: #f2f4fc; /* blue-100 */
|
|
3684
|
+
--color-tint-yellow: #fff7d9; /* yellow-150 */
|
|
3685
|
+
|
|
3686
|
+
/* semantic — Mirotone green/red/blue ramps (Miro maps these by convention) */
|
|
3687
|
+
--color-success: #1c8f00; /* green-500 */
|
|
3688
|
+
--color-warning: #91771e; /* yellow-650 (AA-safe documented gold) */
|
|
3689
|
+
--color-destructive: #d8182c; /* red-500 */
|
|
3690
|
+
--color-destructive-foreground: #ffffff;
|
|
3691
|
+
--color-info: #3859ff; /* blue-500 */
|
|
3692
|
+
|
|
3693
|
+
/* links — Miro action blue (blue-500 → pressed blue-550) */
|
|
3694
|
+
--color-link: #3859ff;
|
|
3695
|
+
--color-link-pressed: #314cd9;
|
|
3696
|
+
|
|
3697
|
+
/* sidebar hover — Mirotone gray-150, one step off the surface */
|
|
3698
|
+
--el-sidebar-item-bg-hover: #ebecf0;
|
|
3699
|
+
}
|
|
3700
|
+
|
|
3701
|
+
/* Citrine — dark companion. A Mirotone gray-ramp near-black canvas (black
|
|
3702
|
+
#090909 / gray-900 #1a1b1e); the Sunglow yellow brightens to read as text and
|
|
3703
|
+
the fill keeps dark Stratos labels; blue/green/red brighten one ramp step to
|
|
3704
|
+
clear AA on the dark canvas. All values are documented Mirotone steps. */
|
|
3705
|
+
[data-palette='citrine'][data-theme='dark'] {
|
|
3706
|
+
--color-background: #090909; /* Miro black */
|
|
3707
|
+
--color-foreground: #f6f7f9; /* gray-50 */
|
|
3708
|
+
--color-surface: #1a1b1e; /* gray-900 */
|
|
3709
|
+
--color-surface-soft: #18191c; /* gray-950 */
|
|
3710
|
+
|
|
3711
|
+
--color-ink: #f6f7f9;
|
|
3712
|
+
--color-charcoal: #ebecf0; /* gray-150 */
|
|
3713
|
+
--color-slate: #c1c3cd; /* gray-300 */
|
|
3714
|
+
--color-steel: #979cab; /* gray-400 */
|
|
3715
|
+
--color-stone: #656b81; /* gray-500 */
|
|
3716
|
+
--color-muted: #1a1b1e;
|
|
3717
|
+
--color-muted-foreground: #adb1bd; /* gray-350 */
|
|
3718
|
+
|
|
3719
|
+
--color-hairline: #2b2e35; /* gray-750 */
|
|
3720
|
+
--color-hairline-soft: #24262c; /* gray-800 */
|
|
3721
|
+
--color-hairline-strong: #3f424f; /* gray-650 */
|
|
3722
|
+
--color-border: #2b2e35;
|
|
3723
|
+
|
|
3724
|
+
/* Sunglow reads as text on black; fill stays Sunglow with dark Stratos labels */
|
|
3725
|
+
--color-primary: #ffd02f; /* yellow-500 */
|
|
3726
|
+
--color-primary-foreground: #050038; /* Stratos ink on the Sunglow fill */
|
|
3727
|
+
--color-primary-pressed: #d7b029; /* yellow-550 */
|
|
3728
|
+
--color-primary-fill: #ffd02f;
|
|
3729
|
+
|
|
3730
|
+
/* tints — Mirotone deep ramp steps for the dark feature washes */
|
|
3731
|
+
--color-tint-peach: #342c0f; /* yellow-850 */
|
|
3732
|
+
--color-tint-rose: #2f1719; /* red-900 */
|
|
3733
|
+
--color-tint-mint: #11260c; /* green-900 */
|
|
3734
|
+
--color-tint-lavender: #12193e; /* blue-850 */
|
|
3735
|
+
--color-tint-sky: #101633; /* blue-900 */
|
|
3736
|
+
--color-tint-yellow: #28220d; /* yellow-900 */
|
|
3737
|
+
|
|
3738
|
+
--color-accent: #5b76fe; /* blue-450 — brighter Miro blue on dark */
|
|
3739
|
+
--color-accent-orange: #b39223; /* yellow-600 */
|
|
3740
|
+
--color-accent-teal: #7a90fe; /* blue-400 */
|
|
3741
|
+
--color-accent-green: #65b452; /* green-400 */
|
|
3742
|
+
|
|
3743
|
+
--color-success: #65b452; /* green-400 */
|
|
3744
|
+
--color-warning: #ffd850; /* yellow-450 */
|
|
3745
|
+
--color-destructive: #e56673; /* red-400 */
|
|
3746
|
+
--color-destructive-foreground: #090909; /* dark ink on the bright danger */
|
|
3747
|
+
--color-info: #7a90fe; /* blue-400 */
|
|
3748
|
+
|
|
3749
|
+
--color-link: #7a90fe;
|
|
3750
|
+
--color-link-pressed: #97a8fe; /* blue-350 */
|
|
3751
|
+
|
|
3752
|
+
/* sidebar hover — Mirotone gray-800, one step lighter than the surface */
|
|
3753
|
+
--el-sidebar-item-bg-hover: #24262c;
|
|
3754
|
+
}
|
|
3755
|
+
|
|
3756
|
+
/* ── Palette: Candy (data-palette="candy") — Subtask 8.8.34 (MOTIR-1252) ──────
|
|
3757
|
+
A sweet, playful PASTEL-CANDY re-skin led by a LIGHT bubblegum PINK. There is
|
|
3758
|
+
no single brand for a "candy" concept, so — to avoid inventing colours — EVERY value
|
|
3759
|
+
is a documented **Radix Colors** step (@radix-ui/colors): Pink is the primary
|
|
3760
|
+
ramp, Mauve the matched warm neutral, and Sky / Jade / Violet / Amber / Crimson
|
|
3761
|
+
supply the candy rainbow of tints + type hues. Glossier Pink #F5DADF is the
|
|
3762
|
+
pastel-pink MOOD reference; the actual values are Radix.
|
|
3763
|
+
|
|
3764
|
+
⚠️ THE LIGHT-PINK TRAP: a light pink FILL cannot carry white text, so the CTA
|
|
3765
|
+
is a light pink (Radix Pink-6 #efbfdd) with DARK berry-plum labels (Pink-12
|
|
3766
|
+
#651249, ~7.6:1) — the same dark-on-light pattern Amber/Citrine use for gold.
|
|
3767
|
+
--color-primary (pink AS text/icon on a surface) is the AA "text" step
|
|
3768
|
+
Pink-11 #c2298a.
|
|
3769
|
+
|
|
3770
|
+
Re-skins by overriding the Tier-0 --color-* SOURCE; ONLY colour tokens, never a
|
|
3771
|
+
shape/feel token (paletteRegistry.test.ts enforces it). Every --el-* role
|
|
3772
|
+
clears WCAG AA in BOTH themes, verified numerically. See
|
|
3773
|
+
docs/palettes/candy.md. */
|
|
3774
|
+
[data-palette='candy'] {
|
|
3775
|
+
/* brand & primary — a LIGHT bubblegum pink (Radix Pink). FILL is a light pink
|
|
3776
|
+
with DARK berry labels; --color-primary is the Pink-11 text step. */
|
|
3777
|
+
--color-primary: #c2298a; /* Radix Pink-11 — pink text/icon (AA ~5.2:1 on white) */
|
|
3778
|
+
--color-primary-foreground: #651249; /* Radix Pink-12 berry ink on the light-pink fill (~7.6:1) */
|
|
3779
|
+
--color-primary-pressed: #e7acd0; /* Radix Pink-7 */
|
|
3780
|
+
--color-primary-fill: #efbfdd; /* Radix Pink-6 — the light bubblegum pink */
|
|
3781
|
+
|
|
3782
|
+
/* surfaces — candy-paper pinks (Radix Pink-1/2/3) */
|
|
3783
|
+
--color-background: #fffcfe; /* Radix Pink-1 — barely-pink candy white */
|
|
3784
|
+
--color-foreground: #211f26; /* Radix Mauve-12 ink */
|
|
3785
|
+
--color-surface: #fee9f5; /* Radix Pink-3 — candy-pink section */
|
|
3786
|
+
--color-surface-soft: #fef7fb; /* Radix Pink-2 — quieter candy wash */
|
|
3787
|
+
|
|
3788
|
+
/* text scale — Radix Mauve neutral + a Pink-12 berry emphasis */
|
|
3789
|
+
--color-ink: #211f26;
|
|
3790
|
+
--color-charcoal: #651249; /* Pink-12 berry — emphasis / AA-safe text on tints */
|
|
3791
|
+
--color-slate: #65636d; /* Mauve-11 — secondary text */
|
|
3792
|
+
--color-steel: #65636d; /* Mauve-11 — tertiary (Radix Mauve has one AA text grey) */
|
|
3793
|
+
--color-stone: #8e8c99; /* Mauve-9 — faint labels (decorative) */
|
|
3794
|
+
--color-muted: #fee9f5;
|
|
3795
|
+
--color-muted-foreground: #65636d; /* Mauve-11 — captions, AA on white + surfaces */
|
|
3796
|
+
|
|
3797
|
+
/* borders — soft candy-pink hairlines (Radix Pink-5/4/6) */
|
|
3798
|
+
--color-hairline: #f6cee7; /* Pink-5 */
|
|
3799
|
+
--color-hairline-soft: #fbdcef; /* Pink-4 */
|
|
3800
|
+
--color-hairline-strong: #efbfdd; /* Pink-6 */
|
|
3801
|
+
--color-border: #f6cee7;
|
|
3802
|
+
|
|
3803
|
+
/* accent secondary — a candy berry-plum highlight (Radix Plum-9), distinct
|
|
3804
|
+
from the light-pink primary; the candy rainbow covers the type hues */
|
|
3805
|
+
--color-accent: #ab4aba; /* Radix Plum-9 — berry highlight / epic & design hue */
|
|
3806
|
+
--color-accent-orange: #ab6400; /* Radix Amber-11 — candy honey */
|
|
3807
|
+
--color-accent-teal: #208368; /* Radix Jade-11 — candy mint */
|
|
3808
|
+
--color-accent-green: #2a7e3b; /* Radix Grass-11 — candy green */
|
|
3809
|
+
|
|
3810
|
+
/* card tints — Radix step-4 pastels: the candy rainbow (charcoal text AA ≥9:1) */
|
|
3811
|
+
--color-tint-peach: #fbdcef; /* Pink-4 */
|
|
3812
|
+
--color-tint-rose: #fedce7; /* Crimson-4 */
|
|
3813
|
+
--color-tint-mint: #d6f1e3; /* Jade-4 */
|
|
3814
|
+
--color-tint-lavender: #ebe4ff; /* Violet-4 */
|
|
3815
|
+
--color-tint-sky: #d1f0fa; /* Sky-4 */
|
|
3816
|
+
--color-tint-yellow: #ffee9c; /* Amber-4 */
|
|
3817
|
+
|
|
3818
|
+
/* semantic — Radix candy "text" steps (AA-safe) */
|
|
3819
|
+
--color-success: #2a7e3b; /* Grass-11 */
|
|
3820
|
+
--color-warning: #ab6400; /* Amber-11 */
|
|
3821
|
+
--color-destructive: #cb1d63; /* Crimson-11 (white-on AA) */
|
|
3822
|
+
--color-destructive-foreground: #ffffff;
|
|
3823
|
+
--color-info: #00749e; /* Sky-11 */
|
|
3824
|
+
|
|
3825
|
+
/* links — Radix Sky-11 candy blue, distinct from the pink primary */
|
|
3826
|
+
--color-link: #00749e;
|
|
3827
|
+
--color-link-pressed: #1d3e56; /* Sky-12 */
|
|
3828
|
+
|
|
3829
|
+
/* sidebar hover — one shade deeper than the candy-pink surface (Pink-4) */
|
|
3830
|
+
--el-sidebar-item-bg-hover: #fbdcef;
|
|
3831
|
+
}
|
|
3832
|
+
|
|
3833
|
+
/* Candy — dark companion. A pink-tinted near-black canvas (Radix Pink dark
|
|
3834
|
+
1/2/3); the bubblegum pink brightens to read as text, the fill is a bright
|
|
3835
|
+
candy pink with dark labels, and the rainbow tints become deep candy washes.
|
|
3836
|
+
All values are documented Radix dark steps. */
|
|
3837
|
+
[data-palette='candy'][data-theme='dark'] {
|
|
3838
|
+
--color-background: #191117; /* Radix Pink dark-1 — pink-tinted black */
|
|
3839
|
+
--color-foreground: #eeeef0; /* Mauve dark-12 */
|
|
3840
|
+
--color-surface: #37172f; /* Pink dark-3 — candy-plum section */
|
|
3841
|
+
--color-surface-soft: #21121d; /* Pink dark-2 — quieter division */
|
|
3842
|
+
|
|
3843
|
+
--color-ink: #eeeef0;
|
|
3844
|
+
--color-charcoal: #fdd1ea; /* Pink dark-12 — pink emphasis / AA on dark tints */
|
|
3845
|
+
--color-slate: #b5b2bc; /* Mauve dark-11 — secondary */
|
|
3846
|
+
--color-steel: #b5b2bc; /* Mauve dark-11 — tertiary */
|
|
3847
|
+
--color-stone: #6f6d78; /* Mauve dark-9 — faint */
|
|
3848
|
+
--color-muted: #37172f;
|
|
3849
|
+
--color-muted-foreground: #b5b2bc;
|
|
3850
|
+
|
|
3851
|
+
--color-hairline: #692955; /* Pink dark-6 */
|
|
3852
|
+
--color-hairline-soft: #591c47; /* Pink dark-5 */
|
|
3853
|
+
--color-hairline-strong: #833869; /* Pink dark-7 */
|
|
3854
|
+
--color-border: #692955;
|
|
3855
|
+
|
|
3856
|
+
/* bubblegum pink brightened as text; fill a bright candy pink with dark labels */
|
|
3857
|
+
--color-primary: #ff8dcc; /* Pink dark-11 */
|
|
3858
|
+
--color-primary-foreground: #21121d; /* Pink dark-2 ink on the bright pink fill */
|
|
3859
|
+
--color-primary-pressed: #de51a8; /* Pink dark-10 */
|
|
3860
|
+
--color-primary-fill: #ff8dcc;
|
|
3861
|
+
|
|
3862
|
+
/* deep candy washes (Radix dark step-4) */
|
|
3863
|
+
--color-tint-peach: #4b143d; /* Pink dark-4 */
|
|
3864
|
+
--color-tint-rose: #4d122f; /* Crimson dark-4 */
|
|
3865
|
+
--color-tint-mint: #0b3b2c; /* Jade dark-4 */
|
|
3866
|
+
--color-tint-lavender: #33255b; /* Violet dark-4 */
|
|
3867
|
+
--color-tint-sky: #113555; /* Sky dark-4 */
|
|
3868
|
+
--color-tint-yellow: #3f2700; /* Amber dark-4 */
|
|
3869
|
+
|
|
3870
|
+
--color-accent: #e796f3; /* Plum dark-11 — bright berry */
|
|
3871
|
+
--color-accent-orange: #ffca16; /* Amber dark-11 */
|
|
3872
|
+
--color-accent-teal: #1fd8a4; /* Jade dark-11 */
|
|
3873
|
+
--color-accent-green: #71d083; /* Grass dark-11 */
|
|
3874
|
+
|
|
3875
|
+
--color-success: #71d083; /* Grass dark-11 */
|
|
3876
|
+
--color-warning: #ffca16; /* Amber dark-11 */
|
|
3877
|
+
--color-destructive: #ff92ad; /* Crimson dark-11 */
|
|
3878
|
+
--color-destructive-foreground: #191114; /* dark ink on the bright danger */
|
|
3879
|
+
--color-info: #75c7f0; /* Sky dark-11 */
|
|
3880
|
+
|
|
3881
|
+
--color-link: #75c7f0;
|
|
3882
|
+
--color-link-pressed: #c2f3ff; /* Sky dark-12 */
|
|
3883
|
+
|
|
3884
|
+
/* sidebar hover — one shade lighter than the candy-plum surface (Pink dark-4) */
|
|
3885
|
+
--el-sidebar-item-bg-hover: #4b143d;
|
|
3886
|
+
}
|
|
3887
|
+
|
|
3888
|
+
[data-type='grotesk'] {
|
|
3889
|
+
/* Grotesk (7.3.54) — geometric / neo-grotesque headlines AND body/UI. Re-point
|
|
3890
|
+
BOTH the headline (serif) role AND the body/UI (sans) role at Space Grotesk
|
|
3891
|
+
(loaded in app/layout.tsx as --font-grotesk-source), so the WHOLE app (nav,
|
|
3892
|
+
header, buttons, content) re-types — Space Grotesk reads cleanly as a UI sans
|
|
3893
|
+
(Yue, 2026-06-19: the type axis must re-type the whole UI, chrome included).
|
|
3894
|
+
Meta stays JetBrains Mono (--font-mono). ONLY font tokens here (the
|
|
3895
|
+
disjointness guard): no colour (--el / --color) or shape token. */
|
|
3896
|
+
--font-serif: var(--font-grotesk-source), ui-sans-serif, system-ui, sans-serif;
|
|
3897
|
+
--font-sans: var(--font-grotesk-source), ui-sans-serif, system-ui, sans-serif;
|
|
3898
|
+
}
|
|
3899
|
+
|
|
3900
|
+
[data-type='editorial'] {
|
|
3901
|
+
/* Editorial (7.3.55) — a characterful display serif over the humanist sans
|
|
3902
|
+
body. Adds one new face: Fraunces, loaded in app/layout.tsx as
|
|
3903
|
+
--font-editorial-source. Re-point the headline (serif) role at it; body
|
|
3904
|
+
(Inter, --font-sans) and meta (JetBrains, --font-mono) keep the base roles,
|
|
3905
|
+
so the change is a real, visible FACE swap — magazine, considered — not a
|
|
3906
|
+
size tweak. ONLY --font-* role tokens here (the disjointness guard): no
|
|
3907
|
+
colour (--el/--color) or shape (--radius/--spacing/--shadow) token. */
|
|
3908
|
+
--font-serif: var(--font-editorial-source), 'Fraunces', Georgia, serif;
|
|
3909
|
+
}
|
|
3910
|
+
|
|
3911
|
+
[data-type='mono-technical'] {
|
|
3912
|
+
/* Mono-Technical (7.3.56) — a precise, developer-grade pairing. IBM Plex Mono
|
|
3913
|
+
dresses the headline role (--font-serif), the meta/code role (--font-mono)
|
|
3914
|
+
AND the body/UI role (--font-sans), so the WHOLE app (nav, header, buttons,
|
|
3915
|
+
content) re-types to one technical mono voice (Yue, 2026-06-19: the type axis
|
|
3916
|
+
must re-type the whole UI, chrome included). Sharing the technical mono
|
|
3917
|
+
everywhere is what distinguishes this from motir-mono (which uses the
|
|
3918
|
+
JetBrains mono stack). Only --font-* role tokens are touched — colour
|
|
3919
|
+
(--el/--color) and shape (--radius/--spacing) are the other two, independent
|
|
3920
|
+
axes (the disjointness guard). */
|
|
3921
|
+
--font-serif: var(--font-mono-technical-source, ui-monospace), 'SF Mono', Menlo, monospace;
|
|
3922
|
+
--font-mono: var(--font-mono-technical-source, ui-monospace), 'SF Mono', Menlo, monospace;
|
|
3923
|
+
--font-sans: var(--font-mono-technical-source, ui-monospace), 'SF Mono', Menlo, monospace;
|
|
3924
|
+
}
|