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