clairity.css 0.1.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.
@@ -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 { } */