reviewzy 0.2.0 → 0.4.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 +3 -3
- package/package.json +2 -2
- package/src/dashboard/mount.ts +20 -3
- package/src/dashboard/static/components.css +7579 -600
- package/src/dashboard/static/cursor-rules.js +79 -0
- package/src/dashboard/static/cursor.css +134 -0
- package/src/dashboard/static/cursor.js +1749 -0
- package/src/dashboard/static/dashboard.css +281 -258
- package/src/dashboard/static/dashboard.js +331 -209
- package/src/dashboard/static/fonts/jetbrainsmono-latin-ext.woff2 +0 -0
- package/src/dashboard/static/fonts/jetbrainsmono-latin.woff2 +0 -0
- package/src/dashboard/static/fonts/onest-cyrillic.woff2 +0 -0
- package/src/dashboard/static/fonts/onest-latin-ext.woff2 +0 -0
- package/src/dashboard/static/fonts/onest-latin.woff2 +0 -0
- package/src/dashboard/static/icons.svg +184 -0
- package/src/dashboard/static/tokens.css +607 -110
- package/src/dashboard/static/ui.js +2487 -0
- package/src/dashboard/views.tsx +1204 -950
- package/src/dashboard/word-diff.ts +18 -25
- package/src/db/migrations.ts +5 -6
- package/src/db/queries.ts +38 -1
- package/src/mcp/await-approved.ts +17 -10
- package/src/mcp/fetch-approved.ts +11 -6
- package/src/mcp/file-entries.ts +45 -22
- package/src/mcp/list-entries.ts +51 -55
- package/src/mcp/mark-applied.ts +5 -5
- package/src/mcp/server.ts +8 -3
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
/* ============================================================
|
|
2
|
-
|
|
2
|
+
design tokens
|
|
3
|
+
Dark by default, and still full of light.
|
|
4
|
+
Neutrals hold hue 280 (Catppuccin lavender haze), never grey.
|
|
5
|
+
Every ratio in the comments measured, not estimated.
|
|
3
6
|
============================================================ */
|
|
4
7
|
|
|
5
|
-
/* Self-hosted variable fonts
|
|
8
|
+
/* Self-hosted variable fonts - ship the woff2 subsets in fonts/ alongside these
|
|
6
9
|
@font-face blocks. No CDN dependency; faster, private, offline-safe. Subsets:
|
|
7
10
|
latin / latin-ext / cyrillic. Subset your own with fonttools/glyphhanger. */
|
|
8
11
|
@font-face {
|
|
@@ -45,38 +48,310 @@
|
|
|
45
48
|
src: url("fonts/jetbrainsmono-latin-ext.woff2") format("woff2");
|
|
46
49
|
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
|
47
50
|
}
|
|
48
|
-
|
|
49
51
|
:root {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
--
|
|
56
|
-
|
|
57
|
-
--
|
|
58
|
-
--
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
52
|
+
color-scheme: dark;
|
|
53
|
+
|
|
54
|
+
/* ---- Surfaces. The page is NOT the darkest thing possible;
|
|
55
|
+
raising the floor is what makes a dark theme feel airy.
|
|
56
|
+
Surfaces rise TOWARD the light, wells sink below. ---- */
|
|
57
|
+
--well: oklch(0.215 0.020 280); /* inputs, code, tracks */
|
|
58
|
+
--bg: oklch(0.255 0.024 280); /* the page */
|
|
59
|
+
--surface-1: oklch(0.300 0.024 280); /* cards, menus */
|
|
60
|
+
--surface-2: oklch(0.345 0.026 280); /* hover, nested */
|
|
61
|
+
--surface-3: oklch(0.395 0.028 280); /* pressed, selected */
|
|
62
|
+
|
|
63
|
+
/* The sky: one light above the page, the page hue drifted toward the accent,
|
|
64
|
+
painted on body::after as a radial ground 780px tall. The stops are
|
|
65
|
+
declared grounds, never an alpha over the page, so each is a measured pair.
|
|
66
|
+
On the peak: --text 9.89, --text-2 7.13, --text-3 5.37, --text-4 4.62:1,
|
|
67
|
+
--accent-text 7.73. On the mid: 10.87 / 7.84 / 5.90 / 5.08, --accent-text
|
|
68
|
+
8.50. NO HEADROOM: --text-4 falls under 4.5:1 once the ground passes
|
|
69
|
+
L 0.31, so the peak never rises. */
|
|
70
|
+
--sky-peak: oklch(0.305 0.040 252);
|
|
71
|
+
--sky-mid: oklch(0.278 0.032 262);
|
|
72
|
+
/* The accent band's ground, the page's closing ask: the page tone turned
|
|
73
|
+
toward the sapphire. --text 11.26, --text-2 8.12, --text-3 6.11, --text-4
|
|
74
|
+
5.26:1, --accent-text 8.81, and --accent as a fill 7.21 against it. */
|
|
75
|
+
--band-accent: oklch(0.265 0.050 242);
|
|
76
|
+
|
|
77
|
+
/* The system draws its own scrollbar, so the thumb is a graphic this file owns
|
|
78
|
+
and it clears 3:1 on every ground a scroller has: 5.20:1 on --well, 4.68 on
|
|
79
|
+
--bg, 4.05 on --surface-1, 3.43 on a --surface-2 hover tint. --surface-3, the
|
|
80
|
+
colour the retired native rule used, measures 1.67:1 on --bg. */
|
|
81
|
+
--scrollbar-thumb: oklch(0.640 0.028 280);
|
|
82
|
+
|
|
83
|
+
/* Lines are a fallback, not the separation device */
|
|
84
|
+
--line: oklch(0.345 0.026 280);
|
|
85
|
+
--line-soft: oklch(0.310 0.024 280);
|
|
86
|
+
|
|
87
|
+
/* ---- Elevation. A lit top edge plus a dark contact shadow, both in-hue.
|
|
88
|
+
Never rgb(0 0 0): black shadow on lavender goes muddy.
|
|
89
|
+
|
|
90
|
+
A DARK PAGE HAS NO ROOM FOR A WIDE SOFT SHADOW. The page sits 46/255 on
|
|
91
|
+
the blue channel, so a shadow has 46 levels of headroom, and a wide blur
|
|
92
|
+
spends most of them below the quantization floor. The final band, where
|
|
93
|
+
the shadow paints exactly 1 level, is then wide, flat and bounded by a
|
|
94
|
+
clean closed curve: that curve is what reads as a ring drawn around the
|
|
95
|
+
box rather than as light. It widens with the outermost blur radius and
|
|
96
|
+
with how faint the shadow is. Measured beside a card: 28px of blur
|
|
97
|
+
peaking 5 levels deep drew 15px of ring, 40px peaking 10 drew 14px, and
|
|
98
|
+
14px peaking 11 drew 4px. Every blur here stays at or under 22px, every
|
|
99
|
+
coat lands with real depth, which is also what a physical shadow does:
|
|
100
|
+
dark where the object meets the surface, gone a short way out.
|
|
101
|
+
|
|
102
|
+
Measured on a 300x140 card, blue channel against --bg, deepest pixel and
|
|
103
|
+
the width of that last 1-level band:
|
|
104
|
+
--elev-1 23 levels under, 11 beside; band 3px under, 2px beside
|
|
105
|
+
--elev-2 23 / 12; band 7 / 6
|
|
106
|
+
--elev-float 34 / 18; band 8 / 6
|
|
107
|
+
The shipped v1 set measured 9 / 5 with a 15px band, which is the ring.
|
|
108
|
+
|
|
109
|
+
DEPTH IS NOT WHAT DRAWS THE CORNER, REACH IS. --elev-1's outer coat ran
|
|
110
|
+
`0 8px 16px -6px` and its footprint reached 21px under the card, 13 beside
|
|
111
|
+
and 18 out of the corner on the diagonal, so the far end of the footprint
|
|
112
|
+
closed as a curve tracing the box and the corner read as a smear. Depth was
|
|
113
|
+
never the problem: at `0 4px 8px -4px` the deepest pixel moves 24 to 23 and
|
|
114
|
+
the reach halves to 10 / 6 / 10.
|
|
115
|
+
|
|
116
|
+
THE EDGE IS WHAT THE EYE READS AS THE CARD STARTING, not the tone step.
|
|
117
|
+
A card sits .045 L over the page and .065 is not available: the fourth
|
|
118
|
+
text step falls to 4.40:1 on it. So separation is bought on the edge and
|
|
119
|
+
the contact instead, where no colour moves and no ratio re-derives.
|
|
120
|
+
Measured on a card at --surface-1: --lit-1 paints 18 sRGB levels of lift
|
|
121
|
+
along the top, --lit-2 21.
|
|
122
|
+
|
|
123
|
+
A CARD CARRIES A DRAWN LINE. `.card` and `.stat-card` take `--card-line`,
|
|
124
|
+
a crisp 1px line drawn outside the box (box-shadow 0 0 0 1px, zero blur,
|
|
125
|
+
content box unchanged). A card at rest
|
|
126
|
+
wears the one line and no coat. Every other raised surface keeps the straight --lit-1,
|
|
127
|
+
and glass keeps --glass-edge. The line is a hairline, not a graphic, so it
|
|
128
|
+
is not held to a 3:1 floor; measured with tools/contrast.mjs against the
|
|
129
|
+
card's own fill and every surface it sits on, tightest first: sky peak
|
|
130
|
+
2.03:1, --surface-1 (the fill) 2.08:1, sky mid 2.24:1, --band-accent
|
|
131
|
+
2.32:1, --bg 2.41:1, --well 2.67:1. */
|
|
132
|
+
--lit-1: inset 0 1px 0 0 oklch(1 0 0 / 0.085);
|
|
133
|
+
--lit-2: inset 0 1px 0 0 oklch(1 0 0 / 0.100);
|
|
134
|
+
--card-line: oklch(0.48 0.032 280);
|
|
135
|
+
--contact-1: 0 1px 2px -1px oklch(0.11 0.025 280 / 0.72),
|
|
136
|
+
0 2px 4px -1px oklch(0.11 0.025 280 / 0.66),
|
|
137
|
+
0 4px 8px -4px oklch(0.11 0.025 280 / 0.60);
|
|
138
|
+
--contact-2: 0 1px 2px -1px oklch(0.11 0.025 280 / 0.66),
|
|
139
|
+
0 4px 8px -2px oklch(0.11 0.025 280 / 0.62),
|
|
140
|
+
0 10px 18px -8px oklch(0.11 0.025 280 / 0.58);
|
|
141
|
+
--elev-1: var(--lit-1), var(--contact-1);
|
|
142
|
+
--elev-2: var(--lit-2), var(--contact-2);
|
|
143
|
+
--elev-float: var(--lit-2),
|
|
144
|
+
0 2px 4px -1px oklch(0.09 0.022 280 / 0.72),
|
|
145
|
+
0 6px 12px -3px oklch(0.09 0.022 280 / 0.68),
|
|
146
|
+
0 14px 22px -10px oklch(0.09 0.022 280 / 0.66);
|
|
147
|
+
--backdrop: oklch(0.16 0.02 280 / 0.66);
|
|
148
|
+
|
|
149
|
+
/* ---- Glass. Real background blur, on the surfaces something passes behind:
|
|
150
|
+
the mobile topbar, the stuck section header, the floating panels, and a
|
|
151
|
+
panel over the scrim. The navbar is opaque in both of its states. The scrim
|
|
152
|
+
itself stays a flat tint.
|
|
153
|
+
|
|
154
|
+
NEITHER THE ALPHA NOR THE BAR TONE IS TASTE. A glass surface takes on part
|
|
155
|
+
of whatever is behind it, and on a dark page that is nearly always dark, so
|
|
156
|
+
a surface tinted with --surface-1 reads as an opaque slab whatever the blur
|
|
157
|
+
(the 0.90 first material moved 22 levels over a white plate and two or
|
|
158
|
+
three over real content: every panel read as "not blurred"). Bars therefore
|
|
159
|
+
tint with --bg: a bar is the page held still, and content passing under it
|
|
160
|
+
is the only thing that says a surface is there. Panels keep the raised
|
|
161
|
+
tone at the clearest alpha the dim ramp allows: --text-2 holds 4.67:1 on
|
|
162
|
+
the worst case at 0.86 and fails at 0.82 (4.09:1), so 0.86 is the
|
|
163
|
+
floor. On that worst case, a white plate composited through the panel,
|
|
164
|
+
--text holds 6.48:1, --glass-dim 4.67, --glass-danger 4.62. The panel
|
|
165
|
+
moves 35.7 sRGB levels black to white, the measured (1 - alpha) * 255
|
|
166
|
+
identity confirmed on a plate. Filter chains do not buy the floor back:
|
|
167
|
+
crushing the backdrop enough to protect it crushes the blur with it,
|
|
168
|
+
measured across nine chains. ---- */
|
|
169
|
+
--glass-bar-tint: oklch(0.255 0.024 280 / 0.90);
|
|
170
|
+
/* the bar re-floors the same tokens as a panel, since the glass block names
|
|
171
|
+
both: --text-3/--text-4/--text-placeholder become --glass-dim, --danger
|
|
172
|
+
becomes --glass-danger, --text-disabled becomes --glass-disabled. Measured
|
|
173
|
+
over the bar's worst plate (white through 0.90 of the page colour): --text-2
|
|
174
|
+
6.21:1, --glass-danger 6.14:1, --glass-disabled 4.34:1. */
|
|
175
|
+
--glass-tint: oklch(0.300 0.024 280 / 0.86);
|
|
176
|
+
--glass-blur: 22px;
|
|
177
|
+
--glass-sat: 1.25;
|
|
178
|
+
--glass-dim: oklch(0.800 0.030 280); /* --text-3 and --text-4 on a panel:
|
|
179
|
+
4.67:1 on the worst case; unlifted, .720 / .680 fall to 3.52 / 3.03 */
|
|
180
|
+
--glass-danger: oklch(0.84 0.140 15); /* 4.62:1 on the panel worst case.
|
|
181
|
+
--danger is 3.31:1 there, and L 0.78 fell to 4.04 at
|
|
182
|
+
0.86, so the re-floor moves with the alpha */
|
|
183
|
+
--glass-disabled: oklch(0.700 0.030 280); /* 3.27:1 on the panel worst case.
|
|
184
|
+
--text-disabled falls to 2.49 there, and the opacity: 0.45 costume this
|
|
185
|
+
replaces measured 2.17, which is what the panel had been shipping */
|
|
186
|
+
/* A translucent tint over a translucent surface composites twice and drifts 21
|
|
187
|
+
levels with the backdrop: --accent-text on it measures 4.40:1. The same mix
|
|
188
|
+
laid down opaque holds one tone and 5.95:1 on a panel. This is the accent's
|
|
189
|
+
share of that opaque fill; the glass block mixes --accent-quiet from it onto
|
|
190
|
+
each surface's own --glass-ground, so a bar's fill reads the bar's tone. */
|
|
191
|
+
--glass-quiet-share: 16%;
|
|
192
|
+
/* --danger-text on a glass surface, where --danger is already --glass-danger:
|
|
193
|
+
the hover and press tints mix opaque onto the surface (--tint-ground), so
|
|
194
|
+
the plate behind it no longer moves them. On the panel 5.64 / 4.74:1, on
|
|
195
|
+
the bar 6.55 / 5.38, the menu hover 6.50; 0.86 leaves the panel press at
|
|
196
|
+
4.52, so 0.88 holds a margin over the floor. */
|
|
197
|
+
--glass-danger-text: oklch(0.88 0.140 15);
|
|
198
|
+
/* what every translucent tint in components.css mixes onto. Transparent, so
|
|
199
|
+
off glass a tint paints exactly as `X p%, transparent` did. A glass surface
|
|
200
|
+
sets its own tone here, turning each tint inside it into an opaque mix the
|
|
201
|
+
backdrop cannot move; every rule that paints an opaque ground resets it to
|
|
202
|
+
transparent, so a tint nested in that ground composites over it. One value
|
|
203
|
+
for both themes. */
|
|
204
|
+
--tint-ground: transparent;
|
|
205
|
+
/* the same glass tone for a tint painted OUTSIDE its own box (an invalid
|
|
206
|
+
field's ring): an opaque ground resets --tint-ground on itself, but its
|
|
207
|
+
ring lands on whatever surrounds it, so no rule resets this one */
|
|
208
|
+
--glass-ground: transparent;
|
|
209
|
+
/* --accent-line on a glass surface, laid opaque on the surface's own
|
|
210
|
+
--glass-ground at the same 38 percent, so a focus ring and a primary edge
|
|
211
|
+
stop drifting with the backdrop the way the quiet share stopped the fill */
|
|
212
|
+
--glass-line-share: 38%;
|
|
213
|
+
/* The one place a drawn line beats the lit edge. A glass panel has no reliable
|
|
214
|
+
tone difference from its backdrop, since it takes that backdrop on, so the
|
|
215
|
+
edge is what says where the pane starts. Still a hairline. */
|
|
216
|
+
--glass-edge: oklch(1 0 0 / 0.10);
|
|
217
|
+
/* a dimmed icon is a graphic against the surface it sits on: on the dark
|
|
218
|
+
glass panel 0.85 leaves a text-3 icon at 2.99:1, under the 3:1 floor, so
|
|
219
|
+
the glass block lifts this to 0.90: 3.15 dark and 3.89 light for the
|
|
220
|
+
dimmest icons on the panel worst cases */
|
|
221
|
+
--icon-dim: 0.7;
|
|
222
|
+
|
|
223
|
+
/* ---- Text. Nothing is pure white; purity reads as hard. ---- */
|
|
224
|
+
--text: oklch(0.900 0.030 280); /* 11.70:1 on --bg */
|
|
225
|
+
--text-2: oklch(0.800 0.030 280); /* 8.43:1 */
|
|
226
|
+
--text-3: oklch(0.720 0.030 280); /* 6.35:1 */
|
|
227
|
+
--text-4: oklch(0.680 0.030 280); /* 5.47:1, the floor */
|
|
228
|
+
/* the hint inside a control. One step under --text-4 so a placeholder does
|
|
229
|
+
not read as a typed value or as metadata: 4.99:1 on --well. */
|
|
230
|
+
--text-placeholder: oklch(0.630 0.030 280);
|
|
231
|
+
/* a control that cannot act, replacing the opacity: 0.45
|
|
232
|
+
ten declarations across twelve component classes rode. It takes the 3:1 graphics floor rather than the 4.5:1
|
|
233
|
+
text floor, so it can sit a real step under --text-4: 4.99:1 on --well, 4.50
|
|
234
|
+
on --bg, 3.89 on --surface-1, 3.29 on the --surface-2 a disabled fill takes.
|
|
235
|
+
Dark shares the placeholder's value on purpose, since both say "present, not
|
|
236
|
+
live" and this ramp has exactly one step down there. Light does not: its
|
|
237
|
+
placeholder is pinned to --text-4 by the text floor, and a disabled label
|
|
238
|
+
pinned with it would read as enabled metadata, which is the whole defect. */
|
|
239
|
+
--text-disabled: oklch(0.630 0.030 280);
|
|
240
|
+
|
|
241
|
+
/* ---- Accent: Catppuccin sapphire. The break in the overcast. ---- */
|
|
242
|
+
--accent: oklch(0.76 0.110 238); /* 7.49:1 as text on --bg */
|
|
243
|
+
--accent-hover: oklch(0.82 0.115 238); /* brighter in dark */
|
|
244
|
+
--accent-press: oklch(0.70 0.105 238);
|
|
245
|
+
--accent-text: oklch(0.82 0.115 238); /* 9.15:1 as type, the only accent as text */
|
|
246
|
+
--accent-quiet: oklch(0.76 0.110 238 / 0.16);
|
|
247
|
+
--accent-line: oklch(0.76 0.110 238 / 0.38);
|
|
248
|
+
--accent-glow: 0 0 10px 0 oklch(0.76 0.110 238 / 0.55);
|
|
249
|
+
--on-accent: oklch(0.22 0.020 280); /* 8.22:1 on the fill */
|
|
250
|
+
|
|
251
|
+
/* no on-colour: identity rule 7 makes accent-2 annotation only, never a fill,
|
|
252
|
+
so the pair it would sit on cannot exist */
|
|
253
|
+
--accent-2: oklch(0.72 0.130 42); /* editorial, 6.09:1, once per screen */
|
|
254
|
+
|
|
255
|
+
/* The brand mark's ring: rose, the identity accent. A 2px graphic on the
|
|
256
|
+
.sona, held to the 3:1 graphics floor on every bar surface it sits on;
|
|
257
|
+
measured with tools/contrast.mjs: surface-1 6.06:1, well 7.78:1, --bg
|
|
258
|
+
7.01:1, sky peak 5.93:1 dark; 5.16 / 4.20 / 4.59 / 5.16 light. */
|
|
259
|
+
--brand-ring: oklch(0.76 0.110 340);
|
|
260
|
+
|
|
261
|
+
/* ---- Status. Levelled under --text so no pill outshouts a title. ---- */
|
|
262
|
+
--success: oklch(0.80 0.130 150); /* 8.89:1 */
|
|
263
|
+
--warning: oklch(0.84 0.110 90); /* 9.68:1 */
|
|
264
|
+
--danger: oklch(0.72 0.140 15); /* 5.98:1 */
|
|
265
|
+
/* the danger label on its own hover and press tint: --danger there falls to
|
|
266
|
+
4.04 / 3.49:1 on surface-1, because label and tint are one hue. Lightness
|
|
267
|
+
stepped, chroma held: 6.04 / 5.16:1 on --bg, 5.27 / 4.55 on surface-1, and
|
|
268
|
+
5.76 on .menu-item.danger's 10 percent hover. */
|
|
269
|
+
--danger-text: oklch(0.81 0.140 15);
|
|
270
|
+
--on-danger: oklch(0.22 0.02 15); /* 6.58:1 on the fill */
|
|
271
|
+
--info: var(--accent); /* folded in: it was the same hue */
|
|
272
|
+
|
|
273
|
+
/* ---- Chart series. Four slots; the ORDER is the CVD mechanism, and what it
|
|
274
|
+
buys is that each dichromat's CLOSEST pair never lands on adjacent slots.
|
|
275
|
+
Simulated (Vienot, Brettel & Mollon 1999) and measured as oklab distance:
|
|
276
|
+
protan's closest pair is 1-3 at 0.0355 with orange sitting between them,
|
|
277
|
+
deutan's is 2-4 at 0.0516 with mauve sitting between them, and the closest
|
|
278
|
+
pair a legend does put side by side is 3-4 at 0.1666 / 0.1419. A fifth
|
|
279
|
+
series is a legend problem, not a palette problem, and yellow and red never
|
|
280
|
+
join a series: they read as status.
|
|
281
|
+
|
|
282
|
+
THOSE TWO ARE THE ONLY DICHROMACIES THIS RAMP IS MEASURED AGAINST. Vienot
|
|
283
|
+
publishes a projection plane for protan and deutan only; the tritan row that
|
|
284
|
+
circulates with them moved an equal-lightness blue/yellow pair from 0.2630
|
|
285
|
+
APART to 0.3601, so it cannot be a tritan simulation and no number taken
|
|
286
|
+
from it belongs here. Until one is modelled, treat a two-series chart as
|
|
287
|
+
needing direct labels rather than a legend.
|
|
288
|
+
|
|
289
|
+
Slot 3 is mauve rather than the sky it shipped as. Sky measured 0.0955 from
|
|
290
|
+
sapphire in normal vision, 2.5x closer than any other pair, and the two read
|
|
291
|
+
as one series; mauve puts the ramp's worst normal-vision pair at 0.1411.
|
|
292
|
+
Measured on --surface-1 (WCAG 1.4.11, 3:1 graphics floor):
|
|
293
|
+
5.21 / 5.27 / 6.62 / 6.45:1. Re-measure if --surface-1 moves. ---- */
|
|
294
|
+
--chart-series-1: oklch(0.70 0.110 238); /* sapphire */
|
|
295
|
+
--chart-series-2: oklch(0.72 0.130 42); /* orange */
|
|
296
|
+
--chart-series-3: oklch(0.78 0.100 305); /* mauve */
|
|
297
|
+
--chart-series-4: oklch(0.75 0.120 150); /* green */
|
|
298
|
+
|
|
299
|
+
/* ---- Radius. Softer than v1's 1px, which rendered as 0. ---- */
|
|
300
|
+
--r-1: 10px; /* controls */
|
|
301
|
+
--r-2: 14px; /* cards, menus, icon tiles */
|
|
302
|
+
--r-3: 18px; /* modals, sheets */
|
|
303
|
+
--r-4: 24px; /* KPI tiles */
|
|
304
|
+
--r-pill: 999px;
|
|
305
|
+
|
|
306
|
+
/* ---- Motion. Duration follows distance. ---- */
|
|
307
|
+
--ease: cubic-bezier(0.2, 0, 0, 1); /* state changes, exits */
|
|
308
|
+
--ease-out: cubic-bezier(0.16, 1, 0.3, 1); /* entrances, arrivals */
|
|
309
|
+
--dur-tint: 120ms; /* no travel: colour, opacity, border */
|
|
310
|
+
--dur-near: 180ms; /* under 12px: toast, popover, tooltip */
|
|
311
|
+
--dur-far: 260ms; /* a panel's worth: sheet, accordion */
|
|
312
|
+
--dur-cross: 360ms; /* across the viewport: drawer */
|
|
313
|
+
--dur-slide: 440ms; /* sliding indicators only */
|
|
314
|
+
--stagger: 40ms; /* per item, capped at 6 */
|
|
315
|
+
--dur-out: calc(var(--dur-tint) * 0.6); /* exits run at 0.6x */
|
|
316
|
+
--dur-out-near: calc(var(--dur-near) * 0.6);
|
|
317
|
+
--dur-out-far: calc(var(--dur-far) * 0.6);
|
|
318
|
+
|
|
319
|
+
/* ---- The jelly press. A spring settle, NOT a travel
|
|
320
|
+
distance, so it takes no --dur-* slot: --press-dur is how long the wobble
|
|
321
|
+
takes to settle, and --press-spring is the 320/18 spring sampled over
|
|
322
|
+
49 points as a linear() curve, normalized to settle at 1. ---- */
|
|
323
|
+
--press-dur: 380ms;
|
|
324
|
+
--press-spring: linear(0, 2.0833% 0.0098, 4.1667% 0.0372, 6.2500% 0.0793, 8.3333% 0.1334, 10.4167% 0.1970, 12.5000% 0.2675, 14.5833% 0.3428, 16.6667% 0.4208, 18.7500% 0.4996, 20.8333% 0.5777, 22.9167% 0.6538, 25.0000% 0.7267, 27.0833% 0.7954, 29.1667% 0.8592, 31.2500% 0.9176, 33.3333% 0.9702, 35.4167% 1.0168, 37.5000% 1.0574, 39.5833% 1.0919, 41.6667% 1.1205, 43.7500% 1.1435, 45.8333% 1.1613, 47.9167% 1.1740, 50.0000% 1.1823, 52.0833% 1.1865, 54.1667% 1.1871, 56.2500% 1.1845, 58.3333% 1.1793, 60.4167% 1.1718, 62.5000% 1.1626, 64.5833% 1.1520, 66.6667% 1.1404, 68.7500% 1.1281, 70.8333% 1.1155, 72.9167% 1.1028, 75.0000% 1.0904, 77.0833% 1.0783, 79.1667% 1.0668, 81.2500% 1.0560, 83.3333% 1.0461, 85.4167% 1.0370, 87.5000% 1.0289, 89.5833% 1.0217, 91.6667% 1.0155, 93.7500% 1.0103, 95.8333% 1.0060, 97.9167% 1.0026, 1);
|
|
325
|
+
|
|
326
|
+
/* ---- Type. 9 distinct steps; 11px and 12px are real tokens. ---- */
|
|
327
|
+
--t-1: 11px; --t-2: 12px; --t-3: 14px; --t-4: 16px;
|
|
328
|
+
--t-5: 20px; --t-6: 26px; --t-7: 34px; --t-8: 46px;
|
|
329
|
+
--t-9: 64px;
|
|
330
|
+
--caps: 0.09em; /* the one tracking value */
|
|
331
|
+
|
|
332
|
+
/* ---- Controls share one height ladder with inputs ---- */
|
|
333
|
+
--ctl-sm: 30px; --ctl: 36px; --ctl-lg: 44px;
|
|
334
|
+
|
|
335
|
+
/* ---- Layout ---- */
|
|
336
|
+
--page-w: 1200px; /* .page column; one override retunes a build */
|
|
337
|
+
/* prose measure, MEASURED against loaded Onest at --t-4 rather than estimated.
|
|
338
|
+
A line that runs the full page column is a line nobody finishes, and erring
|
|
339
|
+
short is the safer side of 66. The width sweep behind the value, five
|
|
340
|
+
candidates against two samples, is in references/rhythm.md; it lived here
|
|
341
|
+
too and the two copies had already drifted apart on one sample. */
|
|
342
|
+
--page-narrow: 528px;
|
|
343
|
+
--navbar-h: 56px; /* the navbar's own height, and nothing else reads it as an offset */
|
|
344
|
+
--topbar-h: 52px; /* the mobile bar's own height */
|
|
345
|
+
/* How far down anything pinned to the top has to start, which is NOT a bar's
|
|
346
|
+
height: it is the height of whatever bar this page actually holds, and a
|
|
347
|
+
sidebar shell holds none. Derived on the root below, never set by a page. */
|
|
348
|
+
--pin-top: 0px;
|
|
349
|
+
|
|
350
|
+
/* ---- Fonts ---- */
|
|
66
351
|
--font-sans: "Onest", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
|
|
67
352
|
--font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
68
353
|
|
|
69
|
-
/*
|
|
70
|
-
--text-xs: 12px;
|
|
71
|
-
--text-sm: 13px;
|
|
72
|
-
--text-base: 14px;
|
|
73
|
-
--text-md: 16px;
|
|
74
|
-
--text-lg: 18px;
|
|
75
|
-
--text-xl: 22px;
|
|
76
|
-
--text-2xl: 28px;
|
|
77
|
-
--text-3xl: 36px;
|
|
78
|
-
|
|
79
|
-
/* Spacing (4px scale) */
|
|
354
|
+
/* ---- Spacing (4px scale) ---- */
|
|
80
355
|
--space-0: 0;
|
|
81
356
|
--space-1: 4px;
|
|
82
357
|
--space-2: 8px;
|
|
@@ -89,91 +364,220 @@
|
|
|
89
364
|
--space-12: 48px;
|
|
90
365
|
--space-16: 64px;
|
|
91
366
|
--space-20: 80px;
|
|
367
|
+
}
|
|
92
368
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
--radius-pill: 999px;
|
|
369
|
+
/* Radius preset: sharp is the opt-in variant, real 0 */
|
|
370
|
+
[data-radius="sharp"] { --r-1: 0px; --r-2: 0px; --r-3: 0px; --r-4: 0px; }
|
|
96
371
|
|
|
97
|
-
|
|
98
|
-
|
|
372
|
+
/* ---- Light theme (Latte). EVERY colour and elevation token is
|
|
373
|
+
re-declared, because a partial override is the bug that broke
|
|
374
|
+
v1's light theme: its tints were mixed from the dark accent.
|
|
375
|
+
Ratios measured against THIS block's --bg. ---- */
|
|
376
|
+
[data-theme="light"] {
|
|
377
|
+
color-scheme: light;
|
|
99
378
|
|
|
100
|
-
/*
|
|
101
|
-
--
|
|
102
|
-
--
|
|
103
|
-
--
|
|
104
|
-
--
|
|
105
|
-
--
|
|
106
|
-
|
|
379
|
+
--surface-3: oklch(0.905 0.010 275); /* pressed: light goes DOWN */
|
|
380
|
+
/* 3.58:1 on --well, 3.92 on --bg, 4.40 on --surface-1, 3.69 on the hover tint */
|
|
381
|
+
--scrollbar-thumb: oklch(0.570 0.010 275);
|
|
382
|
+
--surface-2: oklch(0.935 0.008 275); /* hover */
|
|
383
|
+
--well: oklch(0.925 0.008 275);
|
|
384
|
+
--bg: oklch(0.955 0.006 275);
|
|
385
|
+
--surface-1: oklch(0.995 0.003 275); /* cards still rise */
|
|
386
|
+
/* Light falls onto a light page too: the top goes whiter, which only raises
|
|
387
|
+
every text ratio there. On the peak: --text 13.46, --text-4 5.55:1,
|
|
388
|
+
--accent-text 5.68; on the mid 12.72 / 5.25 / 5.37. The peak sits at the
|
|
389
|
+
card's own lightness, so a card under it separates on the drawn line
|
|
390
|
+
alone. */
|
|
391
|
+
--sky-peak: oklch(0.995 0.008 240);
|
|
392
|
+
--sky-mid: oklch(0.975 0.007 255);
|
|
393
|
+
/* --text 11.17, --text-2 6.94, --text-3 4.92, --text-4 4.61:1, --accent-text
|
|
394
|
+
4.72; --accent as a fill 3.82 against it, over the 3:1 graphics floor */
|
|
395
|
+
--band-accent: oklch(0.930 0.030 238);
|
|
107
396
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
[data-radius="soft"] { --radius: 1px; }
|
|
111
|
-
[data-radius="rounded"] { --radius: 6px; }
|
|
397
|
+
--line: oklch(0.890 0.012 275); /* barely there, 1.22:1 */
|
|
398
|
+
--line-soft: oklch(0.920 0.010 275);
|
|
112
399
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
--
|
|
116
|
-
--
|
|
117
|
-
--
|
|
118
|
-
|
|
400
|
+
--text: oklch(0.300 0.020 275); /* 11.98:1 */
|
|
401
|
+
--text-2: oklch(0.420 0.020 275); /* 7.44:1 */
|
|
402
|
+
--text-3: oklch(0.500 0.018 275); /* 5.27:1 */
|
|
403
|
+
--text-4: oklch(0.515 0.016 275); /* 4.94:1 on --bg, 4.52:1 on --well, the floor */
|
|
404
|
+
/* 4.52:1 on --well is the floor already; light has no headroom under it, so
|
|
405
|
+
the placeholder holds --text-4's value and the dark block alone dims. */
|
|
406
|
+
--text-placeholder: oklch(0.515 0.016 275);
|
|
407
|
+
/* the 3:1 floor is what buys the step the 4.5:1 one denies the placeholder:
|
|
408
|
+
3.23:1 on --surface-3, 3.44 on --well, 3.54 on --surface-2, 3.76 on --bg,
|
|
409
|
+
4.23 on --surface-1 */
|
|
410
|
+
--text-disabled: oklch(0.580 0.016 275);
|
|
119
411
|
|
|
120
|
-
--
|
|
121
|
-
--
|
|
122
|
-
--
|
|
412
|
+
--accent: oklch(0.55 0.140 238); /* 4.60:1 under --on-accent; 4.09:1 as text */
|
|
413
|
+
--accent-hover: oklch(0.49 0.135 238);
|
|
414
|
+
--accent-press: oklch(0.44 0.125 238);
|
|
415
|
+
--accent-text: oklch(0.50 0.130 238); /* 5.06:1 as type; accent as text fails 4.5 */
|
|
416
|
+
--accent-quiet: oklch(0.55 0.140 238 / 0.12);
|
|
417
|
+
--accent-line: oklch(0.55 0.140 238 / 0.34);
|
|
418
|
+
--accent-glow: 0 0 10px 0 oklch(0.55 0.140 238 / 0.30);
|
|
419
|
+
--on-accent: oklch(0.995 0.003 238);
|
|
123
420
|
|
|
124
|
-
--
|
|
125
|
-
--text-muted: #BAC2DE;
|
|
126
|
-
--text-dim: #A6ADC8;
|
|
127
|
-
--text-faint: #7F849C;
|
|
128
|
-
--text-on-accent: #11111B;
|
|
129
|
-
}
|
|
421
|
+
--accent-2: oklch(0.52 0.150 42); /* 5.16:1 */
|
|
130
422
|
|
|
131
|
-
|
|
132
|
-
[data-theme="light"] {
|
|
133
|
-
--bg: #EFF1F5;
|
|
134
|
-
--bg-raised: #FFFFFF;
|
|
135
|
-
--bg-sunken: #E6E9EF;
|
|
136
|
-
--bg-hover: #DCE0E8;
|
|
423
|
+
--brand-ring: oklch(0.55 0.140 340);
|
|
137
424
|
|
|
138
|
-
--
|
|
139
|
-
--
|
|
140
|
-
--
|
|
425
|
+
--success: oklch(0.50 0.150 150); /* 4.85:1 */
|
|
426
|
+
--warning: oklch(0.50 0.130 90); /* 5.25:1 */
|
|
427
|
+
--danger: oklch(0.52 0.190 15); /* 5.36:1 */
|
|
428
|
+
/* hover / press: 5.25 / 4.61:1 on --bg, 5.87 / 5.10 on surface-1, 6.49 on
|
|
429
|
+
.menu-item.danger's hover; --danger there falls to 4.14 / 3.63 on --bg */
|
|
430
|
+
--danger-text: oklch(0.46 0.190 15);
|
|
431
|
+
--on-danger: oklch(0.995 0.003 15);
|
|
432
|
+
--info: var(--accent);
|
|
141
433
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
434
|
+
/* The lit edge flips to the underside: light falls from above, so
|
|
435
|
+
on a light page the visible edge is the shadow, not the highlight.
|
|
436
|
+
Same geometry as the dark set, so both themes carry one shadow shape and
|
|
437
|
+
only the alphas differ. A light page has 244 levels of headroom, so the
|
|
438
|
+
banding that governs the dark set is not the binding constraint here; the
|
|
439
|
+
geometry is shared because a component must not change depth on a toggle.
|
|
440
|
+
Measured on a 300x140 card, blue channel against --bg, deepest pixel and
|
|
441
|
+
the last 1-level band: --elev-1 30 under / 13 beside, band 2 / 2;
|
|
442
|
+
--elev-2 32 / 14, band 3 / 2; --elev-float 42 / 19, band 5 / 3. */
|
|
147
443
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
444
|
+
/* The underside line is a mid tone on a near-white card, so an alpha buys
|
|
445
|
+
fewer levels here than dark's white on --surface-1: at 0.10 it painted 15
|
|
446
|
+
against dark's 18. The two themes are matched on the PAINTED delta rather
|
|
447
|
+
than on each other's alphas, measured on a card: 0.125 lands 18, 0.145
|
|
448
|
+
lands 21. Matched in sRGB levels, not in oklab: a
|
|
449
|
+
perceptual match wants 0.165, which reaches 93 percent of --line-soft's
|
|
450
|
+
own step on a card and starts reading as a drawn line rather than a lit
|
|
451
|
+
edge. */
|
|
452
|
+
--lit-1: inset 0 -1px 0 0 oklch(0.55 0.03 275 / 0.125);
|
|
453
|
+
--lit-2: inset 0 -1px 0 0 oklch(0.55 0.03 275 / 0.145);
|
|
454
|
+
/* the card line is a hairline, not a graphic, so no 3:1 floor applies;
|
|
455
|
+
measured with tools/contrast.mjs against every surface a card sits on,
|
|
456
|
+
tightest first: --well 1.99:1, --band-accent 2.03:1, --bg 2.18:1,
|
|
457
|
+
--sky-mid 2.31:1, --surface-1 2.45:1, --sky-peak 2.45:1. */
|
|
458
|
+
--card-line: oklch(0.72 0.020 275);
|
|
459
|
+
--contact-1: 0 1px 2px -1px oklch(0.55 0.03 275 / 0.26),
|
|
460
|
+
0 2px 4px -1px oklch(0.55 0.03 275 / 0.23),
|
|
461
|
+
0 4px 8px -4px oklch(0.55 0.03 275 / 0.21);
|
|
462
|
+
--contact-2: 0 1px 2px -1px oklch(0.55 0.03 275 / 0.24),
|
|
463
|
+
0 4px 8px -2px oklch(0.55 0.03 275 / 0.21),
|
|
464
|
+
0 10px 18px -8px oklch(0.55 0.03 275 / 0.19);
|
|
465
|
+
--elev-1: var(--lit-1), var(--contact-1);
|
|
466
|
+
--elev-2: var(--lit-2), var(--contact-2);
|
|
467
|
+
--elev-float: var(--lit-2),
|
|
468
|
+
0 2px 4px -1px oklch(0.55 0.03 275 / 0.26),
|
|
469
|
+
0 6px 12px -3px oklch(0.55 0.03 275 / 0.23),
|
|
470
|
+
0 14px 22px -10px oklch(0.55 0.03 275 / 0.21);
|
|
471
|
+
--backdrop: oklch(0.45 0.02 275 / 0.32);
|
|
151
472
|
|
|
152
|
-
--
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
473
|
+
/* Glass. The same split the dark block ships: bars tint with --bg, panels
|
|
474
|
+
hold surface-1 at the clearest alpha the ramp allows. Worst case on a light
|
|
475
|
+
page is black behind the panel; composited through it, --text holds 10.76:1,
|
|
476
|
+
--text-2 6.68, the dim step 4.72 at 0.500 (re-measured twice 2026-09-02,
|
|
477
|
+
independently) and --danger 4.81. The shipped --text-4 value 0.515 measures
|
|
478
|
+
4.44 there, so it joins the dim step. 0.90 is 25.5 sRGB levels black to
|
|
479
|
+
white. --accent-text stays bound to its opaque quiet fill: 4.89:1 on a
|
|
480
|
+
panel's, but 4.38 on a bar's own, so every accent label that can sit on a
|
|
481
|
+
bar carries a mix: the shared light remix at 4.98, `.btn-primary`'s own
|
|
482
|
+
at 6.27.
|
|
156
483
|
|
|
157
|
-
|
|
484
|
+
The bar alpha is the floor, not a preference. At 0.92 the bar re-floors
|
|
485
|
+
--text-3/--text-4/--text-placeholder to --glass-dim, which measured 4.41:1
|
|
486
|
+
over black, and --glass-danger 4.48:1, both under 4.5:1. At 0.93 both clear:
|
|
487
|
+
--glass-dim 4.52:1 and --glass-danger 4.59:1 over black, and --glass-disabled
|
|
488
|
+
3.22:1 over the 3:1 floor. */
|
|
489
|
+
--glass-bar-tint: oklch(0.955 0.006 275 / 0.93);
|
|
490
|
+
--glass-tint: oklch(0.995 0.003 275 / 0.90);
|
|
491
|
+
--glass-dim: oklch(0.500 0.018 275);
|
|
492
|
+
--glass-danger: var(--danger);
|
|
493
|
+
--glass-danger-text: var(--danger-text);
|
|
494
|
+
/* light needs no lift here: --text-disabled already measures 3.37:1 on the
|
|
495
|
+
panel worst case, where dark's falls to 2.49 */
|
|
496
|
+
--glass-disabled: var(--text-disabled);
|
|
497
|
+
--glass-quiet-share: 12%;
|
|
498
|
+
--glass-line-share: 34%;
|
|
499
|
+
--glass-edge: oklch(0.45 0.03 275 / 0.12);
|
|
500
|
+
|
|
501
|
+
/* A light page has less room under a dim than a dark one: at the 0.7 the dark
|
|
502
|
+
block ships, --text-4 measures 2.79:1 on --bg and 2.98 on --surface-1 here,
|
|
503
|
+
both under the 3:1 graphics floor, and --text-3 2.90 / 3.10. Lifted to the
|
|
504
|
+
same 0.90 the glass block uses, which takes the worst of those to 4.04, and
|
|
505
|
+
means an icon does not change when it crosses onto a glass panel. */
|
|
506
|
+
--icon-dim: 0.90;
|
|
507
|
+
|
|
508
|
+
/* Chart series on the light surface-1: 5.81 / 5.80 / 5.12 / 5.55:1. Slot 3 is
|
|
509
|
+
mauve here too: against sky the 1-3 pair measured 0.0790 in oklab and now
|
|
510
|
+
measures 0.1556.
|
|
511
|
+
|
|
512
|
+
SLOTS 1 AND 4 ARE HELD AT THE sRGB BOUNDARY. At their
|
|
513
|
+
old chromas the browser clipped them (slot 1's linear red measured -0.0182,
|
|
514
|
+
slot 4's -0.0020), so the declared values named colours nobody saw and every
|
|
515
|
+
distance taken from a declaration overstated the gap. The gamut runs out at
|
|
516
|
+
C 0.1110 for slot 1 and 0.1378 for slot 4 at these lightnesses and hues;
|
|
517
|
+
both sit under that with a little margin; the sRGB check reds on any slot
|
|
518
|
+
outside the gamut, on every run. */
|
|
519
|
+
--chart-series-1: oklch(0.50 0.110 238);
|
|
520
|
+
--chart-series-2: oklch(0.52 0.150 42);
|
|
521
|
+
--chart-series-3: oklch(0.55 0.150 305);
|
|
522
|
+
--chart-series-4: oklch(0.50 0.135 150);
|
|
158
523
|
}
|
|
159
524
|
|
|
160
|
-
/* ---- Optional categorical palette (
|
|
525
|
+
/* ---- Optional categorical palette (Catppuccin accent ramp) ----
|
|
161
526
|
For category coding only: per-language dots, syntax, tags, multi-series charts.
|
|
162
|
-
NOT for status (use --success / --warning / --danger
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
--cat-rosewater
|
|
166
|
-
--cat-
|
|
167
|
-
--cat-
|
|
168
|
-
--cat-
|
|
169
|
-
--cat-
|
|
527
|
+
NOT for status (use --success / --warning / --danger) and NOT for chrome.
|
|
528
|
+
Converted from the v1 hex ramp, computed not estimated. */
|
|
529
|
+
:root {
|
|
530
|
+
--cat-rosewater: oklch(0.923 0.024 30); --cat-flamingo: oklch(0.880 0.042 18);
|
|
531
|
+
--cat-pink: oklch(0.870 0.075 336); --cat-mauve: oklch(0.787 0.119 305);
|
|
532
|
+
--cat-red: oklch(0.756 0.130 3); --cat-maroon: oklch(0.782 0.090 9);
|
|
533
|
+
--cat-peach: oklch(0.824 0.101 53); --cat-yellow: oklch(0.919 0.070 87);
|
|
534
|
+
--cat-green: oklch(0.858 0.109 143); --cat-teal: oklch(0.858 0.079 183);
|
|
535
|
+
--cat-sky: oklch(0.847 0.083 210); --cat-sapphire: oklch(0.791 0.096 229);
|
|
536
|
+
--cat-blue: oklch(0.766 0.111 260); --cat-lavender: oklch(0.817 0.091 277);
|
|
170
537
|
}
|
|
171
538
|
[data-theme="light"] {
|
|
172
|
-
--cat-rosewater
|
|
173
|
-
--cat-
|
|
174
|
-
--cat-
|
|
175
|
-
--cat-
|
|
176
|
-
--cat-
|
|
539
|
+
--cat-rosewater: oklch(0.714 0.105 33); --cat-flamingo: oklch(0.686 0.126 21);
|
|
540
|
+
--cat-pink: oklch(0.726 0.174 338); --cat-mauve: oklch(0.555 0.250 297);
|
|
541
|
+
--cat-red: oklch(0.550 0.216 20); --cat-maroon: oklch(0.625 0.197 20);
|
|
542
|
+
--cat-peach: oklch(0.692 0.204 42); --cat-yellow: oklch(0.714 0.149 68);
|
|
543
|
+
--cat-green: oklch(0.625 0.177 140); --cat-teal: oklch(0.602 0.098 201);
|
|
544
|
+
--cat-sky: oklch(0.682 0.145 235); --cat-sapphire: oklch(0.648 0.107 213);
|
|
545
|
+
--cat-blue: oklch(0.559 0.226 262); --cat-lavender: oklch(0.664 0.175 273);
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
/* ---- Language hues (GitHub-recognizable categorical) ----
|
|
549
|
+
The categorical ramp's per-language extension, for dots, bar segments and
|
|
550
|
+
legend marks. Hue and chroma are each language's own GitHub colour;
|
|
551
|
+
lightness is re-derived per theme so every mark clears the 3:1 graphics
|
|
552
|
+
floor on its theme's binding surface: dark marks are light and bind on the
|
|
553
|
+
lightest surface a dark-theme mark can sit on (--surface-1); light marks
|
|
554
|
+
are dark and bind on the darkest light-theme surface (--well). Worst
|
|
555
|
+
measured: dark 3.1:1, light 3.1:1. */
|
|
556
|
+
:root {
|
|
557
|
+
--cat-rust: oklch(0.768 0.081 51); --cat-javascript: oklch(0.896 0.153 102);
|
|
558
|
+
--cat-typescript: oklch(0.569 0.078 220); --cat-python: oklch(0.573 0.102 247);
|
|
559
|
+
--cat-csharp: oklch(0.559 0.179 141); --cat-java: oklch(0.604 0.123 69);
|
|
560
|
+
--cat-go: oklch(0.695 0.130 224); --cat-html: oklch(0.622 0.194 35);
|
|
561
|
+
--cat-css: oklch(0.585 0.104 300); --cat-shell: oklch(0.823 0.196 135);
|
|
562
|
+
--cat-php: oklch(0.579 0.091 272); --cat-kotlin: oklch(0.687 0.189 297);
|
|
563
|
+
--cat-qml: oklch(0.639 0.189 139); --cat-c: oklch(0.575 0.020 280);
|
|
564
|
+
--cat-cpp: oklch(0.661 0.206 7); --cat-vue: oklch(0.702 0.132 160);
|
|
565
|
+
--cat-svelte: oklch(0.654 0.234 34); --cat-lua: oklch(0.584 0.188 264);
|
|
566
|
+
--cat-nix: oklch(0.656 0.187 280); --cat-dockerfile: oklch(0.573 0.029 220);
|
|
567
|
+
--cat-makefile: oklch(0.564 0.137 135);
|
|
568
|
+
}
|
|
569
|
+
[data-theme="light"] {
|
|
570
|
+
--cat-rust: oklch(0.609 0.081 51); --cat-javascript: oklch(0.598 0.153 102);
|
|
571
|
+
--cat-typescript: oklch(0.523 0.078 220); --cat-python: oklch(0.535 0.102 247);
|
|
572
|
+
--cat-csharp: oklch(0.540 0.179 141); --cat-java: oklch(0.604 0.123 69);
|
|
573
|
+
--cat-go: oklch(0.588 0.130 224); --cat-html: oklch(0.622 0.194 35);
|
|
574
|
+
--cat-css: oklch(0.418 0.104 300); --cat-shell: oklch(0.585 0.196 135);
|
|
575
|
+
--cat-php: oklch(0.492 0.091 272); --cat-kotlin: oklch(0.622 0.189 297);
|
|
576
|
+
--cat-qml: oklch(0.586 0.189 139); --cat-c: oklch(0.450 0.020 280);
|
|
577
|
+
--cat-cpp: oklch(0.629 0.206 7); --cat-vue: oklch(0.587 0.132 160);
|
|
578
|
+
--cat-svelte: oklch(0.629 0.234 34); --cat-lua: oklch(0.271 0.188 264);
|
|
579
|
+
--cat-nix: oklch(0.616 0.187 280); --cat-dockerfile: oklch(0.406 0.029 220);
|
|
580
|
+
--cat-makefile: oklch(0.516 0.137 135);
|
|
177
581
|
}
|
|
178
582
|
|
|
179
583
|
/* ---- Base reset ---- */
|
|
@@ -181,33 +585,126 @@
|
|
|
181
585
|
html { font-size: 16px; }
|
|
182
586
|
body {
|
|
183
587
|
font-family: var(--font-sans);
|
|
184
|
-
font-size: var(--
|
|
588
|
+
font-size: var(--t-3);
|
|
185
589
|
line-height: 1.55;
|
|
186
590
|
font-weight: 400;
|
|
187
591
|
color: var(--text);
|
|
188
592
|
background: var(--bg);
|
|
593
|
+
accent-color: var(--accent);
|
|
189
594
|
-webkit-font-smoothing: antialiased;
|
|
190
595
|
text-wrap: pretty;
|
|
191
596
|
}
|
|
192
|
-
|
|
193
|
-
|
|
597
|
+
/* The sky: a 62% x 100% ellipse hung from the top centre, reaching --bg at its
|
|
598
|
+
own edge, so the 780px box draws no seam where it ends. It is its own layer
|
|
599
|
+
at z -2, under the dot grid, so it can move slower than the page: it drifts
|
|
600
|
+
up at a quarter of the scroll speed, and is gone once the
|
|
601
|
+
page has scrolled 3120px. Absolute by default, which scrolls it with the page,
|
|
602
|
+
so an engine with no scroll timeline and a reduced-motion reader both get the
|
|
603
|
+
plain ground. */
|
|
604
|
+
body::after {
|
|
605
|
+
content: "";
|
|
606
|
+
position: absolute;
|
|
607
|
+
inset: 0 0 auto;
|
|
608
|
+
height: 780px;
|
|
609
|
+
z-index: -2;
|
|
610
|
+
pointer-events: none;
|
|
611
|
+
background:
|
|
612
|
+
radial-gradient(ellipse 62% 100% at 50% 0%,
|
|
613
|
+
var(--sky-peak) 0%, var(--sky-mid) 42%, var(--bg) 100%);
|
|
614
|
+
}
|
|
615
|
+
@supports (animation-timeline: scroll()) {
|
|
616
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
617
|
+
body::after {
|
|
618
|
+
position: fixed;
|
|
619
|
+
animation: sky-drift linear both;
|
|
620
|
+
animation-timeline: scroll(root);
|
|
621
|
+
animation-range: 0 3120px;
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
@keyframes sky-drift { to { transform: translateY(-780px); } }
|
|
626
|
+
::selection { background: var(--accent-quiet); color: var(--text); }
|
|
627
|
+
/* `font-family` does not inherit into a <button>: the UA sets it, together with
|
|
628
|
+
a 2px outset border, ButtonFace, ButtonText and centred text, and an author
|
|
629
|
+
rule beats a UA rule at any specificity. Every component class that lands on a
|
|
630
|
+
button would otherwise have to restate all five, and the ones that do not
|
|
631
|
+
measured in the UA's Arial on a bare button. At (0,0,1) this loses to a
|
|
632
|
+
component's own costume whatever the source order. */
|
|
633
|
+
button {
|
|
634
|
+
font: inherit;
|
|
635
|
+
border: 0;
|
|
636
|
+
background: none;
|
|
637
|
+
color: inherit;
|
|
638
|
+
text-align: inherit;
|
|
639
|
+
}
|
|
640
|
+
:focus-visible {
|
|
641
|
+
outline: 2px solid var(--accent);
|
|
642
|
+
outline-offset: 3px;
|
|
643
|
+
box-shadow: 0 0 0 1px var(--bg);
|
|
644
|
+
}
|
|
645
|
+
/* An author rule beats a UA rule whatever the specificity, so `.btn` and every other block
|
|
646
|
+
here keeps painting under the `hidden` attribute without this line. A script toggling
|
|
647
|
+
`hidden` then silently does nothing. `!important` is load-bearing: the component rules
|
|
648
|
+
are the thing being overridden. */
|
|
649
|
+
[hidden] { display: none !important; }
|
|
650
|
+
|
|
651
|
+
/* ---- Document defaults: pages stop hand-rolling typography. ---- */
|
|
652
|
+
a { color: var(--accent-text); text-decoration: none; }
|
|
653
|
+
/* :where() zeroes the specificity, so every component class that sets
|
|
654
|
+
`text-decoration: none` in its base rule outranks this. At (0,1,1) it beat
|
|
655
|
+
them all and underlined the sidebar rows, a `.btn` written as a link and every
|
|
656
|
+
menu row; four components had already restated the reset on hover. */
|
|
657
|
+
:where(a):hover { text-decoration: underline; text-underline-offset: 2px; }
|
|
658
|
+
h1, h2, h3, h4 { color: var(--text); text-wrap: balance; }
|
|
659
|
+
h1 { font: 600 var(--t-8)/1.1 var(--font-sans); letter-spacing: -0.03em; }
|
|
660
|
+
h2 { font: 600 var(--t-6)/1.15 var(--font-sans); letter-spacing: -0.02em; }
|
|
661
|
+
h3 { font: 500 var(--t-5)/1.2 var(--font-sans); letter-spacing: -0.01em; }
|
|
662
|
+
h4 { font: 500 var(--t-4)/1.3 var(--font-sans); }
|
|
663
|
+
/* An anchored jump clears whatever bar this page actually holds, not a fixed
|
|
664
|
+
navbar height: --pin-top is 0 on a page with no bar at all. */
|
|
665
|
+
[id] { scroll-margin-top: calc(var(--pin-top) + var(--space-2)); }
|
|
194
666
|
|
|
195
667
|
/* ---- Utilities ---- */
|
|
196
668
|
.label {
|
|
197
|
-
font:
|
|
198
|
-
letter-spacing:
|
|
669
|
+
font: 500 11px/1.4 var(--font-sans);
|
|
670
|
+
letter-spacing: var(--caps);
|
|
199
671
|
text-transform: uppercase;
|
|
200
|
-
color: var(--text-
|
|
672
|
+
color: var(--text-3);
|
|
201
673
|
}
|
|
202
674
|
.num {
|
|
203
675
|
font-variant-numeric: tabular-nums;
|
|
204
676
|
font-feature-settings: "tnum";
|
|
205
677
|
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
678
|
+
/* Unbreakable machine strings wrap inside their box; they never widen a page.
|
|
679
|
+
The five component selectors below are the same costume, named here rather
|
|
680
|
+
than restated in components.css. `.copy-chip-fill > .copy-chip-value` at
|
|
681
|
+
(0,2,0) still turns it back off, and `.chart-ticks-band .chart-tick` keeps its
|
|
682
|
+
own position and translate. */
|
|
683
|
+
.truncate,
|
|
684
|
+
.select-value,
|
|
685
|
+
.tree-label,
|
|
686
|
+
.status-legend-name,
|
|
687
|
+
.copy-chip-value,
|
|
688
|
+
.chart-ticks-band .chart-tick { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
689
|
+
.clamp-2 {
|
|
690
|
+
display: -webkit-box;
|
|
691
|
+
-webkit-line-clamp: 2;
|
|
692
|
+
-webkit-box-orient: vertical;
|
|
693
|
+
overflow: hidden;
|
|
694
|
+
}
|
|
695
|
+
/* `.stepper-compact .step-text` is this costume under another name, so it is
|
|
696
|
+
named here. The navbar's own label cannot join: it hides only under
|
|
697
|
+
`@media (max-width: 768px)`, and this rule has no condition to carry. */
|
|
698
|
+
.sr-only,
|
|
699
|
+
.stepper-compact .step-text {
|
|
700
|
+
position: absolute;
|
|
701
|
+
width: 1px;
|
|
702
|
+
height: 1px;
|
|
703
|
+
padding: 0;
|
|
704
|
+
margin: -1px;
|
|
705
|
+
overflow: hidden;
|
|
706
|
+
clip: rect(0 0 0 0);
|
|
707
|
+
clip-path: inset(50%);
|
|
708
|
+
white-space: nowrap;
|
|
709
|
+
border: 0;
|
|
213
710
|
}
|