@filipgutica/ui 1.8.1 → 1.9.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 CHANGED
@@ -122,8 +122,9 @@ import { UiSiteHeader } from '@filipgutica/ui/site'
122
122
 
123
123
  `project` is `wtree`, `annoterm`, `devps`, `workbench`, `filip-stack`, or `ui`. The project
124
124
  menu is a native disclosure that works without JavaScript; Escape closes it and
125
- returns focus to its button. Without a default slot the header renders a
126
- System, Light, and Dark picker. A page with its own appearance control passes it
125
+ returns focus to its button. Inside `.fg-site-docs`, at 640px and below, the
126
+ header hides `links` so it fits one row, so repeat those destinations in the footer. Without a
127
+ default slot the header renders a System, Light, and Dark picker. A page with its own appearance control passes it
127
128
  in the default slot and uses `useSiteAppearance({ applyToRoot: false })` when it
128
129
  owns the root element.
129
130
 
@@ -167,8 +168,9 @@ import read from './captures/read.html?raw'
167
168
  `aria-current="location"`, a text-only marker. An item with `current: true` gets
168
169
  `aria-current="page"`, the selected fill and accent bar; `code: true` shows the
169
170
  label in the monospace font. The page grid places the rail, which stays in view
170
- on desktop. At 800px and below it is a native Menu disclosure until the page is
171
- interactive, then a drawer. The drawer closes on navigation and focuses the
171
+ on desktop. At 800px and below it is a native disclosure until the page is
172
+ interactive, then a drawer. The disclosure, the drawer button, and the drawer
173
+ title all show `label`. The drawer closes on navigation and focuses the
172
174
  destination section, and focus follows the visible control across the
173
175
  breakpoint.
174
176
 
@@ -180,9 +182,11 @@ focus returns to the button on close. Phones show the button as an icon. `v-mode
180
182
  holds the active ID and defaults to the first item. Each capture has the ID
181
183
  `idPrefix` + `id` (`frame-` by default), so a deep link such as `#frame-read`
182
184
  selects its tab on load and on hash change. Without JavaScript every capture is
183
- listed under its own heading and scrolls at full size. The site owns terminal
184
- colors: set `--site-capture-bg` on `:root`, because the dialog renders outside
185
- the panel.
185
+ listed under its own heading and scrolls at full size. Captures use a dark
186
+ terminal palette for the `.term` root and the converter's ANSI classes (`.tf0`
187
+ to `.tf15`, `.tb0` to `.tb15`, `.tf-dim`, `.tbold`, and the others). To change
188
+ it, set `--site-capture-bg`, `--t-bg`, `--t-fg`, `--t-dim`, or `--t0` to `--t15`
189
+ on `:root`, because the dialog renders outside the panel.
186
190
 
187
191
  `UiCommandList` renders a table of monospace commands and descriptions. Use the
188
192
  `command` slot to show a key as `<kbd>`, the `description` slot for `<kbd>` or
@@ -198,6 +202,53 @@ pairs, `.fg-site-definitions` for a two-column `dl` of `dt` and `dd` that stacks
198
202
  on phones, `.fg-site-sr-only` for visually hidden text, and `.fg-site-skip` for
199
203
  a skip link that appears on focus.
200
204
 
205
+ ### Docs shell
206
+
207
+ Add `fg-site-docs` to the element that wraps a project page's header, rail,
208
+ main content, and footer. The shell then supplies:
209
+
210
+ - the frame: the header across the top, the `--sidebar` rail beside `main`, and
211
+ the footer on the frame edges. At 800px and below the rail stacks above `main`.
212
+ - element defaults inside the shell, plus the page background and font on
213
+ `body`: box sizing, heading sizes and margins, paragraph measure, links,
214
+ `kbd`, and a 2px focus outline.
215
+ - `.fg-site-footer` for the footer and `.fg-site-legend` for a `dl` that explains
216
+ what a capture shows.
217
+
218
+ ```vue
219
+ <template>
220
+ <div class="fg-site-docs">
221
+ <UiSiteHeader project="devps" />
222
+ <UiSiteNav label="On this page" :groups="navigation" />
223
+ <main id="main">
224
+ <section aria-labelledby="title">…</section>
225
+ <dl class="fg-site-legend">
226
+ <div><dt>PORTS</dt><dd>The ports the server listens on.</dd></div>
227
+ </dl>
228
+ </main>
229
+ <footer class="fg-site-footer">
230
+ <a href="https://github.com/filipgutica">Built by Filip Gutica</a>
231
+ <nav aria-label="Project links">
232
+ <a href="https://github.com/filipgutica/devps/releases">Releases</a>
233
+ </nav>
234
+ </footer>
235
+ </div>
236
+ </template>
237
+ ```
238
+
239
+ The header, rail, `main`, and footer must be direct children of the shell. The
240
+ element defaults sit in the `base` cascade layer, so library components and the
241
+ site's own rules win. That holds only when the site declares the layer order
242
+ before importing `site.css`: `@import "tailwindcss"` does this, or start the
243
+ stylesheet with `@layer base, components, utilities;`. To adopt the shell, delete
244
+ the site's own frame, element base, `kbd`, footer, legend, and ANSI palette
245
+ rules. Without `fg-site-docs`, the frame, element defaults, and phone header
246
+ rules do not apply. These apply on every page: the `fg-site-footer` and
247
+ `fg-site-legend` classes wherever a page uses them; the capture terminal palette
248
+ for every `UiCapture`, in the `components` layer, so a site's own unlayered
249
+ `.term` and ANSI rules and its `--t*` values still win; and `UiSiteNav`, whose
250
+ phone toggle and drawer title now show its `label`.
251
+
201
252
  ## Development
202
253
 
203
254
  ```sh
package/dist/site.js CHANGED
@@ -250,7 +250,7 @@ var I = "tool-site-theme", L = (e) => e === "system" || e === "light" || e === "
250
250
  }, [d("summary", {
251
251
  class: "fg-site-rail__summary",
252
252
  "aria-controls": O(t)
253
- }, " Menu ", 8, pe), p(Z, {
253
+ }, D(e.label), 9, pe), p(Z, {
254
254
  "content-id": O(t),
255
255
  label: e.label,
256
256
  groups: e.groups,
@@ -264,7 +264,7 @@ var I = "tool-site-theme", L = (e) => e === "system" || e === "light" || e === "
264
264
  key: 1,
265
265
  open: l.value,
266
266
  "onUpdate:open": h[0] ||= (e) => l.value = e,
267
- title: "Navigation",
267
+ title: e.label,
268
268
  onCloseAutoFocus: E
269
269
  }, {
270
270
  trigger: M(() => [p(r, {
@@ -272,7 +272,7 @@ var I = "tool-site-theme", L = (e) => e === "system" || e === "light" || e === "
272
272
  size: "lg",
273
273
  class: "fg-site-rail__toggle"
274
274
  }, {
275
- default: M(() => [...h[1] ||= [f(" Menu ", -1)]]),
275
+ default: M(() => [f(D(e.label), 1)]),
276
276
  _: 1
277
277
  })]),
278
278
  default: M(() => [p(Z, {
@@ -288,7 +288,7 @@ var I = "tool-site-theme", L = (e) => e === "system" || e === "light" || e === "
288
288
  "active-id"
289
289
  ])]),
290
290
  _: 1
291
- }, 8, ["open"]))], 8, de));
291
+ }, 8, ["open", "title"]))], 8, de));
292
292
  }
293
293
  }), ge = [
294
294
  "aria-label",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@filipgutica/ui",
3
- "version": "1.8.1",
3
+ "version": "1.9.0",
4
4
  "description": "A compact Vue design system with Tailwind CSS and VS Code theme support",
5
5
  "type": "module",
6
6
  "sideEffects": [
@@ -24,16 +24,16 @@
24
24
  .fg-code-block__header {
25
25
  display: flex;
26
26
  align-items: center;
27
- gap: 0.75rem;
27
+ gap: var(--space-3);
28
28
  min-height: 2.5rem;
29
- padding: 0.35rem 0.75rem;
29
+ padding: 0.35rem var(--space-3);
30
30
  border-bottom: 1px solid color-mix(in srgb, var(--color-code-text) 16%, transparent);
31
31
  background: color-mix(in srgb, var(--color-code-text) 5%, var(--color-code));
32
- font-size: 0.75rem;
32
+ font-size: var(--text-xs);
33
33
  }
34
34
  .fg-code-block__title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; }
35
35
  .fg-code-block__language { opacity: 0.7; }
36
- .fg-code-block__actions { display: flex; align-items: center; gap: 0.5rem; margin-left: auto; flex-shrink: 0; }
36
+ .fg-code-block__actions { display: flex; align-items: center; gap: var(--space-2); margin-left: auto; flex-shrink: 0; }
37
37
  .fg-code-block__copy {
38
38
  display: inline-flex;
39
39
  align-items: center;
@@ -41,9 +41,9 @@
41
41
  gap: 0.4rem;
42
42
  min-width: var(--control-lg);
43
43
  min-height: var(--control-lg);
44
- padding: 0.25rem 0.4rem;
44
+ padding: var(--space-1) 0.4rem;
45
45
  border: 0;
46
- border-radius: 0.25rem;
46
+ border-radius: var(--radius-inline);
47
47
  background: transparent;
48
48
  color: inherit;
49
49
  font: inherit;
@@ -54,15 +54,15 @@
54
54
  /* Inset: the code block clips its content. */
55
55
  .fg-code-block__copy:focus-visible, .fg-code-block__pre:focus-visible { outline: 2px solid var(--color-focus); outline-offset: -2px; }
56
56
  .fg-figure__plate:focus-visible { outline: 2px solid var(--color-focus); outline-offset: 2px; }
57
- .fg-code-block__pre { margin: 0; padding: 1rem; overflow: auto; max-height: 32rem; tab-size: 2; }
58
- .fg-code-block__pre, .fg-code-block__pre code { font-family: var(--font-mono); font-size: 0.8125rem; line-height: 1.65; }
57
+ .fg-code-block__pre { margin: 0; padding: var(--space-4); overflow: auto; max-height: 32rem; tab-size: 2; }
58
+ .fg-code-block__pre, .fg-code-block__pre code { font-family: var(--font-mono); font-size: var(--text-sm); line-height: 1.65; }
59
59
  .fg-code-block__pre code { display: block; color: inherit; }
60
60
  .fg-code-block__line { display: flex; min-height: 1.65em; }
61
61
  .fg-code-block__text { white-space: pre; }
62
62
  .fg-code-block__number { min-width: 2.5em; padding-right: 1em; flex-shrink: 0; text-align: right; opacity: 0.5; user-select: none; }
63
63
  .fg-code-block[data-wrap="true"] .fg-code-block__text { min-width: 0; white-space: pre-wrap; overflow-wrap: anywhere; }
64
64
  .fg-code-block__status:empty { display: none; }
65
- .fg-code-block__status:not(:empty) { display: block; padding: 0.5rem 0.75rem; font-size: 0.75rem; }
65
+ .fg-code-block__status:not(:empty) { display: block; padding: var(--space-2) var(--space-3); font-size: var(--text-xs); }
66
66
  .fg-code-block[data-variant="compact"] {
67
67
  position: relative;
68
68
  display: grid;
@@ -122,7 +122,7 @@
122
122
  display: inline-flex;
123
123
  align-items: center;
124
124
  justify-content: center;
125
- gap: 0.5rem;
125
+ gap: var(--space-2);
126
126
  color: var(--color-button-text);
127
127
  background: var(--color-button-bg);
128
128
  box-shadow: none;
@@ -167,19 +167,19 @@
167
167
  .fg-button[data-size="sm"],
168
168
  .fg-input[data-size="sm"],
169
169
  .fg-select[data-size="sm"] {
170
- font-size: 0.8125rem;
170
+ font-size: var(--text-sm);
171
171
  }
172
172
 
173
173
  .fg-button[data-size="md"],
174
174
  .fg-input[data-size="md"],
175
175
  .fg-select[data-size="md"] {
176
- font-size: 0.875rem;
176
+ font-size: var(--text-md);
177
177
  }
178
178
 
179
179
  .fg-button[data-size="lg"],
180
180
  .fg-input[data-size="lg"],
181
181
  .fg-select[data-size="lg"] {
182
- font-size: 1rem;
182
+ font-size: var(--text-lg);
183
183
  }
184
184
 
185
185
  .fg-button[data-size="sm"] { min-height: var(--control-sm); }
@@ -193,20 +193,20 @@
193
193
  .fg-select[data-size="lg"] { height: var(--control-lg); }
194
194
 
195
195
  .fg-button[data-size="sm"] { padding: 0 0.6rem; }
196
- .fg-button[data-size="md"] { padding: 0 0.75rem; }
197
- .fg-button[data-size="lg"] { padding: 0 1rem; }
196
+ .fg-button[data-size="md"] { padding: 0 var(--space-3); }
197
+ .fg-button[data-size="lg"] { padding: 0 var(--space-4); }
198
198
 
199
199
  /* Deprecated UiButton aliases retained for existing consumers. */
200
200
  .fg-button[data-size="compact"] {
201
201
  min-height: var(--control-md);
202
202
  padding: 0 0.7rem;
203
- font-size: 0.8125rem;
203
+ font-size: var(--text-sm);
204
204
  }
205
205
 
206
206
  .fg-button[data-size="default"] {
207
207
  min-height: var(--control-lg);
208
- padding: 0 1rem;
209
- font-size: 1rem;
208
+ padding: 0 var(--space-4);
209
+ font-size: var(--text-lg);
210
210
  }
211
211
 
212
212
  .fg-button[data-variant="secondary"] {
@@ -243,7 +243,7 @@
243
243
  .fg-button[data-variant="text"] {
244
244
  height: auto;
245
245
  min-height: auto;
246
- padding: 0.25rem;
246
+ padding: var(--space-1);
247
247
  color: var(--color-link);
248
248
  background: transparent;
249
249
  text-decoration: underline;
@@ -339,7 +339,7 @@
339
339
  .fg-input[data-size="md"] { padding: 0 0.65rem; }
340
340
  .fg-input[data-size="lg"] { padding: 0 0.7rem; }
341
341
 
342
- .fg-select[data-size="sm"] { padding: 0 1.6rem 0 0.5rem; }
342
+ .fg-select[data-size="sm"] { padding: 0 1.6rem 0 var(--space-2); }
343
343
  .fg-select[data-size="md"] { padding: 0 1.7rem 0 0.6rem; }
344
344
  .fg-select[data-size="lg"] { padding: 0 1.8rem 0 0.7rem; }
345
345
 
@@ -361,14 +361,14 @@
361
361
 
362
362
  .fg-field__label {
363
363
  color: var(--color-text);
364
- font-size: 0.8125rem;
364
+ font-size: var(--text-sm);
365
365
  font-weight: 600;
366
366
  }
367
367
 
368
368
  .fg-field__description,
369
369
  .fg-field__error {
370
370
  margin: 0;
371
- font-size: 0.8125rem;
371
+ font-size: var(--text-sm);
372
372
  }
373
373
 
374
374
  .fg-field__description { color: var(--color-muted); }
@@ -413,21 +413,21 @@
413
413
  min-height: var(--control-sm);
414
414
  align-items: center;
415
415
  gap: 0.45rem;
416
- font-size: 0.8125rem;
416
+ font-size: var(--text-sm);
417
417
  }
418
418
 
419
419
  .fg-checkbox[data-size="md"] {
420
420
  min-height: var(--control-md);
421
421
  align-items: center;
422
422
  gap: 0.55rem;
423
- font-size: 0.875rem;
423
+ font-size: var(--text-md);
424
424
  }
425
425
 
426
426
  .fg-checkbox[data-size="lg"] {
427
427
  min-height: var(--control-lg);
428
428
  align-items: center;
429
429
  gap: 0.65rem;
430
- font-size: 1rem;
430
+ font-size: var(--text-lg);
431
431
  }
432
432
 
433
433
  .fg-checkbox[data-size] .fg-checkbox__control { margin-top: 0; }
@@ -435,7 +435,7 @@
435
435
  .fg-checkbox[data-size="sm"] .fg-checkbox__control {
436
436
  width: 1rem;
437
437
  height: 1rem;
438
- border-radius: 0.25rem;
438
+ border-radius: var(--radius-inline);
439
439
  }
440
440
 
441
441
  .fg-checkbox[data-size="md"] .fg-checkbox__control {
@@ -455,14 +455,14 @@
455
455
  .fg-radio-card-group {
456
456
  display: grid;
457
457
  grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
458
- gap: 0.75rem;
458
+ gap: var(--space-3);
459
459
  }
460
460
 
461
461
  .fg-radio-card {
462
462
  position: relative;
463
463
  display: block;
464
464
  min-width: 0;
465
- padding: 0.75rem;
465
+ padding: var(--space-3);
466
466
  overflow: hidden;
467
467
  border: 1px solid var(--color-border-subtle);
468
468
  border-radius: var(--radius-panel);
@@ -493,15 +493,15 @@
493
493
  .fg-radio-card__content {
494
494
  display: grid;
495
495
  min-width: 0;
496
- gap: 0.25rem;
496
+ gap: var(--space-1);
497
497
  padding-right: 1.75rem;
498
498
  }
499
499
 
500
500
  .fg-radio-card__indicator {
501
501
  position: absolute;
502
502
  z-index: 1;
503
- top: 0.75rem;
504
- right: 0.75rem;
503
+ top: var(--space-3);
504
+ right: var(--space-3);
505
505
  display: block;
506
506
  width: 1rem;
507
507
  height: 1rem;
@@ -523,7 +523,7 @@
523
523
  padding: 0.18rem 0.48rem;
524
524
  color: var(--color-muted);
525
525
  background: var(--color-surface-muted);
526
- font-size: 0.75rem;
526
+ font-size: var(--text-xs);
527
527
  font-weight: 500;
528
528
  line-height: 1.25;
529
529
  }
@@ -537,8 +537,8 @@
537
537
  .fg-alert {
538
538
  display: flex;
539
539
  align-items: flex-start;
540
- gap: 0.75rem;
541
- padding: 0.75rem 1rem;
540
+ gap: var(--space-3);
541
+ padding: var(--space-3) var(--space-4);
542
542
  border: 1px solid var(--color-border-subtle);
543
543
  border-radius: var(--radius-control);
544
544
  color: var(--color-text);
@@ -562,8 +562,8 @@
562
562
  box-shadow: none;
563
563
  }
564
564
 
565
- .fg-surface[data-padding="compact"] { padding: 0.75rem; }
566
- .fg-surface[data-padding="default"] { padding: 1rem; }
565
+ .fg-surface[data-padding="compact"] { padding: var(--space-3); }
566
+ .fg-surface[data-padding="default"] { padding: var(--space-4); }
567
567
 
568
568
  .fg-card {
569
569
  min-width: 0;
@@ -805,8 +805,8 @@
805
805
  top: 50%;
806
806
  left: 50%;
807
807
  display: grid;
808
- width: min(32rem, calc(100vw - 2rem));
809
- max-height: min(42rem, calc(100vh - 2rem));
808
+ width: min(32rem, calc(100vw - var(--space-8)));
809
+ max-height: min(42rem, calc(100vh - var(--space-8)));
810
810
  overflow: hidden;
811
811
  transform: translate(-50%, -50%);
812
812
  border: 1px solid var(--color-border-subtle);
@@ -821,8 +821,8 @@
821
821
  display: flex;
822
822
  align-items: flex-start;
823
823
  justify-content: space-between;
824
- gap: 1rem;
825
- padding: 1rem;
824
+ gap: var(--space-4);
825
+ padding: var(--space-4);
826
826
  }
827
827
 
828
828
  .fg-dialog__header { border-bottom: 1px solid var(--color-border-subtle); }
@@ -836,21 +836,21 @@
836
836
  }
837
837
 
838
838
  .fg-dialog__description {
839
- margin: 0.25rem 0 0;
839
+ margin: var(--space-1) 0 0;
840
840
  color: var(--color-muted);
841
- font-size: 0.875rem;
841
+ font-size: var(--text-md);
842
842
  }
843
843
 
844
844
  .fg-dialog__body {
845
845
  min-height: 0;
846
846
  overflow: auto;
847
- padding: 1rem;
847
+ padding: var(--space-4);
848
848
  }
849
849
 
850
850
  .fg-dialog__close {
851
851
  display: inline-grid;
852
852
  flex: none;
853
- width: 2.75rem;
853
+ width: var(--control-lg);
854
854
  min-height: var(--control-lg);
855
855
  padding: 0;
856
856
  place-items: center;
@@ -900,7 +900,7 @@
900
900
  bottom: 0;
901
901
  left: 0;
902
902
  width: 100%;
903
- max-height: calc(100vh - 1rem);
903
+ max-height: calc(100vh - var(--space-4));
904
904
  transform: none;
905
905
  border-radius: var(--radius-panel) var(--radius-panel) 0 0;
906
906
  }
package/src/site.css CHANGED
@@ -1,7 +1,113 @@
1
1
  /* Shared chrome and page helpers for the project sites. Use with
2
2
  * @filipgutica/ui/themes/graphite-indigo.css, which supplies the type, space, and
3
3
  * page-measure roles this file reads. Nothing here targets site-specific markup. */
4
+ /* Element defaults for a docs page inside .fg-site-docs. They sit in the base layer, so
5
+ * library components and the site's own rules win, as long as the site declares the layer
6
+ * order first (Tailwind's import does, or write @layer base, components, utilities;).
7
+ * Pages without .fg-site-docs get none of them. */
8
+ @layer base {
9
+ html:has(.fg-site-docs) { scroll-padding-top: var(--space-6); }
10
+ body:has(.fg-site-docs) {
11
+ margin: 0;
12
+ background: var(--color-bg);
13
+ color: var(--color-text);
14
+ font: var(--text-md) / var(--leading) var(--font-ui);
15
+ -webkit-font-smoothing: antialiased;
16
+ }
17
+ .fg-site-docs,
18
+ .fg-site-docs *,
19
+ .fg-site-docs *::before,
20
+ .fg-site-docs *::after { box-sizing: border-box; }
21
+ .fg-site-docs :where(h1, h2, h3, p, dl, dd, figure) { margin: 0; }
22
+ .fg-site-docs :where(h1, h2, h3) {
23
+ font-weight: var(--weight-semibold);
24
+ line-height: var(--leading-tight);
25
+ text-wrap: balance;
26
+ }
27
+ .fg-site-docs :where(h1) { max-width: 22ch; font-size: var(--text-3xl); letter-spacing: var(--tracking-tight); }
28
+ .fg-site-docs :where(h2) { font-size: var(--text-xl); }
29
+ .fg-site-docs :where(p) { max-width: var(--measure); }
30
+ .fg-site-docs :where(code, kbd, pre) { font-family: var(--font-mono); }
31
+ .fg-site-docs :where(a) { color: var(--color-link); text-underline-offset: 0.18em; }
32
+ .fg-site-docs :where(kbd) {
33
+ display: inline-block;
34
+ padding: 0 var(--space-1);
35
+ border: 1px solid var(--color-border);
36
+ border-bottom-width: 2px;
37
+ border-radius: var(--radius-inline);
38
+ background: var(--color-surface);
39
+ font-size: var(--text-xs);
40
+ white-space: nowrap;
41
+ }
42
+ .fg-site-docs :where(:focus-visible) { outline: 2px solid var(--color-focus); outline-offset: 2px; }
43
+ }
44
+
4
45
  @layer components {
46
+ /* Docs shell: header across the top, the --sidebar rail beside main, and the footer
47
+ * on the frame edges. Header, rail, main, and footer are direct children. */
48
+ .fg-site-docs {
49
+ display: grid;
50
+ grid-template-columns: var(--sidebar) minmax(0, 1fr);
51
+ column-gap: var(--space-8);
52
+ max-width: calc(var(--page-max) + var(--sidebar) + var(--space-8) + var(--space-12));
53
+ margin-inline: auto;
54
+ padding-inline: var(--space-6);
55
+ }
56
+ .fg-site-docs > .fg-site-header {
57
+ --site-frame: 100%;
58
+ --site-gutter: 0px;
59
+ grid-column: 1 / -1;
60
+ min-width: 0;
61
+ }
62
+ .fg-site-docs > main {
63
+ display: grid;
64
+ grid-template-columns: minmax(0, 1fr);
65
+ gap: var(--space-12);
66
+ min-width: 0;
67
+ padding-block: var(--space-8) var(--space-16);
68
+ }
69
+ .fg-site-docs > .fg-site-footer { grid-column: 1 / -1; }
70
+
71
+ /* Footer: an attribution link at the start, project links in a nav at the end. */
72
+ .fg-site-footer {
73
+ display: flex;
74
+ flex-wrap: wrap;
75
+ align-items: center;
76
+ justify-content: space-between;
77
+ gap: 0 var(--space-5);
78
+ padding-block: var(--space-6) var(--space-8);
79
+ border-top: 1px solid var(--color-border-subtle);
80
+ color: var(--color-muted);
81
+ font-size: var(--text-sm);
82
+ }
83
+ .fg-site-footer nav { display: flex; flex-wrap: wrap; gap: 0 var(--space-5); }
84
+ .fg-site-footer a {
85
+ display: inline-flex;
86
+ min-height: var(--control-lg);
87
+ align-items: center;
88
+ color: var(--color-muted);
89
+ text-decoration: none;
90
+ }
91
+ .fg-site-footer a:hover { color: var(--color-text); text-decoration: underline; }
92
+
93
+ /* Legend: a dl of what a capture shows, with one div per dt and dd pair. */
94
+ .fg-site-legend {
95
+ display: grid;
96
+ grid-template-columns: repeat(auto-fit, minmax(min(var(--sidebar), 100%), 1fr));
97
+ gap: 0 var(--space-6);
98
+ margin: 0;
99
+ font-size: var(--text-sm);
100
+ }
101
+ .fg-site-legend > div {
102
+ display: grid;
103
+ align-content: start;
104
+ gap: var(--space-1);
105
+ padding-block: var(--space-3);
106
+ border-top: 1px solid var(--color-border-subtle);
107
+ }
108
+ .fg-site-legend dt { font-family: var(--font-mono); font-weight: var(--weight-medium); }
109
+ .fg-site-legend dd { margin: 0; color: var(--color-muted); }
110
+
5
111
  /* A centered frame. The header uses the same edges as the page below it.
6
112
  * Set --site-frame on the header or a frame to change its width. */
7
113
  .fg-site-frame,
@@ -138,8 +244,8 @@
138
244
  .fg-site-nav a:focus-visible { outline: 2px solid var(--color-focus); outline-offset: -2px; }
139
245
 
140
246
  /* Section rail (UiSiteNav). The page grid places it; on desktop it stays in view
141
- * beside the article. At 800px and below it becomes a Menu disclosure, replaced
142
- * by a drawer once the page is interactive. */
247
+ * beside the article. At 800px and below it becomes a disclosure named by the
248
+ * label prop, replaced by a drawer once the page is interactive. */
143
249
  .fg-site-rail {
144
250
  position: sticky;
145
251
  top: var(--space-6);
@@ -168,9 +274,9 @@
168
274
  .fg-site-rail__group-label { margin-block-end: var(--space-1); }
169
275
 
170
276
  /* Capture panel (UiCapture): tabs in the header, the capture as the body, and the
171
- * command and caption as the footer. The site owns terminal colors and sets the
172
- * screen background with --site-capture-bg on :root, which also reaches the
173
- * full-size viewer. Nothing clips the panel, so focus rings inside it stay whole. */
277
+ * command and caption as the footer. The screen uses a dark terminal palette by
278
+ * default. A site can set --site-capture-bg and --t* values on :root, which also
279
+ * reach the full-size viewer. Nothing clips the panel, so focus rings inside it stay whole. */
174
280
  .fg-capture {
175
281
  position: relative;
176
282
  min-width: 0;
@@ -220,7 +326,59 @@
220
326
  overflow-x: auto;
221
327
  background: var(--site-capture-bg, var(--color-code));
222
328
  }
329
+ /* Terminal markup gets the dark default; a plain <pre> keeps the code background its text expects. */
330
+ .fg-capture__screen:has(> .term) { background: var(--site-capture-bg, var(--t-bg, #131316)); }
223
331
  .fg-capture__screen > * { width: max-content; margin: 0; }
332
+ /* Terminal colors for checked-in capture markup: .term and the ANSI classes the capture
333
+ * converter emits. Each color reads the site's --t* value first, so a site palette on
334
+ * :root still wins, and these defaults apply only where a site sets none. */
335
+ .fg-capture__screen .term {
336
+ padding: var(--space-4);
337
+ color: var(--t-fg, #e6e6ea);
338
+ font: var(--weight-regular) var(--text-sm) / 1 var(--font-mono);
339
+ font-variant-ligatures: none;
340
+ tab-size: 8;
341
+ }
342
+ .fg-capture__screen .term i { display: inline-block; width: 1ch; overflow: visible; font-style: normal; }
343
+ .fg-capture__screen .tf0 { color: var(--t0, #2b2b31); }
344
+ .fg-capture__screen .tf1 { color: var(--t1, #f0776c); }
345
+ .fg-capture__screen .tf2 { color: var(--t2, #4cc38a); }
346
+ .fg-capture__screen .tf3 { color: var(--t3, #e2a54b); }
347
+ .fg-capture__screen .tf4 { color: var(--t4, #7d97ff); }
348
+ .fg-capture__screen .tf5 { color: var(--t5, #c792ea); }
349
+ .fg-capture__screen .tf6 { color: var(--t6, #5fd3d9); }
350
+ .fg-capture__screen .tf7 { color: var(--t7, #c8c8d0); }
351
+ .fg-capture__screen .tf8 { color: var(--t8, #a0a0ab); }
352
+ .fg-capture__screen .tf9 { color: var(--t9, #ff8f85); }
353
+ .fg-capture__screen .tf10 { color: var(--t10, #6fd3a0); }
354
+ .fg-capture__screen .tf11 { color: var(--t11, #e8b56a); }
355
+ .fg-capture__screen .tf12 { color: var(--t12, #7d97ff); }
356
+ .fg-capture__screen .tf13 { color: var(--t13, #d9a8f0); }
357
+ .fg-capture__screen .tf14 { color: var(--t14, #8ee3e8); }
358
+ .fg-capture__screen .tf15 { color: var(--t15, #ffffff); }
359
+ .fg-capture__screen .tb0 { background: var(--t0, #2b2b31); }
360
+ .fg-capture__screen .tb1 { background: var(--t1, #f0776c); }
361
+ .fg-capture__screen .tb2 { background: var(--t2, #4cc38a); }
362
+ .fg-capture__screen .tb3 { background: var(--t3, #e2a54b); }
363
+ .fg-capture__screen .tb4 { background: var(--t4, #7d97ff); }
364
+ .fg-capture__screen .tb5 { background: var(--t5, #c792ea); }
365
+ .fg-capture__screen .tb6 { background: var(--t6, #5fd3d9); }
366
+ .fg-capture__screen .tb7 { background: var(--t7, #c8c8d0); }
367
+ .fg-capture__screen .tb8 { background: var(--t8, #a0a0ab); }
368
+ .fg-capture__screen .tb9 { background: var(--t9, #ff8f85); }
369
+ .fg-capture__screen .tb10 { background: var(--t10, #6fd3a0); }
370
+ .fg-capture__screen .tb11 { background: var(--t11, #e8b56a); }
371
+ .fg-capture__screen .tb12 { background: var(--t12, #7d97ff); }
372
+ .fg-capture__screen .tb13 { background: var(--t13, #d9a8f0); }
373
+ .fg-capture__screen .tb14 { background: var(--t14, #8ee3e8); }
374
+ .fg-capture__screen .tb15 { background: var(--t15, #ffffff); }
375
+ .fg-capture__screen .tf-dim { color: var(--t-dim, #a0a0ab); }
376
+ .fg-capture__screen .tf-bg { color: var(--t-bg, #131316); }
377
+ .fg-capture__screen .tb-fg { background: var(--t-fg, #e6e6ea); }
378
+ .fg-capture__screen .tbold { font-weight: var(--weight-semibold); }
379
+ .fg-capture__screen .tital { font-style: italic; }
380
+ .fg-capture__screen .tul { text-decoration: underline; text-underline-offset: 0.18em; }
381
+
224
382
  .fg-capture__screen[data-fit] { overflow: hidden; }
225
383
  .fg-capture__screen[data-fit] > * { transform: scale(var(--capture-scale, 1)); transform-origin: top left; }
226
384
  .fg-capture__expand {
@@ -407,6 +565,8 @@
407
565
  }
408
566
  /* Keep in sync with MOBILE_QUERY in UiSiteNav.vue. */
409
567
  @media (max-width: 800px) {
568
+ .fg-site-docs { grid-template-columns: minmax(0, 1fr); }
569
+ .fg-site-docs > main { gap: var(--space-8); padding-top: var(--space-6); }
410
570
  .fg-site-rail {
411
571
  position: static;
412
572
  max-height: none;
@@ -432,8 +592,20 @@
432
592
  .fg-site-rail__menu .fg-site-rail__group { grid-template-columns: repeat(2, minmax(0, 1fr)); }
433
593
  .fg-site-rail__menu .fg-site-rail__group-label { grid-column: 1 / -1; }
434
594
  .fg-site-rail__nav a { min-height: var(--control-lg); }
595
+ /* The drawer title already shows the label; the heading still names the nav. */
596
+ .fg-drawer__content .fg-site-rail__label {
597
+ position: absolute;
598
+ width: 1px;
599
+ height: 1px;
600
+ overflow: hidden;
601
+ clip-path: inset(50%);
602
+ white-space: nowrap;
603
+ }
435
604
  }
436
605
  @media (max-width: 640px) {
606
+ .fg-site-docs { padding-inline: var(--space-4); }
607
+ /* Docs-shell pages keep one header row on phones and repeat these links in their footer. */
608
+ .fg-site-docs .fg-site-header__links { display: none; }
437
609
  .fg-dialog__content.fg-capture-viewer { width: 100%; max-width: none; }
438
610
  /* Phones keep the icon; the accessible name still says Full size. */
439
611
  .fg-capture[data-enhanced="true"] .fg-tabs__list { margin-inline-end: calc(var(--space-2) + var(--control-lg)); }
package/src/theme.css CHANGED
@@ -75,6 +75,7 @@
75
75
  --color-warning-surface: color-mix(in srgb, var(--color-warning) 13%, var(--color-surface));
76
76
  --color-success-surface: color-mix(in srgb, var(--color-success) 12%, var(--color-surface));
77
77
  --color-info-surface: color-mix(in srgb, var(--color-info) 12%, var(--color-surface));
78
+ --radius-inline: 0.25rem;
78
79
  --radius-control: 0.375rem;
79
80
  --radius-panel: 0.5rem;
80
81
  --radius-pill: 999px;
@@ -147,6 +148,7 @@
147
148
  --color-ui-warning: var(--color-warning);
148
149
  --color-ui-success: var(--color-success);
149
150
  --color-ui-info: var(--color-info);
151
+ --radius-ui-inline: var(--radius-inline);
150
152
  --radius-ui-control: var(--radius-control);
151
153
  --radius-ui-panel: var(--radius-panel);
152
154
  }
@@ -41,7 +41,7 @@
41
41
  --color-syntax-attribute: var(--color-syntax-number);
42
42
  --color-error: #c7372d;
43
43
  --color-warning: #86532e;
44
- --color-success: #1f8a5b;
44
+ --color-success: #157249;
45
45
  --color-info: #175cd3;
46
46
  --color-progress: var(--color-accent);
47
47
  --color-error-surface: color-mix(in srgb, var(--color-error) 10%, var(--color-bg));
@@ -64,7 +64,7 @@
64
64
  :root.dark {
65
65
  color-scheme: dark;
66
66
  --color-bg: #151619;
67
- --color-surface: #191a1d;
67
+ --color-surface: #0e0f12;
68
68
  --color-surface-raised: #212226;
69
69
  --color-surface-muted: #303238;
70
70
  --color-text: #f4f5f8;