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,946 @@
1
+ /******************************************************************************\
2
+ COMPONENTS - for more complex sites, composable components can provide
3
+ greater aesthetics and usability.
4
+ \******************************************************************************/
5
+
6
+
7
+ /* -----------------------------------------------------------------------------
8
+ // #sectioning #containers
9
+ // -------------------------------------------------------------------------- */
10
+
11
+ /* #body - auto-grid by default - overrides base.css default body definition.
12
+ set --columns to the number of columns desired */
13
+ body {
14
+ display: grid;
15
+ grid-template-columns: [full-start] var(--padding, var(--m))
16
+ [padded-start] repeat(var(--columns, auto-fit),
17
+ minmax(var(--grid), 1fr)) [padded-end]
18
+ var(--padding, var(--m)) [full-end];
19
+ column-gap: var(--gap); /* flexible between 400 - 800px */
20
+ max-width: unset;
21
+ }
22
+
23
+ /* some containers should span full or padded width by default if in the grid */
24
+ :where(:not(.grid)) > main { grid-column: padded; }
25
+ nav, header, footer {
26
+ grid-column: full;
27
+ }
28
+
29
+ /* the top header typically should sit flush to the top of the page and use
30
+ padding; TODO: this seems to be over-specified */
31
+ body > header:first-child {
32
+ margin-top: 0;
33
+ }
34
+
35
+ /* #footer - centered grid by default; this grid overrides base.css definition*/
36
+ footer {
37
+ grid-template-columns: [full-start] repeat(auto-fit,
38
+ minmax(var(--column), 1fr)) [full-end];
39
+ grid-auto-rows: min-content; /* don't unnecessarily expand rows */
40
+ column-gap: var(--m);
41
+ container-type: unset; /* safari doesn't like contain with grid */
42
+ padding-inline: var(--padding, var(--m)); /* flexible padding */
43
+ }
44
+
45
+ footer > p { grid-column: 1 / -1; } /* take up a whole row in the grid */
46
+
47
+
48
+ /* -----------------------------------------------------------------------------
49
+ // #headings -
50
+ // -------------------------------------------------------------------------- */
51
+
52
+ header:not(.banner) {
53
+ display: flex;
54
+ flex-wrap: wrap;
55
+ justify-content: space-between;
56
+ }
57
+
58
+ header :where(h1,h2,h3,h4,h5,h6) {
59
+ }
60
+
61
+ header :where(p) {
62
+ flex: 1 0 100%;
63
+ }
64
+
65
+
66
+ /* -----------------------------------------------------------------------------
67
+ // site #footer - typical fat footer; #footer is the singular site footer
68
+ // -------------------------------------------------------------------------- */
69
+
70
+ /* #fat footers are typically distinct and feature a set of columns with links*/
71
+ .fat, :where(body) > :where(footer):last-of-type { /* [0010] specificity */
72
+ grid-column: 1 / -1; /* fat footers are full width */
73
+ max-width: -webkit-fill-available; /* take full width */
74
+ max-width: -moz-available; /* take full width on firefox */
75
+ --muted: var(--300, GrayText);
76
+ color: var(--whitish);
77
+ background-color: var(--blackish);
78
+ }
79
+
80
+ /* .nav - flex container to group/align columns of <nav>s - [0010] specificity*/
81
+ .nav {
82
+ grid-column: 1 / -1; /* break free of the grid, if any */
83
+ display: flex;
84
+ flex-wrap: wrap;
85
+ margin-inline: var(--padding); /* responsive inline, outer margin */
86
+ column-gap: var(--l); /* fixed inline, inner gap */
87
+ }
88
+
89
+ /* #nav - column-oriented navs - [0001] specificity */
90
+ :where(.nav) nav {
91
+ grid-column: unset;
92
+ flex-direction: column;
93
+ flex-grow: 1; /* take up the whole role if orphaned */
94
+ flex-basis: calc((40vw - 100%) * 999);
95
+ align-items: start; /* some items will float otherwise */
96
+ row-gap: var(--s); /* add some vertical spacing */
97
+ justify-content: start;
98
+ min-width: var(--column); /* 44vw/10rem for narrow screens */
99
+ }
100
+
101
+ /* style headers in the site footer */
102
+ :where(.fat, body > footer:last-of-type)
103
+ :is(h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6) {
104
+ color: var(--muted);
105
+ font-weight: var(--light);
106
+ }
107
+
108
+ :where(.fat, body > footer:last-of-type) a {
109
+ font-weight: var(--light);
110
+ }
111
+
112
+ :where(.fat, body > footer:last-of-type) a:not(:hover) {
113
+ color: var(--whitish);
114
+ text-decoration: none;
115
+ }
116
+
117
+
118
+ /* -----------------------------------------------------------------------------
119
+ // #footnotes
120
+ // -------------------------------------------------------------------------- */
121
+
122
+ /* #footnotes - footnotes/endnotes typically should have the semantic marker of
123
+ [role="doc-endnote"]; :target doesn't depend on color-scheme here - it's
124
+ always dark on light. transition to the highlight, to draw the eye to it. */
125
+ [role="doc-endnotes"] {
126
+ grid-column: 1 / -1;
127
+ }
128
+
129
+ :where([role="doc-endnote"]):target { /* [0010] specificity */
130
+ color: var(--blackish);
131
+ background-color: hsl(var(--yellow), 90%, 75%);
132
+ transition: color 1s 0.5s, background-color 1s 0.5s;
133
+ }
134
+
135
+ /* ::marker text is outside :target, so match color-scheme text color */
136
+ :where([role="doc-endnote"]):target::marker { /* [0011] specificity */
137
+ color: var(--text);
138
+ }
139
+
140
+
141
+ /* -----------------------------------------------------------------------------
142
+ // #icons - svg icons
143
+ // -------------------------------------------------------------------------- */
144
+
145
+ i[class^='i-'] {
146
+ --icon-color: var(--color, currentColor);
147
+ -webkit-mask: var(--icon) no-repeat center / var(--field-icon);
148
+ mask: var(--icon) no-repeat center / var(--field-icon);
149
+ background-color: var(--icon-color);
150
+ padding: var(--inline-padding);
151
+ min-height: var(--ml); /* else naked icons collapse into a line */
152
+ }
153
+
154
+
155
+ /* -----------------------------------------------------------------------------
156
+ // #banner - header that usually includes a hero image and perhaps a nav
157
+ // -------------------------------------------------------------------------- */
158
+
159
+ .banner {
160
+ --content-width: 60ch;
161
+ --banner-height: 50vh;
162
+ --banner-max-height: 600px;
163
+ display: grid;
164
+ grid-template-areas: "banner";
165
+ place-items: center;
166
+ align-self: flex-start;
167
+ height: var(--banner-height);
168
+ /*max-height: var(--banner-max-height);*/
169
+ /*background-color: var(--primary-transparent);*/ /* bg color was meant
170
+ to provide contrast in case an image was missing,
171
+ but makes banners look weird */
172
+ overflow: hidden;
173
+ }
174
+
175
+ /* extra margin for banner menu */
176
+ /*.banner menu { margin-block: var(--ml); }*/
177
+
178
+ /* banner #content - constrain content width with a css variable set on parent*/
179
+ .banner .content { width: var(--content-width); }
180
+
181
+ /* banner grid */
182
+ .banner > * { grid-area: banner; }
183
+ .banner > nav {
184
+ place-self: start center;
185
+ width: var(--line-length);
186
+ }
187
+ .banner nav menu { /*flex: auto;*/ }
188
+
189
+ .banner > img, .hero {
190
+ height: min(var(--banner-height), var(--banner-max-height));
191
+ width: 100%;
192
+ max-width: none;
193
+ z-index: -1;
194
+ }
195
+
196
+ .banner figure {
197
+ --figure-max-height: 10rem;
198
+ grid-area: unset;
199
+ max-height: var(--figure-max-height);
200
+ background-color: unset;
201
+ border: unset;
202
+ margin: unset;
203
+ padding: unset;
204
+ }
205
+
206
+ .banner:has(figure) {
207
+ margin-bottom: 0;
208
+ }
209
+
210
+ /*remove top margin when the .banner is the first element on the page - [0010]*/
211
+ :where(body) > :where(main) > :where(.banner):first-of-type {
212
+ margin-top: 0;
213
+ }
214
+
215
+ /* -----------------------------------------------------------------------------
216
+ // #alert - the alert flash message box for providing timely info to the user
217
+ // -------------------------------------------------------------------------- */
218
+
219
+ /* #alert - the default alert box is an info box */
220
+ .alert {
221
+ color: var(--info);
222
+ background-color: var(--bg-info);
223
+ border: var(--solid) var(--info-low);
224
+ border-radius: var(--radius);
225
+ max-width: var(--line-length); /* don't just go max-width */
226
+ margin: var(--m) 0;
227
+ padding: 0 var(--m);
228
+ }
229
+
230
+ /* #headers - a single header style since alerts only need 1 header typically */
231
+ .alert :where(h1,h2,h3,h4,h5,h6) {
232
+ color: var(--info-low);
233
+ font-size: var(--large);
234
+ font-weight: var(--bold);
235
+ margin-top: var(--m);
236
+ }
237
+
238
+ /* #success #warning #error #alert - other alert types get their own colors */
239
+ .success.alert {
240
+ color: var(--success);
241
+ background-color: var(--bg-success);
242
+ border: var(--solid) var(--success-low);
243
+ }
244
+ .warning.alert {
245
+ color: var(--warning);
246
+ background-color: var(--bg-warning);
247
+ border: var(--solid) var(--warning-low);
248
+ }
249
+ .error.alert {
250
+ color: var(--error);
251
+ background-color: var(--bg-error);
252
+ border: var(--solid) var(--error-low);
253
+ }
254
+ .success.alert :where(h1,h2,h3,h4,h5,h6) { color: var(--success-low); }
255
+ .warning.alert :where(h1,h2,h3,h4,h5,h6) { color: var(--warning-low); }
256
+ .error.alert :where(h1,h2,h3,h4,h5,h6) { color: var(--error-low); }
257
+
258
+
259
+ /* -----------------------------------------------------------------------------
260
+ // main #nav - the body element should only ever have one main header element,
261
+ // within which, we can assume any nav element is the main nav.
262
+ // -------------------------------------------------------------------------- */
263
+
264
+ body:has(> header > nav) {
265
+ /*padding-top: 0;*/
266
+ }
267
+
268
+ nav.horizontal {
269
+ align-items: center; /* vertical alignment */
270
+ }
271
+
272
+ .column {
273
+ grid-column: unset;
274
+ flex-direction: column;
275
+ align-items: start; /* some items will float otherwise */
276
+ row-gap: var(--s); /* add some vertical spacing */
277
+ }
278
+
279
+ nav > div {
280
+ display: inline-flex;
281
+ align-items: center;
282
+ gap: var(--m);
283
+ }
284
+
285
+ /* both column menus & popup button sub-menus should flex vertically */
286
+ .column menu, button[aria-haspopup="true"] + menu {
287
+ flex-direction: column;
288
+ align-items: start; /* left justify items */
289
+ /*justify-content: center;*/
290
+ }
291
+
292
+ /* TODO: figure out why both a max-height of var(--height) and an unset
293
+ max-height results in a vertical shift of #nav #a images */
294
+ body > nav a, body > header > nav a {
295
+ max-height: 2.75rem;
296
+ }
297
+ /* visited links in the nav menu shouldn't be different colors */
298
+ :where([aria-label="primary"], nav menu li) a:visited { color: var(--link); }
299
+
300
+ /*body > nav a.button, body > header > nav a.button {
301
+ white-space: nowrap;
302
+ }*/
303
+
304
+ /*body > nav > menu > li, body > header > nav > menu > li {
305
+ padding: 0 var(--m);
306
+ }*/
307
+
308
+ /* #nav #search - retracting nav search form - the background svg icon is used
309
+ as the trigger to expand the search box via a :focus transition */
310
+ :where(nav) [type="search"] { /* [0010] specificity */
311
+ /*background-color: unset;*/
312
+ outline: none;
313
+ border: none;
314
+ width: var(--height); /* form icons are square */
315
+ min-width: 0;
316
+ padding: 0;
317
+ cursor: pointer; /* unconventional usage for 'clickable' */
318
+ transition: all var(--transition) ease-out;
319
+ }
320
+ /* expanded state of retracting nav search form - [0010] specificity */
321
+ :where(nav) :where([type="search"]):focus,
322
+ :where(nav) :where([type="search"]):not(:placeholder-shown) {
323
+ background-color: revert;
324
+ border: var(--solid) var(--highlighted);
325
+ width: auto;
326
+ padding-left: var(--height);
327
+ cursor: text;
328
+ transition: all var(--transition) ease-out;
329
+ }
330
+ /* <form>s by default subtract padding from max-width, so reset that */
331
+ :where(nav) form { max-width: var(--line-length); }
332
+
333
+ /*:where(nav) button {
334
+ min-height: unset;
335
+ }*/
336
+
337
+ /* visually differentiate active page in nav menus */
338
+ [aria-current="page"] {
339
+ /*color: var(--primary-900);*/
340
+ text-decoration: unset;
341
+ pointer-events: none; /* make link non-clickable */
342
+ cursor: default;
343
+ }
344
+
345
+ nav [aria-haspopup="true"] + menu > li {
346
+ display: block;
347
+ max-width: var(--line-length);
348
+ }
349
+
350
+ nav [aria-haspopup="true"] + menu {
351
+ position: absolute;
352
+ visibility: hidden;
353
+ margin: 0;
354
+ padding: var(--m);
355
+ }
356
+
357
+ nav.column [aria-haspopup="true"] + menu {
358
+ width: fit-content;
359
+ margin-block: 0;
360
+ }
361
+
362
+ nav:not(.column) [aria-haspopup="true"] + menu {
363
+ background-color: var(--background);
364
+ border: var(--solid) var(--border);
365
+ border-radius: var(--radius);
366
+ }
367
+
368
+ nav [aria-haspopup="true"]:focus-within + menu {
369
+ visibility: visible;
370
+ transition: visibility position var(--transition);
371
+ }
372
+
373
+ nav.column [aria-haspopup="true"]:focus-within + menu {
374
+ position: relative;
375
+ }
376
+
377
+ nav [aria-haspopup="true"]:focus + menu {
378
+ visibility: visible;
379
+ transition: visibility position var(--transition);
380
+ }
381
+ nav.column [aria-haspopup="true"]:focus + menu {
382
+ position: relative;
383
+ }
384
+
385
+ nav.column menu {
386
+ width: 100%; /* otherwise menu items might wrap prematurely*/
387
+ margin-block: var(--m); /* add back vertical spacing */
388
+ }
389
+
390
+ nav.column :where(button, .button):not(.toggle) {
391
+ width: 100%;
392
+ }
393
+
394
+ /* TODO: this pushes padding into the next column */
395
+ :not(aside) > nav.column {
396
+ /*padding: 0 var(--m) var(--m) 0;*/
397
+ }
398
+
399
+ menu > li > button, menu > li > button:hover {
400
+ /* display: flex;
401
+ font-size: var(--text);
402
+ line-height: var(--normal);
403
+ color: var(--link);
404
+ background-color: unset;
405
+ border: none;
406
+ text-transform: revert;
407
+ text-decoration: underline;
408
+ padding: 0;*/
409
+ }
410
+
411
+
412
+ /* -----------------------------------------------------------------------------
413
+ // #buttons - button classes to mimic the styles of button types for other
414
+ elements
415
+ // -------------------------------------------------------------------------- */
416
+
417
+ /* #primary #button - a button class that mirrors the primary button style
418
+ // .......................................................................... */
419
+
420
+ /* TODO: reconcile with button class in base.css; this is one instance where not
421
+ using SASS/SCSS hurts, but it's a minor pain for less tooling/dependencies.*/
422
+ .button { /* [0010] specificity */
423
+ -webkit-appearance: button; /* normalize.css */
424
+ appearance: button;
425
+ display: inline-flex; /* allows centering of content */
426
+ place-items: center; /* does the actual centering */
427
+ color: var(--action);
428
+ background-color: var(--foreground); /* inverted style for primary
429
+ actions - will override for .secondary */
430
+ font-size: var(--small);
431
+ font-weight: var(--bold);
432
+ line-height: 1.15;
433
+ text-transform: uppercase;
434
+ text-decoration: none;
435
+ white-space: nowrap;
436
+ text-align: center; /* centered text, not left justified */
437
+ justify-content: center;
438
+ cursor: pointer; /* controversial for buttons but common */
439
+ width: fit-content; /* don't grow to container width */
440
+ height: fit-content; /* don't grow to container height */
441
+ min-height: var(--height); /* to match input fields */
442
+ padding: 0 var(--m);
443
+ border: var(--solid) var(--transparent);
444
+ border-radius: var(--radius);
445
+ }
446
+
447
+ .button:hover { /* [0020] specificity */
448
+ color: var(--action); /* needed to override anchor links */
449
+ background-color: var(--bg-highlight);
450
+ border-color: var(--transparent);
451
+ }
452
+
453
+ /* #secondary #button - less emphasized than the standard button
454
+ // .......................................................................... */
455
+
456
+ button.secondary, .secondary.button { /* [0011] / [0020] specificity */
457
+ color: var(--foreground); /* invert foreground/background */
458
+ background-color: var(--background); /* invert foreground/background */
459
+ border-color: var(--foreground);
460
+ }
461
+
462
+ :where(button, .button).secondary:hover { /* [0020] specificity */
463
+ background-color: var(--contrast); /* TODO: make grey in dark mode */
464
+ }
465
+
466
+ /* #tertiary #button - a link-like button
467
+ // .......................................................................... */
468
+
469
+ button.tertiary, .tertiary.button { /* [0011] / [0020] specificity */
470
+ color: var(--foreground); /* invert foreground/background */
471
+ background-color: unset; /* border is transparent */
472
+ padding-inline: var(--xxs);
473
+ }
474
+
475
+ :where(button, .button).tertiary:hover { /* [0020] specificity */
476
+ text-decoration: underline; /* TODO: make grey in dark mode */
477
+ border-color: var(--transparent);
478
+ }
479
+
480
+
481
+ /* #warning - a button for major changes TEMP:
482
+ // .......................................................................... */
483
+
484
+ :where(button, .button).warning {
485
+ --icon-color: var(--warning-low);
486
+ color: var(--warning-low);
487
+ background-color: var(--warning);
488
+ }
489
+
490
+ :where(button, .button).warning:hover {
491
+ --icon-color: var(--warning);
492
+ color: var(--warning);
493
+ background-color: var(--warning-low);
494
+ border-color: var(--warning);
495
+ }
496
+
497
+ :where(button, .button).secondary.warning {
498
+ --icon-color: var(--warning);
499
+ color: var(--warning);
500
+ background-color: unset;
501
+ border-color: var(--warning);
502
+ }
503
+
504
+ :where(button, .button).secondary.warning:hover {
505
+ background-color: var(--warning-low);
506
+ }
507
+
508
+ :where(button, .button).tertiary.warning {
509
+ --icon-color: var(--warning);
510
+ color: var(--warning);
511
+ }
512
+
513
+ :where(button, .button).tertiary.warning:hover {
514
+ background-color: unset;
515
+ border-color: var(--transparent);
516
+ }
517
+
518
+
519
+ /* #danger - a button for irrevocable actions TEMP: --secondary should be
520
+ replaced by --error or perhaps --danger
521
+ // .......................................................................... */
522
+
523
+ .danger {
524
+ --icon-color: var(--secondary-100);
525
+ color: var(--secondary-100);
526
+ background-color: var(--secondary);
527
+ }
528
+
529
+ .danger:hover {
530
+ --icon-color: var(--secondary);
531
+ color: var(--secondary);
532
+ background-color: var(--secondary-100);
533
+ border-color: var(--secondary);
534
+ }
535
+
536
+ .secondary.danger {
537
+ --icon-color: var(--secondary);
538
+ color: var(--secondary);
539
+ background-color: unset;
540
+ border-color: var(--secondary);
541
+ }
542
+
543
+ .secondary.danger:hover {
544
+ background-color: var(--secondary-200);
545
+ }
546
+
547
+ .tertiary.danger {
548
+ --icon-color: var(--secondary);
549
+ color: var(--secondary);
550
+ }
551
+
552
+ .tertiary.danger:hover {
553
+ background-color: unset;
554
+ border-color: var(--transparent);
555
+ }
556
+
557
+
558
+ /* #buttons - a flex container for inlining buttons, typically a <div>
559
+ // .......................................................................... */
560
+
561
+ /* TODO: the main form grid should inline elements rather than forcing it here
562
+ .bottons inlines/wraps multiple buttons where necessary - [0010] specificity*/
563
+ .buttons {
564
+ list-style: none;
565
+ display: flex; /* overwrite display: grid; */
566
+ flex-wrap: wrap; /* wrap buttons when necessary */
567
+ /*justify-content: flex-end;*/ /* looks better on narrow screens */
568
+ gap: var(--s); /* don't let adjacent buttons touch */
569
+ grid-column: elements; /* put buttons in the inputs grid column */
570
+ }
571
+ /* menu buttons can delegate spacing to the parent <menu> - [0011] specificity*/
572
+ menu.buttons { margin-inline: 0; }
573
+ /* <p>s take up a whole grid row, so no need to column it - [0012] specificity*/
574
+ .buttons p :is(button, input) { grid-column: none; }
575
+
576
+ form .buttons { max-width: var(--field-max) }
577
+
578
+ /*form .buttons.right { padding-right: calc(var(--xl) - var(--s)) }*/
579
+
580
+ .roomy.buttons { gap: var(--ml); }
581
+
582
+ /* fix buttons floating and getting squished at smaller screen widths */
583
+ @container (max-width: 28rem) {
584
+ form { max-width: unset; }
585
+ form .buttons {
586
+ max-width: unset;
587
+ grid-column: -1/1;
588
+ }
589
+ label { font-size: var(--small); }
590
+ }
591
+
592
+ /* #popup #button - actuates a popup section - currently for dropdown menu items
593
+ // .......................................................................... */
594
+
595
+ button[aria-haspopup="true"] {
596
+ all: unset;
597
+ color: var(--link);
598
+ font-weight: var(--bold);
599
+ text-decoration: underline;
600
+ }
601
+
602
+ button[aria-haspopup="true"]::after {
603
+ display: inline-block; /* negates text-decoration */
604
+ content: '\00A0\25be'; /* appends inverted triangle */
605
+ }
606
+
607
+ button[aria-haspopup="true"]:hover {
608
+ color: var(--active);
609
+ text-decoration: none;
610
+ cursor: pointer; /* controversial for buttons but common*/
611
+ }
612
+
613
+ /* #toggle - hamburger button for toggling sections of pages
614
+ // .......................................................................... */
615
+ .toggle { /* [0010] specifitiy */
616
+ color: var(--background);
617
+ padding: 0;
618
+ aspect-ratio: 1;
619
+ height: 2.75rem;
620
+ width: fit-content;
621
+ }
622
+
623
+ .toggle::before { /* [0011] specifitiy */
624
+ content: '≡'; /* '\2261'; */
625
+ font-size: 2rem;
626
+ vertical-align: super;
627
+ }
628
+
629
+ .tertiary.toggle:hover { /* .tertiary has inverted colors so invert the hover */
630
+ color: var(--active);
631
+ background-color: unset;
632
+ text-decoration: none;
633
+ }
634
+
635
+ /* HACK: fixes offset on toggle (hamburger) buttons - [0020] specificity */
636
+ .toggle.button { display:unset; }
637
+
638
+ /* TODO: see if we can use only css to toggle the menu into the burger menu */
639
+ nav:has(.toggle[aria-expanded]) > menu {
640
+ /*visibility: visible;*/
641
+ }
642
+
643
+ nav:has(.toggle[aria-expanded=false]) > menu {
644
+ /*visibility: hidden;*/
645
+ }
646
+
647
+
648
+ /* TODO: moved from states.css - make sure these get applied correctly */
649
+ [rel~="external"].button { padding: 0 var(--ms); }
650
+ [rel~="external"].button::after {
651
+ padding: var(--button-padding);
652
+ mask-position-x: right;
653
+ }
654
+ [rel~="external"].button:hover::after { background-color: var(--foreground); }
655
+
656
+
657
+
658
+ /* -----------------------------------------------------------------------------
659
+ // #selectmenu - Open UI replacement to the standard select element -
660
+ https://open-ui.org/prototypes/selectmenu
661
+ // -------------------------------------------------------------------------- */
662
+
663
+ selectmenu { }
664
+
665
+ selectmenu::part(button) {
666
+ color: var(--text);
667
+ background-color: var(--bg-field);
668
+ min-width: var(--width);
669
+ height: var(--height);
670
+ border-radius: var(--radius);
671
+ }
672
+
673
+ selectmenu::part(listbox) {
674
+ margin-top: var(--m);
675
+ border: var(--solid) var(--border);
676
+ border-radius: var(--radius);
677
+ }
678
+
679
+
680
+ /* -----------------------------------------------------------------------------
681
+ // #card - general purpose card - cards should appear in a container to get the
682
+ // benefits of container query-based responsiveness. cards are composed
683
+ // of an image plus some content. note that content should go in a
684
+ // content div due to limitations of css grid not allowing content flow
685
+ // in a grid area: https://github.com/w3c/csswg-drafts/issues/4416
686
+ // -------------------------------------------------------------------------- */
687
+
688
+ /* cards should typically be lists of articles */
689
+ .cards {
690
+ display: grid;
691
+ grid-template-columns: repeat(auto-fit, minmax(var(--container), 1fr));
692
+ gap: var(--m);
693
+
694
+ /* display: flex;
695
+ flex-wrap: wrap;
696
+ max-width: var(--line-length);*/
697
+ column-gap: var(--m); /* gap only for side-by-side placement */
698
+ }
699
+
700
+ /* in most cases .cards should contain a set of articles, otherwise use .card */
701
+ .card, .cards > article {
702
+ flex: 1 0 calc((11rem - 100%) * 999);
703
+ /*display: block;*/
704
+ display: flex;
705
+ flex-direction: column;
706
+ column-gap: var(--m); /* gap only for side-by-side placement */
707
+ margin: var(--m) 0;
708
+ }
709
+
710
+ .cards .contents {
711
+ display: flex;
712
+ flex-direction: column;
713
+ }
714
+
715
+ .cards .meta {
716
+ margin: 0;
717
+ }
718
+
719
+ :where(.card, .cards) :where(h1,h2,h3,h4,h5,h6):first-child {
720
+ margin-top: 0;
721
+ }
722
+
723
+ :where(.card) > *, :where(.cards) > article > * {
724
+ margin-block: var(--ms); /* margin rather than gap for control */
725
+ }
726
+
727
+ .card > .buttons:last-child, .cards > article > .buttons:last-child {
728
+ margin-top: auto; /* make sure buttons are at the bottom */
729
+ }
730
+
731
+ @container (min-width: 35rem) {
732
+ /*.card { flex-direction: row; }*/ /* interferes with single-card section */
733
+ .card > img, .cards > article > img {
734
+ max-height: var(--width);
735
+ max-width: var(--width);
736
+ }
737
+ /* change orphaned last item to row layout for 3-wide grid */
738
+ .cards :last-child:nth-child(3n - 2) {
739
+ grid-column: span 3;
740
+ flex-direction: row;
741
+ }
742
+ }
743
+
744
+ /* TEMP: use media queries for browsers that don't support container queries */
745
+ @supports not (container-type: inline-size) {
746
+ @media (min-width: 47rem) {
747
+ /*.card { flex-direction: row; }*/ /* interferes with single-card section */
748
+ .card > img, .cards > article > img {
749
+ max-height: var(--width);
750
+ max-width: var(--width);
751
+ }
752
+ .cards :last-child:nth-child(3n - 2) {
753
+ grid-column-end: span 3;
754
+ flex-direction: row;
755
+ }
756
+ }}
757
+
758
+ /* -----------------------------------------------------------------------------
759
+ // #tags - an inline list of metatags; can be text, links, icons, or other
760
+ // inline list content.
761
+ // -------------------------------------------------------------------------- */
762
+
763
+ /* .tags should generally be an unordered list. margin/padding get in the way of
764
+ a compact presentation of tags. */
765
+ .tags {
766
+ margin: 0;
767
+ padding: 0;
768
+ }
769
+
770
+ .tags > li {
771
+ display: inline-block;
772
+ color: var(--secondary);
773
+ font-size: var(--small);
774
+ font-weight: var(--bold);
775
+ text-transform: uppercase;
776
+ letter-spacing: 0.02em;
777
+ }
778
+
779
+ .tags > li + li {
780
+ margin-left: var(--m);
781
+ }
782
+
783
+ .tags + :where(h1,h2,h3,h4,h5,h6) {
784
+ margin-top: var(--s);
785
+ }
786
+
787
+ /* .tags should add spacing if inserted elsewhere */
788
+ .tags:has(+ .buttons) {
789
+ margin-bottom: var(--m);
790
+ }
791
+
792
+ /* TEMP: add margin-top to buttons for browsers that don't support :has() */
793
+ @supports not (:has(a)) {
794
+ .tags + .buttons {
795
+ margin-top: var(--m);
796
+ }}
797
+
798
+
799
+ /* -----------------------------------------------------------------------------
800
+ // #grid - multi-column grid adjustments
801
+ // -------------------------------------------------------------------------- */
802
+
803
+ /* #grid - apply grid with columns of width var(--column) to an element - [0010]
804
+ // .......................................................................... */
805
+ .grid {
806
+ display: grid;
807
+ grid-template-columns: [full-start] repeat(auto-fit,
808
+ minmax(var(--column), 1fr)) [full-end];
809
+ column-gap: var(--m);
810
+ container-type: none; /* containment clashes with grid on safari */
811
+ }
812
+
813
+ /* for layouts with left & right asides, put main in the middle grid columns */
814
+ /* grrr, firefox is not forgiving with :has() rules, so this must be separated*/
815
+ .dual-aside {
816
+ grid-column: 2 / -2;
817
+ }
818
+
819
+ /*full is a grid area for a row that spans all columns - [0000] specificity */
820
+ :where(.grid) > :where(header,hgroup,h1,h2,h3,h4,h5,h6,nav:not(.column),footer){
821
+ grid-column: full;
822
+ }
823
+ /* apply default padding/border to .grid asides - [0001] specificity */
824
+ :where(.grid) > aside {
825
+ padding: var(--xs) var(--s) var(--s);
826
+ border-radius: var(--radius);
827
+ }
828
+ /* apply default margin to nav menus inside .grid asides - [0001] specificity */
829
+ :where(.grid) > :where(aside) > :where(nav) > menu {
830
+ margin: var(--m) 0;
831
+ }
832
+ /* put the first aside in the first grid column */
833
+ :where(.grid:has(> aside)) > :where(aside):first-of-type {/*[0010] specificity*/
834
+ grid-column: 1;
835
+ }
836
+ /* spread main across the remaining grid columns */
837
+ :where(.grid) > :where(aside) + main, /* [0001] specificity */
838
+ :where(.grid) :where(aside, nav.column) + :where(main, .main) { /* [0000] */
839
+ grid-column: 2 / -1;
840
+ }
841
+
842
+ /* TODO: double check this selector for unintended consequences - [0001] */
843
+ :where(.grid) :where(aside, nav.column) + :where(main, .main) p {
844
+ margin-top: 0;
845
+ }
846
+
847
+ /* HACK: full-width paragraphs in .grid to ... - [0001] specificity */
848
+ :where(.grid) > p { grid-column: full; }
849
+
850
+ /* remove top margin from the very first heading and no more than that */
851
+ .grid > :where(main) > :where(header):first-child,
852
+ .grid > :where(main) > :where(section) > :where(header):first-child,
853
+ .grid > :where(main) > :where(section) > :where(header) >
854
+ :where(h1,h2,h3,h4,h5,h6):first-child {
855
+ margin-top: 0;
856
+ }
857
+
858
+ /* :has() is preferred over using classes, but firefox lacks support so far */
859
+ @supports selector(:has(*)) {
860
+ :where(.grid:has(> aside, > main)) > main { /* [0001] specificity */
861
+ grid-column: 2 / -1;
862
+ }
863
+
864
+ :where(.grid:has(> aside + main + aside)) > main { /* [0001] specificity */
865
+ grid-column: 2 / -2;
866
+ }
867
+ /* [0010] specificity */
868
+ :where(.grid:has(> aside + main + aside)) > :where(aside):nth-of-type(2n) {
869
+ grid-column: -2;
870
+ }
871
+ }
872
+
873
+ /* #form #grid - general purpose 2-column grid, as used on forms by default
874
+ // .......................................................................... */
875
+ .form.grid {
876
+ display: grid;
877
+ grid-template-columns: [labels full-start] 3fr [elements] 7fr [full-end];
878
+ gap: var(--m);
879
+ /*grid-row: auto;*/
880
+ /*max-width: calc(var(--line-length) - 2 * var(--m));*/
881
+ }
882
+ .form.grid :is(h3, p) {
883
+ grid-column: full;
884
+ margin: 0;
885
+ }
886
+ .form.grid h3 { margin-top: var(--m); }
887
+ .form.grid label:not(.visually-hidden) { display: contents; }
888
+ .form.grid :is(meter, progress) { grid-column: elements; }
889
+
890
+ /* #label - class to emulate the form label grid style - [0010] specificity
891
+ // .......................................................................... */
892
+ .label {
893
+ display: inline-block;
894
+ grid-column: labels;
895
+ text-align: right;
896
+ }
897
+
898
+ /* #input - class to emulate the form input grid style - [0010] specificity
899
+ // .......................................................................... */
900
+ .input {
901
+ display: inline-block;
902
+ grid-column: elements;
903
+ }
904
+
905
+
906
+ /* -----------------------------------------------------------------------------
907
+ // #progressbar - a circular progress bar component - from almond.css
908
+ // -------------------------------------------------------------------------- */
909
+
910
+ [role="progressbar"] {
911
+ --value: 50;
912
+ --thick: 50%;
913
+ --medium: 58%;
914
+ --thin: 67%;
915
+ --thickness: var(--medium);
916
+ aspect-ratio: 1;
917
+ border-radius: 50%;
918
+ display: grid;
919
+ font-size: 2em;
920
+ overflow: hidden;
921
+ place-items: center;
922
+ position: relative;
923
+ width: 100%;
924
+ }
925
+
926
+ [role="progressbar"]::before {
927
+ content: "";
928
+ background: conic-gradient(var(--primary) calc(var(--value) * 1%),
929
+ #0000 0);
930
+ background-color: var(--primary-100);
931
+ height: 100%;
932
+ left: 0;
933
+ -webkit-mask: radial-gradient(#0000 var(--thickness), #000 0);
934
+ mask: radial-gradient(#0000 var(--thickness), #000 0);
935
+ position: absolute;
936
+ top: 0;
937
+ transition: background-color 0.5s;
938
+ width: 100%;
939
+ }
940
+ [role="progressbar"]::after {
941
+ counter-reset: percentage var(--value);
942
+ content: counter(percentage) "%";
943
+ }
944
+ [role="progressbar"]:hover::before {
945
+ background-color: var(--primary-300);
946
+ }