@guildofgleks/ui 21.9.0 → 21.10.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/styles/button.css CHANGED
@@ -1,387 +1,401 @@
1
- /*
2
- * The button's visual block, in a **global** stylesheet rather than the component's own.
3
- *
4
- * `gog-button` is not the only thing that renders it: `[gogButton]` applies these same classes to
5
- * a consumer's own `<a>` or `<button>`, and Angular's emulated encapsulation would never let a
6
- * component stylesheet reach an element declared in someone else's template. The same reason
7
- * `gogBadge` and `gog-collapsible` keep their CSS here.
8
- *
9
- * Token layering (see theme.css):
10
- * --gog-button-<prop> per-instance override. Never declared here or in the
11
- * theme, so it always wins when a consumer sets it.
12
- * --gog-button-variant-<prop> internal, set by the variant classes below.
13
- * --gog-button-size-<prop> internal, set by the size classes below.
14
- * --gog-button-<variant>-<prop> the actual values, all in theme.css.
15
- * Nothing in this file is a literal: every colour, font, metric and duration resolves
16
- * to a theme token, so a theme can restyle the button without touching this stylesheet.
17
- */
18
- .gog-btn {
19
- /* Scopes the spinner rendered inside a loading button to the variant's spinner colour. */
20
- --gog-spinner-color: var(
21
- --gog-button-spinner-color,
22
- var(--gog-button-variant-spinner-color, var(--gog-button-primary-spinner-color))
23
- );
24
-
25
- gap: var(--gog-button-gap);
26
- border: var(--gog-button-border-width) var(--gog-button-border-style)
27
- var(--gog-button-border, var(--gog-button-variant-border, var(--gog-button-primary-border)));
28
- border-radius: var(--gog-button-radius);
29
- padding: var(--gog-button-padding, var(--gog-button-size-padding, var(--gog-button-md-padding)));
30
- font-family: var(--gog-button-font-family);
31
- font-weight: var(--gog-button-font-weight);
32
- font-size: var(
33
- --gog-button-font-size,
34
- var(--gog-button-size-font-size, var(--gog-button-md-font-size))
35
- );
36
- letter-spacing: var(--gog-button-letter-spacing);
37
- text-transform: var(--gog-button-text-transform);
38
- line-height: var(--gog-button-line-height);
39
- background-color: var(
40
- --gog-button-bg,
41
- var(--gog-button-variant-bg, var(--gog-button-primary-bg))
42
- );
43
- color: var(--gog-button-color, var(--gog-button-variant-color, var(--gog-button-primary-color)));
44
- box-shadow: var(
45
- --gog-button-shadow,
46
- var(--gog-button-variant-shadow, var(--gog-button-primary-shadow))
47
- );
48
- cursor: pointer;
49
- /* An `<a>` carries the UA underline; a `<button>` never did, which is why the component never
50
- needed this. Reset here so `[gogButton]` on a link is visually identical to the component. */
51
- text-decoration: none;
52
- transition:
53
- background-color var(--gog-button-transition-duration) var(--gog-easing),
54
- box-shadow var(--gog-button-transition-duration) var(--gog-easing),
55
- color var(--gog-button-transition-duration) var(--gog-easing);
56
- }
57
-
58
- /*
59
- * The doubled class on this rule and the disabled one below is deliberate, and the only two
60
- * rules in this file that need it.
61
- *
62
- * `[gogButton]` goes on the *consumer's* own element, which is the element they style — and
63
- * Angular's emulated encapsulation stamps `[_ngcontent-…]` onto every rule in a component
64
- * stylesheet, so an ordinary `.my-btn { cursor: pointer }` is (0,2,0). That exactly ties
65
- * `.gog-btn:disabled` and wins on source order, because this file loads before the app's own
66
- * styles. Measured in the showcase: a disabled button went from `not-allowed`/`0.4` to
67
- * `pointer`/`1` under a single-class consumer rule — it looked and felt enabled while disabled.
68
- *
69
- * `:hover` and `:active` below need no help: their `:not(:disabled)` already makes them (0,3,0).
70
- * The base `.gog-btn` is left beatable on purpose — restyling the look is the consumer's call.
71
- * These two are not look, they are state and focus visibility, and losing them silently is a
72
- * correctness and accessibility regression rather than a taste one. A consumer who genuinely
73
- * wants to restyle them can still do it by being specific about it.
74
- */
75
- .gog-btn.gog-btn:focus-visible {
76
- outline: var(--gog-button-focus-ring-width) solid
77
- var(
78
- --gog-button-hover-bg,
79
- var(--gog-button-variant-hover-bg, var(--gog-button-primary-hover-bg))
80
- );
81
- outline-offset: var(--gog-button-focus-ring-offset);
82
- }
83
-
84
- .gog-btn:hover:not(:disabled) {
85
- background-color: var(
86
- --gog-button-hover-bg,
87
- var(--gog-button-variant-hover-bg, var(--gog-button-primary-hover-bg))
88
- );
89
- color: var(
90
- --gog-button-hover-color,
91
- var(--gog-button-variant-hover-color, var(--gog-button-primary-hover-color))
92
- );
93
- box-shadow: var(
94
- --gog-button-hover-shadow,
95
- var(--gog-button-variant-hover-shadow, var(--gog-button-primary-hover-shadow))
96
- );
97
- }
98
-
99
- /*
100
- * The held state. It changes colour *and* scales, and the colour is the part that matters:
101
- * `prefers-reduced-motion` switches the transform off below, and until 21.9.0 that was the whole
102
- * rule — so a reader with animations off pressed a button and nothing whatsoever happened. The
103
- * ripple does not cover it either: it is off by default and is itself suppressed under reduced
104
- * motion, deliberately, because it really is decoration. A colour change is not.
105
- *
106
- * This sits after `:hover` on purpose. Both are (0,3,0), so source order decides, and a pointer
107
- * is nearly always hovering the button it is pressing — the press has to win that tie or it is
108
- * invisible exactly when it is happening.
109
- */
110
- .gog-btn:active:not(:disabled) {
111
- background-color: var(
112
- --gog-button-press-bg,
113
- var(--gog-button-variant-press-bg, var(--gog-button-primary-press-bg))
114
- );
115
- color: var(
116
- --gog-button-press-color,
117
- var(--gog-button-variant-press-color, var(--gog-button-primary-press-color))
118
- );
119
- transform: scale(var(--gog-button-active-scale));
120
- }
121
-
122
- /*
123
- * A toggle button that is on, and the only state here that persists rather than tracking the
124
- * pointer. It therefore has to survive the ones that do not: `:hover` and `:active` both own the
125
- * background, so a ring is what is left that they cannot paint over. Last of the state rules and
126
- * (0,3,0) like them, so it wins the tie by source order — a toggled button that loses its ring
127
- * the moment the pointer arrives would announce `aria-pressed="true"` and show nothing, which is
128
- * the WCAG 1.4.1 failure this rule exists to prevent.
129
- *
130
- * `mixed` is styled like `true`: a partially-checked toggle is on for the purpose of "does this
131
- * look different from off".
132
- *
133
- * This matches `[gogButton]` on a consumer's own element too, which is the whole reason it keys
134
- * off the attribute rather than an input — the directive styles markup you own, so you write
135
- * `aria-pressed` there yourself and get the look for free.
136
- */
137
- .gog-btn[aria-pressed='true']:not(:disabled),
138
- .gog-btn[aria-pressed='mixed']:not(:disabled) {
139
- box-shadow: var(
140
- --gog-button-toggled-shadow,
141
- var(--gog-button-variant-toggled-shadow, var(--gog-button-primary-toggled-shadow))
142
- );
143
- }
144
-
145
- @media (prefers-reduced-motion: reduce) {
146
- .gog-btn {
147
- transition: none;
148
- }
149
-
150
- /* Only the movement goes. The colour above stays, and with the transition off it lands on the
151
- first frame of the press instead of easing in. */
152
- .gog-btn:active:not(:disabled) {
153
- transform: none;
154
- }
155
- }
156
-
157
- .gog-btn.gog-btn:disabled {
158
- opacity: var(--gog-button-disabled-opacity);
159
- cursor: not-allowed;
160
- }
161
-
162
- /* === Variants === */
163
-
164
- /* primary: solid gold */
165
- .gog-btn--primary {
166
- --gog-button-variant-bg: var(--gog-button-primary-bg);
167
- --gog-button-variant-color: var(--gog-button-primary-color);
168
- --gog-button-variant-border: var(--gog-button-primary-border);
169
- --gog-button-variant-shadow: var(--gog-button-primary-shadow);
170
- --gog-button-variant-hover-bg: var(--gog-button-primary-hover-bg);
171
- --gog-button-variant-hover-color: var(--gog-button-primary-hover-color);
172
- --gog-button-variant-hover-shadow: var(--gog-button-primary-hover-shadow);
173
- --gog-button-variant-toggled-shadow: var(--gog-button-primary-toggled-shadow);
174
- --gog-button-variant-press-bg: var(--gog-button-primary-press-bg);
175
- --gog-button-variant-press-color: var(--gog-button-primary-press-color);
176
- --gog-button-variant-spinner-color: var(--gog-button-primary-spinner-color);
177
- }
178
-
179
- .gog-btn--secondary {
180
- --gog-button-variant-bg: var(--gog-button-secondary-bg);
181
- --gog-button-variant-color: var(--gog-button-secondary-color);
182
- --gog-button-variant-border: var(--gog-button-secondary-border);
183
- --gog-button-variant-shadow: var(--gog-button-secondary-shadow);
184
- --gog-button-variant-hover-bg: var(--gog-button-secondary-hover-bg);
185
- --gog-button-variant-hover-color: var(--gog-button-secondary-hover-color);
186
- --gog-button-variant-hover-shadow: var(--gog-button-secondary-hover-shadow);
187
- --gog-button-variant-toggled-shadow: var(--gog-button-secondary-toggled-shadow);
188
- --gog-button-variant-press-bg: var(--gog-button-secondary-press-bg);
189
- --gog-button-variant-press-color: var(--gog-button-secondary-press-color);
190
- --gog-button-variant-spinner-color: var(--gog-button-secondary-spinner-color);
191
- }
192
-
193
- /* outline: transparent with golden border */
194
- .gog-btn--outline {
195
- --gog-button-variant-bg: var(--gog-button-outline-bg);
196
- --gog-button-variant-color: var(--gog-button-outline-color);
197
- --gog-button-variant-border: var(--gog-button-outline-border);
198
- --gog-button-variant-shadow: var(--gog-button-outline-shadow);
199
- --gog-button-variant-hover-bg: var(--gog-button-outline-hover-bg);
200
- --gog-button-variant-hover-color: var(--gog-button-outline-hover-color);
201
- --gog-button-variant-hover-shadow: var(--gog-button-outline-hover-shadow);
202
- --gog-button-variant-toggled-shadow: var(--gog-button-outline-toggled-shadow);
203
- --gog-button-variant-press-bg: var(--gog-button-outline-press-bg);
204
- --gog-button-variant-press-color: var(--gog-button-outline-press-color);
205
- --gog-button-variant-spinner-color: var(--gog-button-outline-spinner-color);
206
- }
207
-
208
- /* ghost: no border, subtle hover */
209
- .gog-btn--ghost {
210
- --gog-button-variant-bg: var(--gog-button-ghost-bg);
211
- --gog-button-variant-color: var(--gog-button-ghost-color);
212
- --gog-button-variant-border: var(--gog-button-ghost-border);
213
- --gog-button-variant-shadow: var(--gog-button-ghost-shadow);
214
- --gog-button-variant-hover-bg: var(--gog-button-ghost-hover-bg);
215
- --gog-button-variant-hover-color: var(--gog-button-ghost-hover-color);
216
- --gog-button-variant-hover-shadow: var(--gog-button-ghost-hover-shadow);
217
- --gog-button-variant-toggled-shadow: var(--gog-button-ghost-toggled-shadow);
218
- --gog-button-variant-press-bg: var(--gog-button-ghost-press-bg);
219
- --gog-button-variant-press-color: var(--gog-button-ghost-press-color);
220
- --gog-button-variant-spinner-color: var(--gog-button-ghost-spinner-color);
221
- }
222
-
223
- /* === Severity ===
224
- *
225
- * `severity` is orthogonal to `variant` — a destructive action can be filled, outlined or a
226
- * ghost and is destructive in all three — so these do not add a fifth variant. They re-point the
227
- * `--gog-button-variant-*` layer the variant classes above set, and they come *after* those
228
- * classes so the tie is decided by source order where specificity allows it.
229
- *
230
- * Two halves. The severity class carries only the ingredients (`--gog-button-severity-*`, from
231
- * the per-severity tokens in theme.css); the shape rules below say what a variant does with
232
- * them, once each rather than once per severity. Four plus three blocks instead of sixteen, and
233
- * a fifth status colour would be one more block rather than four.
234
- *
235
- * The shape rules are `(0,2,0)` through the doubled `:is()`, which is what puts them above the
236
- * `(0,1,0)` variant classes regardless of the order the two classes appear in on the element.
237
- */
238
- .gog-btn--success {
239
- --gog-button-severity-fill: var(--gog-button-success-fill);
240
- --gog-button-severity-on-fill: var(--gog-button-success-on-fill);
241
- --gog-button-severity-fill-hover: var(--gog-button-success-fill-hover);
242
- --gog-button-severity-fill-press: var(--gog-button-success-fill-press);
243
- --gog-button-severity-ink: var(--gog-button-success-ink);
244
- --gog-button-severity-wash: var(--gog-button-success-wash);
245
- }
246
-
247
- .gog-btn--danger {
248
- --gog-button-severity-fill: var(--gog-button-danger-fill);
249
- --gog-button-severity-on-fill: var(--gog-button-danger-on-fill);
250
- --gog-button-severity-fill-hover: var(--gog-button-danger-fill-hover);
251
- --gog-button-severity-fill-press: var(--gog-button-danger-fill-press);
252
- --gog-button-severity-ink: var(--gog-button-danger-ink);
253
- --gog-button-severity-wash: var(--gog-button-danger-wash);
254
- }
255
-
256
- .gog-btn--warning {
257
- --gog-button-severity-fill: var(--gog-button-warning-fill);
258
- --gog-button-severity-on-fill: var(--gog-button-warning-on-fill);
259
- --gog-button-severity-fill-hover: var(--gog-button-warning-fill-hover);
260
- --gog-button-severity-fill-press: var(--gog-button-warning-fill-press);
261
- --gog-button-severity-ink: var(--gog-button-warning-ink);
262
- --gog-button-severity-wash: var(--gog-button-warning-wash);
263
- }
264
-
265
- .gog-btn--info {
266
- --gog-button-severity-fill: var(--gog-button-info-fill);
267
- --gog-button-severity-on-fill: var(--gog-button-info-on-fill);
268
- --gog-button-severity-fill-hover: var(--gog-button-info-fill-hover);
269
- --gog-button-severity-fill-press: var(--gog-button-info-fill-press);
270
- --gog-button-severity-ink: var(--gog-button-info-ink);
271
- --gog-button-severity-wash: var(--gog-button-info-wash);
272
- }
273
-
274
- /* The two filled variants: the status colour is the fill, and the label is the one that reads
275
- on it. `secondary` loses its own neutral fill here, which is the point — a severity says what
276
- the action does, and the variant is left saying only how loud it is. */
277
- :is(.gog-btn--success, .gog-btn--danger, .gog-btn--warning, .gog-btn--info):is(
278
- .gog-btn--primary,
279
- .gog-btn--secondary
280
- ) {
281
- --gog-button-variant-bg: var(--gog-button-severity-fill);
282
- --gog-button-variant-color: var(--gog-button-severity-on-fill);
283
- --gog-button-variant-border: transparent;
284
- --gog-button-variant-shadow: none;
285
- --gog-button-variant-hover-bg: var(--gog-button-severity-fill-hover);
286
- --gog-button-variant-hover-color: var(--gog-button-severity-on-fill);
287
- --gog-button-variant-hover-shadow: none;
288
- --gog-button-variant-toggled-shadow: inset 0 0 0 var(--gog-button-toggled-ring-width)
289
- var(--gog-button-severity-on-fill);
290
- --gog-button-variant-press-bg: var(--gog-button-severity-fill-press);
291
- --gog-button-variant-press-color: var(--gog-button-severity-on-fill);
292
- --gog-button-variant-spinner-color: var(--gog-button-severity-on-fill);
293
- }
294
-
295
- /* Outline: the border and the label are the *ink* rather than the raw status colour, because
296
- this label is text on the page and the raw hue clears AA in only five of the eleven themes.
297
- Hovering fills with the status colour proper, which is where `-on-fill` comes back. */
298
- :is(.gog-btn--success, .gog-btn--danger, .gog-btn--warning, .gog-btn--info).gog-btn--outline {
299
- --gog-button-variant-bg: transparent;
300
- --gog-button-variant-color: var(--gog-button-severity-ink);
301
- --gog-button-variant-border: var(--gog-button-severity-ink);
302
- --gog-button-variant-shadow: none;
303
- --gog-button-variant-hover-bg: var(--gog-button-severity-fill);
304
- --gog-button-variant-hover-color: var(--gog-button-severity-on-fill);
305
- --gog-button-variant-hover-shadow: none;
306
- --gog-button-variant-toggled-shadow: inset 0 0 0 var(--gog-button-toggled-ring-width)
307
- var(--gog-button-severity-ink);
308
- --gog-button-variant-press-bg: var(--gog-button-severity-fill-press);
309
- --gog-button-variant-press-color: var(--gog-button-severity-on-fill);
310
- --gog-button-variant-spinner-color: var(--gog-button-severity-ink);
311
- }
312
-
313
- /* Ghost: a wash on hover rather than a fill, the same shape the accent ghost has. Its hover
314
- label is `--gog-text-color` for the reason 21.9.0 recorded on `--gog-button-ghost-hover-color`
315
- a label and a ground made of the same hue walk toward each other — and the press then fills
316
- properly, which is also what the accent ghost does. */
317
- :is(.gog-btn--success, .gog-btn--danger, .gog-btn--warning, .gog-btn--info).gog-btn--ghost {
318
- --gog-button-variant-bg: transparent;
319
- --gog-button-variant-color: var(--gog-button-severity-ink);
320
- --gog-button-variant-border: transparent;
321
- --gog-button-variant-shadow: none;
322
- --gog-button-variant-hover-bg: var(--gog-button-severity-wash);
323
- --gog-button-variant-hover-color: var(--gog-text-color);
324
- --gog-button-variant-hover-shadow: none;
325
- --gog-button-variant-toggled-shadow: inset 0 0 0 var(--gog-button-toggled-ring-width)
326
- var(--gog-button-severity-ink);
327
- --gog-button-variant-press-bg: var(--gog-button-severity-fill-press);
328
- --gog-button-variant-press-color: var(--gog-button-severity-on-fill);
329
- --gog-button-variant-spinner-color: var(--gog-button-severity-ink);
330
- }
331
-
332
- /* === Sizes === */
333
- .gog-btn--xsm {
334
- --gog-button-size-padding: var(--gog-button-xsm-padding);
335
- --gog-button-size-font-size: var(--gog-button-xsm-font-size);
336
- }
337
-
338
- .gog-btn--sm {
339
- --gog-button-size-padding: var(--gog-button-sm-padding);
340
- --gog-button-size-font-size: var(--gog-button-sm-font-size);
341
- }
342
-
343
- .gog-btn--md {
344
- --gog-button-size-padding: var(--gog-button-md-padding);
345
- --gog-button-size-font-size: var(--gog-button-md-font-size);
346
- }
347
-
348
- .gog-btn--lg {
349
- --gog-button-size-padding: var(--gog-button-lg-padding);
350
- --gog-button-size-font-size: var(--gog-button-lg-font-size);
351
- }
352
-
353
- .gog-btn--slg {
354
- --gog-button-size-padding: var(--gog-button-slg-padding);
355
- --gog-button-size-font-size: var(--gog-button-slg-font-size);
356
- }
357
-
358
- .gog-btn--loading {
359
- position: relative;
360
- pointer-events: none;
361
- /* Loading buttons stay natively focusable (aria-disabled + aria-busy instead of
362
- `disabled`) so keyboard users don't lose focus mid-action. `:disabled` styling
363
- below therefore no longer covers this state, so the dim is applied explicitly. */
364
- opacity: var(--gog-button-loading-opacity);
365
- }
366
-
367
- .gog-btn__content--hidden {
368
- visibility: hidden;
369
- }
370
-
371
- /* absolute center so the spinner is always in the middle of the button */
372
- .gog-btn__spinner {
373
- position: absolute;
374
- top: 50%;
375
- left: 50%;
376
- transform: translate(-50%, -50%);
377
- max-width: var(--gog-button-spinner-max-size);
378
- max-height: var(--gog-button-spinner-max-size);
379
- }
380
-
381
- /*
382
- * `gog-button` gets its full width through `:host`, having a wrapper element to work with.
383
- * `[gogButton]` has none — the element *is* the button — so it needs a class of its own.
384
- */
385
- .gog-btn--full-width {
386
- width: 100%;
387
- }
1
+ /*
2
+ * The button's visual block, in a **global** stylesheet rather than the component's own.
3
+ *
4
+ * `gog-button` is not the only thing that renders it: `[gogButton]` applies these same classes to
5
+ * a consumer's own `<a>` or `<button>`, and Angular's emulated encapsulation would never let a
6
+ * component stylesheet reach an element declared in someone else's template. The same reason
7
+ * `gogBadge` and `gog-collapsible` keep their CSS here.
8
+ *
9
+ * Token layering (see theme.css):
10
+ * --gog-button-<prop> per-instance override. Never declared here or in the
11
+ * theme, so it always wins when a consumer sets it.
12
+ * --gog-button-variant-<prop> internal, set by the variant classes below.
13
+ * --gog-button-size-<prop> internal, set by the size classes below.
14
+ * --gog-button-<variant>-<prop> the actual values, all in theme.css.
15
+ * Nothing in this file is a literal: every colour, font, metric and duration resolves
16
+ * to a theme token, so a theme can restyle the button without touching this stylesheet.
17
+ */
18
+ .gog-btn {
19
+ /* Scopes the spinner rendered inside a loading button to the variant's spinner colour. */
20
+ --gog-spinner-color: var(
21
+ --gog-button-spinner-color,
22
+ var(--gog-button-variant-spinner-color, var(--gog-button-primary-spinner-color))
23
+ );
24
+
25
+ gap: var(--gog-button-gap);
26
+ border: var(--gog-button-border-width) var(--gog-button-border-style)
27
+ var(--gog-button-border, var(--gog-button-variant-border, var(--gog-button-primary-border)));
28
+ border-radius: var(--gog-button-radius);
29
+ padding: var(--gog-button-padding, var(--gog-button-size-padding, var(--gog-button-md-padding)));
30
+ font-family: var(--gog-button-font-family);
31
+ font-weight: var(--gog-button-font-weight);
32
+ font-size: var(
33
+ --gog-button-font-size,
34
+ var(--gog-button-size-font-size, var(--gog-button-md-font-size))
35
+ );
36
+ letter-spacing: var(--gog-button-letter-spacing);
37
+ text-transform: var(--gog-button-text-transform);
38
+ line-height: var(--gog-button-line-height);
39
+ background-color: var(
40
+ --gog-button-bg,
41
+ var(--gog-button-variant-bg, var(--gog-button-primary-bg))
42
+ );
43
+ color: var(--gog-button-color, var(--gog-button-variant-color, var(--gog-button-primary-color)));
44
+ box-shadow: var(
45
+ --gog-button-shadow,
46
+ var(--gog-button-variant-shadow, var(--gog-button-primary-shadow))
47
+ );
48
+ cursor: pointer;
49
+ /* An `<a>` carries the UA underline; a `<button>` never did, which is why the component never
50
+ needed this. Reset here so `[gogButton]` on a link is visually identical to the component. */
51
+ text-decoration: none;
52
+ transition:
53
+ background-color var(--gog-button-transition-duration) var(--gog-easing),
54
+ box-shadow var(--gog-button-transition-duration) var(--gog-easing),
55
+ color var(--gog-button-transition-duration) var(--gog-easing);
56
+ }
57
+
58
+ /*
59
+ * The doubled class on this rule and the disabled one below is deliberate, and the only two
60
+ * rules in this file that need it.
61
+ *
62
+ * `[gogButton]` goes on the *consumer's* own element, which is the element they style — and
63
+ * Angular's emulated encapsulation stamps `[_ngcontent-…]` onto every rule in a component
64
+ * stylesheet, so an ordinary `.my-btn { cursor: pointer }` is (0,2,0). That exactly ties
65
+ * `.gog-btn:disabled` and wins on source order, because this file loads before the app's own
66
+ * styles. Measured in the showcase: a disabled button went from `not-allowed`/`0.4` to
67
+ * `pointer`/`1` under a single-class consumer rule — it looked and felt enabled while disabled.
68
+ *
69
+ * `:hover` and `:active` below need no help: their `:not(:disabled)` already makes them (0,3,0).
70
+ * The base `.gog-btn` is left beatable on purpose — restyling the look is the consumer's call.
71
+ * These two are not look, they are state and focus visibility, and losing them silently is a
72
+ * correctness and accessibility regression rather than a taste one. A consumer who genuinely
73
+ * wants to restyle them can still do it by being specific about it.
74
+ */
75
+ .gog-btn.gog-btn:focus-visible {
76
+ outline: var(--gog-button-focus-ring-width) solid
77
+ var(
78
+ --gog-button-hover-bg,
79
+ var(--gog-button-variant-hover-bg, var(--gog-button-primary-hover-bg))
80
+ );
81
+ outline-offset: var(--gog-button-focus-ring-offset);
82
+ }
83
+
84
+ .gog-btn:hover:not(:disabled) {
85
+ background-color: var(
86
+ --gog-button-hover-bg,
87
+ var(--gog-button-variant-hover-bg, var(--gog-button-primary-hover-bg))
88
+ );
89
+ color: var(
90
+ --gog-button-hover-color,
91
+ var(--gog-button-variant-hover-color, var(--gog-button-primary-hover-color))
92
+ );
93
+ box-shadow: var(
94
+ --gog-button-hover-shadow,
95
+ var(--gog-button-variant-hover-shadow, var(--gog-button-primary-hover-shadow))
96
+ );
97
+ }
98
+
99
+ /*
100
+ * The held state. It changes colour *and* scales, and the colour is the part that matters:
101
+ * `prefers-reduced-motion` switches the transform off below, and until 21.9.0 that was the whole
102
+ * rule — so a reader with animations off pressed a button and nothing whatsoever happened. The
103
+ * ripple does not cover it either: it is off by default and is itself suppressed under reduced
104
+ * motion, deliberately, because it really is decoration. A colour change is not.
105
+ *
106
+ * This sits after `:hover` on purpose. Both are (0,3,0), so source order decides, and a pointer
107
+ * is nearly always hovering the button it is pressing — the press has to win that tie or it is
108
+ * invisible exactly when it is happening.
109
+ */
110
+ .gog-btn:active:not(:disabled) {
111
+ background-color: var(
112
+ --gog-button-press-bg,
113
+ var(--gog-button-variant-press-bg, var(--gog-button-primary-press-bg))
114
+ );
115
+ color: var(
116
+ --gog-button-press-color,
117
+ var(--gog-button-variant-press-color, var(--gog-button-primary-press-color))
118
+ );
119
+ transform: scale(var(--gog-button-active-scale));
120
+ }
121
+
122
+ /*
123
+ * A toggle button that is on, and the only state here that persists rather than tracking the
124
+ * pointer. It therefore has to survive the ones that do not: `:hover` and `:active` both own the
125
+ * background, so a ring is what is left that they cannot paint over. Last of the state rules and
126
+ * (0,3,0) like them, so it wins the tie by source order — a toggled button that loses its ring
127
+ * the moment the pointer arrives would announce `aria-pressed="true"` and show nothing, which is
128
+ * the WCAG 1.4.1 failure this rule exists to prevent.
129
+ *
130
+ * `mixed` is styled like `true`: a partially-checked toggle is on for the purpose of "does this
131
+ * look different from off".
132
+ *
133
+ * This matches `[gogButton]` on a consumer's own element too, which is the whole reason it keys
134
+ * off the attribute rather than an input — the directive styles markup you own, so you write
135
+ * `aria-pressed` there yourself and get the look for free.
136
+ *
137
+ * **A disabled toggle keeps the ring** (21.10.0). It carried `:not(:disabled)` until then, copied
138
+ * from the pointer states above where the guard is correct — but "on, and you cannot change it
139
+ * right now" is a real state, and `disabled` on a real `<button>` does not remove `aria-pressed`,
140
+ * so dropping the look left the button announcing itself as on and looking exactly like an off
141
+ * one: the failure this rule was written to prevent, in the one case nobody looks at.
142
+ * `gog-chip` had it right from the start (`:host(.gog-chip--selected)`, no disabled guard) and
143
+ * the two had drifted apart. The `--gog-button-disabled-opacity` on the rule below dims the ring
144
+ * along with everything else, which is the correct amount of "unavailable".
145
+ *
146
+ * The doubled class is what the removed `:not(:disabled)` was silently paying for: both are
147
+ * (0,3,0), and this rule has to stay level with `:hover:not(:disabled)` to win the tie by source
148
+ * order. It buys the same protection `:focus-visible` and `:disabled` have from a consumer's
149
+ * single-class rule, which is right for a state and wrong for a look — see the comment on those.
150
+ */
151
+ .gog-btn.gog-btn[aria-pressed='true'],
152
+ .gog-btn.gog-btn[aria-pressed='mixed'] {
153
+ box-shadow: var(
154
+ --gog-button-toggled-shadow,
155
+ var(--gog-button-variant-toggled-shadow, var(--gog-button-primary-toggled-shadow))
156
+ );
157
+ }
158
+
159
+ @media (prefers-reduced-motion: reduce) {
160
+ .gog-btn {
161
+ transition: none;
162
+ }
163
+
164
+ /* Only the movement goes. The colour above stays, and with the transition off it lands on the
165
+ first frame of the press instead of easing in. */
166
+ .gog-btn:active:not(:disabled) {
167
+ transform: none;
168
+ }
169
+ }
170
+
171
+ .gog-btn.gog-btn:disabled {
172
+ opacity: var(--gog-button-disabled-opacity);
173
+ cursor: not-allowed;
174
+ }
175
+
176
+ /* === Variants === */
177
+
178
+ /* primary: solid gold */
179
+ .gog-btn--primary {
180
+ --gog-button-variant-bg: var(--gog-button-primary-bg);
181
+ --gog-button-variant-color: var(--gog-button-primary-color);
182
+ --gog-button-variant-border: var(--gog-button-primary-border);
183
+ --gog-button-variant-shadow: var(--gog-button-primary-shadow);
184
+ --gog-button-variant-hover-bg: var(--gog-button-primary-hover-bg);
185
+ --gog-button-variant-hover-color: var(--gog-button-primary-hover-color);
186
+ --gog-button-variant-hover-shadow: var(--gog-button-primary-hover-shadow);
187
+ --gog-button-variant-toggled-shadow: var(--gog-button-primary-toggled-shadow);
188
+ --gog-button-variant-press-bg: var(--gog-button-primary-press-bg);
189
+ --gog-button-variant-press-color: var(--gog-button-primary-press-color);
190
+ --gog-button-variant-spinner-color: var(--gog-button-primary-spinner-color);
191
+ }
192
+
193
+ .gog-btn--secondary {
194
+ --gog-button-variant-bg: var(--gog-button-secondary-bg);
195
+ --gog-button-variant-color: var(--gog-button-secondary-color);
196
+ --gog-button-variant-border: var(--gog-button-secondary-border);
197
+ --gog-button-variant-shadow: var(--gog-button-secondary-shadow);
198
+ --gog-button-variant-hover-bg: var(--gog-button-secondary-hover-bg);
199
+ --gog-button-variant-hover-color: var(--gog-button-secondary-hover-color);
200
+ --gog-button-variant-hover-shadow: var(--gog-button-secondary-hover-shadow);
201
+ --gog-button-variant-toggled-shadow: var(--gog-button-secondary-toggled-shadow);
202
+ --gog-button-variant-press-bg: var(--gog-button-secondary-press-bg);
203
+ --gog-button-variant-press-color: var(--gog-button-secondary-press-color);
204
+ --gog-button-variant-spinner-color: var(--gog-button-secondary-spinner-color);
205
+ }
206
+
207
+ /* outline: transparent with golden border */
208
+ .gog-btn--outline {
209
+ --gog-button-variant-bg: var(--gog-button-outline-bg);
210
+ --gog-button-variant-color: var(--gog-button-outline-color);
211
+ --gog-button-variant-border: var(--gog-button-outline-border);
212
+ --gog-button-variant-shadow: var(--gog-button-outline-shadow);
213
+ --gog-button-variant-hover-bg: var(--gog-button-outline-hover-bg);
214
+ --gog-button-variant-hover-color: var(--gog-button-outline-hover-color);
215
+ --gog-button-variant-hover-shadow: var(--gog-button-outline-hover-shadow);
216
+ --gog-button-variant-toggled-shadow: var(--gog-button-outline-toggled-shadow);
217
+ --gog-button-variant-press-bg: var(--gog-button-outline-press-bg);
218
+ --gog-button-variant-press-color: var(--gog-button-outline-press-color);
219
+ --gog-button-variant-spinner-color: var(--gog-button-outline-spinner-color);
220
+ }
221
+
222
+ /* ghost: no border, subtle hover */
223
+ .gog-btn--ghost {
224
+ --gog-button-variant-bg: var(--gog-button-ghost-bg);
225
+ --gog-button-variant-color: var(--gog-button-ghost-color);
226
+ --gog-button-variant-border: var(--gog-button-ghost-border);
227
+ --gog-button-variant-shadow: var(--gog-button-ghost-shadow);
228
+ --gog-button-variant-hover-bg: var(--gog-button-ghost-hover-bg);
229
+ --gog-button-variant-hover-color: var(--gog-button-ghost-hover-color);
230
+ --gog-button-variant-hover-shadow: var(--gog-button-ghost-hover-shadow);
231
+ --gog-button-variant-toggled-shadow: var(--gog-button-ghost-toggled-shadow);
232
+ --gog-button-variant-press-bg: var(--gog-button-ghost-press-bg);
233
+ --gog-button-variant-press-color: var(--gog-button-ghost-press-color);
234
+ --gog-button-variant-spinner-color: var(--gog-button-ghost-spinner-color);
235
+ }
236
+
237
+ /* === Severity ===
238
+ *
239
+ * `severity` is orthogonal to `variant` — a destructive action can be filled, outlined or a
240
+ * ghost and is destructive in all three — so these do not add a fifth variant. They re-point the
241
+ * `--gog-button-variant-*` layer the variant classes above set, and they come *after* those
242
+ * classes so the tie is decided by source order where specificity allows it.
243
+ *
244
+ * Two halves. The severity class carries only the ingredients (`--gog-button-severity-*`, from
245
+ * the per-severity tokens in theme.css); the shape rules below say what a variant does with
246
+ * them, once each rather than once per severity. Four plus three blocks instead of sixteen, and
247
+ * a fifth status colour would be one more block rather than four.
248
+ *
249
+ * The shape rules are `(0,2,0)` through the doubled `:is()`, which is what puts them above the
250
+ * `(0,1,0)` variant classes regardless of the order the two classes appear in on the element.
251
+ */
252
+ .gog-btn--success {
253
+ --gog-button-severity-fill: var(--gog-button-success-fill);
254
+ --gog-button-severity-on-fill: var(--gog-button-success-on-fill);
255
+ --gog-button-severity-fill-hover: var(--gog-button-success-fill-hover);
256
+ --gog-button-severity-fill-press: var(--gog-button-success-fill-press);
257
+ --gog-button-severity-ink: var(--gog-button-success-ink);
258
+ --gog-button-severity-wash: var(--gog-button-success-wash);
259
+ }
260
+
261
+ .gog-btn--danger {
262
+ --gog-button-severity-fill: var(--gog-button-danger-fill);
263
+ --gog-button-severity-on-fill: var(--gog-button-danger-on-fill);
264
+ --gog-button-severity-fill-hover: var(--gog-button-danger-fill-hover);
265
+ --gog-button-severity-fill-press: var(--gog-button-danger-fill-press);
266
+ --gog-button-severity-ink: var(--gog-button-danger-ink);
267
+ --gog-button-severity-wash: var(--gog-button-danger-wash);
268
+ }
269
+
270
+ .gog-btn--warning {
271
+ --gog-button-severity-fill: var(--gog-button-warning-fill);
272
+ --gog-button-severity-on-fill: var(--gog-button-warning-on-fill);
273
+ --gog-button-severity-fill-hover: var(--gog-button-warning-fill-hover);
274
+ --gog-button-severity-fill-press: var(--gog-button-warning-fill-press);
275
+ --gog-button-severity-ink: var(--gog-button-warning-ink);
276
+ --gog-button-severity-wash: var(--gog-button-warning-wash);
277
+ }
278
+
279
+ .gog-btn--info {
280
+ --gog-button-severity-fill: var(--gog-button-info-fill);
281
+ --gog-button-severity-on-fill: var(--gog-button-info-on-fill);
282
+ --gog-button-severity-fill-hover: var(--gog-button-info-fill-hover);
283
+ --gog-button-severity-fill-press: var(--gog-button-info-fill-press);
284
+ --gog-button-severity-ink: var(--gog-button-info-ink);
285
+ --gog-button-severity-wash: var(--gog-button-info-wash);
286
+ }
287
+
288
+ /* The two filled variants: the status colour is the fill, and the label is the one that reads
289
+ on it. `secondary` loses its own neutral fill here, which is the point — a severity says what
290
+ the action does, and the variant is left saying only how loud it is. */
291
+ :is(.gog-btn--success, .gog-btn--danger, .gog-btn--warning, .gog-btn--info):is(
292
+ .gog-btn--primary,
293
+ .gog-btn--secondary
294
+ ) {
295
+ --gog-button-variant-bg: var(--gog-button-severity-fill);
296
+ --gog-button-variant-color: var(--gog-button-severity-on-fill);
297
+ --gog-button-variant-border: transparent;
298
+ --gog-button-variant-shadow: none;
299
+ --gog-button-variant-hover-bg: var(--gog-button-severity-fill-hover);
300
+ --gog-button-variant-hover-color: var(--gog-button-severity-on-fill);
301
+ --gog-button-variant-hover-shadow: none;
302
+ --gog-button-variant-toggled-shadow: inset 0 0 0 var(--gog-button-toggled-ring-width)
303
+ var(--gog-button-severity-on-fill);
304
+ --gog-button-variant-press-bg: var(--gog-button-severity-fill-press);
305
+ --gog-button-variant-press-color: var(--gog-button-severity-on-fill);
306
+ --gog-button-variant-spinner-color: var(--gog-button-severity-on-fill);
307
+ }
308
+
309
+ /* Outline: the border and the label are the *ink* rather than the raw status colour, because
310
+ this label is text on the page and the raw hue clears AA in only five of the eleven themes.
311
+ Hovering fills with the status colour proper, which is where `-on-fill` comes back. */
312
+ :is(.gog-btn--success, .gog-btn--danger, .gog-btn--warning, .gog-btn--info).gog-btn--outline {
313
+ --gog-button-variant-bg: transparent;
314
+ --gog-button-variant-color: var(--gog-button-severity-ink);
315
+ --gog-button-variant-border: var(--gog-button-severity-ink);
316
+ --gog-button-variant-shadow: none;
317
+ --gog-button-variant-hover-bg: var(--gog-button-severity-fill);
318
+ --gog-button-variant-hover-color: var(--gog-button-severity-on-fill);
319
+ --gog-button-variant-hover-shadow: none;
320
+ --gog-button-variant-toggled-shadow: inset 0 0 0 var(--gog-button-toggled-ring-width)
321
+ var(--gog-button-severity-ink);
322
+ --gog-button-variant-press-bg: var(--gog-button-severity-fill-press);
323
+ --gog-button-variant-press-color: var(--gog-button-severity-on-fill);
324
+ --gog-button-variant-spinner-color: var(--gog-button-severity-ink);
325
+ }
326
+
327
+ /* Ghost: a wash on hover rather than a fill, the same shape the accent ghost has. Its hover
328
+ label is `--gog-text-color` for the reason 21.9.0 recorded on `--gog-button-ghost-hover-color`
329
+ a label and a ground made of the same hue walk toward each other — and the press then fills
330
+ properly, which is also what the accent ghost does. */
331
+ :is(.gog-btn--success, .gog-btn--danger, .gog-btn--warning, .gog-btn--info).gog-btn--ghost {
332
+ --gog-button-variant-bg: transparent;
333
+ --gog-button-variant-color: var(--gog-button-severity-ink);
334
+ --gog-button-variant-border: transparent;
335
+ --gog-button-variant-shadow: none;
336
+ --gog-button-variant-hover-bg: var(--gog-button-severity-wash);
337
+ --gog-button-variant-hover-color: var(--gog-text-color);
338
+ --gog-button-variant-hover-shadow: none;
339
+ --gog-button-variant-toggled-shadow: inset 0 0 0 var(--gog-button-toggled-ring-width)
340
+ var(--gog-button-severity-ink);
341
+ --gog-button-variant-press-bg: var(--gog-button-severity-fill-press);
342
+ --gog-button-variant-press-color: var(--gog-button-severity-on-fill);
343
+ --gog-button-variant-spinner-color: var(--gog-button-severity-ink);
344
+ }
345
+
346
+ /* === Sizes === */
347
+ .gog-btn--xsm {
348
+ --gog-button-size-padding: var(--gog-button-xsm-padding);
349
+ --gog-button-size-font-size: var(--gog-button-xsm-font-size);
350
+ }
351
+
352
+ .gog-btn--sm {
353
+ --gog-button-size-padding: var(--gog-button-sm-padding);
354
+ --gog-button-size-font-size: var(--gog-button-sm-font-size);
355
+ }
356
+
357
+ .gog-btn--md {
358
+ --gog-button-size-padding: var(--gog-button-md-padding);
359
+ --gog-button-size-font-size: var(--gog-button-md-font-size);
360
+ }
361
+
362
+ .gog-btn--lg {
363
+ --gog-button-size-padding: var(--gog-button-lg-padding);
364
+ --gog-button-size-font-size: var(--gog-button-lg-font-size);
365
+ }
366
+
367
+ .gog-btn--slg {
368
+ --gog-button-size-padding: var(--gog-button-slg-padding);
369
+ --gog-button-size-font-size: var(--gog-button-slg-font-size);
370
+ }
371
+
372
+ .gog-btn--loading {
373
+ position: relative;
374
+ pointer-events: none;
375
+ /* Loading buttons stay natively focusable (aria-disabled + aria-busy instead of
376
+ `disabled`) so keyboard users don't lose focus mid-action. `:disabled` styling
377
+ below therefore no longer covers this state, so the dim is applied explicitly. */
378
+ opacity: var(--gog-button-loading-opacity);
379
+ }
380
+
381
+ .gog-btn__content--hidden {
382
+ visibility: hidden;
383
+ }
384
+
385
+ /* absolute center so the spinner is always in the middle of the button */
386
+ .gog-btn__spinner {
387
+ position: absolute;
388
+ top: 50%;
389
+ left: 50%;
390
+ transform: translate(-50%, -50%);
391
+ max-width: var(--gog-button-spinner-max-size);
392
+ max-height: var(--gog-button-spinner-max-size);
393
+ }
394
+
395
+ /*
396
+ * `gog-button` gets its full width through `:host`, having a wrapper element to work with.
397
+ * `[gogButton]` has none — the element *is* the button — so it needs a class of its own.
398
+ */
399
+ .gog-btn--full-width {
400
+ width: 100%;
401
+ }