@danieldeusing/design 0.20.0 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -1
- package/dist/danieldeusing-design.css +327 -1
- package/dist/danieldeusing-design.min.css +2 -2
- package/package.json +1 -1
- package/runtime/index.js +2 -0
- package/runtime/pagination.js +341 -0
- package/runtime/select.js +539 -0
- package/src/base.css +14 -0
- package/src/components.css +311 -0
- package/src/print.css +1 -0
- package/templates/documentation.html +11 -6
- package/templates/page-chrome.html +6 -1
package/src/components.css
CHANGED
|
@@ -133,6 +133,28 @@
|
|
|
133
133
|
transform: translateY(-2px);
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
+
/* THE BUTTON HAD NO DISABLED STATE — the select next to it did, and they disagreed.
|
|
137
|
+
`select:disabled` has been `opacity: 0.45; cursor: default` since 0.21.0, so a control row
|
|
138
|
+
holding a greyed dropdown beside a fully-lit button that does nothing when pressed was saying
|
|
139
|
+
two different things about the same condition. A pager's `prev` on page one is the case that
|
|
140
|
+
made it unavoidable: it is disabled far more often than not.
|
|
141
|
+
|
|
142
|
+
BELOW the hover rules on purpose. `:disabled` and `--ghost:hover` are both (0,2,0), so equal
|
|
143
|
+
specificity is settled by source order — declared above them, a disabled button would light up
|
|
144
|
+
under the pointer and reinstate exactly the lie this removes. Same trap that made every compact
|
|
145
|
+
button render at CTA size in 0.13.0.
|
|
146
|
+
|
|
147
|
+
Opacity and nothing else about the fill: a disabled PRIMARY button must stay filled and just go
|
|
148
|
+
quiet. Setting `background: transparent` here would strip it to `--primary-foreground` text on
|
|
149
|
+
the page background — near-invisible on all four themes — so the one property that reads as
|
|
150
|
+
"off" on both variants is the only one touched. */
|
|
151
|
+
.btn-terminal:disabled {
|
|
152
|
+
opacity: 0.45;
|
|
153
|
+
cursor: default;
|
|
154
|
+
box-shadow: none;
|
|
155
|
+
transform: none;
|
|
156
|
+
}
|
|
157
|
+
|
|
136
158
|
/* A ROW-SIZED BUTTON. The system's button is a landing-page CTA at 12px/24px; a table row that
|
|
137
159
|
offers `remove` needs the same button at the size of its text. This is the size ONLY — colour,
|
|
138
160
|
corners and the `> ` prefix still come from .btn-terminal / --ghost, so a compact button cannot
|
|
@@ -191,6 +213,49 @@
|
|
|
191
213
|
.btn-terminal--destructive { min-width: 44px; min-height: 44px; }
|
|
192
214
|
}
|
|
193
215
|
|
|
216
|
+
/* THE EDIT ROW ACTION — the same icon button as the bin, in the ordinary colour.
|
|
217
|
+
|
|
218
|
+
`edit →` was a word and an arrow in a table cell wide enough to hold them, next to a bin that
|
|
219
|
+
is 22px square. Two controls doing the same job in one column, one of them four times the
|
|
220
|
+
width of the other, and at 375px the label broke mid-word into "edi / t →" — which is why
|
|
221
|
+
cockpit carried a `white-space: nowrap` rule to prop it up. An icon says it in the space the
|
|
222
|
+
bin already proved is enough.
|
|
223
|
+
|
|
224
|
+
IT IS NOT DESTRUCTIVE AND MUST NOT BORROW THAT COLOUR. Editing is an ordinary action; red is
|
|
225
|
+
reserved for the one press that cannot be taken back. So this class carries NO colour at all —
|
|
226
|
+
compose it with `--ghost` and it is `--primary` on a `--border` outline like every other
|
|
227
|
+
secondary control, and with nothing and it is the filled primary button. That is also why it is
|
|
228
|
+
not `--destructive` with the fill swapped: colour is the bin's whole warning, and the delta
|
|
229
|
+
here is only the glyph and the box that holds it.
|
|
230
|
+
|
|
231
|
+
THE GLYPH IS A MASK, for the reason the bin's comment gives: `currentColor` re-takes whatever
|
|
232
|
+
colour the composition landed on, on all four themes, with nothing declared per-theme. A pasted
|
|
233
|
+
SVG would be one `fill=` that is wrong on two of them, once per surface that adopts it.
|
|
234
|
+
|
|
235
|
+
AN ACCESSIBLE NAME IS MANDATORY — see the bin. Dropping the word "edit" leaves a button whose
|
|
236
|
+
entire content is a mask, and a mask has no text: a screen reader announces "button", and a
|
|
237
|
+
column of them announces "button" a dozen times. `aria-label` must name the TARGET
|
|
238
|
+
("edit poi/vu3"), not repeat the verb, or the column is a dozen identical announcements
|
|
239
|
+
instead. `bin/design-conformance` fails an icon button with no accessible name. */
|
|
240
|
+
.btn-terminal--edit {
|
|
241
|
+
display: inline-flex; align-items: center; justify-content: center;
|
|
242
|
+
padding: 0.3rem;
|
|
243
|
+
}
|
|
244
|
+
/* Kills the `> ` prefix, which would otherwise be the entire text content of an icon button.
|
|
245
|
+
--ghost already blanks it; repeated here so the class is correct on a filled button too. */
|
|
246
|
+
.btn-terminal--edit::before { content: ""; }
|
|
247
|
+
.btn-terminal--edit::after {
|
|
248
|
+
content: ""; display: block; width: 0.875rem; height: 0.875rem;
|
|
249
|
+
background: currentColor;
|
|
250
|
+
-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04l-3.75-3.75-1.83 1.83 3.75 3.75 1.83-1.83z'/%3E%3C/svg%3E") center / contain no-repeat;
|
|
251
|
+
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04l-3.75-3.75-1.83 1.83 3.75 3.75 1.83-1.83z'/%3E%3C/svg%3E") center / contain no-repeat;
|
|
252
|
+
}
|
|
253
|
+
/* 44px under a thumb, same as the bin: `min-*` grows the tap target without moving the glyph,
|
|
254
|
+
which the flexbox keeps centred. Never set a width on it. */
|
|
255
|
+
@media (pointer: coarse) {
|
|
256
|
+
.btn-terminal--edit { min-width: 44px; min-height: 44px; }
|
|
257
|
+
}
|
|
258
|
+
|
|
194
259
|
/* Quiet, underlined link that warms to the primary colour on hover. */
|
|
195
260
|
.link-quiet {
|
|
196
261
|
color: var(--muted-foreground);
|
|
@@ -362,6 +427,252 @@ html[data-theme="warm"] .dropdown-item[data-theme-value="warm"] {
|
|
|
362
427
|
text-shadow: 0 0 8px var(--glow);
|
|
363
428
|
}
|
|
364
429
|
|
|
430
|
+
/* ─────────────────────────────────────────────────────────────────────────
|
|
431
|
+
Select — the estate's own dropdown, because a native one's list CANNOT be
|
|
432
|
+
styled. Pairs with runtime/select.js.
|
|
433
|
+
|
|
434
|
+
THE CONSTRAINT THAT DECIDES EVERYTHING HERE: a `<select>`'s option list is
|
|
435
|
+
drawn by the OPERATING SYSTEM, outside the page, and no stylesheet reaches
|
|
436
|
+
it. So every surface in the estate has been rendering a rounded, blue-
|
|
437
|
+
highlighted, system-font menu out of a terminal UI — the one component that
|
|
438
|
+
could not be made to look like the rest, no matter what the page declared.
|
|
439
|
+
`appearance: base-select` would fix it in Chrome 135+ and nowhere else, so
|
|
440
|
+
Safari would keep the system menu: the estate would then be inconsistent
|
|
441
|
+
WITH ITSELF, which is worse than being consistently wrong.
|
|
442
|
+
|
|
443
|
+
So the list is rebuilt in the page. runtime/select.js keeps the real
|
|
444
|
+
`<select>` as the value (it is still what a form submits and what page code
|
|
445
|
+
reads) and paints a button + listbox over it. THE MARKUP CONTRACT IS
|
|
446
|
+
NOTHING: author a plain `<select>`, exactly as with `<table>`.
|
|
447
|
+
|
|
448
|
+
TWO RENDERINGS, ONE DECLARATION. The bare `<select>` (JS off, or before the
|
|
449
|
+
runtime has run) and the `.select-trigger` that replaces it must be the same
|
|
450
|
+
control at the same size — a reader who tabs into a page mid-enhancement
|
|
451
|
+
must not see the box change shape. Splitting their look across two rules is
|
|
452
|
+
how they would drift, so the closed control is declared ONCE for both, by
|
|
453
|
+
ELEMENT and by class together. Five copies of `.cfg-sel` in cockpit are what
|
|
454
|
+
this replaces; none of them agreed about `:disabled`.
|
|
455
|
+
|
|
456
|
+
THE BORDER IS 60% OF --foreground, MEASURED, NOT --border. A control's edge
|
|
457
|
+
is the thing that says "this is a control" and WCAG 1.4.11 wants 3:1 for
|
|
458
|
+
that; `--border` is a CONTAINER hairline and measures 1.37 / 2.00 / 1.61 /
|
|
459
|
+
1.42 against `--background` on the four themes — invisible as a control
|
|
460
|
+
edge, and correct for the cards it was made for. The estate's hand-rolled
|
|
461
|
+
selects used `currentColor 30%`, which is 1.70 at worst. 60% is the first
|
|
462
|
+
step that clears 3:1 on all four themes against ALL THREE surfaces a control
|
|
463
|
+
can land on (worst: warm 3.24 over --background, 3.24 over --muted). Mixed
|
|
464
|
+
with `transparent` rather than with a surface so it composites correctly
|
|
465
|
+
wherever it is dropped, and derived from a token so it stays per-theme.
|
|
466
|
+
|
|
467
|
+
THE CARET IS TWO GRADIENT HALVES, not an SVG. It has to be `currentColor` —
|
|
468
|
+
a data-URI with a baked fill would need declaring four times or be wrong on
|
|
469
|
+
two themes (the luminance gap in tokens.css), and a `::after` is impossible
|
|
470
|
+
because a `<select>` cannot have generated content. Two abutting squares,
|
|
471
|
+
each half-filled on the diagonal, draw the same ▾ on both renderings.
|
|
472
|
+
───────────────────────────────────────────────────────────────────────── */
|
|
473
|
+
select,
|
|
474
|
+
.select-trigger {
|
|
475
|
+
appearance: none;
|
|
476
|
+
-webkit-appearance: none;
|
|
477
|
+
max-width: 100%;
|
|
478
|
+
padding: 0.28rem 1.45rem 0.28rem 0.5rem;
|
|
479
|
+
font: inherit;
|
|
480
|
+
font-size: var(--fs-sm);
|
|
481
|
+
line-height: var(--lh-tight);
|
|
482
|
+
text-align: left;
|
|
483
|
+
color: var(--foreground);
|
|
484
|
+
background-color: transparent;
|
|
485
|
+
background-image:
|
|
486
|
+
linear-gradient(45deg, transparent 50%, currentColor 50%),
|
|
487
|
+
linear-gradient(135deg, currentColor 50%, transparent 50%);
|
|
488
|
+
background-position:
|
|
489
|
+
right 0.78rem center,
|
|
490
|
+
right 0.5rem center;
|
|
491
|
+
background-size: 0.28rem 0.28rem;
|
|
492
|
+
background-repeat: no-repeat;
|
|
493
|
+
border: 1px solid color-mix(in srgb, var(--foreground) 60%, transparent);
|
|
494
|
+
cursor: pointer;
|
|
495
|
+
transition: border-color 0.15s ease;
|
|
496
|
+
}
|
|
497
|
+
select:hover,
|
|
498
|
+
.select-trigger:hover,
|
|
499
|
+
.select-trigger[aria-expanded="true"] {
|
|
500
|
+
border-color: var(--primary);
|
|
501
|
+
}
|
|
502
|
+
select:disabled,
|
|
503
|
+
.select-trigger:disabled {
|
|
504
|
+
opacity: 0.45;
|
|
505
|
+
cursor: default;
|
|
506
|
+
}
|
|
507
|
+
/* Open: the caret turns over, so the control says "the list is out" without
|
|
508
|
+
relying on the panel being in view — it may have flipped above the trigger. */
|
|
509
|
+
.select-trigger[aria-expanded="true"] {
|
|
510
|
+
background-image:
|
|
511
|
+
linear-gradient(135deg, transparent 50%, currentColor 50%),
|
|
512
|
+
linear-gradient(45deg, currentColor 50%, transparent 50%);
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/* The wrapper the runtime puts around the pair. It is `relative` so the real
|
|
516
|
+
`<select>` can be laid exactly over the trigger: NOT `display: none`, and not
|
|
517
|
+
`visibility: hidden` either — Chrome refuses to show a validation bubble on a
|
|
518
|
+
control it cannot focus and then blocks the submit with no message at all, so
|
|
519
|
+
a `required` select would silently stop working. Transparent and over the
|
|
520
|
+
trigger, the bubble still points at the right box. */
|
|
521
|
+
.select-field {
|
|
522
|
+
position: relative;
|
|
523
|
+
display: inline-flex;
|
|
524
|
+
max-width: 100%;
|
|
525
|
+
vertical-align: middle;
|
|
526
|
+
}
|
|
527
|
+
.select-field > select {
|
|
528
|
+
position: absolute;
|
|
529
|
+
inset: 0;
|
|
530
|
+
width: 100%;
|
|
531
|
+
height: 100%;
|
|
532
|
+
opacity: 0;
|
|
533
|
+
pointer-events: none;
|
|
534
|
+
}
|
|
535
|
+
.select-trigger {
|
|
536
|
+
display: inline-flex;
|
|
537
|
+
align-items: center;
|
|
538
|
+
width: 100%;
|
|
539
|
+
}
|
|
540
|
+
.select-value {
|
|
541
|
+
/* min-width: 0 is what lets a flex item shrink below its content — without it
|
|
542
|
+
`text-overflow: ellipsis` never fires and a long option label blows the
|
|
543
|
+
control out of its column instead of being clipped. */
|
|
544
|
+
min-width: 0;
|
|
545
|
+
overflow: hidden;
|
|
546
|
+
text-overflow: ellipsis;
|
|
547
|
+
white-space: nowrap;
|
|
548
|
+
}
|
|
549
|
+
/* An empty flex item has no line box, so a select whose current option has no
|
|
550
|
+
text (`<option value="">`) would render a control HALF THE HEIGHT of the one
|
|
551
|
+
beside it. A zero-width space gives it a line and no width, and screen readers
|
|
552
|
+
ignore it — unlike an ` `, which they read out. */
|
|
553
|
+
.select-value::after {
|
|
554
|
+
content: "\200b";
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
/* THE PANEL IS position: fixed AND APPENDED OUT OF THE FLOW, for two reasons
|
|
558
|
+
this estate hits constantly: selects live inside `.tablewrap` (which scrolls,
|
|
559
|
+
so it clips) and inside modal `<dialog>`s (a top layer, which nothing outside
|
|
560
|
+
can paint above). An absolutely-positioned panel would be cut off in the
|
|
561
|
+
first and hidden behind the second. runtime/select.js appends it to the
|
|
562
|
+
nearest open <dialog> when there is one and to <body> otherwise.
|
|
563
|
+
z-index 60 clears the scanline overlay (40), .dropdown-panel (50) and
|
|
564
|
+
footer.status (50). */
|
|
565
|
+
.select-panel {
|
|
566
|
+
position: fixed;
|
|
567
|
+
z-index: 60;
|
|
568
|
+
margin: 0;
|
|
569
|
+
padding: 0.15rem 0;
|
|
570
|
+
overflow-y: auto;
|
|
571
|
+
overscroll-behavior: contain;
|
|
572
|
+
list-style: none;
|
|
573
|
+
font-size: var(--fs-sm);
|
|
574
|
+
line-height: var(--lh-tight);
|
|
575
|
+
color: var(--popover-foreground);
|
|
576
|
+
background: var(--popover);
|
|
577
|
+
border: 1px solid color-mix(in srgb, var(--foreground) 60%, transparent);
|
|
578
|
+
box-shadow: 0 0 20px var(--glow-soft);
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
/* SELECTION CANNOT BE A COLOUR HERE, and that is measured, not felt. `--primary`
|
|
582
|
+
against `--popover-foreground` — the selected option's ink against its
|
|
583
|
+
neighbours' — is 1.65:1 on warm, 1.31 on green, 1.48 on mono, 1.27 on paper.
|
|
584
|
+
Two inks a reader cannot tell apart is not a marking. Same finding as the
|
|
585
|
+
rail's current row (0.19.0), same answer: POSITION AND AREA. The selected
|
|
586
|
+
option is the only one with a left edge marker, and it is bold; the colour is
|
|
587
|
+
the third signal, not the only one.
|
|
588
|
+
|
|
589
|
+
The keyboard's active option and the mouse's hover are the SAME state on
|
|
590
|
+
purpose — there is one pointer of attention, and showing two would ask the
|
|
591
|
+
reader which one Enter is going to take. */
|
|
592
|
+
.select-option {
|
|
593
|
+
display: flex;
|
|
594
|
+
align-items: center;
|
|
595
|
+
gap: 0.4rem;
|
|
596
|
+
padding: 0.28rem 0.9rem 0.28rem 0.55rem;
|
|
597
|
+
border-inline-start: 2px solid transparent;
|
|
598
|
+
white-space: nowrap;
|
|
599
|
+
cursor: pointer;
|
|
600
|
+
}
|
|
601
|
+
.select-option:hover,
|
|
602
|
+
.select-option[data-active="true"] {
|
|
603
|
+
background: var(--muted);
|
|
604
|
+
}
|
|
605
|
+
.select-option[aria-selected="true"] {
|
|
606
|
+
color: var(--primary);
|
|
607
|
+
font-weight: 700;
|
|
608
|
+
border-inline-start-color: var(--primary);
|
|
609
|
+
}
|
|
610
|
+
/* 65% of the ink, not 45%: below that warm lands at 2.94:1 and the option stops
|
|
611
|
+
being readable at all, and "unavailable" still has to be legible to be
|
|
612
|
+
understood as a choice that exists. 65% measures 3.75 at worst (warm). */
|
|
613
|
+
.select-option[aria-disabled="true"] {
|
|
614
|
+
color: color-mix(in srgb, var(--popover-foreground) 65%, var(--popover));
|
|
615
|
+
cursor: default;
|
|
616
|
+
}
|
|
617
|
+
.select-option[aria-disabled="true"]:hover {
|
|
618
|
+
background: none;
|
|
619
|
+
}
|
|
620
|
+
/* <optgroup> label. Nothing in the estate uses one today; it is here because a
|
|
621
|
+
listbox that silently drops the grouping a consumer wrote is worse than one
|
|
622
|
+
that never supported it. */
|
|
623
|
+
.select-group {
|
|
624
|
+
padding: 0.35rem 0.9rem 0.15rem;
|
|
625
|
+
color: var(--muted-foreground);
|
|
626
|
+
font-size: var(--fs-xs);
|
|
627
|
+
text-transform: uppercase;
|
|
628
|
+
letter-spacing: 0.06em;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
/* ─────────────────────────────────────────────────────────────────────────
|
|
632
|
+
TABLE PAGER — twenty rows at a time, and the reader can say otherwise.
|
|
633
|
+
|
|
634
|
+
Built and placed by runtime/pagination.js; a page writes no part of this. It
|
|
635
|
+
INVENTS NOTHING: the two buttons are `.btn-terminal--ghost.btn-terminal--compact`
|
|
636
|
+
(the same pair every refresh and cancel in the estate wears) and the size picker
|
|
637
|
+
is a bare `<select>` that `initSelects()` enhances. So there is no new colour
|
|
638
|
+
here, no new control, and nothing to keep in step at the next release — only a
|
|
639
|
+
row that decides where those three things sit.
|
|
640
|
+
|
|
641
|
+
Ghost, not filled: a pager is not the primary action of any view it appears in,
|
|
642
|
+
and two filled buttons side by side compete. The status text is
|
|
643
|
+
`--muted-foreground` because it is a measurement of the table, not part of it —
|
|
644
|
+
4.84:1 at worst (warm on --card), so it clears AA while staying quiet.
|
|
645
|
+
|
|
646
|
+
The status is left and the controls are right (`margin-inline-start: auto` on the
|
|
647
|
+
picker pushes the pair over), which puts "where am I" at the start of the line and
|
|
648
|
+
"take me elsewhere" at the end. It wraps rather than scrolls: on a phone the three
|
|
649
|
+
parts stack, and a control that has gone off the side of a table is worse than a
|
|
650
|
+
control on a second line.
|
|
651
|
+
───────────────────────────────────────────────────────────────────────── */
|
|
652
|
+
.table-pager {
|
|
653
|
+
display: flex;
|
|
654
|
+
flex-wrap: wrap;
|
|
655
|
+
align-items: center;
|
|
656
|
+
gap: 0.45rem 0.9rem;
|
|
657
|
+
margin-block-start: 0.6rem;
|
|
658
|
+
font-size: var(--fs-sm);
|
|
659
|
+
}
|
|
660
|
+
.table-pager-status {
|
|
661
|
+
margin: 0;
|
|
662
|
+
color: var(--muted-foreground);
|
|
663
|
+
}
|
|
664
|
+
.table-pager-size {
|
|
665
|
+
display: inline-flex;
|
|
666
|
+
align-items: center;
|
|
667
|
+
gap: 0.4rem;
|
|
668
|
+
margin-inline-start: auto;
|
|
669
|
+
color: var(--muted-foreground);
|
|
670
|
+
}
|
|
671
|
+
.table-pager-nav {
|
|
672
|
+
display: inline-flex;
|
|
673
|
+
gap: 0.4rem;
|
|
674
|
+
}
|
|
675
|
+
|
|
365
676
|
/* ─────────────────────────────────────────────────────────────────────────
|
|
366
677
|
ELI5 — an "explain like I'm 5" callout box (first-time-term explainers).
|
|
367
678
|
───────────────────────────────────────────────────────────────────────── */
|
package/src/print.css
CHANGED
|
@@ -98,13 +98,13 @@
|
|
|
98
98
|
on docs.danieldeusing.de, NOT for a doc opened from disk over file://. -->
|
|
99
99
|
<link
|
|
100
100
|
rel="stylesheet"
|
|
101
|
-
href="https://cdn.jsdelivr.net/npm/@danieldeusing/design@0.
|
|
102
|
-
onerror="this.onerror=null;this.href='/_design/danieldeusing-design-0.
|
|
101
|
+
href="https://cdn.jsdelivr.net/npm/@danieldeusing/design@0.22.0/dist/danieldeusing-design.min.css"
|
|
102
|
+
onerror="this.onerror=null;this.href='/_design/danieldeusing-design-0.22.0.min.css'"
|
|
103
103
|
/>
|
|
104
104
|
<link
|
|
105
105
|
rel="stylesheet"
|
|
106
|
-
href="https://cdn.jsdelivr.net/npm/@danieldeusing/design@0.
|
|
107
|
-
onerror="this.onerror=null;this.href='/_design/danieldeusing-design-0.
|
|
106
|
+
href="https://cdn.jsdelivr.net/npm/@danieldeusing/design@0.22.0/src/fonts.css"
|
|
107
|
+
onerror="this.onerror=null;this.href='/_design/danieldeusing-design-0.22.0.fonts.css'"
|
|
108
108
|
/>
|
|
109
109
|
|
|
110
110
|
<style>
|
|
@@ -340,8 +340,8 @@ flowchart TD
|
|
|
340
340
|
</footer>
|
|
341
341
|
|
|
342
342
|
<script type="module">
|
|
343
|
-
import { applyStoredTheme, initThemeSwitcher, initDropdowns, initTerminal, initAnimToggle, initBurgerNav, initLsNav, initTableScroll, initDiagramZoom, initMinimap, initTooltips } from
|
|
344
|
-
"https://cdn.jsdelivr.net/npm/@danieldeusing/design@0.
|
|
343
|
+
import { applyStoredTheme, initThemeSwitcher, initDropdowns, initSelects, initTerminal, initAnimToggle, initBurgerNav, initLsNav, initTableScroll, initDiagramZoom, initMinimap, initTooltips } from
|
|
344
|
+
"https://cdn.jsdelivr.net/npm/@danieldeusing/design@0.22.0/runtime/index.js";
|
|
345
345
|
applyStoredTheme();
|
|
346
346
|
initThemeSwitcher();
|
|
347
347
|
initDropdowns();
|
|
@@ -354,6 +354,11 @@ flowchart TD
|
|
|
354
354
|
// Every table gets a .tablewrap parent, so a table wider than its column scrolls
|
|
355
355
|
// itself instead of pushing the whole page sideways. Already-wrapped ones are skipped.
|
|
356
356
|
initTableScroll();
|
|
357
|
+
// Every <select> gets the estate's dropdown instead of the OS one. Called
|
|
358
|
+
// unconditionally, beside initTableScroll() and for the same reason: both have no
|
|
359
|
+
// markup contract, so both are a no-op on a page that has none of their element —
|
|
360
|
+
// and neither can be forgotten on the day a page grows one.
|
|
361
|
+
initSelects();
|
|
357
362
|
|
|
358
363
|
// Mermaid diagrams — themed from the design tokens, re-rendered on theme switch.
|
|
359
364
|
// Delete this block (and the pre.mermaid CSS) if the page has no diagrams.
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
jumps. The runtime export exists for surfaces that genuinely cannot inline.
|
|
49
49
|
2. At the end of <body>, import and call the runtime:
|
|
50
50
|
import { initLsNav, initBurgerNav, initThemeSwitcher, initDropdowns,
|
|
51
|
-
initAnimToggle, initTableScroll }
|
|
51
|
+
initAnimToggle, initTableScroll, initSelects }
|
|
52
52
|
from "@danieldeusing/design/runtime";
|
|
53
53
|
initBurgerNav() is not optional here: the markup below ships a .nav-burger, and
|
|
54
54
|
below the 48rem breakpoint that button is the ONLY way to reach the navigation.
|
|
@@ -59,6 +59,11 @@
|
|
|
59
59
|
4. `initTableScroll()` wraps every table so a wide one scrolls itself rather than
|
|
60
60
|
scrolling the whole page sideways. The markup contract is nothing — author a
|
|
61
61
|
plain <table>. Call it again after rendering more rows.
|
|
62
|
+
5. `initSelects()` replaces the OS dropdown on every <select> with the estate's own —
|
|
63
|
+
the one component CSS alone can never fix, because the option list is drawn outside
|
|
64
|
+
the page. The markup contract is nothing here either: author a plain <select>, and
|
|
65
|
+
it stays the element that holds the value and fires input/change. Selects rendered
|
|
66
|
+
later are picked up on their own, so there is nothing to call again.
|
|
62
67
|
-->
|
|
63
68
|
|
|
64
69
|
<!-- ─────────────── <head> ─────────────── -->
|