clairity.css 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,756 @@
1
+ /******************************************************************************\
2
+ STATES - these are convenience styles that go beyond defining the base elements.
3
+ in general, these add richness to the UI/UX while still being minimal
4
+ and "classless", but not pseudo-classless.
5
+
6
+ regarding new selectors, :not() for a single element is well-supported
7
+ but not so for a list of selectors (until very recently). :has() is
8
+ only safari 15.4+ and chrome 105+, with firefox support on the way.
9
+ \******************************************************************************/
10
+
11
+
12
+ /* -----------------------------------------------------------------------------
13
+ // #root - global states - listed first so they can be overriden subsequently
14
+ // -------------------------------------------------------------------------- */
15
+
16
+ /* #:hover - default :hover border for form inputs and buttons */
17
+ :hover { border-color: var(--highlighted); } /* [0010] specificity */
18
+
19
+ /* #disabled #hover - must be >= specificity of equivablent non-disabled field*/
20
+ :is([disabled], :disabled):hover { /* [0020] specificity */
21
+ color: var(--inactive); /* for select options */
22
+ background-color: var(--bg-inactive); /* messes up unchecked radio */
23
+ border: var(--solid) var(--border); /* to overcome :hover */
24
+ cursor: not-allowed; /* TODO: shouldn't need this? */
25
+ --icon-color: unset; /* revert icon color to previous value */
26
+ }
27
+
28
+ /* #:focus - the focused pseudo-class, used by links, form fields, etc. */
29
+ :focus { }
30
+ /* #:focus-visible - applies to keyboard-focused buttons but not mouse-focused.
31
+ note safari only started supporting this with 15.4 - [0010] specificity */
32
+ :focus-visible {
33
+ outline: var(--dashed) var(--emphasized);
34
+ outline-offset: var(--thick);
35
+ }
36
+
37
+ /* #:active - actuated element, typically also has :focus, useful for keyboard
38
+ users and accessibility in general */
39
+ :active { } /* TODO: consider low-vision/keyboard users */
40
+
41
+ /* #:empty - has no children and no content other than certain whitespace */
42
+ :empty { }
43
+
44
+
45
+ /* -----------------------------------------------------------------------------
46
+ // #sectioning - block type elements meant to hold other content elements
47
+ // -------------------------------------------------------------------------- */
48
+
49
+
50
+
51
+ /* -----------------------------------------------------------------------------
52
+ // #headers - heading-related elements
53
+ // -------------------------------------------------------------------------- */
54
+
55
+ /*style header fragment no differently to headers- [0001] / [0011] specificity*/
56
+ :is(h1, h2, h3, h4, h5, h6) :where([href^="#"]),
57
+ :is(h1, h2, h3, h4, h5, h6) :where([href^="#"]):hover,
58
+ :is(h1, h2, h3, h4, h5, h6) :where([href^="#"]):visited {
59
+ color: unset;
60
+ font-weight: unset;
61
+ text-decoration: unset;
62
+ }
63
+
64
+ /* header fragment indicator - [0001] specificity */
65
+ :where(h1, h2, h3, h4, h5, h6) :where([href^="#"]:hover)::after {
66
+ /*display: inline-flex;*/
67
+ font-size: var(--smaller);
68
+ color: var(--inactive);
69
+ content: "#";
70
+ margin-left: var(--s);
71
+ }
72
+
73
+
74
+ /* -----------------------------------------------------------------------------
75
+ // #links - for linking to other documents
76
+ // -------------------------------------------------------------------------- */
77
+
78
+ /* placeholder links shouldn't be styled like links until an href is added */
79
+ :where(a, a:hover):not([href]) { /* [0011]/[0021] specificity */
80
+ color: inherit;
81
+ text-decoration: none;
82
+ }
83
+
84
+ /* #mailto #tel #sms #file #external #bookmark #download - special link icons -
85
+ this uses the svg mask technique to place & color icons on hover; as such, it
86
+ must be in a separate pseudo-element so masking doesn't bleed out to text */
87
+ /* #icons - set --icon to an svg from icons.css, set color with --icon-color */
88
+ [href^="tel:"] { --icon: var(--i-phone); }
89
+ [href^="sms:"] { --icon: var(--i-message-2); }
90
+ [href^="file:"] { --icon: var(--i-file); }
91
+ [download] { --icon: var(--i-file-download); }
92
+ [href^="mailto:"] { --icon: var(--i-mail); }
93
+ [rel~="external"] { --icon: var(--i-external-link); }
94
+ [rel~="bookmark"] { --icon: var(--i-bookmark); }
95
+
96
+ /* chrome still needs prefixed -webkit-mask as of 20221001 */
97
+ :where([href^="tel:"], [href^="sms:"], [href^="file:"], [download])::before,
98
+ :where([href^="mailto:"], [rel~="external"], [rel~="bookmark"])::after {
99
+ content: ''; /* without content, no icon appears */
100
+ -webkit-mask: var(--icon) no-repeat center / var(--field-icon);
101
+ mask: var(--icon) no-repeat center / var(--field-icon);
102
+ background-color: var(--icon-color);
103
+ padding: var(--inline-padding);
104
+ }
105
+
106
+ :where([href^="mailto:"], [rel~="external"], [rel~="bookmark"])::after {
107
+ margin-left: var(--xs);
108
+ padding-top: var(--xs);
109
+ }
110
+
111
+ a:where([href^="mailto:"], [href^="tel:"], [href^="sms:"], [href^="file:"],
112
+ [rel~="external"], [rel~="bookmark"], [download]):hover {
113
+ --icon-color: var(--active);
114
+ }
115
+
116
+ /* give followed fragment links some room at the top - [0010] specificity */
117
+ [id] { scroll-margin-top: var(--l); }
118
+
119
+ /* #:any-link matches both :link and :visited for anchors with an href; safari
120
+ support is currently lagging, so don't use for now */
121
+ :any-link { }
122
+ /* #:local-link - targets same-document links - currently unspported */
123
+ /*:local-link { }*/
124
+
125
+
126
+ /* -----------------------------------------------------------------------------
127
+ // #lists - ordered and unordered lists of items
128
+ // -------------------------------------------------------------------------- */
129
+ /* #ul #ol - removes vertical gaps for lists within lists - uses :where() to get
130
+ [0001] specificity rather than [0002] */
131
+ :where(ul, ol) :is(ul, ol) { margin: 0; }
132
+
133
+
134
+ /* -----------------------------------------------------------------------------
135
+ // #tables - for tabular data
136
+ // -------------------------------------------------------------------------- */
137
+
138
+ /* makes the first column sticky - [0012] specificity */
139
+ :not(tfoot) > tr th:first-of-type {
140
+ position: sticky; /* works best w/ <table> display ≅ block */
141
+ left: 0;
142
+ background-color: var(--background); /* is transparent otherwise */
143
+ }
144
+ /* makes the table header row sticky - [0002] specificity */
145
+ thead th {
146
+ position: sticky;
147
+ top: 0;
148
+ z-index: 1; /* puts header row on top of tbody rows, including
149
+ header column */
150
+ background-color: var(--background); /* is transparent otherwise */
151
+ }
152
+ /* sticks top left box on top of both header columns/rows - [0012] specificity*/
153
+ thead th:first-child { z-index: 2; }
154
+
155
+ /* zebra-striping of table rows - [0013] specificity */
156
+ tbody tr:nth-child(even), tbody tr:nth-child(even) th {
157
+ background-color: var(--bg-contrast);
158
+ }
159
+ /* this is more robust because it excludes hidden rows, but only safari and
160
+ chrome support this syntax - [0023] specificity */
161
+ @supports selector(:nth-child(even of :not([hidden]))) {
162
+ tbody tr:where(:nth-child( even of :not([hidden]) )),
163
+ tbody tr:where(:nth-child( even of :not([hidden]) )) th {
164
+ background-color: var(--bg-contrast);
165
+ }}
166
+
167
+ /* hover effect for table rows */
168
+ tbody tr:hover, tbody tr:hover th {
169
+ background-color: var(--contrast);
170
+ }
171
+ /* adds sticky borders below thead and above tfoot */
172
+ thead tr:last-of-type th {box-shadow: inset 0 -2px var(--secondary, --neutral);}
173
+ tfoot :where(td, th) {box-shadow: inset 0 2px var(--secondary, --neutral);}
174
+
175
+
176
+ /* -----------------------------------------------------------------------------
177
+ // #forms - for submitting data to the server
178
+ // -------------------------------------------------------------------------- */
179
+ /* #form #fieldset #focus-within - highlight whole forms & fieldsets when one of
180
+ its contained elements is focused - [0010] specificity */
181
+ :where(form, fieldset):focus-within {
182
+ /*background-color: var(--bg-contrast);*/
183
+ }
184
+ /* #legend #fieldset #hover - remove :hover effect on legend & fieldset by
185
+ setting the same border color - [0010] specificity */
186
+ :where(legend, fieldset):hover {
187
+ border-color: var(--border);
188
+ }
189
+
190
+ /* #form #ul - since lists in forms typically list out form controls, remove
191
+ the bullet styling from them - [0002] specificity */
192
+ form ul { list-style: none; }
193
+
194
+ /* #form #section #p #div - subgrid is firefox 71+ & safari 16+ only 20221001 */
195
+ @supports (grid-template-columns: subgrid) {
196
+ /* :where() can be used here, since @supports is also a recent addition */
197
+ form :where(section, p, div) { /* [0001] specificity */
198
+ grid-column: full;
199
+ display: grid;
200
+ grid-template-columns: subgrid;
201
+ gap: inherit;
202
+ }}
203
+ /*form section :where(h1,h2,h3,h4,h5,h6) + p { display: block; }*/
204
+
205
+ /* chrome/edge doesn't support subgrid yet but does support :has() & :where() */
206
+ @supports selector(:has(*)) {
207
+ fieldset:where(:has(label, input, button, textarea, select, fieldset)) {
208
+ margin: 0;
209
+ display: grid;
210
+ grid-template-columns:[labels full-start] 3fr [elements] 7fr [full-end];
211
+ gap: var(--m);
212
+ }
213
+ fieldset:where(:has(p :is([type='checkbox'], [type='radio'])):not(:has(select,
214
+ textarea, [type="text"], [type="date"], [type="email"], [type="password"],
215
+ [type="datetime-local"], [type="search"]))) {
216
+ row-gap: 0;
217
+ }
218
+ /* since grid properties fall through p tags, assign margin-top to first child
219
+ elements to get a gap between paragraphs of different input types */
220
+ fieldset p:has([type='checkbox'], [type='radio']) + p:has([type='radio'],
221
+ [type='checkbox']) :is([type='radio'], [type='checkbox'],
222
+ label):first-of-type {
223
+ margin-top: var(--m);
224
+ }
225
+ /* chrome/edge doesn't support subgrid, so pierce the grid barrier instead */
226
+ fieldset :where(p:has(label, input, button, textarea, select, fieldset)) {
227
+ display: contents;
228
+ }
229
+ /* labels go on the labels gridline */
230
+ :where(fieldset :is(p, div)) label {
231
+ grid-column: labels;
232
+ }
233
+ /* lists that contain radios/checkboxes go on the elements gridline */
234
+ form:has(fieldset :where(select, textarea, [type="text"], [type="date"],
235
+ [type="email"], [type="password"], [type="datetime-local"],
236
+ [type="search"])) fieldset ul:has(li label :where([type='radio'],
237
+ [type='checkbox'])) {
238
+ grid-column: elements;
239
+ }
240
+
241
+ /* inputs on the elements gridline; pad right side away from fieldset line */
242
+ :where(fieldset) :where(p):has(input, button, textarea, select, fieldset)
243
+ :where(input:not(:is([type='checkbox'],[type='radio']), button,
244
+ textarea, select, fieldset)) {
245
+ grid-column: elements;
246
+ margin-right: var(--m);
247
+ }
248
+ /* for radios & checks, invert the placement of the input and label */
249
+ :where(fieldset) :where(p):has(label) :where([type='checkbox'],
250
+ [type='radio']):has(+ label) {
251
+ grid-column: labels;
252
+ align-self: center;
253
+ justify-self: end;
254
+ }
255
+ :where(fieldset p:has(label) :is([type='checkbox'],
256
+ [type='radio']):has(+ label)) + label {
257
+ grid-column: elements;
258
+ justify-self: start;
259
+ }
260
+ /* for paragraph tags that aren't form element containers, span the grid */
261
+ :where(fieldset) :where(p, div, h2, h3, h4, h5, h6):not(:has(label, input,
262
+ button, textarea, select, fieldset)) {
263
+ display: block;
264
+ grid-column: full;
265
+ }
266
+ }
267
+ /*form :where(fieldset, input, select, button, input[type="button"]) {
268
+ grid-column: elements;
269
+ }*/
270
+
271
+ fieldset > :last-child {
272
+ margin-bottom: 0;
273
+ }
274
+
275
+
276
+ /* -----------------------------------------------------------------------------
277
+ // #inputs - standard form input elements
278
+ // -------------------------------------------------------------------------- */
279
+
280
+ /* if [size] is set on an input, respect that choice - [0011] specificity */
281
+ input[size] {
282
+ width: fit-content;
283
+ min-width: unset;
284
+ max-width: unset;
285
+ }
286
+
287
+ /* #input #optgroup #option #textarea #select #read-only #focus - bg field icons
288
+ are Field system color by default, and --icon-color on focus/hover */
289
+ :where(input, optgroup, option, textarea, select):focus {
290
+ border-color: var(--highlighted);
291
+ --icon-color: var(--primary);
292
+ }
293
+
294
+ /* input images */
295
+ [type="date"], [type="time"], [type="datetime"], [type="datetime-local"],
296
+ [type="month"], [type="week"], [type="url"], [type="email"],
297
+ [type="number"], [type="tel"], [type="password"], [type="search"] {
298
+ padding-left: var(--height);
299
+ background-blend-mode: var(--icon-blend-mode);
300
+ }
301
+ [type="date"], [type="month"], [type="week"] {
302
+ background-image: var(--colorize-icon), var(--i-calendar); }
303
+ [type="datetime"], [type="datetime-local"], [type="time"] {
304
+ background-image: var(--colorize-icon), var(--i-calendar-time); }
305
+ [type="url"] { background-image: var(--colorize-icon), var(--i-browser); }
306
+ [type="email"] { background-image: var(--colorize-icon), var(--i-mail); }
307
+ [type="number"] { background-image: var(--colorize-icon), var(--i-numbers); }
308
+ [type="tel"] { background-image: var(--colorize-icon), var(--i-phone); }
309
+ [type="password"]{ background-image: var(--colorize-icon), var(--i-lock); }
310
+ [type="search"] { background-image: var(--colorize-icon), var(--i-search); }
311
+
312
+ /*@media (prefers-color-scheme: dark) {
313
+ [type="date"], [type="time"], [type="datetime"], [type="datetime-local"],
314
+ [type="week"], [type="month"], [type="url"], [type="email"],
315
+ [type="number"], [type="tel"], [type="password"], [type="search"] {
316
+ mask: var(--icon) no-repeat center left / var(--field-icon);
317
+ background-color: var(--icon-color);
318
+ mask-repeat: no-repeat;
319
+ background-image: unset;
320
+ }
321
+ [type="date"], [type="month"], [type="week"] { --icon: var(--i-calendar); }
322
+ [type="datetime"], [type="datetime-local"], [type="time"] {
323
+ --icon: var(--i-calendar-time); }
324
+ [type="url"] { --icon: var(--i-browser); }
325
+ [type="email"] { --icon: var(--i-mail); }
326
+ [type="number"] { --icon: var(--i-numbers); }
327
+ [type="tel"] { --icon: var(--i-phone); }
328
+ [type="password"]{ --icon: var(--i-lock); }
329
+ [type="search"] { --icon: var(--i-search); }
330
+ }*/
331
+
332
+ /* hide nav search's placeholder text if the background icon is present; opacity
333
+ is used here because other methods are disallowed on ::placeholder - [0001]*/
334
+ :where(nav) :where([type="search"])::placeholder { opacity: 0; }
335
+
336
+ :where(input, optgroup, option, textarea, select):required:is(:valid,:in-range){
337
+ border-color: var(--success-high, forestgreen);
338
+ }
339
+ :where(input, optgroup, option, textarea, select):required:is(:valid,
340
+ :in-range):hover, :where(input, optgroup, option, textarea,
341
+ select):required:is(:valid, :in-range):focus {
342
+ border-color: var(--success, limegreen);
343
+ }
344
+ /* remove borders on :read-only text and textarea fields */
345
+ :where(textarea, [type="text"], [type="date"], [type="time"], [type="datetime"],
346
+ [type="datetime-local"], [type="month"],[type="week"], [type="email"],
347
+ [type="password"], [type="url"], [type="tel"], [type="number"],
348
+ [type="search"]):read-only:enabled {
349
+ border-style: none;
350
+ }
351
+
352
+ /*:read-only text fields shouldn't look editable on hover - [0040] specificity*/
353
+ :where(textarea, input:not([type="radio"],[type="checkbox"],[type="button"],
354
+ [type="reset"],[type="submit"],[type="image"])):read-only:enabled:hover {
355
+ border-style: none;
356
+ border-color: var(--emphasized);
357
+ }
358
+ /* deemphasize :read-only text fields on :focus - [0030] specificity*/
359
+ :where(textarea, [type="text"], [type="date"], [type="time"], [type="datetime"],
360
+ [type="datetime-local"], [type="month"],[type="week"], [type="email"],
361
+ [type="password"], [type="url"], [type="tel"], [type="number"],
362
+ [type="search"]):read-only:focus {
363
+ border-style: none;
364
+ border-color: var(--deemphasized);
365
+ }
366
+
367
+ :where(textarea, [type="text"], [type="date"], [type="time"], [type="datetime"],
368
+ [type="datetime-local"], [type="month"],[type="week"], [type="email"],
369
+ [type="password"], [type="url"], [type="tel"], [type="number"],
370
+ [type="search"]):hover {
371
+ --icon-color: var(--primary);
372
+ }
373
+
374
+ /* #:invalid - :invalid pseudo-class for when a field fails validation */
375
+ /*:where(input, optgroup, option, textarea, select):required:empty,*/
376
+ :where(input, optgroup, option, textarea, select):is(:invalid, :out-of-range) {
377
+ border-color: var(--warning-high, gold);
378
+ }
379
+ /* #invalid:hover - for when an :invalid field is also :hover'ed */
380
+ /*:where(input, optgroup, option, textarea, select):required:empty:hover,*/
381
+ :where(input, optgroup, option, textarea, select):is(:invalid,
382
+ :out-of-range):hover, :where(input, optgroup, option, textarea,
383
+ select):is(:invalid, :out-of-range):focus {
384
+ border-color: var(--warning, khaki);
385
+ }
386
+
387
+ /* HACK: these two rules undo validation borders on initial load because
388
+ browsers other than firefox don't support :user-invalid */
389
+ /*:is(input, optgroup, option, textarea, select):optional:not(:valid,
390
+ [type="button"], [type="reset"]) {
391
+ border-color: var(--border);
392
+ }
393
+ :is(input, optgroup, option, textarea, select):optional:not(:valid, :disabled,
394
+ [type="button"], [type="reset"]):hover {
395
+ border-color: var(--highlighted);
396
+ }
397
+ */
398
+ /* #:user-invalid - experimental - firefox only for now */
399
+ :where(input, optgroup, option, textarea, select):user-invalid {
400
+ border-color: var(--error-high, firebrick);
401
+ }
402
+ :where(input, optgroup, option, textarea, select):user-invalid:hover,
403
+ :where(input, optgroup, option, textarea, select):user-invalid:focus {
404
+ border-color: var(--error, red);
405
+ }
406
+
407
+
408
+ /* -----------------------------------------------------------------------------
409
+ // #radio #checkbox - radio buttons and checkboxes
410
+ // -------------------------------------------------------------------------- */
411
+
412
+ /* indeterminate checkboxes - activated only via js - [0010] specificity */
413
+ :where([type="checkbox"]):indeterminate {
414
+ border-color: var(--warning);
415
+ background-image: var(--colorize-icon), var(--i-dash);
416
+ background-blend-mode: var(--icon-blend-mode);
417
+ background-position: center;
418
+ background-size: 1.1em;
419
+ box-shadow: unset;
420
+ }
421
+ :where([type="checkbox"]):indeterminate:required { /* [0020] specificity */
422
+ border-color: var(--error);
423
+ }
424
+ :where([type="checkbox"]):indeterminate:hover {
425
+ --icon-color: var(--300);
426
+ }
427
+
428
+ /* #radio #checkbox #label #disabled #checked - these 3 rules style the disabled
429
+ radio/checkbox and its subsequent label - [0010] specificity using :where();
430
+ don't want first rule to be [0001] as regular label rules would override it*/
431
+ :where([type="radio"], [type="checkbox"]):disabled + :where(label) {
432
+ color: var(--inactive);
433
+ cursor: not-allowed;
434
+ }
435
+ /* needs to be >= specificity of global :disabled:hover rule to override it */
436
+ :is([type="radio"], [type="checkbox"]):disabled { /* [0020] specificity */
437
+ background-color: var(--unchecked); /* removes "check" */
438
+ border: var(--solid) var(--deemphasized); /* grey border */
439
+ box-shadow: var(--inset) var(--unchecked); /* inset bg */
440
+ }
441
+ :is([type="radio"]:checked, [type="checkbox"]:checked):disabled {
442
+ background-color: var(--deactivated); /* greyed-out "check" */
443
+ }
444
+
445
+ /* if radios/checkboxes are inline, insert a gap between them for readability */
446
+ :where([type="radio"] + label, [type="checkbox"] + label) +
447
+ :is([type="radio"], [type="checkbox"]) { /* [0010] specificity */
448
+ margin-left: var(--m);
449
+ }
450
+
451
+ /* inputs are (somewhat unspecified as) "replaced elements" & therefore can't
452
+ have ::before or ::after pseudo-elements on itself; use on label instead */
453
+ /* mark required fields' labels; labels usually follow checkboxes & radios */
454
+ /* :has() is only safari 16+ and chrome 105+ for now */
455
+ @supports selector(:has(*)) {
456
+ label:has(+ :is(input, select):not([type="checkbox"], [type="radio"]):required)::after,
457
+ input:where([type="checkbox"], [type="radio"]):required + label::after {
458
+ content: " *";
459
+ }
460
+ /* mark as successful the fieldset & legend borders for valid radio buttons */
461
+ /* :has() is safari only for now */
462
+
463
+ fieldset:has([type="radio"]:required:checked),
464
+ fieldset:has([type="radio"]:required:checked) legend {
465
+ border-color: var(--success, forestgreen);
466
+ }
467
+ }
468
+
469
+ [type="checkbox"]:required:user-invalid + label {
470
+ color: var(--error, red);
471
+ }
472
+
473
+
474
+ /* -----------------------------------------------------------------------------
475
+ // specialized inputs - #date #time #datetime-local #month #week #datetime
476
+ // #email #password #search #file #image #url #tel #color
477
+ // #range #number #hidden
478
+ // -------------------------------------------------------------------------- */
479
+
480
+ /* search decorations - [0011] specificity */
481
+ [type="search"]::-webkit-search-cancel-button,
482
+ [type="search"]::-webkit-search-decoration {
483
+ appearance: none; /* needed? */
484
+ }
485
+
486
+
487
+ /* -----------------------------------------------------------------------------
488
+ // #buttons - <button>s and <input>s of type=button, reset, and submit
489
+ // -------------------------------------------------------------------------- */
490
+
491
+ /* #button #submit #hover - [0010] specificity */
492
+ :where(button, [type="button"], [type="submit"]):hover,
493
+ ::file-selector-button:hover {
494
+ background-color: var(--bg-highlight);
495
+ border-color: var(--foreground);
496
+ }
497
+ /* #reset #hover */
498
+ :where([type="reset"]):hover { /* [0010] specificity */
499
+ background-color: var(--contrast); /* TODO: make grey in dark mode */
500
+ }
501
+
502
+ /* #disabled & disabled #hover button states - needs >= specificity of
503
+ non-disabled versions */
504
+ :is(button, button:hover, [type="button"], [type="submit"]):disabled,
505
+ [type="file"]:disabled::file-selector-button { /* [0020] specificity */
506
+ color: var(--inactive);
507
+ background-color: var(--bg-inactive);
508
+ border-color: var(--deemphasized);
509
+ cursor: not-allowed;
510
+ }
511
+
512
+ [type="file"]:disabled {
513
+ background-color: unset;
514
+ border: unset;
515
+ cursor: not-allowed;
516
+ }
517
+
518
+ [type="reset"]:disabled {
519
+ color: var(--inactive);
520
+ background-color: var(--background);
521
+ border-color: var(--deemphasized);
522
+ cursor: not-allowed;
523
+ }
524
+
525
+ /* highlights the :focus style */
526
+ :where(button, [type="button"], [type="reset"], [type="submit"],
527
+ [type="image"]):not(:disabled):active {
528
+ outline: var(--dashed) var(--bg-highlight);
529
+ outline-offset: var(--thick);
530
+ }
531
+
532
+ /* includes css from .tertiary.button - [00xx] specificity */
533
+ [role="alert"] button:empty {
534
+ color: currentColor; /* invert foreground/background */
535
+ background-color: unset; /* border is transparent */
536
+ padding-inline: var(--xxs);
537
+ float: right;
538
+ }
539
+
540
+ [role="alert"] button:empty::after {
541
+ content: '⨉';
542
+ font-weight: var(--boldest);
543
+ }
544
+
545
+ [role="alert"] button:empty:hover {
546
+ text-decoration: underline; /* TODO: make grey in dark mode */
547
+ border-color: var(--transparent);
548
+ }
549
+
550
+ /*form:has(button[aria-label="close" i]:empty) {
551
+ padding-inline: var(--xxs);
552
+ float: right;
553
+ }
554
+ */
555
+ /* -----------------------------------------------------------------------------
556
+ // #selections - dropdown/multi-item single-/multiple-selection form controls
557
+ // -------------------------------------------------------------------------- */
558
+
559
+ /* #option #hover #disabled */
560
+ option:not(:disabled, [disabled]):hover { /* [0011] specificity */
561
+ color: var(--whitish);
562
+ background-color: var(--bg-highlight);
563
+ }
564
+ option:disabled, option:disabled:hover { /* [0011] / [0021] specificity */
565
+ border-color: var(--transparent);
566
+ background-color: unset;
567
+ }
568
+
569
+ /* #select #multiple #hover #active - dropdown selection form controls- [0011]*/
570
+ select:not(:disabled, [disabled]):hover, select:active { /* dropdown chevron */
571
+ background-image: url("data:image/svg+xml;utf8,<svg \
572
+ xmlns='http://www.w3.org/2000/svg' width='40' height='40'><path \
573
+ d='M0,10 20,30 40,10' fill='none' stroke='%23666' stroke-width='2'/></svg>");
574
+ }
575
+ /* multi-selects shouldn't have chevron - [0020] specificity */
576
+ [multiple]:hover, [multiple]:active, select[size]:hover, select[size]:active {
577
+ background-image: none;
578
+ }
579
+ /* #multiple #option #checked */
580
+ [multiple] option:disabled:checked {
581
+ background-color: var(--deactivated) linear-gradient(0deg,
582
+ var(--deactivated) 0%, var(--deactivated) 100%);
583
+ }
584
+ /* #multiple #focus #option #disabled #checked */
585
+ :where(select[size], [multiple]):focus option:not(:disabled):checked {
586
+ /* --checked may be invalid due to SelectedItem, so provide a fallback */
587
+ background-color: var(--checked, LinkText) linear-gradient(0deg,
588
+ var(--checked, LinkText) 0%, var(--checked, LinkText) 100%);
589
+ }
590
+
591
+ /* #label for taller #multiple #textarea should align top - firefox 106 still
592
+ doesn't properly support :has() with combinator selectors */
593
+ @supports selector(:has(*)) {
594
+ label:where(:has(+ [multiple],+ textarea,+ select[size])){/*[0001] specificity*/
595
+ vertical-align: top;
596
+ align-self: start;
597
+ }}
598
+
599
+
600
+ /* -----------------------------------------------------------------------------
601
+ // #outputs - informational output/display
602
+ // -------------------------------------------------------------------------- */
603
+
604
+ progress:hover { border-color: revert; }
605
+
606
+ /* specify theme colors for chrome - [0011] specificity */
607
+ :-moz-meter-optimum::-moz-meter-bar { background-color: var(--success);}
608
+ :-moz-meter-sub-optimum::-moz-meter-bar { background-color: var(--warning);}
609
+ :-moz-meter-sub-sub-optimum::-moz-meter-bar { background-color: var(--error); }
610
+
611
+
612
+ /* -----------------------------------------------------------------------------
613
+ // #block text elements - elements that contain text blocks
614
+ // -------------------------------------------------------------------------- */
615
+
616
+ blockquote::before { /* [0002] specificity is the least possible here */
617
+ content: open-quote;
618
+ color: var(--pale);
619
+ font-size: 5rem;
620
+ font-family: var(--serif);
621
+ left: var(--xs);
622
+ line-height: 1;
623
+ position: absolute;
624
+ top: 0;
625
+ z-index: -1;
626
+ }
627
+ blockquote > :first-child { /* [0011] specificity */
628
+ margin-top: 0;
629
+ text-indent: var(--m);
630
+ }
631
+ blockquote > :last-child { /* [0011] specificity */
632
+ margin-bottom: 0;
633
+ }
634
+
635
+
636
+ /* -----------------------------------------------------------------------------
637
+ // #inline elements - elements that occur principally within text blocks
638
+ // -------------------------------------------------------------------------- */
639
+
640
+ /* #q - inline quotations -note that you must add the closing smart quote as
641
+ <q>'s automatic quotes are added via pseudo elements */
642
+ q[cite]::after { content: "” (cf. " attr(cite) ") "; }
643
+
644
+ /* #kbd #hover - remove default :hover styles - [0011] specificity */
645
+ kbd:hover { border: var(--solid) var(--distinguished); }
646
+
647
+ /* #:dir, #:lang - linguistic pseudo-classes */
648
+ /*:dir { }*/ /* experimental */
649
+ /*:lang { }*/
650
+
651
+
652
+ /* -----------------------------------------------------------------------------
653
+ // #media / #embedded - elements that add multimedia to a page
654
+ // -------------------------------------------------------------------------- */
655
+
656
+ /* #figure */
657
+ figure:hover { border-color: var(--border); }
658
+ /* #figure #headings - #h1 #h2 #h3 #h4 #h5 #h6 ~*/
659
+ figure > :where(h1,h2,h3,h4,h5,h6):first-child { margin-top: 0; }
660
+
661
+ /* #blockquote inside #figure - safari only for now - :where is older than :has
662
+ so it can be used here to reduce specificity - [0001] specificity */
663
+ figure:where(:has(blockquote)) {
664
+ background-color: revert;
665
+ }
666
+ /* fix doubled margin of a blockquote in a figure - [0002] specificity */
667
+ figure > blockquote {
668
+ text-align: start;
669
+ margin: var(--ms);
670
+ padding: var(--ms);
671
+ max-width: calc(var(--line-length) - (var(--ms) * 2));
672
+ }
673
+
674
+ /* #svg - [0001] specificity */
675
+ svg:not(:where(:root)) {
676
+ width: var(--m);
677
+ /*max-width: 100%;*/
678
+ overflow: hidden;
679
+ }
680
+
681
+ /* #audio - audio w/p controls shouldn't take up space - [0011] specificity */
682
+ audio:not([controls]) {
683
+ display: none;
684
+ height: 0;
685
+ }
686
+
687
+ /* resource state pseudo-classes */
688
+ /*:playing { }*/
689
+ /*:paused { }*/
690
+ /*:fullscreen { }*/
691
+ /*:picture-in-picture { }*/
692
+ /* time-dimensional pseudo-classes */
693
+ /*:current { }*/ /* experimental */
694
+ /*:past { }*/ /* experimental */
695
+ /*:future { }*/ /* experimental */
696
+
697
+
698
+ /* -----------------------------------------------------------------------------
699
+ // #interactive - built-in interactive components
700
+ // -------------------------------------------------------------------------- */
701
+
702
+ /* #dialog - built-in modal overlay - comes with 'Esc' support with no js! */
703
+ /* wrap long dialogs in a container to scroll the body content sans button */
704
+ dialog > :where(div, section, article) {
705
+ overflow-y: auto; /* scrolls as necessary w/ height set */
706
+ max-height: calc(80vh * 0.8); /* container queries needed! */
707
+ }
708
+
709
+ dialog button {
710
+ margin-top: var(--ms);
711
+ }
712
+
713
+ dialog :where(h1, h2, h3, h4, h5, h6) {
714
+ border-bottom: var(--solid) var(--border);
715
+ border-radius: var(--radius) var(--radius) 0 0;
716
+ color: var(--distinct); /* differentiate dialog headings */
717
+ font-size: var(--large); /* dialogs typically only need 1 heading
718
+ level, so make it all the same size */
719
+ margin: 0 0 0 calc(-1 * var(--m)); /* negative left margin
720
+ lines up heading with paragraph text */
721
+ padding: 0 var(--m) var(--m); /* top padding provided by dialog
722
+ padding so remove it here */
723
+ }
724
+
725
+ /* #details - an accordion-like widget for question & answer style layouts */
726
+ /* #summary - the 'question' sub-element of details */
727
+ details:where([open]) summary {
728
+ border-bottom: var(--solid) var(--border);
729
+ }
730
+
731
+ details + details {
732
+ border-top: 0;
733
+ border-radius: 0;
734
+ }
735
+
736
+ details:first-of-type {
737
+ border-top-left-radius: var(--radius);
738
+ border-top-right-radius: var(--radius);
739
+ }
740
+
741
+ details:last-of-type {
742
+ border-bottom-left-radius: var(--radius);
743
+ border-bottom-right-radius: var(--radius);
744
+ }
745
+
746
+
747
+ /* -----------------------------------------------------------------------------
748
+ // #breaks - spacing elements
749
+ // -------------------------------------------------------------------------- */
750
+
751
+
752
+ /* -----------------------------------------------------------------------------
753
+ // #@page - fixed page-oriented class, usually for print layouts
754
+ // -------------------------------------------------------------------------- */
755
+ /* @page pseudo-classes */
756
+ /* @page :first, @page :left, @page :right, @page :blank { } */