@guildofgleks/ui 21.10.0 → 21.12.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/CHANGELOG.md CHANGED
@@ -4,6 +4,522 @@ All notable changes to `@guildofgleks/ui` are documented here. Format follows
4
4
  [Keep a Changelog](https://keepachangelog.com/en/1.0.0/); this project has not yet
5
5
  reached 1.0, so breaking changes may land in minor versions.
6
6
 
7
+ ## [21.12.0] - 10.09.2026
8
+
9
+ ### Added
10
+
11
+ - **`npm run check:oklch` — the perceptual half of the palette gate**, and a CI step. WCAG's
12
+ ratio is a luminance formula: it cannot say whether a state step is visible, whether a status
13
+ colour has stopped being a colour, or whether two statuses are the same hue. Three rules, every
14
+ threshold set after measuring all eleven shipped palettes rather than before. It found two
15
+ defects on its first run, neither of which any contrast ratio would ever have caught — both are
16
+ fixed below.
17
+
18
+ **One rule `docs/backlog.md` asked for is deliberately not in it.** "Ramps must be monotonic and
19
+ evenly spaced in L" fails eight of the eleven palettes, and eight of them are right: on a light
20
+ ground the hover fill is _darker_ than the rest state, which is a decision taken in 21.7.0 with
21
+ the numbers behind it. What is checkable is that the step exists, not which way it points.
22
+
23
+ - **`--gog-control-boundary-color`, and `npm run suggest:color`.** A foundation token for the edge
24
+ that _identifies_ a control, separate from `--gog-border-color`, which draws dividers, table
25
+ rules and panel outlines and is deliberately kept faint. `gog-chip`, `gog-toggle` and
26
+ `gog-button-toggle` had been reading the decorative one as their own boundary; measured across
27
+ all eleven themes they sat at 1.18 to 2.17:1, against the 3:1 WCAG SC 1.4.11 requires, and
28
+ neither their border nor their fill carried it. All three read the new token now.
29
+
30
+ Its eleven values are computed rather than chosen. `npm run suggest:color -- <ink> <ground>
31
+ [target]` takes a hex or a `--gog-*` token, resolves it per theme, and walks lightness in OKLCH
32
+ — holding hue and chroma, so a gold comes back gold rather than brown — until the ratio clears,
33
+ then verifies the answer by measuring it. It reports per theme, because a palette problem is
34
+ almost never in one theme alone, and it says so plainly when a hue cannot reach the target at
35
+ any lightness instead of returning a colour that silently misses.
36
+
37
+ - **An elevation ladder — `--gog-elevation-0` through `-5` — and the ten knobs a theme turns to
38
+ place its own shadows on it.** Six heights, Z doubling: 0, 1, 2, 4, 8, 16. A step is two lights,
39
+ a contact shadow that hugs the object and never moves and a key light whose offset is Z and
40
+ whose blur is three times it. The library's own surfaces are placed on it: a toggle thumb at 1,
41
+ an `elevated` card or panel at 2, anything anchored to a control at 3 (the four dropdown panels,
42
+ the tooltip, the menu), a toast at 4, a modal dialog at 5.
43
+
44
+ **What it replaces is the reason it exists.** Eleven themes hand-authored 22 shadow values with
45
+ no relationship to each other, and three defects were living inside that: `--gog-menu-shadow`
46
+ aliased `--gog-dialog-shadow`, so a dropdown menu carried a modal's elevation while the four
47
+ dropdown panels beside it carried a panel's — two overlays of one kind, four steps apart;
48
+ `material` and `primeng` each declared `--gog-dialog-shadow` byte-identical to their own
49
+ `--gog-panel-shadow`, so on both a modal had the elevation of a card; and the same panel sat at
50
+ 4px of lift on the light theme and 10px on the dark one, with nothing anywhere stating that a
51
+ panel has two heights. None of the three is a value anybody typed wrong. They are what happens
52
+ when a number has to be restated by hand in a tenth file.
53
+
54
+ **The blur is three times the offset, and that number was measured rather than adopted.** The
55
+ ruleset this comes from specifies two (`docs/component-geometry.md`, L10). Every shadow the
56
+ library shipped disagreed: across 26 layers the ratio runs 1.33 to 6.00, the median is exactly
57
+ 3.00, and 16 of the 26 are 3.00 on the nose. It lands well — at the top step the blur is 48px,
58
+ which is the dialog's own shipped blur to the pixel, so what moves there is the height and not
59
+ the softness.
60
+
61
+ **A theme keeps its character, because the style is three of the knobs.** `--gog-elevation-key-x`,
62
+ `-key-y` and `-key-blur` are multipliers per unit of Z, and between them they cover every style
63
+ in the package: leave them for a soft drop shadow, set the two offsets to a fraction and the blur
64
+ to `0` for `bevel` and `ledger`'s hard offset, set `-key-y` to `0` and keep the blur and the key
65
+ light becomes `terminal`'s phosphor glow — the ladder never learns what a glow is. `parchment`
66
+ climbs at a quarter of the rate, because paper does not float. Three of the four styles reproduce
67
+ their theme's shipped panel exactly.
68
+
69
+ The hairline ring and the top-edge catch light are deliberately **not** part of a step, and
70
+ compose over one instead (`var(--gog-elevation-ring), var(--gog-elevation-3)`). An `elevated`
71
+ card on a dark ground must not have a ring — a ring is what `outlined` draws, and with it the two
72
+ variants render identically, which was a real defect fixed in 21.7.1. Folding it into the ladder
73
+ would have reintroduced it.
74
+
75
+ `npm run check:elevation` is a CI step from the day it went green. It checks the things that
76
+ actually drift rather than the ladder's arithmetic, which is asserted by construction: a theme
77
+ declares all ten knobs or none, no shadow token may be hand-written unless it is a named
78
+ non-height (an inset ring, an accent glow), and no preset may write a shadow at all.
79
+
80
+ - **`--gog-select-panel-radius` and `--gog-multiselect-panel-radius`.** Both default to
81
+ `var(--gog-radius)`, so nothing renders differently — what changes is that the dropdown panel
82
+ and the field it hangs from are now two boxes with two tokens. They had been one: the panel
83
+ painted `--gog-select-radius` / `--gog-multiselect-radius`, the _field's_ radius, so a theme
84
+ that rounded the control into a pill rounded the overlay into one as well. The filter input's
85
+ radius and the option row's radius both derive from the panel's corner, so they followed it
86
+ too. `gog-autocomplete` and `gog-datepicker` already declared this token; this is the pair of
87
+ the four dropdowns on `GogDropdownBase` that did not.
88
+
89
+ - **`--gog-select-options-padding` and `--gog-select-option-radius`**, the interior the select's
90
+ panel did not have. Its rows were full-bleed and square inside a rounded panel, while
91
+ `gog-autocomplete` and `gog-multiselect` inset theirs by `--gog-space-4` and round them
92
+ concentrically — three panels on one base, three interiors. The select now matches: a 4px
93
+ gutter and a `max(0px, panel radius - gutter)` row, derived rather than restated so it stays
94
+ true once `--gog-density` moves the padding.
95
+
96
+ ### Changed
97
+
98
+ - **`one-light`'s pressed state was the same colour as its rest state.**
99
+ `--gog-accent-dim` sat 0.023 of OKLCH lightness from `--gog-accent-color` — under the ~0.03 a
100
+ flat area needs to read as different at all — so a pressed button and a field boundary looked
101
+ like the rest state. Both values passed WCAG comfortably and were still the same colour, which
102
+ is the whole argument for measuring in a perceptual space. Now `#1e53c6`, same hue and chroma,
103
+ six hundredths of lightness down.
104
+
105
+ - **`terminal`'s info and success colours were the same green.** 4.6 degrees of hue and 0.032 of
106
+ lightness apart, so an info badge could not be told from a success badge — and greyscale did not
107
+ separate them either. `--gog-info-color` is now a phosphor cyan (`#4dd0e1`), 61 degrees from
108
+ success and 98 from warning. The theme already signalled in red and yellow, so a second phosphor
109
+ is the consistent answer rather than a departure from its character.
110
+
111
+ - **Eight focus indicators were nearly invisible and now read the accent.** `gog-checkbox`,
112
+ `gog-radio-group` and `gog-multiselect` drew their `:focus-visible` outline from
113
+ `--gog-accent-pale` — a wash, measured at 1.38:1 against the light page — and it was the only
114
+ thing marking focus on those controls. `gog-chip` reached for `--gog-chip-border`, its
115
+ decorative hairline, because it had a focus-ring width and offset but no colour;
116
+ `gog-accordion`'s header read `--gog-accordion-hover-ring`, so a hover-weight colour was doing
117
+ focus duty. Fourteen of the library's focus rings already read `--gog-accent-color`; these were
118
+ the outliers. New tokens: `--gog-chip-focus-ring-color` and
119
+ `--gog-accordion-focus-ring-color`. Visible, and meant to be: a focused checkbox now carries a
120
+ solid accent ring where it had a pale halo.
121
+
122
+ - **`material` and `primeng` give their form fields a boundary that can be seen.** Both pointed
123
+ `--gog-input-field-border` and four siblings at `--gog-border-color`, the decorative hairline,
124
+ putting every text field, select, multiselect and checkbox edge at 1.18–1.62:1. Each now carries
125
+ its own grey at the lightness that clears 3.2:1 against the harder of its two grounds, computed
126
+ by `suggest:color` with hue and chroma held, so both themes keep their own neutral. The
127
+ decorative token is untouched in both.
128
+
129
+ - **`gog-menu`'s panel sits at the dropdown tier instead of the dialog's.** Its shadow was an
130
+ alias of `--gog-dialog-shadow` (`0 24px 48px`, alpha 0.5 on the base themes) while
131
+ `gog-select`, `gog-multiselect`, `gog-autocomplete` and `gog-datepicker` all read
132
+ `--gog-panel-shadow`. A menu is the same kind of object as those four — an overlay anchored to
133
+ the control that opened it — so it now reads the same token they do, four steps down. Visible:
134
+ an open menu is noticeably lighter, and matches a select panel opened beside it. Override
135
+ `--gog-menu-shadow` to put it back.
136
+
137
+ - **`gog-chip`'s avatar is one ratio of the chip's type instead of five px values.**
138
+ `--gog-chip-<size>-avatar-size` is `1.5em` at every size — 16.5 / 18 / 21 / 24 / 27px against
139
+ the 14 / 16 / 20 / 24 / 28px it was. Those five were 1.27, 1.33, 1.43, 1.50 and 1.56 of the
140
+ label's font size: one relationship with five opinions, drifting monotonically, which is what
141
+ a value picked by eye per size looks like. 1.5 is the ratio `lg` already held, and it keeps the
142
+ intent the rest of the component is built on — the avatar reads larger than the label and hugs
143
+ the chip's leading edge, which is what `--gog-chip-avatar-inset-ratio` exists for. The tokens
144
+ stay per-size, so an override of one keeps working.
145
+
146
+ - **`gog-chip`'s remove button is one ratio of the chip's type instead of five px values.**
147
+ `--gog-chip-<size>-remove-size` is `1.125em` at every size — 12.4 / 13.5 / 15.8 / 18 / 20.3px
148
+ against the 12 / 14 / 16 / 18 / 20px it was. Those five were 1.091, 1.167, 1.143, 1.125 and
149
+ 1.111 of the label: non-monotonic and inside a 7% band, which is the signature of a px ladder
150
+ converted to `rem` rather than of five judgements. 1.125 is the ratio `lg` already held, and it
151
+ keeps what all five values agreed on — the mark a reader can press reads a shade heavier than
152
+ the one that only decorates. `lg` does not move and no other size moves by more than half a
153
+ pixel. This completes the chip: avatar, icon and remove are now three ratios of one type scale.
154
+
155
+ **The painted box is computed differently as a result**, and that is the part a theme override
156
+ can notice. `.gog-chip__remove` reads the token as its own `font-size`, so the box could no
157
+ longer restate the token — in every property but `font-size`, `em` resolves against the
158
+ element's _own_ size, and the multiplication would have compounded the ratio against the size it
159
+ had just produced. It is `calc(1em * var(--gog-chip-remove-scale))` now, which is the same
160
+ length by construction and stays correct whether an override is written in `em`, `rem` or `px`.
161
+
162
+ - **`--gog-chip-<size>-icon-size` is `1em`.** Nothing renders differently: the five rem values it
163
+ replaces were 11 / 12 / 14 / 16 / 18px against a label of exactly 11 / 12 / 14 / 16 / 18px, so
164
+ the ratio was already 1.00 five times over — stated by hand rather than by rule. Writing it as
165
+ `em` also means an override of a single size's font-size now carries its icon along, which the
166
+ rem ladder did not.
167
+
168
+ - **The select's option list gained a wrapper element**, `.gog-select__options`, to carry the
169
+ gutter above. On the scrolled content rather than on `gog-scroll` itself, which measures its
170
+ host to size the thumb.
171
+
172
+ ## [21.11.0] - 09.09.2026
173
+
174
+ ### Added
175
+
176
+ - **`--gog-space-40`**, the one step the spacing scale was missing. Added because the optical ratio
177
+ requires it rather than because a design asked for it: horizontal padding is exactly twice
178
+ vertical, `slg` controls carry 20px of vertical padding, and the scale went 32 and then jumped to 48. It multiplies `--gog-density` like every other step.
179
+
180
+ - **`--gog-text-2xs` (0.6875rem, 11px)**, the step below `xs`. `gog-chip` and `gog-tag` had both
181
+ written that value as a literal at `xsm` — the same number chosen twice, independently, which is
182
+ a missing rung rather than two opinions. Nothing renders differently; a theme moving the scale
183
+ now moves both with it.
184
+
185
+ - **A leading token for every block that sets a font size** — around forty-five of them, and the
186
+ reason is law 4 rather than tidiness: a block that changes its size while inheriting its leading
187
+ changes the ratio silently. A `--gog-text-xs` numeric table cell inside a `--gog-text-md` page
188
+ inherited the page's leading and landed on a ratio nobody chose. Where the role is shared the
189
+ token is shared: `--gog-field-line-height`, `--gog-field-label-line-height` and
190
+ `--gog-field-error-line-height` are declared once and aliased by every field, the way the padding
191
+ tier already works. Six roles, one leading each — the full table is in
192
+ `docs/component-geometry.md`.
193
+
194
+ ### Removed
195
+
196
+ - **`--gog-space-2`, `-6`, `-10`, `-14` and `-18` are gone from the spacing scale**, which is now
197
+ ten steps and every one of them a multiple of 4. This is the end of the sweep rather than the
198
+ start of it: the five could only come out once the last of their 102 readers had moved, and each
199
+ of those moves is a component entry above.
200
+
201
+ **Three lengths stay off the grid on purpose**, each with the reason in its own comment: a toggle
202
+ thumb's 2px inset, a scrollbar thumb's, and the 2px gap between the resize grip's three
203
+ hairlines. A length _inside_ a single painted mark defines that mark's shape rather than spacing
204
+ two things apart — at 4px the grip's stripes are a 20px block in the corner of every textarea
205
+ instead of a 9px hint. `--gog-focus-ring-offset` was never part of the scale and is unaffected.
206
+
207
+ If your app reads one of the five, replace it with the neighbouring step: the library rounded
208
+ **up** everywhere, because it had seventeen pointer targets under 24×24 and none over.
209
+
210
+ ### Changed
211
+
212
+ - **`gog-tag`'s padding lands on the 4px grid, at a ratio of exactly 2.0.** Horizontal padding is
213
+ twice vertical at every size, which is the library's optical-ratio law (`styling.instructions.md`);
214
+ the tag had been running 3.00 at `xsm` down to 1.60 at `slg`, five different opinions about one
215
+ shape. New values, per size: `4/8`, `4/8`, `8/16`, `8/16`, `12/24` (block/inline), with the gap
216
+ following the block padding as before.
217
+
218
+ **Two adjacent sizes now share a padding**, and that is the finding this first component
219
+ produced rather than an oversight. Five _distinct_ vertical paddings on a 4px grid would have to
220
+ run 4, 8, 12, 16, 20, which doubles `slg` and makes a tag the size of a button. So the size step
221
+ is carried by the type scale — 11, 12, 14, 16, 18px — and the padding repeats between adjacent
222
+ steps rather than the geometry inflating to keep five distinct numbers. Every sized component in
223
+ this release follows the same rule.
224
+
225
+ - **The shared control tier is on the grid, at a ratio of 2.0.** `--gog-control-padding-y`/`-x`
226
+ go from `10/14` (a ratio of 1.40) to `12/24`, and `--gog-field-md-padding-*` derives from them —
227
+ so an `md` input, select, multiselect, autocomplete and datepicker are all padded by this one
228
+ pair. `--gog-control-icon-offset` follows from 10px to 12px, which also moves the four
229
+ `*-actions-inset` tokens that read it.
230
+
231
+ - **`--gog-control-checkbox-padding` is 8px, not 6px**, which is the padding a checkable control's
232
+ `<label>` carries around its box. It is part of the pointer target rather than decoration: a
233
+ 12px `xsm` box inside 8px of padding is a 28px target, and that is how the smallest checkbox,
234
+ radio and toggle clear WCAG 2.5.8 without the painted box growing at all. The literal fallback
235
+ in `checkable-control.config.ts` moves with it — a fallback that disagrees with the token is a
236
+ second default nobody can find.
237
+
238
+ - **`gog-panel`'s four off-grid lengths move to the grid** — `xsm` vertical padding 10 to 12,
239
+ `sm` 14 to 16 with its gap 10 to 12, `md` 18 to 20. A surface like the card, and outside the
240
+ optical ratio for the same reason.
241
+
242
+ - **`gog-scroll`'s thumb hit padding is 8px, not 6px** — the invisible band around the thumb that
243
+ makes it easier to grab, so the grid moves it in the helpful direction. **The thumb's 2px inset
244
+ stays**, for the same reason the toggle's does: a length inside a single painted mark defines
245
+ that mark's shape, and 4px on a thin scrollbar would leave the thumb barely wider than the
246
+ hairline it rides in.
247
+
248
+ - **`gog-slider`'s thumb has a 24×24 pointer target and still paints at 16px**, and its gap goes
249
+ from 6px to 8px. This is the one control in the library a pointer _drags_, and a 24px dot would
250
+ cover the value it points at. It stops at 24 rather than 2.5.5's 44 on purpose: the track is
251
+ clickable along its whole length, so the coarse-pointer story is "tap the track", not "hit the
252
+ thumb". The AAA gap is written down in the stylesheet rather than left implicit.
253
+
254
+ - **`gog-datepicker` and `gog-calendar` are on the grid, at a ratio of 2.0.** The calendar's
255
+ footer buttons go from 4/10 (a ratio of 2.50, the only block in the library padded _too_ wide
256
+ for its height) to 4/8; the time row's gap 6 to 8 and its input 4/6 to 4/8; the datepicker's
257
+ actions gap 6 to 8 and its panel gap 2 to 4.
258
+
259
+ - **`gog-autocomplete`'s option row is 8/16 at a ratio of 2.0**, from 8/10, with its gap 10 to 12,
260
+ the actions gap 6 to 8 and the panel gap 2 to 4. An option row is a control — it is what a
261
+ pointer picks — so it takes the ratio while the panel holding it does not.
262
+
263
+ - **`gog-multiselect`'s panel chrome is on the grid** — the controls row 6/10 to 8/12, the
264
+ actions gap 6 to 8, the filter row 6 to 8 with its input at 8/16 (a ratio of 2.0, from 1.33),
265
+ the empty state 10 to 12, the `+N` overflow chip's gap 6 to 8, and the panel offset 2 to 4.
266
+
267
+ - **`gog-select`'s panel chrome is on the grid** — the filter row's padding 6 to 8, its input
268
+ 8/16 at a ratio of 2.0 (from 1.33), the empty-state padding 10 to 12, the clear button's gap 6
269
+ to 8, the option row's gap 10 to 12, and the panel's offset from the field 2px to 4px.
270
+
271
+ - **`gog-dialog`'s header padding is on the grid** — its bottom side goes from 14px to 16px, so
272
+ the header is padded 16/20/16 rather than 16/20/14.
273
+
274
+ - **`gog-menu`'s item padding is 8/16 at a ratio of 2.0**, from 8/12, and the panel's own gap
275
+ goes from 2px to 4px. A menu item is a control — it is the row a pointer activates — so unlike
276
+ the panel around it, the ratio applies.
277
+
278
+ - **`gog-tooltip`'s padding is 8/12, not 6/10** — on the grid. A bubble around text rather than a
279
+ control, so it stays outside the optical ratio.
280
+
281
+ - **`gog-toast` is on the grid** — the stack gap 10 to 12, its own padding 12/14 to 12/16, the
282
+ content gap 10 to 12, the action row's gap 6 to 8 and the close button's padding 2/4 to 4/8. A
283
+ surface, so outside the optical ratio; its two buttons are controls and are checked as targets.
284
+
285
+ - **`gog-card`'s four off-grid lengths move to the grid** — `xsm` side padding 10 to 12 and its
286
+ gap 6 to 8, `sm` side padding 14 to 16, `lg` gap 14 to 16. The card is **outside** the
287
+ optical-ratio law and stays at roughly 1.2: it frames projected content rather than balancing a
288
+ label, and 2.0 would give it twice as much padding at its sides as above.
289
+
290
+ - **`gog-table`'s cell padding is on the grid** — vertical `4/4/8/12/16` per size and horizontal
291
+ 16px, from `2/2/6/10/14` and 14. A row is 2 to 4px taller at every density. The table is
292
+ deliberately **outside** the optical-ratio law: a cell's padding is row density and a column
293
+ rhythm, not a label balanced inside a control, and 2.0 would put 32px between every column.
294
+ The sortable header cell is still checked as a pointer target, which is where the law that
295
+ applies to it lives.
296
+
297
+ - **`gog-tabs` is on the grid at a ratio of 2.0** — `8/16`, `8/16`, `12/24`, `16/32`, `20/40`
298
+ (vertical/horizontal), from 1.67, 1.75, 1.50, 1.50, 1.40. `xsm` and `sm` now share a padding and
299
+ are told apart by their type, as everywhere else in this release.
300
+
301
+ - **`gog-accordion`'s header is on the grid at a ratio of 2.0**, and it had the widest drift in
302
+ the library: 2.00, 1.67, 1.40, 1.14, 1.00 — by `slg` the header was padded equally on all four
303
+ sides, which is a box rather than a row. Now `4/8`, `8/16`, `12/24`, `16/32`, `20/40`. The body's
304
+ two off-grid paddings and the three 2px content gaps move with it.
305
+
306
+ **An `xsm` header is 24px tall**, up from 20. It grows rather than inflating a hit area because
307
+ headers stack directly on one another: an invisible target reaching past the row would overlap
308
+ the neighbouring header's, and two targets claiming the same pixels is worse than one small
309
+ target.
310
+
311
+ - **`gog-chip` is on the grid at a ratio of 2.0, and it is the one component whose paint had to
312
+ grow.** Padding goes to `4/8`, `4/8`, `8/16`, `12/24`, `12/24` (block/inline) and the gaps to 4,
313
+ 8, 8, 12, 12.
314
+
315
+ **A chip is 24px tall at minimum now**, which adds 0.8px at `xsm` and nothing at any other size —
316
+ `sm` measures 24.4px on its own. Everywhere else in this release an undersized target grew an invisible hit area and
317
+ the paint stayed put; a chip cannot do that, because `.gog-chip__surface` clips — the ripple and
318
+ the pill shape both need `overflow: hidden` — so a hit area larger than the surface is cut off at
319
+ the surface's edge. The same line is what gives the remove button room: a 24px target inside a
320
+ 21px box is impossible for exactly the same reason.
321
+
322
+ **The remove button's target is 24×24 while its glyph stays between 13px and 22px.** An X drawn
323
+ at 24px inside a chip would be the loudest thing in it.
324
+
325
+ - **`gog-toggle`'s state chrome is on the grid** — the offset of the on/off state labels and the
326
+ gap between a state label and the thumb both go from 6px to 8px. **The thumb's inset stays at
327
+ 2px**, deliberately and now with the reason written next to it: at 2px the thumb is 83% of a
328
+ 24px track, which is what makes a switch read as a switch, and 4px would take it to 67%. A
329
+ length inside a single painted mark is not spacing between two things.
330
+
331
+ - **`gog-radio-group`'s two gaps are 12px, not 10px** — between a radio and its label, and
332
+ between the options in a group.
333
+
334
+ - **`gog-checkbox`'s gap between box and label is 12px, not 10px** — its one length off the 4px
335
+ grid.
336
+
337
+ - **The shared field tier is on the grid, at a ratio of 2.0.** Every text-bearing control that
338
+ reads it — `gog-inputfield`, `gog-textarea`, `gog-select`, `gog-multiselect`, `gog-autocomplete`
339
+ and `gog-datepicker` — is padded `4/8`, `8/16`, `12/24`, `16/32`, `20/40` (vertical/horizontal).
340
+ It had run 2.00, 1.67, 1.40, 1.29, 1.11 across the five sizes, drifting further from the ratio at
341
+ every step up; `slg` gains the most, from 20px of side padding to 40px. The icon offsets follow
342
+ the same grid — `xsm` 6 to 8, `lg` 14 to 16, `slg` 18 to 20 — which moves where the clear button
343
+ and the chevron sit inside a field.
344
+
345
+ - **`gog-button`'s padding is on the grid, at a ratio of exactly 2.0.** Horizontal padding is twice
346
+ vertical at every size step: `4/8`, `8/16`, `12/24`, `16/32`, `20/40` (vertical/horizontal). It
347
+ had run 2.00, 1.75, 1.67, 1.50, 1.40 across the five — monotonic drift, which is what per-size
348
+ eyeballing looks like from the outside. **`xsm` does not move and every other size gets wider**;
349
+ `slg` gains the most, from 28px of side padding to 40px.
350
+
351
+ - **Every `gog-button` carries a 24×24 pointer target, whatever its padding says.** A transparent
352
+ `::before` centred on the button supplies the size when the button is short, and no painted edge
353
+ moves. It is inert whenever the button is already big enough — which, it turns out, includes
354
+ `xsm` at its own padding: 4 + 12 + 4 is 20, but the button draws a 2px border on each side and
355
+ `box-sizing` is `border-box`, so it measures 24 in a browser. What the rule is actually for is a
356
+ button whose padding has been overridden, like `gog-toast`'s own two, which land at 20px at any
357
+ size class.
358
+
359
+ - **`gogBadge` hangs 8px outside its host's corner, not 6px.** The badge's one off-grid length, and
360
+ the commit that had to settle the tie-break behind all of them: every off-grid step this library
361
+ used sat exactly halfway between two grid steps, so snapping to 4px is a _direction_ rather than
362
+ a rounding rule. It rounds up. Here that is also right on its own terms — a 20px badge clears the
363
+ host's corner radius at `--gog-radius: 8px` at 8px of overhang, and did not quite at 6px.
364
+
365
+ - **`gog-autocomplete`'s option row is concentric with its panel at every density.** The radius was
366
+ `calc(var(--gog-radius) - 4px)`, which is the right number only where `--gog-density` is 1: the
367
+ 4px it subtracts is the list's own padding, and that padding scales with density while a restated
368
+ literal does not. It now reads the two tokens it is derived from, so at `--gog-density: 0.85` the
369
+ corner is 4.6px rather than 4px and the gap to the panel stays constant. Nothing moves at the
370
+ default density.
371
+
372
+ - **`gog-menu`'s item corner is 12px, not 8px.** The menu panel paints `--gog-panel-radius`, which
373
+ is the base radius plus 8, and insets its items by `--gog-menu-padding` (4px) — so a concentric
374
+ item corner is 16 − 4 = 12px, and the item had been repeating the base `--gog-radius` instead.
375
+ At the default theme the first and last items were visibly squarer than the corner they sit in.
376
+ `--gog-menu-item-radius` now derives from the panel radius and the padding rather than restating
377
+ a value, so a theme that changes either carries the item with it.
378
+
379
+ - **`gog-multiselect`'s panel interior is concentric with the panel — two corners, both derived.**
380
+ The option row went from 6px to 4px (8px panel, 4px of list padding), and **the filter input's
381
+ corner is now square**. The second one looks like a value clamped to zero and is not: the filter
382
+ is inset by 8px, which is exactly the panel's radius, and where the inset equals the radius the
383
+ inner box's corner point lands on the centre of the outer arc — so a right angle is equidistant
384
+ from the whole curve, and it is the only shape that keeps the gap constant. Both now read the
385
+ panel radius and their own padding rather than restating a number, which is also what makes them
386
+ hold at a density other than 1.
387
+
388
+ - **`gog-select`'s filter input has a square corner**, for the same reason the multiselect's does:
389
+ it is inset by exactly the panel's radius, and at that distance a right angle is the only corner
390
+ that stays an equal distance from the panel's curve. `--gog-select-filter-input-radius` derives
391
+ from the panel radius and the filter padding rather than repeating `--gog-radius`.
392
+
393
+ - **`gog-scroll`'s thumb derives its corner from the track**, `--gog-scroll-track-radius` minus
394
+ `--gog-scroll-thumb-inset`, where it had repeated `--gog-radius`. **No pixel changes today**: at
395
+ both track widths (10px and 6px, less 2px of inset per side) either radius already exceeds half
396
+ the thumb's width and renders as a full pill. It matters to a theme that raises `--gog-radius`
397
+ on a wide scroller, where the thumb would otherwise carry the same corner as the track it sits
398
+ inside.
399
+
400
+ - **`gog-accordion`'s chevron is a ratio of its header instead of a px ladder, and it fits its box
401
+ now.** Ten literals become two: `1em` for the em basis and `1.4em` for the surrounding box.
402
+ Writing it as a ratio surfaced two bugs the ladder had hidden. It was **px beside a label in
403
+ `rem`**, so raising the browser's text size grew the label and left the chevron where it was —
404
+ and its box/label ratio drifted 0.92 to 1.29 across the five sizes. And **the glyph overflowed
405
+ its box at every size**: the font-size token is the em basis for the `gog-icon` inside, which
406
+ renders at 1.2em of it, so a 21.6px mark sat in a 20px box. The chevrons at `xsm`, `sm` and `md`
407
+ are identical now, because those three sizes all label with `--gog-text-xs` and the chevron
408
+ ladder was the only thing distinguishing them.
409
+
410
+ - **`--gog-panel-heading-line-height` is `snug` (1.3), not 1.25** — the one leading in the library
411
+ outside `--gog-line-height-*`, which runs 1.2 then 1.3 with nothing between.
412
+
413
+ - **Eight literal `line-height` declarations in component stylesheets now read tokens**, so a theme
414
+ can reach them at all: the checkbox and input icons, the three clear buttons, the toggle's state
415
+ label and the textarea's own field, which is the one place the library renders a paragraph the
416
+ consumer typed. The dialog's was dead code — its close button already read the token two lines
417
+ below.
418
+
419
+ - **`gog-tooltip`, `gog-menu` and `gog-toast` cap themselves against the viewport.**
420
+ `--gog-tooltip-max-width`, `--gog-menu-max-width` and `--gog-toast-max-width` each become
421
+ `min(<cap>, calc(100vw - <margin> * 2))` — an overlay positioned against the screen itself rather
422
+ than a container, where "no wider than the screen" is what the component is for. The margin reads
423
+ the component's own edge-inset token where one exists (toast's `--gog-toast-stack-padding`)
424
+ rather than a new one; menu and tooltip, which have none, read `--gog-space-16` directly.
425
+
426
+ **`gog-confirmation-dialog` deliberately does not get one**, though an earlier draft of this
427
+ release gave it one: it renders inside `.gog-dialog__panel`, which already defaults to `90vw`,
428
+ and the dialog body pads 20px a side inside that — so the width available to it is
429
+ `0.9 * 100vw - 40px`, tighter than any `100vw - margin` clamp above an 80px viewport. The clamp
430
+ could never bind, and an inert declaration is worse than an absent one. Its cap is the measure
431
+ alone. The three dropdown panel widths (`autocomplete`/`select`/`multiselect`, 420px) are
432
+ unchanged for a related reason — each already tracks its trigger field's own width via
433
+ `min-width: 100%`.
434
+
435
+ **A `min-width` outranks a `max-width`, so each overlay's own floor still decides below a
436
+ certain width**, unchanged by this release: menu at 212px of viewport, toast at 312px — both
437
+ narrower than any device in use.
438
+
439
+ - **Three caps move from `px` to `ch`**: tooltip `43ch`, toast `53ch`, the
440
+ confirmation dialog `51ch` — each the nearest whole character to what the cap already rendered,
441
+ so a consumer raising the relevant font-size token now widens the bubble with it instead of the
442
+ text silently dropping from 47 characters a line to 30. Menu stays in `px`: its items do not
443
+ wrap, so a character measure would be measuring nothing. `docs/component-geometry.md`, "D7 —
444
+ taken", has the survey and the reasoning, including a live-caught bug in the fix itself: `ch`
445
+ resolves against the font of the element `max-width` is declared on, not a descendant's, which
446
+ toast's own message/container split got wrong on the first pass.
447
+
448
+ ### Fixed
449
+
450
+ - **`gog-dialog`'s title now states its own size.** `.gog-dialog__title` (the library's only
451
+ `<h2>`) declared neither `font-size` nor `line-height`, so it rendered at the browser's default
452
+ 24px bold rather than a token. `--gog-dialog-title-font-size` reads `--gog-text-xl` — exactly
453
+ today's rendered size, so nothing changes visibly — and `--gog-dialog-title-line-height` reads
454
+ `--gog-line-height-snug`, law 4's `heading` role. A theme raising `--gog-text-xl` now resizes the
455
+ dialog title with it, which it previously could not.
456
+
457
+ - **`gog-confirmation-dialog`'s title and description now read the type scale.** Both carried a
458
+ dead utility class (`heading-md`, `body-sm`) with no CSS rule anywhere in the library, so the
459
+ title rendered at the browser's default `<h3>` size (18.72px) and the description at the
460
+ inherited 16px, neither following `--gog-text-*`. The classes are removed; the title now reads
461
+ `--gog-text-lg` (18px, the nearest step to what already rendered — a dead class named `md` is
462
+ not evidence it was meant to be one) and the description reads `--gog-text-sm` (14px, a visible
463
+ 2px shrink, the size every other muted secondary line in the library already uses). A theme
464
+ moving the scale now moves both with it. `--gog-confirmation-dialog-max-width` (51ch) is
465
+ unaffected — a `ch` cap resolves against the font of the element `max-width` is declared on
466
+ (`.confirm-dialog`), not the description's, so the cap's pixel width does not change; only the
467
+ measure it produces against the now-smaller description does, and it stays in L9's 45–75 band.
468
+
469
+ ### Documentation
470
+
471
+ - **Every class a library template applies to its own markup is checked for the `gog-` prefix**
472
+ (`npm run check:class-names`, a CI step). The confirmation-dialog defect above is exactly what an
473
+ unprefixed class can hide: two utility classes with no CSS rule anywhere in the library. The
474
+ check does not require every class to have a rule — roughly 50 of the library's own state and
475
+ behaviour hooks (`gog-scroll--dragging`, `gog-select--floated`, …) legitimately have none — only
476
+ that it be named as this library's own rather than a name that looks borrowed or invented in
477
+ passing. Two legacy unprefixed names on shipped components (`confirm-dialog*`, `slide-left`) are
478
+ named exceptions rather than renamed, since a rename there is a consumer-visible class change.
479
+
480
+ - **The icon set is verified centred, and the audit reversed the rule it was written for.** All 41
481
+ built-in glyphs now pass a check (`npm run check:geometry`, second half): a glyph centres its ink
482
+ in its own viewBox, so that centring the box centres the mark. Nothing moved — every glyph
483
+ already passed, to within a hundredth of a unit horizontally.
484
+
485
+ The audit is the interesting half. The rule had been planned against the ink's **centre of
486
+ mass**, and by that measure the set looks broken: `arrow-right`'s mass sits 2.05 units right of
487
+ centre and `download`'s 3.47 units low, on a 24 grid. Neither is a defect. A directional glyph is
488
+ _supposed_ to carry its mass toward its head, and re-centring one would pull its tail off the
489
+ edge of the box. What the eye reads in a uniform-weight set is the extent, so the extent is what
490
+ is checked — with one branch for a **filled** mark, where a solid triangle's centroid genuinely
491
+ sits a sixth of its width off the box centre. The registry has one filled glyph (`star-filled`,
492
+ 0.51 low) and that branch is what would catch a play triangle the day one is added.
493
+
494
+ - **`README.md` carries the recipe for making the library fluid**, which it had never stated and a
495
+ consumer had to derive: the two-point linear interpolation, worked, applied to the root font size
496
+ (the type scale is in `rem`, so one `clamp()` moves all of it) — with the reason the intercept
497
+ belongs in `rem` rather than `px`, which is WCAG 1.4.4. It also says plainly why the library
498
+ itself ships zero `clamp()`, zero `vw` and zero breakpoints, and how to carry the spacing scale
499
+ along if you want gaps to grow with the type.
500
+
501
+ - **`AGENTS.md`'s dropdown section says when to turn `filter` on**, and the narrower half of the
502
+ rule that most quotations of it drop: choice time grows with the log of the option count, so a
503
+ panel past roughly seven options wants a filter — _unless_ the list is one the reader can
504
+ predict, in which case they are searching rather than choosing and ordering it well is worth as
505
+ much. `GOG_CONFIG.dropdown.filter` sets it once for an app.
506
+
507
+ - **`README.md`'s fluid-sizing section is corrected, and it was wrong before this release as well
508
+ as after it.** It said the library ships zero `clamp()`, zero `vw` and zero breakpoints. The
509
+ overlay caps above made the first half false; the second half had never been true — a dialog
510
+ panel has defaulted to `90vw` and `--gog-dialog-max-height` to `90vh` for as long as both have
511
+ existed, `gog-menu` falls back to `100vh`, and `gog-table`'s `maxHeight` takes `'60vh'` as its own
512
+ documented example. The section now scopes the claim to _component sizing_, lists every viewport
513
+ unit the library actually contains, and says why none of them is the recipe beside it: the recipe
514
+ grows a size with the viewport, these only ever cap one against it.
515
+
516
+ Worth keeping for the shape of the mistake rather than the fact: the claim was checked by
517
+ grepping `.css` and `.scss`, and every counterexample but one lives in exactly those files. The
518
+ one that does not — the dialog panel's `90vw` — is an inline `[style.max-width]` binding in a
519
+ template, and it is the one that mattered, because it is what makes a clamp on the confirmation
520
+ dialog unable to bind. `npm run survey:measure` now inventories viewport units across CSS, SCSS
521
+ **and** templates, so the next such claim is measured rather than recalled.
522
+
7
523
  ## [21.10.0] - 05.09.2026
8
524
 
9
525
  ### Added
@@ -45,7 +561,7 @@ reached 1.0, so breaking changes may land in minor versions.
45
561
 
46
562
  Nothing changes for an app that configures no spinner: an overlay with no `variant` still
47
563
  renders `runic`. The type widens rather than narrows, so a binding that passes a
48
- `GogSpinnerVariant` still compiles; only code that *reads* `overlay.variant()` now has to
564
+ `GogSpinnerVariant` still compiles; only code that _reads_ `overlay.variant()` now has to
49
565
  account for `undefined`.
50
566
 
51
567
  Found from the documentation side rather than from a report — the same way 21.8.0's four
@@ -102,7 +618,7 @@ reached 1.0, so breaking changes may land in minor versions.
102
618
  — the same move the theme's three status colours made in 21.9.0.
103
619
 
104
620
  A false positive is recorded rather than silenced: `.gog-checkbox__box` states the tick's colour
105
- and the *unchecked* box's background in one rule, and those never render together, so the sweep
621
+ and the _unchecked_ box's background in one rule, and those never render together, so the sweep
106
622
  read 1.00:1 in `ledger`. It is the one entry in the script's `REST_PAIRS_NOT_RENDERED` list, and
107
623
  the pair that does render — the tick on the checked background — is measured and passes.
108
624
 
@@ -138,10 +654,10 @@ reached 1.0, so breaking changes may land in minor versions.
138
654
  `gog-progressbar`, whose `GogProgressbarVariant` is now an alias of it.
139
655
 
140
656
  Two colour rules came out of measuring rather than choosing. A **filled** severity button's
141
- label is `--gog-<status>-text-color`, and hover and press deepen the fill *away* from it via
657
+ label is `--gog-<status>-text-color`, and hover and press deepen the fill _away_ from it via
142
658
  `--gog-<status>-shade`, so every state makes the label easier to read rather than harder — the
143
659
  first attempt deepened toward the page's ink unconditionally and cost `primeng`'s info button
144
- 6.44:1 down to 4.23:1 on press, because there the label *is* the ink. A **transparent** one's
660
+ 6.44:1 down to 4.23:1 on press, because there the label _is_ the ink. A **transparent** one's
145
661
  label is `--gog-button-<status>-ink`, the status hue mixed halfway toward the ink, because the
146
662
  raw hue is legible body text in only five of the eleven shipped themes; the 50% is the binding
147
663
  case (`material`'s amber) rather than a round number. All four severities, four variants and
@@ -242,7 +758,7 @@ reached 1.0, so breaking changes may land in minor versions.
242
758
  (which dialogs and menus read), `tooltip` and the blocking `spinner-overlay` were five
243
759
  unrelated literals — 1, 100, 300, 400, 8000 — so an app that had to lift the library above its
244
760
  own chrome edited five tokens and hoped it had found them all. Each is `calc(var(--gog-z-base) +
245
- N)` now: the numbers are unchanged at `--gog-z-base: 0`, and setting it to 10000 gives
761
+ N)` now: the numbers are unchanged at `--gog-z-base: 0`, and setting it to 10000 gives
246
762
  10001/10100/10300/10400/18000 — the same order, one number. The gaps are deliberate, so an app
247
763
  can still slot its own element between two library layers.
248
764
 
@@ -250,7 +766,7 @@ reached 1.0, so breaking changes may land in minor versions.
250
766
  while `gog-accordion`, the `gog-select` and `gog-multiselect` option rows (0.5) and `gog-chip`
251
767
  (0.55) each carried their own. One state should not have four opinions, and the three
252
768
  stragglers were invisible to rule G by construction — it flags a literal only when the value
253
- *matches* the token's. **This is a visual change**: those four fade slightly further now.
769
+ _matches_ the token's. **This is a visual change**: those four fade slightly further now.
254
770
 
255
771
  - **The type scale gained the step it was missing: `--gog-text-slg` (1.25rem).** `gog-button` and
256
772
  the field controls both needed a size for their `slg` variant, the scale went straight from
@@ -264,7 +780,7 @@ reached 1.0, so breaking changes may land in minor versions.
264
780
  retuned `--gog-text-*` moved most of the library and left `gog-button`, `gog-chip`, `gog-tag`,
265
781
  the dialog's close button, the toast's action and close, and the toggle's `lg` state label
266
782
  behind — each held a literal that was byte-for-byte a scale step (`--gog-button-md-font-size:
267
- 1rem` sitting beside `--gog-text-md: 1rem`). Same values, so nothing moves in any theme; the
783
+ 1rem` sitting beside `--gog-text-md: 1rem`). Same values, so nothing moves in any theme; the
268
784
  difference is that retuning the scale now reaches them.
269
785
 
270
786
  `check-tokens` rule G covered radii, strokes, casing and tracking but **not** font size, which
@@ -285,7 +801,7 @@ reached 1.0, so breaking changes may land in minor versions.
285
801
  Found by counting rather than by looking: of 47 radius tokens, 30 already derived from
286
802
  `--gog-radius`, 8 are pills or circles (a shape, not a corner size), 5 are deliberately flat,
287
803
  and these 3 were the remainder. `check-tokens` rule G could not have caught them — it flags a
288
- literal only when its value *equals* a character token's, which is what keeps a pill's `999px`
804
+ literal only when its value _equals_ a character token's, which is what keeps a pill's `999px`
289
805
  from being called drift, and is exactly why a small arbitrary number is the shape of drift it
290
806
  cannot see. `gog-toast` and `gog-accordion` keep their flat corners, on the user's call: those
291
807
  are a chosen shape.
@@ -303,7 +819,7 @@ reached 1.0, so breaking changes may land in minor versions.
303
819
  - **A toggle button now looks toggled.** 21.8.0 taught `gog-button` to forward `aria-pressed`,
304
820
  and nothing in the library styled it — so a toggle could announce itself as on to a screen
305
821
  reader while looking identical to an off one, which is WCAG 1.4.1 from the other side and
306
- exactly the reason `docs/backlog.md` gives for *not* forwarding `aria-pressed` to `gog-chip`.
822
+ exactly the reason `docs/backlog.md` gives for _not_ forwarding `aria-pressed` to `gog-chip`.
307
823
  `aria-pressed="true"` (and `"mixed"`) now draws an inset ring: new
308
824
  `--gog-button-<variant>-toggled-shadow`, with `--gog-button-toggled-shadow` as the per-instance
309
825
  override. A ring rather than a fill because `:hover` and `:active` already own the background —
@@ -320,14 +836,14 @@ reached 1.0, so breaking changes may land in minor versions.
320
836
  drops only the movement. New per-variant `--gog-button-<variant>-press-bg`/`-press-color`
321
837
  tokens and the usual `--gog-button-press-bg`/`-press-color` instance overrides. **Ghost presses
322
838
  to a filled `--gog-accent-dim`, like outline, rather than to a wash** — checked across all 11
323
- themes, and a wash cannot work there: ghost's own *label* is the accent, so tinting its ground
839
+ themes, and a wash cannot work there: ghost's own _label_ is the accent, so tinting its ground
324
840
  with the accent walks the two together, and a 24% wash put the label under 4.5:1 in seven
325
841
  themes. A filled press moves the label to `--gog-accent-text-color`, the pair `check:contrast`
326
842
  already gates, so no future theme can quietly break it. Reduced motion must remove the animation, not the
327
843
  information — the same rule the toast countdown was fixed under in 21.7.1.
328
844
 
329
845
  The family is spelled `press`, not `active`, because `active` already means two different
330
- things in this library — `--gog-tabs-active-color` is the *selected* tab, while
846
+ things in this library — `--gog-tabs-active-color` is the _selected_ tab, while
331
847
  `--gog-scroll-thumb-active-bg` is the thumb being dragged. One name, one meaning. The shipped
332
848
  `--gog-button-active-scale` keeps its spelling: renaming a token consumers already override
333
849
  needs a deprecation cycle, and this is a patch.
@@ -366,12 +882,12 @@ reached 1.0, so breaking changes may land in minor versions.
366
882
  every theme, and it is still a clear step past the 10-12% hover.
367
883
 
368
884
  - **A ghost button's label was under WCAG AA on its own hover, in three themes.** `light`
369
- 3.94:1, `primeng` 4.18:1, `one-light` 4.22:1. The variant's resting label *is*
885
+ 3.94:1, `primeng` 4.18:1, `one-light` 4.22:1. The variant's resting label _is_
370
886
  `--gog-accent-color` and its hover tints the ground with the same accent, so the two walked
371
887
  toward each other. **No background fixes it**, which is why this took a sweep rather than a
372
888
  nudge: a half-strength wash (4.28), a neutral `--gog-hover-color` (4.15), a text scrim (3.91)
373
889
  and an accent-dim wash (3.96) were all measured across the 11 themes, and `light` fails every
374
- one — `--gog-accent-color` as *text* on that theme's background is 4.60:1 to begin with, so
890
+ one — `--gog-accent-color` as _text_ on that theme's background is 4.60:1 to begin with, so
375
891
  there is no headroom to spend on any ground at all. The label now becomes `--gog-text-color`
376
892
  while hovered, which clears 5.29:1 at worst (one-dark) and leaves the wash untouched, so the
377
893
  hover stays the subtle one this variant is documented to have. Ghost's three states now read
@@ -388,7 +904,7 @@ reached 1.0, so breaking changes may land in minor versions.
388
904
  the check that now catches it.
389
905
 
390
906
  - **The other nine pressable surfaces had no press feedback either — now eight of them do.**
391
- `.gog-btn:active` turned out to be the *only* `:active` rule in the library. `gogMenuItem`,
907
+ `.gog-btn:active` turned out to be the _only_ `:active` rule in the library. `gogMenuItem`,
392
908
  `gog-chip`, `gog-tabs` headers, `gog-accordion` headers, `gog-button-toggle-group` options and
393
909
  the `gog-select` / `gog-multiselect` / `gog-autocomplete` option rows all acknowledged a press
394
910
  through the ripple alone — which is off by default and suppressed under reduced motion — so a
@@ -396,7 +912,7 @@ reached 1.0, so breaking changes may land in minor versions.
396
912
  `--gog-<block>-press-bg`: a wash roughly double the 10-12% one its own hover uses, in the same
397
913
  ingredient, with a flat `--gog-border-color` for browsers without `color-mix()`. Two of them
398
914
  are not that shape and say so in place: a `gog-tabs` header paints no background in any other
399
- state (its hover moves the label colour only), and a *selected* button-toggle option is already
915
+ state (its hover moves the label colour only), and a _selected_ button-toggle option is already
400
916
  filled, so it deepens to `--gog-accent-dim` the way the filled button variants do.
401
917
 
402
918
  **`gogCollapsibleTrigger` is deliberately not in that list.** It is the consumer's own element
@@ -422,14 +938,14 @@ reached 1.0, so breaking changes may land in minor versions.
422
938
 
423
939
  - **The outline button's label was unreadable while hovered, in every shipped theme.**
424
940
  `--gog-button-outline-hover-color` resolved to `--gog-primary-color`, the colour of text on the
425
- *page*, while the hover fill is the accent — pale parchment on bright amber in `dark` (1.41:1),
941
+ _page_, while the hover fill is the accent — pale parchment on bright amber in `dark` (1.41:1),
426
942
  light grey on blue in `one-dark` (1.11:1), and failing WCAG AA in all 11 themes, the best of
427
943
  them `light` at 3.65:1. It now resolves to `--gog-accent-text-color`, the token that means "text
428
944
  on an accent fill" and the one both filled variants already used. Found while adding the state
429
945
  above, which would have copied the same mistake into `:active`.
430
946
 
431
947
  - **`check:contrast` gained the pair that hid both.** The script had no pair for a label on the
432
- accent *fill* other than `accentText/accent`, so neither the outline label nor the new held
948
+ accent _fill_ other than `accentText/accent`, so neither the outline label nor the new held
433
949
  state was covered. `accentText/accentDim(active)` is now checked at 4.5:1, and it immediately
434
950
  failed one theme: `one-dark`'s `--gog-accent-dim` moved from `#4b8fca` to `#5399d6` (4.05:1 →
435
951
  4.59:1). That token had only ever been a field border, which is gated at 3:1; making it a fill