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,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
+ }