rastrillo / fiesta Public

Clone
git clone https://amadan.net/rastrillo/fiesta

Plain git — no account needed to clone.

Download

Download this file

1/* signal — graphite and one live cobalt. Structure lives in tokens.css;
2 this file is colour, type and shape.
3
4 ── Direction ─────────────────────────────────────────────────────────
5 THESIS: contrast is spent, not sprinkled. One cobalt marks the place a
6 decision is being made and nothing else borrows it. The arrangement
7 this refuses is the default product-dashboard palette — mid-grey text,
8 an accent tint smeared across every surface, and pillowy corners that
9 make a destructive button look like a suggestion.
10 OWN-WORLD: an anodised instrument faceplate. Cool graphite neutrals
11 with a blue undertone, silk-screened labels, one cobalt indicator that
12 reads as powered. Corners are cut, not moulded — 4px and 2px, the
13 radius of a milled edge. Shadows are short and dense: a panel seated
14 on a panel, never floating over it. Type is grotesk-first, the
15 workshop label face rather than a display serif.
16 MODE: operate. Assertive is not the same as loud — the page is calm
17 graphite until something is actionable, and then it is unmistakable.
18 RISK, stated: near-black plus one bright accent is a well-worn look,
19 and the way this earns its own ground is restraint plus the shape
20 axis, not a brighter blue. If a screen ends up with cobalt in four
21 places, the theme is being used against its grain.
22 FINISH: every pair in both schemes is measured below and gated by
23 ui/contrast_test.go; the direction above is the contract the palette
24 is audited against.
25
26 ── One block, both schemes ───────────────────────────────────────────
27 Every token is declared once, as light-dark(<light>, <dark>), under a
28 single :root that sets color-scheme: light dark. The two rules at the
29 bottom are the whole toggle: setting color-scheme on
30 :root[data-theme=…] re-resolves every light-dark() in the file at
31 once, in both directions. Tokens with one value in both schemes — the
32 font stack, the radii — are written plain.
33
34 light-dark() is a colour function: it may only stand where a <color>
35 is expected. So a shadow token wraps its colour in it and writes the
36 geometry once, outside the call — `0 4px 10px light-dark(a, b)`, never
37 `light-dark(0 4px 10px a, …)`, which no browser would accept as a
38 box-shadow.
39
40 ── WCAG 2.2 AA, measured ─────────────────────────────────────────────
41 Normal text needs 4.5:1; large text and UI component boundaries need
42 3:1. Every pair below was computed from these exact hex values, in
43 both schemes. If you change a token, recompute its rows.
44
45 LIGHT ratio requirement
46 --rst-text on --rst-surface 19.32 4.5:1 text
47 --rst-text on --rst-bg 17.72 4.5:1 text
48 --rst-text on --rst-surface-2 16.93 4.5:1 text
49 --rst-text on --rst-accent-soft 16.68 4.5:1 text
50 --rst-text-muted on --rst-surface 8.76 4.5:1 text
51 --rst-text-muted on --rst-bg 8.04 4.5:1 text
52 --rst-text-muted on --rst-surface-2 7.68 4.5:1 text
53 --rst-text-muted on --rst-accent-soft 7.57 4.5:1 text
54 --rst-text-faint on --rst-surface 6.13 4.5:1 text
55 --rst-text-faint on --rst-bg 5.62 4.5:1 text
56 --rst-text-faint on --rst-surface-2 5.37 4.5:1 text
57 --rst-text-faint on --rst-accent-soft 5.29 4.5:1 text
58 --rst-accent on --rst-surface 5.50 4.5:1 text + focus ring
59 --rst-accent on --rst-bg 5.04 4.5:1 text + focus ring
60 --rst-accent on --rst-surface-2 4.82 4.5:1 text
61 --rst-accent on --rst-accent-soft 4.75 4.5:1 text
62 --rst-on-accent on --rst-accent 5.50 4.5:1 button label
63 --rst-on-accent on --rst-accent-strong 7.77 4.5:1 button label, hover
64 --rst-line-strong on --rst-surface 4.70 3:1 control border
65 --rst-line-strong on --rst-bg 4.31 3:1 control border
66 --rst-line-strong on --rst-surface-2 4.12 3:1 control border
67 pill neutral fg on its own tint 8.36 4.5:1 text
68 pill positive fg on its own tint 6.07 4.5:1 text
69 pill warning fg on its own tint 6.21 4.5:1 text
70 pill negative fg on its own tint 6.00 4.5:1 text
71 --rst-on-accent on negative fg (fill) 7.58 4.5:1 danger button label
72 --rst-on-accent-fill on --rst-accent-fill 5.50 4.5:1 primary button label
73 --rst-on-accent-fill on --rst-accent-fill-hover 7.77 4.5:1 primary button label, hover
74 --rst-on-danger-fill on --rst-danger-fill 7.58 4.5:1 danger button label
75 --rst-on-danger-fill-hover on --rst-danger-fill-hover 7.58 4.5:1 danger button label, hover
76 --rst-text on --rst-row-tint-hover 16.93 4.5:1 list-row-action text, hovered
77 --rst-text on --rst-row-tint-press 16.93 4.5:1 list-row-action text, pressed
78 --rst-control-text-hover on --rst-control-face-hover 5.50 4.5:1 plain button label, hover
79 --rst-text-muted on --rst-control-face-hover 8.76 4.5:1 muted label on a hovered plain face
80 --rst-text on --rst-tint-hover 16.68 4.5:1 row text on the hover tint
81 --rst-text on --rst-tint-press 16.68 4.5:1 row text on the press tint
82 --rst-text-muted on --rst-tint-hover 7.57 4.5:1 muted cell on the hover tint
83 --rst-text-muted on --rst-tint-press 7.57 4.5:1 muted cell on the press tint
84 --rst-text-faint on --rst-tint-hover 5.29 4.5:1 faint cell on the hover tint
85 --rst-text-faint on --rst-tint-press 5.29 4.5:1 faint cell on the press tint
86 --rst-accent on --rst-tint-hover 4.75 4.5:1 a link on a hovered row
87 --rst-nav-current-fg on --rst-nav-current-bg 4.75 4.5:1 the current nav item
88 --rst-track-off on --rst-surface 4.70 3:1 switch off track (1.4.11 boundary)
89 --rst-track-off on --rst-bg 4.31 3:1 switch off track on the page
90 --rst-knob-shade on --rst-track-off 4.70 3:1 switch knob on the off track
91 --rst-knob-shade on --rst-accent-fill 5.50 3:1 switch knob on the on track
92
93 DARK ratio requirement
94 --rst-text on --rst-surface 15.05 4.5:1 text
95 --rst-text on --rst-bg 16.30 4.5:1 text
96 --rst-text on --rst-surface-2 15.59 4.5:1 text
97 --rst-text on --rst-accent-soft 14.47 4.5:1 text
98 --rst-text-muted on --rst-surface 7.85 4.5:1 text
99 --rst-text-muted on --rst-bg 8.51 4.5:1 text
100 --rst-text-muted on --rst-surface-2 8.14 4.5:1 text
101 --rst-text-muted on --rst-accent-soft 7.55 4.5:1 text
102 --rst-text-faint on --rst-surface 6.58 4.5:1 text
103 --rst-text-faint on --rst-bg 7.13 4.5:1 text
104 --rst-text-faint on --rst-surface-2 6.82 4.5:1 text
105 --rst-text-faint on --rst-accent-soft 6.33 4.5:1 text
106 --rst-accent on --rst-surface 6.06 4.5:1 text + focus ring
107 --rst-accent on --rst-bg 6.56 4.5:1 text + focus ring
108 --rst-accent on --rst-surface-2 6.28 4.5:1 text
109 --rst-accent on --rst-accent-soft 5.82 4.5:1 text
110 --rst-on-accent on --rst-accent 6.79 4.5:1 button label
111 --rst-on-accent on --rst-accent-strong 9.77 4.5:1 button label, hover
112 --rst-line-strong on --rst-surface 4.65 3:1 control border
113 --rst-line-strong on --rst-bg 5.03 3:1 control border
114 --rst-line-strong on --rst-surface-2 4.81 3:1 control border
115 pill neutral fg on its own tint 9.26 4.5:1 text
116 pill positive fg on its own tint 9.07 4.5:1 text
117 pill warning fg on its own tint 10.00 4.5:1 text
118 pill negative fg on its own tint 7.84 4.5:1 text
119 --rst-on-accent on negative fg (fill) 9.25 4.5:1 danger button label
120 --rst-on-accent-fill on --rst-accent-fill 6.79 4.5:1 primary button label
121 --rst-on-accent-fill on --rst-accent-fill-hover 9.77 4.5:1 primary button label, hover
122 --rst-on-danger-fill on --rst-danger-fill 9.25 4.5:1 danger button label
123 --rst-on-danger-fill-hover on --rst-danger-fill-hover 9.25 4.5:1 danger button label, hover
124 --rst-text on --rst-row-tint-hover 15.59 4.5:1 list-row-action text, hovered
125 --rst-text on --rst-row-tint-press 15.59 4.5:1 list-row-action text, pressed
126 --rst-control-text-hover on --rst-control-face-hover 6.06 4.5:1 plain button label, hover
127 --rst-text-muted on --rst-control-face-hover 7.85 4.5:1 muted label on a hovered plain face
128 --rst-text on --rst-tint-hover 14.47 4.5:1 row text on the hover tint
129 --rst-text on --rst-tint-press 14.47 4.5:1 row text on the press tint
130 --rst-text-muted on --rst-tint-hover 7.55 4.5:1 muted cell on the hover tint
131 --rst-text-muted on --rst-tint-press 7.55 4.5:1 muted cell on the press tint
132 --rst-text-faint on --rst-tint-hover 6.33 4.5:1 faint cell on the hover tint
133 --rst-text-faint on --rst-tint-press 6.33 4.5:1 faint cell on the press tint
134 --rst-accent on --rst-tint-hover 5.82 4.5:1 a link on a hovered row
135 --rst-nav-current-fg on --rst-nav-current-bg 5.82 4.5:1 the current nav item
136 --rst-track-off on --rst-surface 4.65 3:1 switch off track (1.4.11 boundary)
137 --rst-track-off on --rst-bg 5.03 3:1 switch off track on the page
138 --rst-knob-shade on --rst-track-off 4.65 3:1 switch knob on the off track
139 --rst-knob-shade on --rst-accent-fill 6.06 3:1 switch knob on the on track
140
141 The tightest pair in the set is --rst-accent on --rst-accent-soft in
142 light, at 4.75:1 against a 4.5:1 floor — the cobalt is as bright as it
143 can be and still be readable as text on its own tint. Lighten it and
144 the gate goes red; that is the intended tension.
145
146 --rst-line is a hairline divider, not a control border, so 1.4.11's
147 3:1 does not apply to it. Control borders use --rst-line-strong.
148
149 --rst-header-rule is not in the table either, and deliberately so
150 (design doc §6-v2.2): the page header's rule is decoration, not
151 structure — the heading's size, weight and spacing carry the
152 structure — so no contrast floor applies to it and it is not in
153 ui/contrast_test.go's pair table. Do not argue it up to 3:1.
154
155 --rst-shadow-pop, --rst-shadow-knob, --rst-shadow-lift and
156 --rst-overlay are not in the table: they paint a translucent layer
157 over whatever sits underneath, not text over a fixed background, so a
158 contrast ratio does not apply.
159 ──────────────────────────────────────────────────────────────────── */
160
161:root {
162 color-scheme: light dark;
163
164 /* Type: grotesk first, on purpose. The stack asks for the neutral
165 workhorse grotesks a machine actually has before it falls back to
166 whatever the platform calls its UI face. No webfont: a theme is one
167 stylesheet and must not open a connection. */
168 --rst-font: "Helvetica Neue", Helvetica, "Segoe UI Variable Text", "Segoe UI", Roboto, Arial, ui-sans-serif, system-ui, sans-serif;
169
170 /* Shape: milled, not moulded. */
171 --rst-radius: 4px;
172 --rst-radius-sm: 2px;
173 --rst-radius-pill: 999px;
174
175 /* Surfaces and lines: graphite with a blue undertone, so the cobalt
176 reads as the only warm-running thing on the panel. */
177 --rst-bg: light-dark(#f4f5f8, #0b0d10);
178 --rst-surface: light-dark(#ffffff, #14171c);
179 --rst-surface-2: light-dark(#eef0f4, #101318);
180 --rst-line: light-dark(#dde0e7, #232830);
181 --rst-line-strong: light-dark(#6d7482, #7b828f);
182
183 /* The page header's rule, derived rather than authored: the cobalt
184 folded into the line at 45%, so a theme that changes its accent
185 gets a matching rule with no second value to keep in step. signal
186 spends the tint it earns — the rule is visibly blue, which is the
187 loudest this theme is anywhere it is not asking for a click.
188 Decorative: it carries no contrast floor. */
189 --rst-header-rule: color-mix(in oklab, var(--rst-accent) 45%, var(--rst-line));
190
191 /* Text */
192 --rst-text: light-dark(#0c0e12, #e9ebef);
193 --rst-text-muted: light-dark(#454b57, #a4acb9);
194 --rst-text-faint: light-dark(#5b6270, #959dac);
195
196 /* Accent: one electric cobalt. It appears where a decision is made and
197 nowhere else. */
198 --rst-accent: light-dark(#1a56ff, #6d90ff);
199 --rst-accent-strong: light-dark(#0f3fd6, #9ab2ff);
200 --rst-accent-soft: light-dark(#e9eeff, #121a30);
201 --rst-on-accent: light-dark(#ffffff, #05070d);
202
203 /* Status tones: saturated and unambiguous. A pill is never the
204 accent, so an error can never read as a link. */
205 --rst-tone-neutral-fg: light-dark(#3b414d, #c6cdd8);
206 --rst-tone-neutral-bg: light-dark(#e6e8ee, #232830);
207 --rst-tone-positive-fg: light-dark(#08624a, #4fdba2);
208 --rst-tone-positive-bg: light-dark(#d6efe6, #082720);
209 --rst-tone-warning-fg: light-dark(#7a4708, #f3c669);
210 --rst-tone-warning-bg: light-dark(#f8e5c2, #2a2009);
211 --rst-tone-negative-fg: light-dark(#a8121f, #ff8f99);
212 --rst-tone-negative-bg: light-dark(#fbdee0, #2e1418);
213
214 /* Depth: short throw, tight blur, high alpha. Seated, not floating. */
215 --rst-shadow-pop: 0 4px 10px light-dark(rgba(12, 14, 18, 0.22), rgba(0, 0, 0, 0.62));
216 --rst-shadow-knob: 0 1px 2px light-dark(rgba(12, 14, 18, 0.45), rgba(0, 0, 0, 0.6));
217 --rst-shadow-knob-hover: var(--rst-shadow-knob);
218 --rst-shadow-lift: 0 1px 2px light-dark(rgba(12, 14, 18, 0.2), rgba(0, 0, 0, 0.5));
219 --rst-overlay: light-dark(rgba(9, 11, 16, 0.55), rgba(3, 4, 7, 0.72));
220
221 /* The depth axis, declared as the semantic tokens tokens.css read in
222 each place before the axis existed: the fills are the accent, the
223 faces the surface, the tints the soft accent, the linked rows the
224 second surface, the borders, the off track and the focused edge
225 the strong line, the rim the line, the danger fill the negative
226 tone; the highlights, shadows, sheen and press are transparent and
227 0. tokens.css reads every one of these, so a theme cannot leave
228 them out; it can only turn them off or point them back, which is
229 what this does, so this theme renders as it did, and an app's
230 override of --rst-accent, --rst-surface or --rst-line on :root
231 still reaches the controls it reached before (Astra A2). */
232 --rst-accent-fill: var(--rst-accent);
233 --rst-accent-fill-hover: var(--rst-accent-strong);
234 --rst-accent-fill-edge: var(--rst-accent);
235 --rst-accent-fill-edge-hover: var(--rst-accent-strong);
236 --rst-on-accent-fill: var(--rst-on-accent);
237 --rst-danger-fill: var(--rst-tone-negative-fg);
238 --rst-danger-fill-hover: var(--rst-tone-negative-fg);
239 --rst-danger-fill-edge: var(--rst-tone-negative-fg);
240 --rst-danger-fill-edge-hover: var(--rst-tone-negative-fg);
241 --rst-on-danger-fill: var(--rst-on-accent);
242 --rst-on-danger-fill-hover: var(--rst-on-accent);
243 --rst-danger-hover-filter: brightness(0.9);
244 --rst-control-face: var(--rst-surface);
245 --rst-control-face-hover: var(--rst-surface);
246 --rst-control-face-press: transparent;
247 --rst-control-border: var(--rst-line-strong);
248 --rst-control-border-hover: var(--rst-accent);
249 --rst-control-text-hover: var(--rst-accent);
250 --rst-ghost-edge: var(--rst-line-strong);
251 --rst-ghost-face-hover: transparent;
252 --rst-ghost-edge-hover: var(--rst-line-strong);
253 --rst-summary-face: transparent;
254 --rst-summary-face-hover: transparent;
255 --rst-summary-edge: transparent;
256 --rst-summary-edge-hover: transparent;
257 --rst-summary-radius: 0;
258 --rst-depth-hl: transparent;
259 --rst-depth-contact: transparent;
260 --rst-depth-ambient: transparent;
261 --rst-depth-face: 0;
262 --rst-press-drop: 0px;
263 --rst-knob: var(--rst-surface);
264 --rst-knob-shade: var(--rst-surface);
265 --rst-track-off: var(--rst-line-strong);
266 /* The switch's one size, under a mouse and a finger alike: today's. */
267 --rst-switch-w: 34px;
268 --rst-switch-h: 19px;
269 --rst-switch-knob: 15px;
270 --rst-switch-touch-w: 34px;
271 --rst-switch-touch-h: 19px;
272 --rst-switch-touch-knob: 15px;
273 --rst-tint-hover: var(--rst-accent-soft);
274 --rst-tint-press: var(--rst-accent-soft);
275 --rst-tint-tap: transparent;
276 --rst-tint-card-hover: transparent;
277 --rst-tint-tile-hover: var(--rst-surface);
278 --rst-tint-tile-press: var(--rst-surface);
279 --rst-tint-group-hover: transparent;
280 --rst-row-tint-hover: var(--rst-surface-2);
281 --rst-row-tint-press: var(--rst-surface-2);
282 --rst-focus-edge: var(--rst-line-strong);
283 --rst-focus-halo: transparent;
284 --rst-sheen-fill: transparent;
285 --rst-sheen-plain: transparent;
286 --rst-light-rim: var(--rst-line);
287 --rst-light-wash: transparent;
288 --rst-press-wash: 0;
289 --rst-press-spot: 0;
290 --rst-press-wash-plain: 0;
291 --rst-press-spot-plain: 0;
292 --rst-title-size: 1.375rem;
293 --rst-title-weight: 600;
294 --rst-title-track: -0.015em;
295 --rst-heading-size: var(--rst-fs-base);
296 --rst-label-case: uppercase;
297 --rst-label-track: 0.06em;
298 --rst-rail-bg: var(--rst-surface);
299 --rst-nav-current-bg: var(--rst-accent-soft);
300 --rst-nav-current-fg: var(--rst-accent);
301}
302
303/* The whole toggle. Setting color-scheme re-resolves every light-dark()
304 above, so an explicit choice beats the OS in both directions without
305 restating a colour. */
306:root[data-theme="light"] { color-scheme: light; }
307:root[data-theme="dark"] { color-scheme: dark; }
308