@refrakt-md/lumina 0.14.2 → 0.14.3

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/index.css CHANGED
@@ -41,6 +41,7 @@
41
41
  @import './styles/runes/annotate.css';
42
42
  @import './styles/runes/audio.css';
43
43
  @import './styles/runes/api.css';
44
+ @import './styles/runes/badge.css';
44
45
  @import './styles/runes/bento.css';
45
46
  @import './styles/runes/blog.css';
46
47
  @import './styles/runes/bond.css';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@refrakt-md/lumina",
3
3
  "description": "Lumina theme for refrakt.md — design tokens, CSS, identity transform, and layout configs",
4
- "version": "0.14.2",
4
+ "version": "0.14.3",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "repository": {
@@ -83,9 +83,9 @@
83
83
  "build": "tsc"
84
84
  },
85
85
  "dependencies": {
86
- "@refrakt-md/runes": "0.14.2",
87
- "@refrakt-md/transform": "0.14.2",
88
- "@refrakt-md/types": "0.14.2"
86
+ "@refrakt-md/runes": "0.14.3",
87
+ "@refrakt-md/transform": "0.14.3",
88
+ "@refrakt-md/types": "0.14.3"
89
89
  },
90
90
  "devDependencies": {
91
91
  "postcss": "^8.4.0"
@@ -1,7 +1,17 @@
1
1
  /* Blog layout */
2
2
 
3
+ /* The blog header chrome is taller (1.125rem padding + 2rem logo), so
4
+ tell the mobile panel where the header ends. The variable lives on the
5
+ layout root so the panel — a sibling of the header — inherits it. */
6
+ .rf-layout-blog-article {
7
+ --rf-header-height: 4.25rem;
8
+ }
3
9
  /* ---- Header ---- */
4
10
  .rf-blog-header {
11
+ position: sticky;
12
+ top: 0;
13
+ z-index: 100;
14
+ background: var(--rf-color-bg);
5
15
  border-bottom: 1px solid var(--rf-color-border);
6
16
  }
7
17
  .rf-blog-header__inner {
@@ -75,13 +75,25 @@
75
75
  vertical-align: -0.15em;
76
76
  }
77
77
 
78
- /* ---- Fixed sidebar ---- */
78
+ /* ---- Body wrapper: sidebar + content row ----
79
+ * The `body` slot in docsLayout groups sidebar + main so `position: sticky`
80
+ * on the sidebar has a containing block that ends *before* the footer.
81
+ * When the user scrolls past the wrapper's bottom (i.e. when the footer
82
+ * comes into view) sticky releases and the sidebar scrolls away with the
83
+ * page rather than overlaying the footer. */
84
+ .rf-docs-body {
85
+ display: flex;
86
+ align-items: flex-start;
87
+ }
88
+
89
+ /* ---- Sticky sidebar ---- */
79
90
  .rf-docs-sidebar {
80
- position: fixed;
81
- left: 0;
91
+ position: sticky;
82
92
  top: 3.125rem;
83
- bottom: 0;
93
+ align-self: flex-start;
94
+ flex-shrink: 0;
84
95
  width: 240px;
96
+ height: calc(100vh - 3.125rem);
85
97
  overflow-y: auto;
86
98
  padding: 1.5rem;
87
99
  border-right: 1px solid var(--rf-color-border);
@@ -94,13 +106,12 @@
94
106
 
95
107
  /* ---- Content area ---- */
96
108
  .rf-docs-content {
109
+ flex: 1;
110
+ min-width: 0;
97
111
  padding-top: 4.75rem;
98
112
  padding-bottom: 4rem;
99
113
  container-type: inline-size;
100
114
  }
101
- .rf-docs-content--has-nav {
102
- margin-left: 240px;
103
- }
104
115
  .rf-docs-content__inner {
105
116
  max-width: 60rem;
106
117
  margin: 0 auto;
@@ -226,16 +237,28 @@ html:has(.rf-docs-header) {
226
237
  /* ---- Mobile overrides ---- */
227
238
  @media (max-width: 768px) {
228
239
  .rf-docs-header {
229
- position: static;
240
+ position: sticky;
241
+ top: 0;
230
242
  }
231
243
  .rf-docs-header__inner p ~ p {
232
244
  display: none;
233
245
  }
246
+ /* The toolbar is sticky directly below the header so the hamburger —
247
+ which doubles as the open/close trigger for the sidebar-nav panel —
248
+ stays reachable while the panel is open or the page is scrolled.
249
+ `top` is 1px less than the header height so the two bars overlap by
250
+ a pixel — sub-pixel rounding during scroll can otherwise leave a
251
+ hairline gap that lets page content shine through.
252
+ z-index sits above the .rf-mobile-panel (99) so the hamburger
253
+ stays clickable while the panel is open. */
234
254
  .rf-docs-toolbar {
235
255
  display: flex;
256
+ position: sticky;
257
+ top: calc(3.5rem - 1px);
258
+ z-index: 100;
236
259
  }
237
260
  .rf-mobile-panel--nav {
238
- top: 6.25rem;
261
+ top: calc(6.25rem - 1px);
239
262
  }
240
263
  .rf-docs-sidebar {
241
264
  display: none;
@@ -243,9 +266,6 @@ html:has(.rf-docs-header) {
243
266
  .rf-docs-content {
244
267
  padding-top: 2rem;
245
268
  }
246
- .rf-docs-content--has-nav {
247
- margin-left: 0;
248
- }
249
269
  .rf-docs-content__inner {
250
270
  --rf-content-padding: 1.5rem;
251
271
  --rf-docs-wide-inset: 1.5rem;
@@ -1,4 +1,4 @@
1
- /* Shared mobile UI — menu button, fullscreen panel, close */
1
+ /* Shared mobile UI — menu button, panel docked below header, close */
2
2
 
3
3
  /* ---- Mobile menu button (hidden on desktop) ---- */
4
4
  .rf-mobile-menu-btn {
@@ -13,42 +13,41 @@
13
13
  .rf-mobile-menu-btn:hover {
14
14
  color: var(--rf-color-text);
15
15
  }
16
+ /* Dots/X toggle — the SVG has both icons stacked; we show one at a time
17
+ based on the button's aria-expanded state. */
18
+ .rf-mobile-menu-btn__icon-close {
19
+ display: none;
20
+ }
21
+ .rf-mobile-menu-btn[aria-expanded="true"] .rf-mobile-menu-btn__icon-open {
22
+ display: none;
23
+ }
24
+ .rf-mobile-menu-btn[aria-expanded="true"] .rf-mobile-menu-btn__icon-close {
25
+ display: inline;
26
+ }
16
27
 
17
- /* ---- Fullscreen mobile panel ---- */
28
+ /* ---- Mobile panel — docks below the header ----
29
+ The header height is fixed by --rf-header-height (default 3.5rem to
30
+ match the rendered chrome height — 0.625rem padding + 2rem logo +
31
+ line-box rounding). Layouts with a taller header set the variable on
32
+ their layout root so the panel — a sibling of the header — inherits. */
18
33
  .rf-mobile-panel {
19
34
  display: none;
20
35
  position: fixed;
21
- inset: 0;
22
- z-index: 100;
36
+ top: var(--rf-header-height, 3.5rem);
37
+ right: 0;
38
+ bottom: 0;
39
+ left: 0;
40
+ width: 100%;
41
+ max-width: 100vw;
42
+ z-index: 99;
23
43
  background: var(--rf-color-bg, #fff);
44
+ overflow-x: hidden;
24
45
  overflow-y: auto;
25
46
  -webkit-overflow-scrolling: touch;
26
47
  }
27
- .rf-mobile-panel__header {
28
- display: flex;
29
- align-items: center;
30
- justify-content: space-between;
31
- padding: 1.125rem 1.5rem;
32
- border-bottom: 1px solid var(--rf-color-border);
33
- }
34
- .rf-mobile-panel__title {
35
- font-size: 0.85rem;
36
- font-weight: 600;
37
- text-transform: uppercase;
38
- letter-spacing: 0.05em;
39
- color: var(--rf-color-muted);
40
- }
41
- .rf-mobile-panel__close {
42
- background: none;
43
- border: none;
44
- padding: 0.25rem;
45
- cursor: pointer;
46
- color: var(--rf-color-muted);
47
- line-height: 0;
48
- }
49
- .rf-mobile-panel__close:hover {
50
- color: var(--rf-color-text);
51
- }
48
+ /* Layout-specific panels (e.g. the docs sidebar-nav panel that sits
49
+ under the docs header *and* toolbar) override `top` in their own
50
+ layout CSS. */
52
51
  .rf-mobile-panel__nav {
53
52
  padding: 1.5rem;
54
53
  }
@@ -59,15 +58,16 @@
59
58
  display: none;
60
59
  }
61
60
  /* Panel items — top-level links and group items render uniformly. The
62
- four-class selectors below carry specificity (0,4,0), beating the
63
- menubar's mobile rules in nav.css ((0,3,0)) on equal source position
64
- so the panel's typography always wins (nav.css is imported later than
65
- mobile.css in index.css). */
61
+ selectors below need to beat nav.css's nested-columns mobile overrides
62
+ ((0,4,0)) on equal source position (nav.css is imported later than
63
+ mobile.css in index.css), so the columns-context selectors carry
64
+ (0,5,0) by including `.rf-nav-group__panel` in the chain. */
66
65
  .rf-mobile-panel__nav a,
67
66
  .rf-mobile-panel .rf-nav--menubar .rf-nav__top-level .rf-nav-item__link,
68
- .rf-mobile-panel .rf-nav--menubar .rf-nav-group .rf-nav-item__link {
67
+ .rf-mobile-panel .rf-nav--menubar .rf-nav-group .rf-nav-item__link,
68
+ .rf-mobile-panel .rf-nav--menubar .rf-nav-group__panel .rf-nav--columns .rf-nav-item__link {
69
69
  display: block;
70
- padding: 0.5rem 0;
70
+ padding: 0;
71
71
  font-size: 1.5rem;
72
72
  font-weight: 700;
73
73
  color: var(--rf-color-text);
@@ -77,7 +77,8 @@
77
77
  }
78
78
  .rf-mobile-panel__nav a:hover,
79
79
  .rf-mobile-panel .rf-nav--menubar .rf-nav__top-level .rf-nav-item__link:hover,
80
- .rf-mobile-panel .rf-nav--menubar .rf-nav-group .rf-nav-item__link:hover {
80
+ .rf-mobile-panel .rf-nav--menubar .rf-nav-group .rf-nav-item__link:hover,
81
+ .rf-mobile-panel .rf-nav--menubar .rf-nav-group__panel .rf-nav--columns .rf-nav-item__link:hover {
81
82
  color: var(--rf-color-primary, var(--rf-color-text));
82
83
  background: transparent;
83
84
  text-decoration: none;
@@ -88,6 +89,9 @@
88
89
  .rf-mobile-panel .rf-nav--menubar .rf-nav-group h3 {
89
90
  padding: 1.5rem 0 0.25rem;
90
91
  }
92
+ /* Nested column collapse for the menubar drawer is handled in nav.css's
93
+ ≤768px block (it already resets grid-template-columns and the rich-panel
94
+ min-width rules). */
91
95
  .rf-mobile-panel__body {
92
96
  padding: 1rem 1.5rem;
93
97
  }
@@ -101,7 +105,7 @@
101
105
  .rf-mobile-menu-btn {
102
106
  display: block;
103
107
  }
104
- /* The inline menubar collapses behind the hamburger on mobile — the
108
+ /* The inline menubar collapses behind the kebab on mobile — the
105
109
  .rf-mobile-panel duplicates the same nav and styles it as a flat
106
110
  vertical list (see nav.css's rf-nav--menubar mobile rules). */
107
111
  .rf-header__inner > rf-nav,
@@ -109,8 +113,8 @@
109
113
  .rf-blog-header__inner > rf-nav {
110
114
  display: none;
111
115
  }
112
- /* Hide the duplicated brand line inside the mobile panel — the panel
113
- already has its own "Menu" title. */
116
+ /* Hide the duplicated brand line inside the mobile panel — the header
117
+ above the panel already shows it. */
114
118
  .rf-mobile-panel__nav > p:first-child {
115
119
  display: none;
116
120
  }
@@ -415,7 +415,7 @@
415
415
  display: none;
416
416
  }
417
417
 
418
- .rf-mobile-panel--nav {
418
+ .rf-layout-plan .rf-mobile-panel--nav {
419
419
  top: 3.0625rem;
420
420
  }
421
421
 
@@ -0,0 +1,33 @@
1
+ /* Badge rune — standalone inline pill that visually stands out from prose.
2
+ *
3
+ * Unlike the metadata badges that appear inside card chrome (plan items,
4
+ * recipe meta, work item headers), standalone {% badge %} runes are meant
5
+ * to pop. They reuse the universal `--meta-color` set by
6
+ * dimensions/metadata.css's sentiment rules, applied as a sentiment-tinted
7
+ * border, a low-opacity sentiment-tinted background, and sentiment-coloured
8
+ * text. The leading sentiment dot from metadata.css is dropped — the
9
+ * background tint already carries the sentiment cue.
10
+ *
11
+ * rank="primary" boosts emphasis with a heavier background tint and a
12
+ * bolder weight, useful for "Popular" / "Featured" labels that should
13
+ * dominate over their neighbours.
14
+ */
15
+
16
+ .rf-badge {
17
+ vertical-align: baseline;
18
+ border-color: var(--meta-color, var(--rf-color-border));
19
+ background: color-mix(in srgb, var(--meta-color, transparent) 12%, transparent);
20
+ color: var(--meta-color, var(--rf-color-text));
21
+ font-weight: 500;
22
+ }
23
+
24
+ .rf-badge[data-meta-rank="primary"] {
25
+ background: color-mix(in srgb, var(--meta-color, transparent) 22%, transparent);
26
+ font-weight: 600;
27
+ }
28
+
29
+ /* Drop the sentiment dot — the background tint carries the cue for
30
+ * standalone badges, and the dot competes visually with adjacent prose. */
31
+ .rf-badge::before {
32
+ display: none;
33
+ }
@@ -4,7 +4,6 @@
4
4
  padding: 0.875rem 1.25rem;
5
5
  border-radius: var(--rf-radius-md);
6
6
  background: var(--rf-color-surface);
7
- border: 1px solid var(--rf-color-border);
8
7
  }
9
8
  .rf-hint__header {
10
9
  margin-bottom: 0.375rem;
@@ -48,11 +48,19 @@
48
48
  color: var(--rf-color-text);
49
49
  text-decoration: none;
50
50
  }
51
- .rf-nav-item__link--active {
51
+ /* SPEC-055: current page receives aria-current="page" at build time
52
+ * (replaces the legacy --active modifier set at runtime by rf-nav). */
53
+ .rf-nav-item__link[aria-current="page"] {
52
54
  background: color-mix(in srgb, var(--rf-color-primary) 8%, transparent);
53
55
  color: var(--rf-color-primary);
54
56
  font-weight: 600;
55
57
  }
58
+ /* SPEC-055: ancestor-of-current-page link — subtler treatment than the
59
+ * exact-match aria-current state. Themes can collapse the two into a
60
+ * single style if they prefer. */
61
+ .rf-nav-item__link[data-active="ancestor"] {
62
+ color: var(--rf-color-primary);
63
+ }
56
64
  .rf-nav-item {
57
65
  padding: 0.375rem 0.75rem;
58
66
  font-size: 0.85rem;
@@ -90,11 +98,16 @@
90
98
  transition: transform 200ms ease;
91
99
  margin-right: 0.25rem;
92
100
  }
101
+ /* SPEC-054 wraps the group body in `<div data-name="panel">`. Animate that
102
+ * wrapper; keep the direct ul/ol selectors as a fallback for older trees
103
+ * that haven't picked up the panel structure. */
104
+ .rf-nav--collapsible .rf-nav-group > .rf-nav-group__panel,
93
105
  .rf-nav--collapsible .rf-nav-group > ul,
94
106
  .rf-nav--collapsible .rf-nav-group > ol {
95
107
  overflow: hidden;
96
108
  transition: height 200ms ease;
97
109
  }
110
+ .rf-nav--collapsible .rf-nav-group[data-collapsed="true"] > .rf-nav-group__panel,
98
111
  .rf-nav--collapsible .rf-nav-group[data-collapsed="true"] > ul,
99
112
  .rf-nav--collapsible .rf-nav-group[data-collapsed="true"] > ol {
100
113
  height: 0;
@@ -105,6 +118,7 @@
105
118
  }
106
119
 
107
120
  @media (prefers-reduced-motion: reduce) {
121
+ .rf-nav--collapsible .rf-nav-group > .rf-nav-group__panel,
108
122
  .rf-nav--collapsible .rf-nav-group > ul,
109
123
  .rf-nav--collapsible .rf-nav-group > ol,
110
124
  .rf-nav--collapsible .rf-nav-group h2::after,
@@ -134,6 +148,7 @@
134
148
  }
135
149
  .rf-nav--menubar > .rf-nav__top-level li {
136
150
  padding: 0;
151
+ margin: 0;
137
152
  }
138
153
  .rf-nav--menubar > .rf-nav__top-level .rf-nav-item__link {
139
154
  display: inline-block;
@@ -159,6 +174,7 @@
159
174
  padding: 0.375rem 0.75rem;
160
175
  font-size: 0.9rem;
161
176
  font-weight: 500;
177
+ line-height: inherit;
162
178
  text-transform: none;
163
179
  letter-spacing: normal;
164
180
  color: var(--rf-color-muted);
@@ -174,13 +190,17 @@
174
190
  background: var(--rf-color-surface);
175
191
  color: var(--rf-color-text);
176
192
  }
177
- .rf-nav--menubar .rf-nav-group ul {
193
+ /* The dropdown panel is the <div data-name="panel"> wrapper added by the
194
+ * nav schema's buildGroupTag. Positioning + visibility live on this
195
+ * wrapper so the rule works equally well for simple <ul> dropdowns and
196
+ * rich panels containing nested navs / intro / footer slots. */
197
+ .rf-nav--menubar .rf-nav-group > .rf-nav-group__panel {
178
198
  display: block;
179
199
  position: absolute;
180
200
  top: 100%;
181
201
  right: 0;
182
202
  left: auto;
183
- min-width: 12rem;
203
+ min-width: 14rem;
184
204
  padding: 0.5rem;
185
205
  background: var(--rf-color-bg);
186
206
  border: 1px solid var(--rf-color-border);
@@ -193,14 +213,33 @@
193
213
  transform: translateY(-4px);
194
214
  transition: opacity 150ms ease, visibility 150ms ease, transform 150ms ease;
195
215
  }
196
- .rf-nav--menubar .rf-nav-group:hover > ul,
197
- .rf-nav--menubar .rf-nav-group:focus-within > ul,
198
- .rf-nav--menubar .rf-nav-group[data-open="true"] > ul {
216
+ .rf-nav--menubar .rf-nav-group:hover > .rf-nav-group__panel,
217
+ .rf-nav--menubar .rf-nav-group:focus-within > .rf-nav-group__panel,
218
+ .rf-nav--menubar .rf-nav-group[data-open="true"] > .rf-nav-group__panel {
199
219
  opacity: 1;
200
220
  visibility: visible;
201
221
  pointer-events: auto;
202
222
  transform: translateY(0);
203
223
  }
224
+ /* Inside the panel, lists render naturally (no extra positioning). */
225
+ .rf-nav--menubar .rf-nav-group__panel ul {
226
+ list-style: none;
227
+ padding: 0;
228
+ margin: 0;
229
+ }
230
+ .rf-nav--menubar .rf-nav-group__panel li {
231
+ padding: 0;
232
+ }
233
+ /* Flat dropdown items: force the link to fill the panel's content width so
234
+ * the hover hit-area is the whole row rather than just the text. Scoped to
235
+ * `> ul > li > a` so it only catches flat panels — nested columns navs
236
+ * (which sit inside `.rf-nav--columns`) keep their own item styling. */
237
+ .rf-nav--menubar .rf-nav-group__panel > ul > li > .rf-nav-item__link {
238
+ display: block;
239
+ width: 100%;
240
+ box-sizing: border-box;
241
+ padding: 0.5rem 0.75rem;
242
+ }
204
243
  .rf-nav--menubar .rf-nav-group .rf-nav-item__link {
205
244
  font-size: 0.85rem;
206
245
  font-weight: 400;
@@ -265,7 +304,7 @@
265
304
  background: transparent;
266
305
  color: var(--rf-color-muted);
267
306
  }
268
- .rf-nav--menubar .rf-nav-group ul {
307
+ .rf-nav--menubar .rf-nav-group > .rf-nav-group__panel {
269
308
  position: static;
270
309
  display: block;
271
310
  min-width: 0;
@@ -312,6 +351,13 @@
312
351
  background: transparent;
313
352
  color: var(--rf-color-text);
314
353
  }
354
+ /* Standalone columns (footer, section landing) use a text-only treatment
355
+ * for the active item — the generic `.rf-nav-item__link[aria-current="page"]`
356
+ * surface tint would compete with the column layout's quiet rhythm. The
357
+ * nested-in-menubar case re-enables the tint below at higher specificity. */
358
+ .rf-nav--columns .rf-nav-item__link[aria-current="page"] {
359
+ background: transparent;
360
+ }
315
361
 
316
362
  @media (max-width: 600px) {
317
363
  .rf-nav--columns {
@@ -401,3 +447,252 @@
401
447
  grid-template-columns: 1fr;
402
448
  }
403
449
  }
450
+
451
+ /* ─── Per-item description (SPEC-054, any layout) ─────────────────────── */
452
+
453
+ .rf-nav-item__description {
454
+ display: block;
455
+ font-size: 0.8125rem;
456
+ color: var(--rf-color-muted);
457
+ line-height: 1.4;
458
+ margin-top: 0.125rem;
459
+ }
460
+
461
+ /* ─── Menubar group slots (SPEC-054 rich panel content) ──────────────── */
462
+
463
+ /* Rich menubar panels: when the panel contains intro / nested nav / footer
464
+ * blocks, it auto-sizes to fit content rather than the narrow flat dropdown
465
+ * default. */
466
+ .rf-nav--menubar .rf-nav-group__panel:has(.rf-nav-group__intro),
467
+ .rf-nav--menubar .rf-nav-group__panel:has(.rf-nav-group__footer),
468
+ .rf-nav--menubar .rf-nav-group__panel:has(.rf-nav) {
469
+ min-width: 20rem;
470
+ max-width: 36rem;
471
+ }
472
+
473
+ /* When the panel contains a nested columns nav with explicit column wrappers,
474
+ * widen the cap so each column has room for a one-line description rather
475
+ * than wrapping to three or four. */
476
+ .rf-nav--menubar .rf-nav-group__panel:has(.rf-nav--columns > div[data-name="column"]) {
477
+ min-width: 36rem;
478
+ max-width: 56rem;
479
+ }
480
+
481
+ /* Nested columns nav inside a menubar panel: render as a distinct surface
482
+ * card so the column grid reads as a grouped block rather than bleeding
483
+ * into the panel's chrome. Resets the standalone columns padding (which
484
+ * assumes a footer-like context) and tightens the gap for the tighter
485
+ * dropdown box. */
486
+ .rf-nav--menubar .rf-nav-group__panel .rf-nav--columns {
487
+ padding: 0.75rem;
488
+ gap: 1rem;
489
+ background: var(--rf-color-surface);
490
+ border: 1px solid var(--rf-color-border);
491
+ border-radius: var(--rf-radius-sm);
492
+ }
493
+
494
+ /* Items inside a nested menubar columns nav: render the whole link as one
495
+ * generously padded click target with the title on top in regular text
496
+ * colour and the description stacked below in muted colour. The description
497
+ * `<span>` lives inside the `<a>` (see navItem transform), so styling on
498
+ * the link covers both lines. */
499
+ .rf-nav--menubar .rf-nav-group__panel .rf-nav--columns .rf-nav-item {
500
+ padding: 0;
501
+ }
502
+ .rf-nav--menubar .rf-nav-group__panel .rf-nav--columns .rf-nav-item__link {
503
+ display: block;
504
+ padding: 0.75rem;
505
+ color: var(--rf-color-text);
506
+ font-weight: 500;
507
+ border-radius: var(--rf-radius-sm);
508
+ }
509
+ .rf-nav--menubar .rf-nav-group__panel .rf-nav--columns .rf-nav-item__link:hover {
510
+ background: var(--rf-color-surface-hover);
511
+ color: var(--rf-color-text);
512
+ }
513
+ /* Re-enable a neutral surface tint on the active item inside a menubar
514
+ * dropdown — the standalone-columns override above zeroes the background,
515
+ * but here it helps the current page stand out from peers in a tight
516
+ * panel. Uses the purpose-built interactive-surface tokens so the contrast
517
+ * direction stays correct in dark mode. */
518
+ .rf-nav--menubar .rf-nav-group__panel .rf-nav--columns .rf-nav-item__link[aria-current="page"] {
519
+ background: var(--rf-color-surface-active);
520
+ }
521
+ .rf-nav--menubar .rf-nav-group__panel .rf-nav--columns .rf-nav-item__link .rf-nav-item__description {
522
+ font-weight: 400;
523
+ color: var(--rf-color-muted);
524
+ }
525
+
526
+ /* Intro / footer slots inside a menubar panel get airy padding so the
527
+ * eyebrow / "see all" links breathe inside the dropdown frame. */
528
+ .rf-nav--menubar .rf-nav-group__panel > .rf-nav-group__intro {
529
+ margin: 0 0 0.75rem;
530
+ padding: 0.5rem 0.75rem 0;
531
+ }
532
+ .rf-nav--menubar .rf-nav-group__panel > .rf-nav-group__footer {
533
+ margin: 0.75rem 0 0;
534
+ padding: 0 0.75rem 0.5rem;
535
+ }
536
+
537
+ .rf-nav-group__intro {
538
+ margin: 0 0 0.75rem;
539
+ }
540
+
541
+ /* Eyebrow intro: a plain paragraph in the intro slot renders as a small
542
+ * muted lead-in above the panel content. */
543
+ .rf-nav-group__intro p:only-child {
544
+ margin: 0;
545
+ font-size: 0.75rem;
546
+ color: var(--rf-color-muted);
547
+ }
548
+
549
+ .rf-nav-group__footer {
550
+ margin: 0.75rem 0 0;
551
+ }
552
+ .rf-nav-group__footer p {
553
+ margin: 0;
554
+ font-size: 0.8125rem;
555
+ color: var(--rf-color-muted);
556
+ }
557
+ .rf-nav-group__footer a {
558
+ color: var(--rf-color-primary);
559
+ font-weight: 500;
560
+ }
561
+
562
+ /* ─── Multi-section columns (SPEC-054 columns flow rule) ──────────────── */
563
+
564
+ /* When the columns layout has <hr> separators, groups are bucketed into
565
+ * .rf-nav-group__column wrappers (or .rf-nav__column at the nav level for
566
+ * headingless mode). Each column stacks one or more sections vertically. */
567
+ .rf-nav--columns .rf-nav-group__column,
568
+ .rf-nav--columns .rf-nav__column,
569
+ .rf-nav--columns > div[data-name="column"] {
570
+ display: flex;
571
+ flex-direction: column;
572
+ gap: 1.25rem;
573
+ }
574
+
575
+ /* Multi-section columns mode: when the author has placed explicit `---`
576
+ * dividers, the structure already declares the column count. Use
577
+ * grid-auto-flow:column so each `data-name="column"` wrapper becomes its own
578
+ * track — this expands the container to fit the tracks rather than
579
+ * collapsing to one column when the container is narrow (e.g. nested inside
580
+ * a menubar panel at its min-width). */
581
+ .rf-nav--columns:has(> div[data-name="column"]) {
582
+ display: grid;
583
+ grid-template-columns: unset;
584
+ grid-auto-flow: column;
585
+ grid-auto-columns: minmax(14rem, 1fr);
586
+ }
587
+
588
+ /* Stepped collapse: at four columns × 14rem min + gaps the grid needs ~62rem
589
+ * of width, so below ~900px the explicit-column flow would overflow. Step
590
+ * down to a 2×N grid before going single-column at the narrowest breakpoint. */
591
+ @media (max-width: 900px) {
592
+ .rf-nav--columns:has(> div[data-name="column"]) {
593
+ grid-auto-flow: row;
594
+ grid-auto-columns: unset;
595
+ grid-template-columns: repeat(2, minmax(0, 1fr));
596
+ }
597
+ }
598
+
599
+ @media (max-width: 600px) {
600
+ .rf-nav--columns:has(> div[data-name="column"]) {
601
+ grid-auto-flow: row;
602
+ grid-auto-columns: unset;
603
+ grid-template-columns: 1fr;
604
+ }
605
+ }
606
+
607
+ /* ─── Strip layout (SPEC-054 compact secondary nav) ───────────────────── */
608
+
609
+ .rf-nav--strip {
610
+ display: flex;
611
+ flex-wrap: wrap;
612
+ gap: 0.5rem 1rem;
613
+ padding: 0.5rem 0;
614
+ font-size: 0.8125rem;
615
+ }
616
+ .rf-nav--strip ul {
617
+ display: flex;
618
+ flex-wrap: wrap;
619
+ gap: 0.5rem 1rem;
620
+ list-style: none;
621
+ padding: 0;
622
+ margin: 0;
623
+ }
624
+ .rf-nav--strip li {
625
+ padding: 0;
626
+ }
627
+ .rf-nav--strip .rf-nav-item__link {
628
+ display: inline-block;
629
+ padding: 0.25rem 0;
630
+ color: var(--rf-color-muted);
631
+ font-size: 0.8125rem;
632
+ border-radius: 0;
633
+ background: transparent;
634
+ }
635
+ .rf-nav--strip .rf-nav-item__link:hover {
636
+ color: var(--rf-color-text);
637
+ background: transparent;
638
+ }
639
+ .rf-nav--strip .rf-nav-item__link[aria-current="page"] {
640
+ color: var(--rf-color-primary);
641
+ background: transparent;
642
+ }
643
+
644
+ /* When a strip nav is nested inside a menubar panel footer slot, render
645
+ * compact-but-still-tap-friendly. */
646
+ .rf-nav-group__footer .rf-nav--strip {
647
+ padding: 0;
648
+ font-size: 0.75rem;
649
+ }
650
+
651
+ /* ─── Mobile menubar: nested columns collapse ─────────────────────────── */
652
+
653
+ /* Placed at the end of the file so the source order beats the desktop
654
+ * nested-columns rules at equal specificity. On a collapsed menubar
655
+ * drawer (≤ 768px), drop the columns nav's surface card chrome and
656
+ * flatten to a single-column flow so each item visually matches a
657
+ * regular flat-dropdown item rather than a card-styled grid cell. */
658
+ @media (max-width: 768px) {
659
+ .rf-nav--menubar .rf-nav-group__panel .rf-nav--columns {
660
+ padding: 0;
661
+ gap: 0;
662
+ background: transparent;
663
+ border: 0;
664
+ border-radius: 0;
665
+ grid-auto-flow: row;
666
+ grid-auto-columns: unset;
667
+ grid-template-columns: 1fr;
668
+ }
669
+ /* Tighter padding + bolder weight matches the visual rhythm of the
670
+ * regular flat menubar group items above. Hover swaps to a text-colour
671
+ * change rather than a surface tint so the drawer doesn't fill with
672
+ * highlighted bands as the user scrolls. */
673
+ .rf-nav--menubar .rf-nav-group__panel .rf-nav--columns .rf-nav-item__link {
674
+ padding: 0.25rem 0.75rem;
675
+ font-weight: 500;
676
+ }
677
+ .rf-nav--menubar .rf-nav-group__panel .rf-nav--columns .rf-nav-item__link:hover {
678
+ background: transparent;
679
+ color: var(--rf-color-primary);
680
+ }
681
+ /* Descriptions are space-eaters in a vertical drawer — hide them and let
682
+ * the link title carry the line on its own. */
683
+ .rf-nav--menubar .rf-nav-group__panel .rf-nav--columns .rf-nav-item__description {
684
+ display: none;
685
+ }
686
+ /* Rich-panel min-widths (20rem / 36rem) above aren't viewport-gated and
687
+ * would push the drawer wider than the mobile viewport. The drawer
688
+ * renders the menubar as a flat vertical list, so no min-width is
689
+ * needed — clamp to fit. Placed after the rich-panel rules so source
690
+ * order wins the cascade at equal specificity. */
691
+ .rf-nav--menubar .rf-nav-group__panel:has(.rf-nav-group__intro),
692
+ .rf-nav--menubar .rf-nav-group__panel:has(.rf-nav-group__footer),
693
+ .rf-nav--menubar .rf-nav-group__panel:has(.rf-nav),
694
+ .rf-nav--menubar .rf-nav-group__panel:has(.rf-nav--columns > div[data-name="column"]) {
695
+ min-width: 0;
696
+ max-width: 100%;
697
+ }
698
+ }