@getvitops/generator 2.1.0 → 3.0.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,86 @@
1
+ /**
2
+ * Navbar — a horizontal row of links, with alignment.
3
+ *
4
+ * <nav class="navbar navbar--end">
5
+ * <a class="link" href="/guides">Guides</a>
6
+ * <a class="link" href="/reference">Reference</a>
7
+ * </nav>
8
+ *
9
+ * Extracted from patterns/nav.css, where `.navbar` already lived but only as the
10
+ * horizontal half of that file's drawer⇄navbar pair — it could not be used on
11
+ * its own, and it had no way to say where the links should sit. It is a
12
+ * primitive now, so `sitenav` (and anything else that needs a link row at width)
13
+ * composes it instead of restating `display: flex` and a gap.
14
+ *
15
+ * Deliberately NOT a `.cluster`: a cluster wraps and is content-agnostic; a
16
+ * navbar is a single row of navigation that stays a row, and carries the
17
+ * alignment vocabulary a nav actually needs.
18
+ */
19
+ .navbar {
20
+ display: flex;
21
+ align-items: center;
22
+ gap: var(--navbar-gap, 2rem);
23
+
24
+ /* Links stay on one line — a navbar that wraps mid-row reads as broken chrome.
25
+ Above its breakpoint `sitenav` is what decides there is room; below it, the
26
+ drawer is the answer, not a wrapped bar. */
27
+ flex-wrap: nowrap;
28
+ }
29
+
30
+ /* ── Alignment. `--start` is the default and exists so the intent can be stated
31
+ rather than inferred from its absence. ── */
32
+ .navbar--start {
33
+ justify-content: flex-start;
34
+ }
35
+ .navbar--center {
36
+ justify-content: center;
37
+ }
38
+ .navbar--end {
39
+ justify-content: flex-end;
40
+ }
41
+
42
+ /**
43
+ * Push everything after this element to the far edge — the usual "brand on the
44
+ * left, links on the right" split, without a wrapper element per side.
45
+ */
46
+ .navbar__spacer {
47
+ margin-inline-start: auto;
48
+ }
49
+
50
+ .navbar--sticky,
51
+ /* The pre-extraction spelling. Kept so an existing consumer's markup keeps
52
+ working; prefer `.navbar--sticky`. */
53
+ .navbar-sticky {
54
+ position: sticky;
55
+ inset-block-start: var(--navbar-sticky-offset, 0);
56
+ z-index: var(--z-tier-sticky);
57
+ }
58
+
59
+ /* ── Legacy composition: nav.css's `.nav-*` parts inside a navbar. Kept with the
60
+ pattern they belong to so nav.css can be retired as one piece. ── */
61
+ .navbar {
62
+ nav {
63
+ flex: 1;
64
+ flex-direction: row;
65
+ }
66
+
67
+ .nav-items {
68
+ flex: 1;
69
+ flex-direction: row;
70
+ gap: 2.25rem;
71
+ border-bottom: none;
72
+ padding-bottom: 0;
73
+ }
74
+
75
+ .nav-actions {
76
+ flex-direction: row;
77
+ gap: 2.25rem;
78
+ border-top: none;
79
+ padding-top: 0;
80
+ }
81
+
82
+ .nav-header {
83
+ border-bottom: none;
84
+ padding-bottom: 0;
85
+ }
86
+ }
@@ -0,0 +1,383 @@
1
+ /**
2
+ * Navshell — a navigation ASIDE beside main content at width, collapsing to a
3
+ * toggle + drawer below it. One markup tree, two presentations, no JS beyond the
4
+ * Popover polyfill.
5
+ *
6
+ * <div class="navshell navshell--bp-md">
7
+ * <div class="navshell__bar">
8
+ * <button class="navshell__toggle" popovertarget="site-nav">Menu</button>
9
+ * </div>
10
+ * <div id="site-nav" popover class="navshell__panel drawer drawer--left">
11
+ * <button class="navshell__close" popovertarget="site-nav" popovertargetaction="hide">×</button>
12
+ * <nav class="navshell__nav" aria-label="Documentation"> … </nav>
13
+ * </div>
14
+ * <div class="navshell__main"> … </div>
15
+ * </div>
16
+ *
17
+ * It NESTS — that is the point, and it is what `.toc-layout` (which this
18
+ * supersedes) could not do. A docs page is a site nav wrapping a page nav:
19
+ * the outer promotes to two columns at `md`, the inner waits for `lg`, and
20
+ * neither needs to know about the other.
21
+ *
22
+ * The mobile panel composes `.drawer`, so this file declares NO animation and no
23
+ * `::backdrop`: the edge modifier states the slide direction, the top-layer
24
+ * driver in animation.css runs it both ways, and popover.css owns the scrim.
25
+ */
26
+
27
+ .navshell {
28
+ /* RESET FIRST — these two must sit above the breakpoint blocks, and EVERY
29
+ root must re-declare them.
30
+ Both custom properties inherit, and this pattern is built to nest. Without
31
+ the reset, an outer instance already past ITS breakpoint hands its flag and
32
+ column template down to an inner instance that is still narrow, and the
33
+ inner one renders its desktop layout early. Re-declaring on every root
34
+ severs the inheritance at each instance boundary.
35
+ `.navshell--bp-*` and `.navshell` are the same specificity, so ORDER is what
36
+ lets the breakpoint blocks win. */
37
+ --_navshell-wide: 0;
38
+ --_navshell-cols: minmax(0, 1fr);
39
+
40
+ display: grid;
41
+ grid-template-columns: var(--_navshell-cols);
42
+ align-items: start;
43
+
44
+ /* Stacked, `gap` is the space between the toggle row and the content — a
45
+ different job from the space between two columns, and the column value is
46
+ far too big for it. `--navshell-gap` is applied in the breakpoint blocks
47
+ instead, which is also the only place it CAN be: the desktop style query
48
+ below cannot reach `.navshell` itself, because a style query resolves
49
+ against an ancestor container and never the declaring element. */
50
+ gap: var(--navshell-stack-gap, 0.75rem);
51
+
52
+ /* Resolved once here, read as `--_navshell-size` below, so the fallback chain
53
+ is stated in one place instead of eight. `--width-nav` is the design
54
+ system's own token (layout.css, beside --width-measure/-breakout/-spotlight);
55
+ the literal is only a last resort for navshell.css used without it. */
56
+ --_navshell-size: var(--navshell-size, var(--width-nav, 16rem));
57
+ }
58
+
59
+ /* ── Promotion thresholds. Four blocks, but each is two lines: they only raise a
60
+ FLAG. The desktop ruleset itself is written once, below. (Contrast
61
+ patterns/sitenav.css, which repeats a ~60-line block per breakpoint and whose
62
+ own header says "CSS has no mixins — edit them together".)
63
+
64
+ `minmax(0, 1fr)` for the content column, never a bare `1fr`: a `1fr` track's
65
+ automatic minimum is min-content, so one wide <pre> pushes the grid past the
66
+ viewport. That is the bug this pattern was written to end.
67
+
68
+ `--navshell-size` carries an inline fallback rather than being registered with
69
+ `@property { inherits: false }`, which is what this wanted. A registered
70
+ property's `initial-value` must be computationally independent, and `16rem`
71
+ depends on the root font-size — so Chrome rejects the whole rule and the
72
+ variable resolves to nothing, which takes `--_navshell-cols` and then
73
+ `grid-template-columns` down with it (measured: the shell silently fell back
74
+ to auto columns). `0`/`0%` initial values, like the two registrations in
75
+ animation.css, are fine; lengths in rem are not.
76
+
77
+ The cost is that `--navshell-size` INHERITS, so an inner instance adopts an
78
+ outer instance's width unless it sets its own. NavShell.astro always emits it
79
+ for exactly this reason. ── */
80
+ @container (min-width: 30rem) {
81
+ .navshell--bp-sm {
82
+ --_navshell-wide: 1;
83
+ gap: var(--navshell-gap, 3rem);
84
+ --_navshell-cols: var(--_navshell-size) minmax(0, 1fr);
85
+ }
86
+ .navshell--bp-sm.navshell--end {
87
+ --_navshell-cols: minmax(0, 1fr) var(--_navshell-size);
88
+ }
89
+ }
90
+ @container (min-width: 48rem) {
91
+ .navshell--bp-md {
92
+ --_navshell-wide: 1;
93
+ gap: var(--navshell-gap, 3rem);
94
+ --_navshell-cols: var(--_navshell-size) minmax(0, 1fr);
95
+ }
96
+ .navshell--bp-md.navshell--end {
97
+ --_navshell-cols: minmax(0, 1fr) var(--_navshell-size);
98
+ }
99
+ }
100
+ @container (min-width: 64rem) {
101
+ .navshell--bp-lg {
102
+ --_navshell-wide: 1;
103
+ gap: var(--navshell-gap, 3rem);
104
+ --_navshell-cols: var(--_navshell-size) minmax(0, 1fr);
105
+ }
106
+ .navshell--bp-lg.navshell--end {
107
+ --_navshell-cols: minmax(0, 1fr) var(--_navshell-size);
108
+ }
109
+ }
110
+ @container (min-width: 80rem) {
111
+ .navshell--bp-xl {
112
+ --_navshell-wide: 1;
113
+ gap: var(--navshell-gap, 3rem);
114
+ --_navshell-cols: var(--_navshell-size) minmax(0, 1fr);
115
+ }
116
+ .navshell--bp-xl.navshell--end {
117
+ --_navshell-cols: minmax(0, 1fr) var(--_navshell-size);
118
+ }
119
+ }
120
+
121
+ /* ── Mobile chrome ────────────────────────────────────────────────────────── */
122
+
123
+ /**
124
+ * The in-shell toggle row.
125
+ *
126
+ * Sticky by default, but BOTH knobs are variables, because two nested instances
127
+ * both sticking to `--top-offset` land exactly on top of each other — which is
128
+ * what happens on a docs page with a site nav and an on-this-page nav. Three
129
+ * ways out, in order of preference:
130
+ *
131
+ * 1. Put the outer toggle in the site header instead (`.navshell-toggle`
132
+ * below), leaving one in-flow bar and no conflict. This is the Starlight
133
+ * arrangement and the reason the standalone toggle exists.
134
+ * 2. Stack them: `--navshell-bar-offset: calc(var(--top-offset) + 2.5rem)` on
135
+ * the inner instance.
136
+ * 3. Opt out: `--navshell-bar-position: static`.
137
+ */
138
+ .navshell__bar {
139
+ position: var(--navshell-bar-position, sticky);
140
+ inset-block-start: var(--navshell-bar-offset, var(--top-offset, 0px));
141
+ z-index: var(--navshell-bar-z, var(--z-tier-sticky));
142
+ display: flex;
143
+ align-items: center;
144
+ gap: var(--navshell-bar-gap, 0.5rem);
145
+ background: var(--navshell-bar-background, none);
146
+ }
147
+
148
+ .navshell-toggle,
149
+ .navshell__toggle,
150
+ .navshell__close {
151
+ display: inline-flex;
152
+ align-items: center;
153
+ justify-content: center;
154
+ gap: 0.5rem;
155
+ padding: 0.5rem;
156
+ background: transparent;
157
+ border: none;
158
+ color: inherit;
159
+ font: inherit;
160
+ cursor: pointer;
161
+ border-radius: var(--br-control, 0.375rem);
162
+
163
+ &:hover {
164
+ background: var(--color-bg-surface-muted);
165
+ }
166
+ &:focus-visible {
167
+ outline: 2px solid var(--navshell-focus-color, var(--color-border-focus));
168
+ outline-offset: 2px;
169
+ }
170
+ }
171
+
172
+ .navshell__close {
173
+ position: absolute;
174
+ inset-block-start: 0.75rem;
175
+ inset-inline-end: 0.75rem;
176
+ }
177
+
178
+ /**
179
+ * A toggle that lives OUTSIDE the shell — in a site header, a navbar, anywhere.
180
+ * `popovertarget` is an id reference, so the HTML works from any position; what
181
+ * it can't inherit is the shell's own "am I wide?" flag, because a style query
182
+ * resolves against an ancestor container and the header is not inside
183
+ * `.navshell`. So it carries its own breakpoint, and the two must agree:
184
+ * `.navshell--bp-md` pairs with `.navshell-toggle--bp-md`.
185
+ *
186
+ * Four two-line blocks rather than one, for the same reason the promotion
187
+ * thresholds are four: the threshold is the one thing that genuinely differs.
188
+ */
189
+ @container (min-width: 30rem) {
190
+ .navshell-toggle--bp-sm {
191
+ display: none;
192
+ }
193
+ }
194
+ @container (min-width: 48rem) {
195
+ .navshell-toggle--bp-md {
196
+ display: none;
197
+ }
198
+ }
199
+ @container (min-width: 64rem) {
200
+ .navshell-toggle--bp-lg {
201
+ display: none;
202
+ }
203
+ }
204
+ @container (min-width: 80rem) {
205
+ .navshell-toggle--bp-xl {
206
+ display: none;
207
+ }
208
+ }
209
+
210
+ /**
211
+ * The panel. Pair with `.drawer` + an edge modifier in markup; everything about
212
+ * how it slides in and dims the page behind is theirs.
213
+ *
214
+ * What IS here is only what a drawer doesn't already know. Size COMPOSES via the
215
+ * drawer's own knob rather than re-declaring `inline-size`: `.drawer` states it
216
+ * under `&, &.drawer--left`, which is (0,2,0) once the edge modifier is on the
217
+ * element, so a (0,1,0) `.navshell__panel { inline-size: … }` silently lost and
218
+ * the panel came out at the drawer's 280px default.
219
+ *
220
+ * The scrolling half IS the "mobile menu doesn't fill the viewport" bug: the
221
+ * hand-rolled version this replaces was `position: absolute; inset-inline: 0`
222
+ * with no block-size, no overflow, no overscroll containment and no stacking — a
223
+ * strip of links pinned under the header that scrolled the article behind it.
224
+ * The drawer supplies the full-height box; these two make it usable.
225
+ *
226
+ * `overscroll-behavior: contain` rather than a scroll lock: the panel is in the
227
+ * top layer so stacking is already correct, and what actually went wrong was
228
+ * scroll CHAINING. A real scroll lock needs `showModal()` or JS, and this is a
229
+ * no-JS pattern.
230
+ */
231
+ .navshell__panel {
232
+ --drawer-max-inline-size: var(--navshell-drawer-size, 20rem);
233
+
234
+ overflow-y: auto;
235
+ overscroll-behavior: contain;
236
+ padding: var(--navshell-panel-padding, 1.5rem);
237
+ /* Clearance for the absolutely-positioned close button. */
238
+ padding-block-start: 3.5rem;
239
+ }
240
+
241
+ .navshell__main {
242
+ /* Flex/grid items floor at min-content; without this the content column is
243
+ held open by its widest <pre> instead of scrolling it. */
244
+ min-inline-size: 0;
245
+
246
+ /* What makes NESTING measure the right thing.
247
+ The promotion thresholds are unnamed `@container (min-width: …)` queries, so
248
+ they resolve against the nearest ancestor with a `container-type` — which,
249
+ for everything in this framework, is `body`. A shell nested inside another
250
+ shell's content column would therefore promote on the VIEWPORT's width while
251
+ sitting in a fraction of it, and go two-column inside a 460px column (seen
252
+ on the Patterns page demo).
253
+ Declaring the content column a container gives an inner shell its parent's
254
+ actual width to measure. It also means ordinary responsive utilities inside
255
+ the column resolve against the column, which is what an unnamed container
256
+ query is supposed to mean. `@container body (…)` queries are unaffected —
257
+ they name their container. */
258
+ container-type: inline-size;
259
+ }
260
+
261
+ /* ── The nav itself ───────────────────────────────────────────────────────── */
262
+
263
+ .navshell__nav {
264
+ display: flex;
265
+ flex-direction: column;
266
+ gap: 0.25rem;
267
+ }
268
+
269
+ .navshell__link {
270
+ display: block;
271
+ padding: 0.375rem 0.75rem;
272
+ border-radius: var(--br-control, 0.375rem);
273
+ text-decoration: none;
274
+ color: inherit;
275
+
276
+ &:hover {
277
+ background: var(--color-bg-surface-muted);
278
+ }
279
+ &:focus-visible {
280
+ outline: 2px solid var(--navshell-focus-color, var(--color-border-focus));
281
+ outline-offset: 2px;
282
+ }
283
+ }
284
+
285
+ /* Nesting depth for a table-of-contents nav, from `data-depth` rather than an
286
+ inline style on every item. */
287
+ .navshell__link[data-depth='2'] {
288
+ padding-inline-start: 1.75rem;
289
+ }
290
+ .navshell__link[data-depth='3'] {
291
+ padding-inline-start: 2.75rem;
292
+ }
293
+
294
+ /**
295
+ * Current item. TWO rules, deliberately not one selector list: `:target-current`
296
+ * is unknown to engines that haven't shipped `scroll-target-group`, and one
297
+ * unknown selector invalidates the WHOLE list — the `[aria-current]` half, which
298
+ * every engine understands, would go down with it.
299
+ */
300
+ .navshell__link[aria-current] {
301
+ font-weight: 600;
302
+ color: var(--navshell-current-color, var(--color-text-brand-primary));
303
+ }
304
+ .navshell__link:target-current {
305
+ font-weight: 600;
306
+ color: var(--navshell-current-color, var(--color-text-brand-primary));
307
+ }
308
+
309
+ /* ── Desktop. ONE block, because the thresholds above only set a flag.
310
+ *
311
+ * Two consequences worth stating:
312
+ *
313
+ * · `.navshell` itself is unreachable from here — a style query resolves against
314
+ * an ANCESTOR container, never the declaring element. That is why the root's
315
+ * own columns come from `--_navshell-cols` above and are restated nowhere.
316
+ * · Scoping is automatic, and it is what makes nesting work. A nested instance's
317
+ * panel resolves this query against the INNER root (its nearest ancestor
318
+ * container), which re-declared `--_navshell-wide: 0`. So an outer nav at `md`
319
+ * and an inner nav at `lg` promote independently, with no `>` combinators and
320
+ * no `.navshell--bp-*` prefix on every inner selector.
321
+ * ── */
322
+ @container style(--_navshell-wide: 1) {
323
+ .navshell__bar,
324
+ .navshell__close {
325
+ display: none;
326
+ }
327
+
328
+ /* `.navshell__panel.drawer`, not `.navshell__panel`.
329
+ The drawer states its box under `&, &.drawer--left`, which is (0,2,0) once
330
+ the edge modifier is on the element — so a (0,1,0) selector here lost every
331
+ conflicting declaration and the panel kept its DRAWER geometry at desktop:
332
+ 320px wide inside a 224px column (a 48px overlap into the content, visible
333
+ as the panel's scrollbar sitting inside the prose) and `block-size: 100dvh`
334
+ instead of `auto`. This compound is (0,2,0) and wins on source order, since
335
+ navshell.css imports after drawer.css. */
336
+ .navshell__panel.drawer {
337
+ /* `[popover]` is UA `display: none` while closed, and the polyfill agrees.
338
+ `!important` for the same reason patterns/sitenav.css needs it: at this
339
+ width the toggle is gone, so nothing can ever re-hide the panel. */
340
+ display: block !important;
341
+
342
+ position: sticky;
343
+ inset-block-start: var(--_navshell-stick, calc(var(--top-offset, 0px) + 1rem));
344
+ inset-inline: auto;
345
+ inline-size: auto;
346
+ max-inline-size: none;
347
+ block-size: auto;
348
+ max-block-size: calc(
349
+ 100dvb - var(--_navshell-stick, calc(var(--top-offset, 0px) + 1rem)) - 1rem
350
+ );
351
+ padding: 0;
352
+ overscroll-behavior: auto;
353
+
354
+ /* Undo the drawer's panel surface — at this width it is part of the page,
355
+ not something floating over it. */
356
+ background: none;
357
+ color: inherit;
358
+ border: none;
359
+ box-shadow: none;
360
+ z-index: auto;
361
+ translate: none;
362
+ opacity: 1;
363
+
364
+ grid-column: 1;
365
+ grid-row: 1;
366
+ }
367
+ .navshell__panel::backdrop {
368
+ display: none;
369
+ }
370
+
371
+ .navshell__main {
372
+ grid-column: 2;
373
+ grid-row: 1;
374
+ }
375
+
376
+ /* The only rules that need to know which side they're on. */
377
+ .navshell--end > .navshell__panel {
378
+ grid-column: 2;
379
+ }
380
+ .navshell--end > .navshell__main {
381
+ grid-column: 1;
382
+ }
383
+ }
@@ -28,22 +28,70 @@
28
28
  inset: auto;
29
29
  }
30
30
 
31
+ /**
32
+ * Open/closed behaviour, at real specificity so a component's own rules can't
33
+ * accidentally reveal a closed popover.
34
+ *
35
+ * This block used to also carry `&:popover-open { opacity: 1 }` and an
36
+ * `@starting-style { opacity: 0 }`. Both are gone, and must not come back:
37
+ * nothing here declared a `transition`, so they never animated anything on their
38
+ * own — but `[popover]:popover-open` is (0,1,0) and this partial is imported
39
+ * AFTER animation.css, so that rule outranked `.transition`'s
40
+ * `opacity: var(--t-opacity, …)` at equal specificity and pinned every top-layer
41
+ * fade to 1. The `open-<fx>` driver cannot work while it exists.
42
+ */
31
43
  [popover] {
32
- &:popover-open {
33
- opacity: 1;
34
- }
35
-
36
44
  &:not(:popover-open):not([open]) {
37
45
  display: none;
38
46
  }
47
+ }
39
48
 
40
- @starting-style {
41
- &:popover-open {
42
- opacity: 0;
43
- }
49
+ /**
50
+ * The scrim — one definition for every top-layer element.
51
+ *
52
+ * This used to be restated in seven partials (dialog, drawer, dropdown,
53
+ * lightbox, nav, sitenav, split-link — 18 `::backdrop` blocks between them),
54
+ * each inventing its own colour, blur and transition. They are now one rule with
55
+ * two knobs.
56
+ *
57
+ * `:where()` for the same reason the visual reset above uses it: this partial is
58
+ * imported after drawer.css and dialog.css, so at plain specificity it would
59
+ * beat their own `::backdrop` rules. At zero specificity any pattern still wins.
60
+ *
61
+ * `overlay`/`display` transition with `allow-discrete` so the scrim fades OUT
62
+ * too — without them the backdrop vanishes the instant the popover closes while
63
+ * the panel is still animating away.
64
+ */
65
+ :where([popover], dialog)::backdrop {
66
+ background-color: var(--scrim, rgb(0 0 0 / 25%));
67
+ backdrop-filter: var(--scrim-filter, none);
68
+ transition:
69
+ background-color var(--scrim-duration, 0.3s),
70
+ overlay var(--scrim-duration, 0.3s) allow-discrete,
71
+ display var(--scrim-duration, 0.3s) allow-discrete;
72
+ }
73
+ :where([popover], dialog):not(:popover-open):not([open])::backdrop {
74
+ background-color: transparent;
75
+ }
76
+ @starting-style {
77
+ :where([popover], dialog):is(:popover-open, [open])::backdrop {
78
+ background-color: transparent;
44
79
  }
45
80
  }
46
81
 
82
+ /**
83
+ * No scrim — a panel you work *alongside* rather than through, where the page
84
+ * behind must stay legible (a live editor, an inspector). Pair with
85
+ * `popover="manual"`: an `auto` popover light-dismisses on the first outside
86
+ * click, which is exactly the interaction a modeless panel exists to allow.
87
+ * `.drawer--modeless` is the historical name and stays as an alias.
88
+ */
89
+ .no-scrim::backdrop,
90
+ .drawer--modeless::backdrop {
91
+ background-color: transparent;
92
+ backdrop-filter: none;
93
+ }
94
+
47
95
  .popover-anchor {
48
96
  anchor-name: --popover-anchor;
49
97
  position: relative;
@@ -14,8 +14,15 @@
14
14
  color: var(--scroll-target-inactive-color, currentColor);
15
15
  }
16
16
 
17
- /* `.is-current` mirrors `:target-current` for the JS scroll-spy fallback
18
- (deferred.ts) on engines without `scroll-target-group` support. */
17
+ /* `.is-current` is a MANUAL hook, mirroring `:target-current` for anything that
18
+ sets it itself.
19
+
20
+ It previously claimed to be "the JS scroll-spy fallback (deferred.ts)". There
21
+ is no such code — nothing in the repo sets `.is-current`, and deferred.ts has
22
+ no scroll-spy. Highlighting is therefore `:target-current` only, which today
23
+ means Chrome; other engines get no highlight until they ship
24
+ `scroll-target-group`, which is a deliberate choice, not an oversight. Set
25
+ `.is-current` yourself if you need it sooner. */
19
26
  .scroll-target-group a:target-current,
20
27
  .scroll-target-group a.is-current {
21
28
  color: var(--scroll-target-active-color, var(--color-bg-brand-primary-solid));
@@ -67,12 +74,19 @@
67
74
  }
68
75
  }
69
76
 
77
+ /* Superseded by `.navshell` (patterns/navshell.css), which is this plus a mobile
78
+ toggle, a parametric breakpoint, an inline-end variant and the ability to
79
+ nest. Kept because it ships in a published major; prefer `.navshell` for new
80
+ work. */
70
81
  .toc-layout {
71
82
  display: grid;
72
- grid-template-columns: 1fr;
83
+ grid-template-columns: minmax(0, 1fr);
73
84
 
74
85
  @container body (inline-size >= 64rem) {
75
- grid-template-columns: 16rem 1fr;
86
+ /* minmax(0, 1fr), never a bare `1fr`: a `1fr` track's automatic minimum is
87
+ min-content, so one wide <pre> or long token in the content column blows
88
+ the grid out and the page scrolls sideways. */
89
+ grid-template-columns: 16rem minmax(0, 1fr);
76
90
  gap: var(--gutter, 2rem);
77
91
  }
78
92
  }