@filipgutica/ui 1.8.1 → 1.10.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
@@ -50,9 +50,10 @@ cancelable `closeAutoFocus` event and focus that heading.
50
50
 
51
51
  `UiTabs` renders every labelled panel during SSR and initial hydration. After
52
52
  mounting, it adds keyboard-accessible tabs and hides inactive panels without
53
- unmounting them. Use unique item values and keep page anchor headings outside
54
- the panels. Re-align an initial hash after layout settles if enhancement changes
55
- the page height.
53
+ unmounting them. A tab strip narrower than its tabs scrolls, fades each clipped
54
+ edge, and keeps the selected tab in view when it resizes. Use unique item values
55
+ and keep page anchor headings outside the panels. Re-align an initial hash after
56
+ layout settles if enhancement changes the page height.
56
57
 
57
58
  Track page sections from a component's setup function:
58
59
 
@@ -68,7 +69,10 @@ The returned readonly ref contains the last supplied heading above the root's
68
69
  CSS `scroll-padding-top` inset, or the final section at the document bottom.
69
70
  IDs must remain stable and in page order; missing elements are ignored after
70
71
  mounting. Bind matching links to `aria-current="location"`. Scrolling, resizing,
71
- layout changes, completed transitions, and restored pages update the value.
72
+ layout changes, completed transitions, and restored pages update the value. After
73
+ a jump to a section, through a `#id` link, a hash change, or a deep link, that
74
+ section stays current until the reader scrolls away from where the jump lands, so
75
+ a page too short to bring it to the top still marks it.
72
76
  The composable does not change focus, the URL, or browser history, and removes
73
77
  its listeners and observer when the component unmounts. During SSR it returns
74
78
  the first supplied ID without accessing browser globals.
@@ -122,8 +126,9 @@ import { UiSiteHeader } from '@filipgutica/ui/site'
122
126
 
123
127
  `project` is `wtree`, `annoterm`, `devps`, `workbench`, `filip-stack`, or `ui`. The project
124
128
  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
129
+ returns focus to its button. Inside `.fg-site-docs`, at 640px and below, the
130
+ header hides `links` so it fits one row, so repeat those destinations in the footer. Without a
131
+ default slot the header renders a System, Light, and Dark picker. A page with its own appearance control passes it
127
132
  in the default slot and uses `useSiteAppearance({ applyToRoot: false })` when it
128
133
  owns the root element.
129
134
 
@@ -167,8 +172,9 @@ import read from './captures/read.html?raw'
167
172
  `aria-current="location"`, a text-only marker. An item with `current: true` gets
168
173
  `aria-current="page"`, the selected fill and accent bar; `code: true` shows the
169
174
  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
175
+ on desktop. At 800px and below it is a native disclosure until the page is
176
+ interactive, then a drawer. The disclosure, the drawer button, and the drawer
177
+ title all show `label`. The drawer closes on navigation and focuses the
172
178
  destination section, and focus follows the visible control across the
173
179
  breakpoint.
174
180
 
@@ -180,9 +186,11 @@ focus returns to the button on close. Phones show the button as an icon. `v-mode
180
186
  holds the active ID and defaults to the first item. Each capture has the ID
181
187
  `idPrefix` + `id` (`frame-` by default), so a deep link such as `#frame-read`
182
188
  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.
189
+ listed under its own heading and scrolls at full size. Captures use a dark
190
+ terminal palette for the `.term` root and the converter's ANSI classes (`.tf0`
191
+ to `.tf15`, `.tb0` to `.tb15`, `.tf-dim`, `.tbold`, and the others). To change
192
+ it, set `--site-capture-bg`, `--t-bg`, `--t-fg`, `--t-dim`, or `--t0` to `--t15`
193
+ on `:root`, because the dialog renders outside the panel.
186
194
 
187
195
  `UiCommandList` renders a table of monospace commands and descriptions. Use the
188
196
  `command` slot to show a key as `<kbd>`, the `description` slot for `<kbd>` or
@@ -194,9 +202,59 @@ description and wraps.
194
202
 
195
203
  `site.css` also includes page helpers: `.fg-site-steps` for an ordered list with
196
204
  numbered circles, `.fg-site-facts` for an inline `dl` of short label and value
197
- pairs, `.fg-site-definitions` for a two-column `dl` of `dt` and `dd` that stacks
198
- on phones, `.fg-site-sr-only` for visually hidden text, and `.fg-site-skip` for
199
- a skip link that appears on focus.
205
+ pairs whose links get a `--control-lg` hit area at 640px and below,
206
+ `.fg-site-definitions` for a two-column `dl` of `dt` and `dd` that stacks on
207
+ phones and keeps `code` such as `--all` on one line, `.fg-site-sr-only` for
208
+ visually hidden text, and `.fg-site-skip` for a skip link that appears on focus.
209
+
210
+ ### Docs shell
211
+
212
+ Add `fg-site-docs` to the element that wraps a project page's header, rail,
213
+ main content, and footer. The shell then supplies:
214
+
215
+ - the frame: the header across the top, the `--sidebar` rail beside `main`, and
216
+ the footer on the frame edges. At 800px and below the rail stacks above `main`.
217
+ - element defaults inside the shell, plus the page background and font on
218
+ `body`: box sizing, heading sizes and margins, paragraph measure, links,
219
+ inline `code` at `--text-sm`, `kbd`, a 2px focus outline, and a `[hidden]`
220
+ rule that outranks the page's own `display` rules.
221
+ - `.fg-site-footer` for the footer and `.fg-site-legend` for a `dl` that explains
222
+ what a capture shows. Legend columns are at least `--site-legend-column` wide,
223
+ `--sidebar` by default; set it on the legend to fit more entries in a row.
224
+
225
+ ```vue
226
+ <template>
227
+ <div class="fg-site-docs">
228
+ <UiSiteHeader project="devps" />
229
+ <UiSiteNav label="On this page" :groups="navigation" />
230
+ <main id="main">
231
+ <section aria-labelledby="title">…</section>
232
+ <dl class="fg-site-legend">
233
+ <div><dt>PORTS</dt><dd>The ports the server listens on.</dd></div>
234
+ </dl>
235
+ </main>
236
+ <footer class="fg-site-footer">
237
+ <a href="https://github.com/filipgutica">Built by Filip Gutica</a>
238
+ <nav aria-label="Project links">
239
+ <a href="https://github.com/filipgutica/devps/releases">Releases</a>
240
+ </nav>
241
+ </footer>
242
+ </div>
243
+ </template>
244
+ ```
245
+
246
+ The header, rail, `main`, and footer must be direct children of the shell. The
247
+ element defaults sit in the `base` cascade layer, so library components and the
248
+ site's own rules win. That holds only when the site declares the layer order
249
+ before importing `site.css`: `@import "tailwindcss"` does this, or start the
250
+ stylesheet with `@layer base, components, utilities;`. To adopt the shell, delete
251
+ the site's own frame, element base, `kbd`, footer, legend, and ANSI palette
252
+ rules. Without `fg-site-docs`, the frame, element defaults, and phone header
253
+ rules do not apply. These apply on every page: the `fg-site-footer` and
254
+ `fg-site-legend` classes wherever a page uses them; the capture terminal palette
255
+ for every `UiCapture`, in the `components` layer, so a site's own unlayered
256
+ `.term` and ANSI rules and its `--t*` values still win; and `UiSiteNav`, whose
257
+ phone toggle and drawer title now show its `label`.
200
258
 
201
259
  ## Development
202
260
 
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { a as e, i as t, n, o as r, r as i, t as a } from "./use-active-section-qEJQS1S9.js";
1
+ import { a as e, i as t, n, o as r, r as i, t as a } from "./use-active-section-DCS_wyYq.js";
2
2
  import { Fragment as o, computed as s, createBlock as c, createCommentVNode as l, createElementBlock as u, createElementVNode as d, createTextVNode as f, createVNode as p, defineComponent as m, inject as h, mergeModels as g, mergeProps as _, normalizeStyle as v, openBlock as y, provide as b, ref as ee, renderList as x, renderSlot as S, resolveDynamicComponent as C, shallowRef as te, toDisplayString as w, unref as T, useId as E, useModel as ne, useSlots as D, watch as re, withCtx as O } from "vue";
3
3
  import { CheckboxIndicator as k, CheckboxRoot as A, Primitive as j, ProgressIndicator as M, ProgressRoot as N, RadioGroupItem as P, RadioGroupRoot as F, SliderRange as I, SliderRoot as L, SliderThumb as R, SliderTrack as z, Toggle as B } from "reka-ui";
4
4
  //#region src/components/UiCard.vue?vue&type=script&setup=true&lang.ts
package/dist/site.js CHANGED
@@ -1,4 +1,4 @@
1
- import { a as e, i as t, n, o as r, r as i, t as a } from "./use-active-section-qEJQS1S9.js";
1
+ import { a as e, i as t, n, o as r, r as i, t as a } from "./use-active-section-DCS_wyYq.js";
2
2
  import { Fragment as o, computed as s, createBlock as c, createCommentVNode as l, createElementBlock as u, createElementVNode as d, createTextVNode as f, createVNode as p, defineComponent as m, mergeModels as h, nextTick as g, normalizeClass as _, normalizeStyle as v, onBeforeUnmount as y, onMounted as b, onUnmounted as x, openBlock as S, ref as C, renderList as w, renderSlot as T, shallowRef as E, toDisplayString as D, unref as O, useId as k, useModel as ee, useSlots as A, useTemplateRef as te, watch as j, withCtx as M } from "vue";
3
3
  import { tryOnMounted as N, usePreferredDark as P, useStorage as F } from "@vueuse/core";
4
4
  //#region src/site/appearance.ts
@@ -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",
@@ -154,7 +154,7 @@ var N = /* @__PURE__ */ c({
154
154
  _: 3
155
155
  }, 8, ["open"]));
156
156
  }
157
- }), H = { class: "fg-tabs" }, U = ["aria-label"], W = [
157
+ }), H = { class: "fg-tabs" }, U = ["aria-label", "data-clipped"], W = [
158
158
  "id",
159
159
  "aria-controls",
160
160
  "aria-selected",
@@ -180,16 +180,34 @@ var N = /* @__PURE__ */ c({
180
180
  u(() => {
181
181
  l.value = !0;
182
182
  });
183
- let C = b("list");
184
- x([l, d], () => {
183
+ let C = b("list"), w = () => {
185
184
  let e = C.value, t = e?.querySelector("[aria-selected=\"true\"]");
186
185
  if (!e || !t) return;
187
- let n = e.getBoundingClientRect(), { left: r, right: i } = t.getBoundingClientRect();
188
- r < n.left ? e.scrollLeft -= n.left - r : i > n.right && (e.scrollLeft += i - n.right);
189
- }, { flush: "post" });
190
- let w = (e) => {
186
+ let n = e.getBoundingClientRect(), r = getComputedStyle(e), i = n.left + (Number.parseFloat(r.scrollPaddingLeft) || 0), a = n.right - (Number.parseFloat(r.scrollPaddingRight) || 0), { left: o, right: s } = t.getBoundingClientRect();
187
+ o < i ? e.scrollLeft -= i - o : s > a && (e.scrollLeft += s - a);
188
+ }, T = m(), E = () => {
189
+ let e = C.value;
190
+ if (!e) return;
191
+ let t = e.scrollWidth - e.clientWidth, n = getComputedStyle(e).direction === "rtl" ? e.scrollLeft + t : e.scrollLeft, r = n > 1, i = n < t - 1;
192
+ T.value = r && i ? "both" : r ? "left" : i ? "right" : void 0;
193
+ }, D = () => {
194
+ w(), E();
195
+ };
196
+ x([
197
+ l,
198
+ d,
199
+ () => n.items
200
+ ], D, {
201
+ flush: "post",
202
+ deep: !0
203
+ }), x(C, (e, t, n) => {
204
+ if (!e || typeof ResizeObserver > "u") return;
205
+ let r = new ResizeObserver(D);
206
+ r.observe(e), n(() => r.disconnect());
207
+ });
208
+ let O = (e) => {
191
209
  e !== d.value && s("update:modelValue", e);
192
- }, T = (e) => {
210
+ }, k = (e) => {
193
211
  if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey || ![
194
212
  "ArrowLeft",
195
213
  "ArrowRight",
@@ -213,7 +231,9 @@ var N = /* @__PURE__ */ c({
213
231
  class: "fg-tabs__list",
214
232
  role: "tablist",
215
233
  "aria-label": n.label,
216
- onKeydown: T
234
+ "data-clipped": T.value,
235
+ onKeydown: k,
236
+ onScrollPassive: E
217
237
  }, [(f(!0), i(e, null, h(n.items, (e) => (f(), i("button", {
218
238
  id: p(e.value),
219
239
  key: e.value,
@@ -223,8 +243,8 @@ var N = /* @__PURE__ */ c({
223
243
  "aria-controls": v(e.value),
224
244
  "aria-selected": e.value === d.value,
225
245
  tabindex: e.value === d.value ? 0 : -1,
226
- onClick: (t) => w(e.value),
227
- onFocus: (t) => w(e.value)
246
+ onClick: (t) => O(e.value),
247
+ onFocus: (t) => O(e.value)
228
248
  }, _(e.label), 41, W))), 128))], 40, U)) : r("", !0), (f(!0), i(e, null, h(n.items, (e) => (f(), i("section", {
229
249
  id: v(e.value),
230
250
  key: e.value,
@@ -248,17 +268,35 @@ var N = /* @__PURE__ */ c({
248
268
  id: e,
249
269
  element: t
250
270
  }] : [];
251
- }), o = () => {
252
- n = void 0;
253
- let e = document.documentElement, r = Number.parseFloat(getComputedStyle(e).scrollPaddingTop) || 0, i = a[0]?.id;
254
- for (let e of a) e.element.getBoundingClientRect().top <= r + 1 && (i = e.id);
255
- window.scrollY > 0 && window.scrollY + window.innerHeight >= e.scrollHeight - 2 && (i = a.at(-1)?.id), t.value = i;
256
- }, s = () => {
257
- n === void 0 && (n = requestAnimationFrame(o));
271
+ }), o = document.documentElement, s = () => Number.parseFloat(getComputedStyle(o).scrollPaddingTop) || 0, c, l = () => {
272
+ if (n = void 0, c) {
273
+ let e = Math.abs(window.scrollY - c.landing);
274
+ e > c.closest + 2 ? c = void 0 : c.closest = Math.min(c.closest, e);
275
+ }
276
+ let e = s(), r = a[0]?.id;
277
+ for (let t of a) t.element.getBoundingClientRect().top <= e + 1 && (r = t.id);
278
+ window.scrollY > 0 && window.scrollY + window.innerHeight >= o.scrollHeight - 2 && (r = a.at(-1)?.id), t.value = c?.id ?? r;
279
+ }, u = () => {
280
+ n === void 0 && (n = requestAnimationFrame(l));
281
+ }, d = (e) => {
282
+ let t = a.find((t) => t.id === e);
283
+ if (c = void 0, t) {
284
+ let n = Math.max(0, o.scrollHeight - window.innerHeight), r = window.scrollY + t.element.getBoundingClientRect().top - s(), i = Math.min(Math.max(r, 0), n);
285
+ c = {
286
+ id: e,
287
+ landing: i,
288
+ closest: Math.abs(window.scrollY - i)
289
+ };
290
+ }
291
+ u();
292
+ }, f = () => d(location.hash.slice(1)), p = (e) => {
293
+ if (e.defaultPrevented || e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;
294
+ let t = e.target instanceof Element ? e.target.closest("a[href^=\"#\"]")?.getAttribute("href") : void 0;
295
+ t && d(t.slice(1));
258
296
  };
259
- window.addEventListener("scroll", s, { passive: !0 }), window.addEventListener("resize", s), window.addEventListener("pageshow", s), document.body.addEventListener("transitionend", s), typeof ResizeObserver < "u" && (r = new ResizeObserver(s), r.observe(document.body)), i = () => {
260
- window.removeEventListener("scroll", s), window.removeEventListener("resize", s), window.removeEventListener("pageshow", s), document.body.removeEventListener("transitionend", s);
261
- }, o();
297
+ window.addEventListener("scroll", u, { passive: !0 }), window.addEventListener("resize", u), window.addEventListener("pageshow", u), window.addEventListener("hashchange", f), document.addEventListener("click", p), document.body.addEventListener("transitionend", u), typeof ResizeObserver < "u" && (r = new ResizeObserver(u), r.observe(document.body)), i = () => {
298
+ window.removeEventListener("scroll", u), window.removeEventListener("resize", u), window.removeEventListener("pageshow", u), window.removeEventListener("hashchange", f), document.removeEventListener("click", p), document.body.removeEventListener("transitionend", u);
299
+ }, location.hash && f(), l();
262
300
  }), d(() => {
263
301
  i?.(), r?.disconnect(), n !== void 0 && cancelAnimationFrame(n);
264
302
  }), p(t);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@filipgutica/ui",
3
- "version": "1.8.1",
3
+ "version": "1.10.0",
4
4
  "description": "A compact Vue design system with Tailwind CSS and VS Code theme support",
5
5
  "type": "module",
6
6
  "sideEffects": [
@@ -12,10 +12,19 @@
12
12
  width: max-content;
13
13
  max-width: 100%;
14
14
  overflow-x: auto;
15
+ scroll-padding-inline: var(--space-6);
15
16
  margin-block-end: var(--space-4);
16
17
  border-bottom: 1px solid var(--color-border-subtle);
17
18
  }
18
19
 
20
+ /* A strip that scrolls fades each clipped edge; the scroll padding keeps a revealed tab clear
21
+ * of the fade. Mask stops are alpha only: #000 keeps a pixel and transparent hides it. */
22
+ .fg-tabs__list[data-clipped="right"] { mask-image: linear-gradient(to right, #000 calc(100% - var(--space-6)), transparent); }
23
+ .fg-tabs__list[data-clipped="left"] { mask-image: linear-gradient(to left, #000 calc(100% - var(--space-6)), transparent); }
24
+ .fg-tabs__list[data-clipped="both"] {
25
+ mask-image: linear-gradient(to right, transparent, #000 var(--space-6), #000 calc(100% - var(--space-6)), transparent);
26
+ }
27
+
19
28
  .fg-tabs__tab {
20
29
  flex: none;
21
30
  min-height: var(--control-md);
@@ -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,120 @@
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
+ /* Tab panels and other enhanced markup use the hidden attribute, so it outranks the page's own
31
+ * display rules. until-found keeps the browser's find-in-page behavior. */
32
+ .fg-site-docs :where([hidden]:not([hidden="until-found"])) { display: none !important; }
33
+ .fg-site-docs :where(code, kbd, pre) { font-family: var(--font-mono); }
34
+ /* Monospace looks larger than the UI face, so inline code steps down: --text-sm in --text-md text.
35
+ * Code inside pre keeps the block's own size. */
36
+ .fg-site-docs :where(:not(pre) > code) { font-size: var(--text-sm); }
37
+ .fg-site-docs :where(a) { color: var(--color-link); text-underline-offset: 0.18em; }
38
+ .fg-site-docs :where(kbd) {
39
+ display: inline-block;
40
+ padding: 0 var(--space-1);
41
+ border: 1px solid var(--color-border);
42
+ border-bottom-width: 2px;
43
+ border-radius: var(--radius-inline);
44
+ background: var(--color-surface);
45
+ font-size: var(--text-xs);
46
+ white-space: nowrap;
47
+ }
48
+ .fg-site-docs :where(:focus-visible) { outline: 2px solid var(--color-focus); outline-offset: 2px; }
49
+ }
50
+
4
51
  @layer components {
52
+ /* Docs shell: header across the top, the --sidebar rail beside main, and the footer
53
+ * on the frame edges. Header, rail, main, and footer are direct children. */
54
+ .fg-site-docs {
55
+ display: grid;
56
+ grid-template-columns: var(--sidebar) minmax(0, 1fr);
57
+ column-gap: var(--space-8);
58
+ max-width: calc(var(--page-max) + var(--sidebar) + var(--space-8) + var(--space-12));
59
+ margin-inline: auto;
60
+ padding-inline: var(--space-6);
61
+ }
62
+ .fg-site-docs > .fg-site-header {
63
+ --site-frame: 100%;
64
+ --site-gutter: 0px;
65
+ grid-column: 1 / -1;
66
+ min-width: 0;
67
+ }
68
+ .fg-site-docs > main {
69
+ display: grid;
70
+ grid-template-columns: minmax(0, 1fr);
71
+ gap: var(--space-12);
72
+ min-width: 0;
73
+ padding-block: var(--space-8) var(--space-16);
74
+ }
75
+ .fg-site-docs > .fg-site-footer { grid-column: 1 / -1; }
76
+
77
+ /* Footer: an attribution link at the start, project links in a nav at the end. */
78
+ .fg-site-footer {
79
+ display: flex;
80
+ flex-wrap: wrap;
81
+ align-items: center;
82
+ justify-content: space-between;
83
+ gap: 0 var(--space-5);
84
+ padding-block: var(--space-6) var(--space-8);
85
+ border-top: 1px solid var(--color-border-subtle);
86
+ color: var(--color-muted);
87
+ font-size: var(--text-sm);
88
+ }
89
+ .fg-site-footer nav { display: flex; flex-wrap: wrap; gap: 0 var(--space-5); }
90
+ .fg-site-footer a {
91
+ display: inline-flex;
92
+ min-height: var(--control-lg);
93
+ align-items: center;
94
+ color: var(--color-muted);
95
+ text-decoration: none;
96
+ }
97
+ .fg-site-footer a:hover { color: var(--color-text); text-decoration: underline; }
98
+
99
+ /* Legend: a dl of what a capture shows, with one div per dt and dd pair. Columns are at
100
+ * least --site-legend-column wide (default --sidebar); a page can lower it to fit more. */
101
+ .fg-site-legend {
102
+ display: grid;
103
+ grid-template-columns: repeat(auto-fit, minmax(min(var(--site-legend-column, var(--sidebar)), 100%), 1fr));
104
+ gap: 0 var(--space-6);
105
+ margin: 0;
106
+ font-size: var(--text-sm);
107
+ }
108
+ .fg-site-legend > div {
109
+ display: grid;
110
+ align-content: start;
111
+ gap: var(--space-1);
112
+ padding-block: var(--space-3);
113
+ border-top: 1px solid var(--color-border-subtle);
114
+ }
115
+ .fg-site-legend dt { font-family: var(--font-mono); font-weight: var(--weight-medium); }
116
+ .fg-site-legend dd { margin: 0; color: var(--color-muted); }
117
+
5
118
  /* A centered frame. The header uses the same edges as the page below it.
6
119
  * Set --site-frame on the header or a frame to change its width. */
7
120
  .fg-site-frame,
@@ -138,8 +251,8 @@
138
251
  .fg-site-nav a:focus-visible { outline: 2px solid var(--color-focus); outline-offset: -2px; }
139
252
 
140
253
  /* 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. */
254
+ * beside the article. At 800px and below it becomes a disclosure named by the
255
+ * label prop, replaced by a drawer once the page is interactive. */
143
256
  .fg-site-rail {
144
257
  position: sticky;
145
258
  top: var(--space-6);
@@ -168,9 +281,9 @@
168
281
  .fg-site-rail__group-label { margin-block-end: var(--space-1); }
169
282
 
170
283
  /* 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. */
284
+ * command and caption as the footer. The screen uses a dark terminal palette by
285
+ * default. A site can set --site-capture-bg and --t* values on :root, which also
286
+ * reach the full-size viewer. Nothing clips the panel, so focus rings inside it stay whole. */
174
287
  .fg-capture {
175
288
  position: relative;
176
289
  min-width: 0;
@@ -220,7 +333,59 @@
220
333
  overflow-x: auto;
221
334
  background: var(--site-capture-bg, var(--color-code));
222
335
  }
336
+ /* Terminal markup gets the dark default; a plain <pre> keeps the code background its text expects. */
337
+ .fg-capture__screen:has(> .term) { background: var(--site-capture-bg, var(--t-bg, #131316)); }
223
338
  .fg-capture__screen > * { width: max-content; margin: 0; }
339
+ /* Terminal colors for checked-in capture markup: .term and the ANSI classes the capture
340
+ * converter emits. Each color reads the site's --t* value first, so a site palette on
341
+ * :root still wins, and these defaults apply only where a site sets none. */
342
+ .fg-capture__screen .term {
343
+ padding: var(--space-4);
344
+ color: var(--t-fg, #e6e6ea);
345
+ font: var(--weight-regular) var(--text-sm) / 1 var(--font-mono);
346
+ font-variant-ligatures: none;
347
+ tab-size: 8;
348
+ }
349
+ .fg-capture__screen .term i { display: inline-block; width: 1ch; overflow: visible; font-style: normal; }
350
+ .fg-capture__screen .tf0 { color: var(--t0, #2b2b31); }
351
+ .fg-capture__screen .tf1 { color: var(--t1, #f0776c); }
352
+ .fg-capture__screen .tf2 { color: var(--t2, #4cc38a); }
353
+ .fg-capture__screen .tf3 { color: var(--t3, #e2a54b); }
354
+ .fg-capture__screen .tf4 { color: var(--t4, #7d97ff); }
355
+ .fg-capture__screen .tf5 { color: var(--t5, #c792ea); }
356
+ .fg-capture__screen .tf6 { color: var(--t6, #5fd3d9); }
357
+ .fg-capture__screen .tf7 { color: var(--t7, #c8c8d0); }
358
+ .fg-capture__screen .tf8 { color: var(--t8, #a0a0ab); }
359
+ .fg-capture__screen .tf9 { color: var(--t9, #ff8f85); }
360
+ .fg-capture__screen .tf10 { color: var(--t10, #6fd3a0); }
361
+ .fg-capture__screen .tf11 { color: var(--t11, #e8b56a); }
362
+ .fg-capture__screen .tf12 { color: var(--t12, #7d97ff); }
363
+ .fg-capture__screen .tf13 { color: var(--t13, #d9a8f0); }
364
+ .fg-capture__screen .tf14 { color: var(--t14, #8ee3e8); }
365
+ .fg-capture__screen .tf15 { color: var(--t15, #ffffff); }
366
+ .fg-capture__screen .tb0 { background: var(--t0, #2b2b31); }
367
+ .fg-capture__screen .tb1 { background: var(--t1, #f0776c); }
368
+ .fg-capture__screen .tb2 { background: var(--t2, #4cc38a); }
369
+ .fg-capture__screen .tb3 { background: var(--t3, #e2a54b); }
370
+ .fg-capture__screen .tb4 { background: var(--t4, #7d97ff); }
371
+ .fg-capture__screen .tb5 { background: var(--t5, #c792ea); }
372
+ .fg-capture__screen .tb6 { background: var(--t6, #5fd3d9); }
373
+ .fg-capture__screen .tb7 { background: var(--t7, #c8c8d0); }
374
+ .fg-capture__screen .tb8 { background: var(--t8, #a0a0ab); }
375
+ .fg-capture__screen .tb9 { background: var(--t9, #ff8f85); }
376
+ .fg-capture__screen .tb10 { background: var(--t10, #6fd3a0); }
377
+ .fg-capture__screen .tb11 { background: var(--t11, #e8b56a); }
378
+ .fg-capture__screen .tb12 { background: var(--t12, #7d97ff); }
379
+ .fg-capture__screen .tb13 { background: var(--t13, #d9a8f0); }
380
+ .fg-capture__screen .tb14 { background: var(--t14, #8ee3e8); }
381
+ .fg-capture__screen .tb15 { background: var(--t15, #ffffff); }
382
+ .fg-capture__screen .tf-dim { color: var(--t-dim, #a0a0ab); }
383
+ .fg-capture__screen .tf-bg { color: var(--t-bg, #131316); }
384
+ .fg-capture__screen .tb-fg { background: var(--t-fg, #e6e6ea); }
385
+ .fg-capture__screen .tbold { font-weight: var(--weight-semibold); }
386
+ .fg-capture__screen .tital { font-style: italic; }
387
+ .fg-capture__screen .tul { text-decoration: underline; text-underline-offset: 0.18em; }
388
+
224
389
  .fg-capture__screen[data-fit] { overflow: hidden; }
225
390
  .fg-capture__screen[data-fit] > * { transform: scale(var(--capture-scale, 1)); transform-origin: top left; }
226
391
  .fg-capture__expand {
@@ -355,6 +520,8 @@
355
520
  }
356
521
  .fg-site-definitions dt { font-weight: var(--weight-medium); }
357
522
  .fg-site-definitions dd { margin: 0; color: var(--color-muted); }
523
+ /* Keep flags such as --all whole instead of breaking after a hyphen. */
524
+ .fg-site-definitions code { white-space: nowrap; }
358
525
 
359
526
  .fg-site-sr-only {
360
527
  position: absolute;
@@ -407,6 +574,8 @@
407
574
  }
408
575
  /* Keep in sync with MOBILE_QUERY in UiSiteNav.vue. */
409
576
  @media (max-width: 800px) {
577
+ .fg-site-docs { grid-template-columns: minmax(0, 1fr); }
578
+ .fg-site-docs > main { gap: var(--space-8); padding-top: var(--space-6); }
410
579
  .fg-site-rail {
411
580
  position: static;
412
581
  max-height: none;
@@ -432,8 +601,30 @@
432
601
  .fg-site-rail__menu .fg-site-rail__group { grid-template-columns: repeat(2, minmax(0, 1fr)); }
433
602
  .fg-site-rail__menu .fg-site-rail__group-label { grid-column: 1 / -1; }
434
603
  .fg-site-rail__nav a { min-height: var(--control-lg); }
604
+ /* The drawer title already shows the label; the heading still names the nav. */
605
+ .fg-drawer__content .fg-site-rail__label {
606
+ position: absolute;
607
+ width: 1px;
608
+ height: 1px;
609
+ overflow: hidden;
610
+ clip-path: inset(50%);
611
+ white-space: nowrap;
612
+ }
435
613
  }
436
614
  @media (max-width: 640px) {
615
+ .fg-site-docs { padding-inline: var(--space-4); }
616
+ /* Docs-shell pages keep one header row on phones and repeat these links in their footer. */
617
+ .fg-site-docs .fg-site-header__links { display: none; }
618
+ /* Fact links, such as a Source link that stands in for the hidden header link, get a
619
+ * touch-sized hit area. The negative margin keeps each fact row one line tall, and the
620
+ * row gap of a list with links makes room for the part of the hit area past the line. */
621
+ .fg-site-facts:has(a) { row-gap: calc(var(--control-lg) - 1lh); }
622
+ .fg-site-facts a {
623
+ display: inline-flex;
624
+ min-height: var(--control-lg);
625
+ align-items: center;
626
+ margin-block: calc((1lh - var(--control-lg)) / 2);
627
+ }
437
628
  .fg-dialog__content.fg-capture-viewer { width: 100%; max-width: none; }
438
629
  /* Phones keep the icon; the accessible name still says Full size. */
439
630
  .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;
@@ -106,6 +106,6 @@
106
106
  --color-warning-surface: color-mix(in srgb, var(--color-warning) 12%, var(--color-bg));
107
107
  --color-success-surface: color-mix(in srgb, var(--color-success) 11%, var(--color-bg));
108
108
  --color-info-surface: color-mix(in srgb, var(--color-info) 10%, var(--color-bg));
109
- --shadow-dialog: 0 8px 24px rgb(0 0 0 / 45%), 0 0 0 1px var(--color-border);
109
+ --shadow-dialog: 0 8px 24px rgb(0 0 0 / 45%), 0 0 0 1px var(--color-border-subtle);
110
110
  --color-status-text: var(--color-text);
111
111
  }