@filipgutica/ui 1.8.0 → 1.8.1

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
@@ -18,6 +18,7 @@ Import the styles from your application stylesheet:
18
18
  ```css
19
19
  @import "tailwindcss";
20
20
  @import "@filipgutica/ui/theme.css";
21
+ @import "@filipgutica/ui/themes/graphite-indigo.css";
21
22
  @import "@filipgutica/ui/components.css";
22
23
  ```
23
24
 
@@ -93,13 +94,10 @@ The gallery's search-and-install dialog is docs-only, not an exported component.
93
94
  Applications can build their own picker with the exported components and theme
94
95
  helpers. `UiSiteHeader` includes the built-in System, Light, and Dark control.
95
96
 
96
- The default theme is unchanged. To opt in to Graphite Indigo, import it after
97
- `theme.css`:
98
-
99
- ```css
100
- @import "@filipgutica/ui/theme.css";
101
- @import "@filipgutica/ui/themes/graphite-indigo.css";
102
- ```
97
+ The install steps above import Graphite Indigo, the palette of the project
98
+ sites, after `theme.css`. To keep the base theme instead, with blue actions and
99
+ warm greys, leave out the `themes/graphite-indigo.css` import. Graphite Indigo
100
+ stays opt-in: `theme.css` alone still gives the base theme.
103
101
 
104
102
  ## Project sites
105
103
 
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-CiflgGBI.js";
1
+ import { a as e, i as t, n, o as r, r as i, t as a } from "./use-active-section-qEJQS1S9.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-CiflgGBI.js";
1
+ import { a as e, i as t, n, o as r, r as i, t as a } from "./use-active-section-qEJQS1S9.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
@@ -1,9 +1,9 @@
1
- import { Fragment as e, computed as t, createBlock as n, createCommentVNode as r, createElementBlock as i, createElementVNode as a, createTextVNode as o, createVNode as s, defineComponent as c, mergeProps as l, onMounted as u, onUnmounted as d, openBlock as f, readonly as p, ref as m, renderList as h, renderSlot as g, toDisplayString as _, unref as v, useId as y, withCtx as b } from "vue";
2
- import { DialogClose as x, DialogContent as S, DialogDescription as C, DialogOverlay as w, DialogPortal as T, DialogRoot as E, DialogTitle as D, DialogTrigger as O, Primitive as k } from "reka-ui";
3
- import { useTimeoutFn as A } from "@vueuse/core";
1
+ import { Fragment as e, computed as t, createBlock as n, createCommentVNode as r, createElementBlock as i, createElementVNode as a, createTextVNode as o, createVNode as s, defineComponent as c, mergeProps as l, onMounted as u, onUnmounted as d, openBlock as f, readonly as p, ref as m, renderList as h, renderSlot as g, toDisplayString as _, unref as v, useId as y, useTemplateRef as b, watch as x, withCtx as S } from "vue";
2
+ import { DialogClose as C, DialogContent as w, DialogDescription as T, DialogOverlay as E, DialogPortal as D, DialogRoot as O, DialogTitle as k, DialogTrigger as A, Primitive as j } from "reka-ui";
3
+ import { useTimeoutFn as M } from "@vueuse/core";
4
4
  //#endregion
5
5
  //#region src/components/UiButton.vue
6
- var j = /* @__PURE__ */ c({
6
+ var N = /* @__PURE__ */ c({
7
7
  __name: "UiButton",
8
8
  props: {
9
9
  disabled: {
@@ -19,7 +19,7 @@ var j = /* @__PURE__ */ c({
19
19
  variant: { default: "primary" }
20
20
  },
21
21
  setup(e) {
22
- return (t, r) => (f(), n(v(k), {
22
+ return (t, r) => (f(), n(v(j), {
23
23
  as: "button",
24
24
  class: "fg-button",
25
25
  "data-size": e.size,
@@ -28,7 +28,7 @@ var j = /* @__PURE__ */ c({
28
28
  disabled: e.disabled || e.loading,
29
29
  type: e.type
30
30
  }, {
31
- default: b(() => [g(t.$slots, "default")]),
31
+ default: S(() => [g(t.$slots, "default")]),
32
32
  _: 3
33
33
  }, 8, [
34
34
  "data-size",
@@ -38,8 +38,8 @@ var j = /* @__PURE__ */ c({
38
38
  "type"
39
39
  ]));
40
40
  }
41
- }), M = ({ unavailableMessage: e, failureMessage: t }) => {
42
- let n = m(!1), r = m(!1), i = m(""), { start: a } = A(() => {
41
+ }), P = ({ unavailableMessage: e, failureMessage: t }) => {
42
+ let n = m(!1), r = m(!1), i = m(""), { start: a } = M(() => {
43
43
  n.value = !1;
44
44
  }, 1500, { immediate: !1 });
45
45
  return {
@@ -61,10 +61,10 @@ var j = /* @__PURE__ */ c({
61
61
  }
62
62
  }
63
63
  };
64
- }, N = { class: "fg-dialog__header" }, P = { class: "fg-dialog__body" }, F = {
64
+ }, F = { class: "fg-dialog__header" }, I = { class: "fg-dialog__body" }, L = {
65
65
  key: 0,
66
66
  class: "fg-dialog__footer"
67
- }, I = /* @__PURE__ */ c({
67
+ }, R = /* @__PURE__ */ c({
68
68
  inheritAttrs: !1,
69
69
  __name: "UiDialog",
70
70
  props: {
@@ -75,34 +75,34 @@ var j = /* @__PURE__ */ c({
75
75
  emits: ["update:open"],
76
76
  setup(e, { emit: t }) {
77
77
  let c = t;
78
- return (t, u) => (f(), n(v(E), {
78
+ return (t, u) => (f(), n(v(O), {
79
79
  open: e.open,
80
80
  "onUpdate:open": u[0] ||= (e) => c("update:open", e)
81
81
  }, {
82
- default: b(() => [s(v(T), null, {
83
- default: b(() => [s(v(w), { class: "fg-dialog__overlay" }), s(v(S), l({
82
+ default: S(() => [s(v(D), null, {
83
+ default: S(() => [s(v(E), { class: "fg-dialog__overlay" }), s(v(w), l({
84
84
  class: "fg-dialog__content",
85
85
  "aria-modal": "true"
86
86
  }, t.$attrs), {
87
- default: b(() => [
88
- a("div", N, [a("div", null, [s(v(D), { class: "fg-dialog__title" }, {
89
- default: b(() => [o(_(e.title), 1)]),
87
+ default: S(() => [
88
+ a("div", F, [a("div", null, [s(v(k), { class: "fg-dialog__title" }, {
89
+ default: S(() => [o(_(e.title), 1)]),
90
90
  _: 1
91
- }), e.description ? (f(), n(v(C), {
91
+ }), e.description ? (f(), n(v(T), {
92
92
  key: 0,
93
93
  class: "fg-dialog__description"
94
94
  }, {
95
- default: b(() => [o(_(e.description), 1)]),
95
+ default: S(() => [o(_(e.description), 1)]),
96
96
  _: 1
97
- })) : r("", !0)]), s(v(x), {
97
+ })) : r("", !0)]), s(v(C), {
98
98
  class: "fg-dialog__close",
99
99
  "aria-label": "Close dialog"
100
100
  }, {
101
- default: b(() => [...u[1] ||= [o(" × ", -1)]]),
101
+ default: S(() => [...u[1] ||= [o(" × ", -1)]]),
102
102
  _: 1
103
103
  })]),
104
- a("div", P, [g(t.$slots, "default")]),
105
- t.$slots.footer ? (f(), i("div", F, [g(t.$slots, "footer")])) : r("", !0)
104
+ a("div", I, [g(t.$slots, "default")]),
105
+ t.$slots.footer ? (f(), i("div", L, [g(t.$slots, "footer")])) : r("", !0)
106
106
  ]),
107
107
  _: 3
108
108
  }, 16)]),
@@ -111,7 +111,7 @@ var j = /* @__PURE__ */ c({
111
111
  _: 3
112
112
  }, 8, ["open"]));
113
113
  }
114
- }), L = { class: "fg-drawer__header" }, R = { class: "fg-drawer__body" }, z = /* @__PURE__ */ c({
114
+ }), z = { class: "fg-drawer__header" }, B = { class: "fg-drawer__body" }, V = /* @__PURE__ */ c({
115
115
  inheritAttrs: !1,
116
116
  __name: "UiDrawer",
117
117
  props: {
@@ -121,32 +121,32 @@ var j = /* @__PURE__ */ c({
121
121
  emits: ["update:open", "closeAutoFocus"],
122
122
  setup(e, { emit: t }) {
123
123
  let i = t;
124
- return (t, c) => (f(), n(v(E), {
124
+ return (t, c) => (f(), n(v(O), {
125
125
  open: e.open,
126
126
  "onUpdate:open": c[1] ||= (e) => i("update:open", e)
127
127
  }, {
128
- default: b(() => [t.$slots.trigger ? (f(), n(v(O), {
128
+ default: S(() => [t.$slots.trigger ? (f(), n(v(A), {
129
129
  key: 0,
130
130
  "as-child": ""
131
131
  }, {
132
- default: b(() => [g(t.$slots, "trigger")]),
132
+ default: S(() => [g(t.$slots, "trigger")]),
133
133
  _: 3
134
- })) : r("", !0), s(v(T), null, {
135
- default: b(() => [s(v(w), { class: "fg-drawer__overlay" }), s(v(S), l({
134
+ })) : r("", !0), s(v(D), null, {
135
+ default: S(() => [s(v(E), { class: "fg-drawer__overlay" }), s(v(w), l({
136
136
  class: "fg-drawer__content",
137
137
  "aria-modal": "true",
138
138
  "aria-describedby": void 0
139
139
  }, t.$attrs, { onCloseAutoFocus: c[0] ||= (e) => i("closeAutoFocus", e) }), {
140
- default: b(() => [a("div", L, [s(v(D), { class: "fg-drawer__title" }, {
141
- default: b(() => [o(_(e.title), 1)]),
140
+ default: S(() => [a("div", z, [s(v(k), { class: "fg-drawer__title" }, {
141
+ default: S(() => [o(_(e.title), 1)]),
142
142
  _: 1
143
- }), s(v(x), {
143
+ }), s(v(C), {
144
144
  class: "fg-drawer__close",
145
145
  "aria-label": "Close drawer"
146
146
  }, {
147
- default: b(() => [...c[2] ||= [o(" × ", -1)]]),
147
+ default: S(() => [...c[2] ||= [o(" × ", -1)]]),
148
148
  _: 1
149
- })]), a("div", R, [g(t.$slots, "default")])]),
149
+ })]), a("div", B, [g(t.$slots, "default")])]),
150
150
  _: 3
151
151
  }, 16)]),
152
152
  _: 3
@@ -154,20 +154,20 @@ var j = /* @__PURE__ */ c({
154
154
  _: 3
155
155
  }, 8, ["open"]));
156
156
  }
157
- }), B = { class: "fg-tabs" }, V = ["aria-label"], H = [
157
+ }), H = { class: "fg-tabs" }, U = ["aria-label"], W = [
158
158
  "id",
159
159
  "aria-controls",
160
160
  "aria-selected",
161
161
  "tabindex",
162
162
  "onClick",
163
163
  "onFocus"
164
- ], U = [
164
+ ], G = [
165
165
  "id",
166
166
  "role",
167
167
  "aria-labelledby",
168
168
  "hidden",
169
169
  "tabindex"
170
- ], W = ["id", "hidden"], G = /* @__PURE__ */ c({
170
+ ], K = ["id", "hidden"], q = /* @__PURE__ */ c({
171
171
  __name: "UiTabs",
172
172
  props: {
173
173
  items: {},
@@ -176,13 +176,20 @@ var j = /* @__PURE__ */ c({
176
176
  },
177
177
  emits: ["update:modelValue"],
178
178
  setup(n, { emit: o }) {
179
- let s = o, c = y(), l = m(!1), d = t(() => n.items.some((e) => e.value === n.modelValue) ? n.modelValue : n.items[0]?.value), p = (e) => `${c}-tab-${encodeURIComponent(e)}`, v = (e) => `${c}-panel-${encodeURIComponent(e)}`, b = (e) => `${c}-heading-${encodeURIComponent(e)}`;
179
+ let s = o, c = y(), l = m(!1), d = t(() => n.items.some((e) => e.value === n.modelValue) ? n.modelValue : n.items[0]?.value), p = (e) => `${c}-tab-${encodeURIComponent(e)}`, v = (e) => `${c}-panel-${encodeURIComponent(e)}`, S = (e) => `${c}-heading-${encodeURIComponent(e)}`;
180
180
  u(() => {
181
181
  l.value = !0;
182
182
  });
183
- let x = (e) => {
183
+ let C = b("list");
184
+ x([l, d], () => {
185
+ let e = C.value, t = e?.querySelector("[aria-selected=\"true\"]");
186
+ 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) => {
184
191
  e !== d.value && s("update:modelValue", e);
185
- }, S = (e) => {
192
+ }, T = (e) => {
186
193
  if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey || ![
187
194
  "ArrowLeft",
188
195
  "ArrowRight",
@@ -194,14 +201,19 @@ var j = /* @__PURE__ */ c({
194
201
  let r = [...t.querySelectorAll("[role=\"tab\"]")], i = r.indexOf(n);
195
202
  if (i < 0 || r.length === 0) return;
196
203
  let a = getComputedStyle(t).direction === "rtl", o = e.key === (a ? "ArrowRight" : "ArrowLeft"), s = r[e.key === "Home" ? 0 : e.key === "End" ? r.length - 1 : (i + (o ? -1 : 1) + r.length) % r.length];
197
- s && (e.preventDefault(), s.focus());
204
+ s && (e.preventDefault(), s.focus(), s.scrollIntoView({
205
+ block: "nearest",
206
+ inline: "nearest"
207
+ }));
198
208
  };
199
- return (t, o) => (f(), i("div", B, [l.value ? (f(), i("div", {
209
+ return (t, o) => (f(), i("div", H, [l.value ? (f(), i("div", {
200
210
  key: 0,
211
+ ref_key: "list",
212
+ ref: C,
201
213
  class: "fg-tabs__list",
202
214
  role: "tablist",
203
215
  "aria-label": n.label,
204
- onKeydown: S
216
+ onKeydown: T
205
217
  }, [(f(!0), i(e, null, h(n.items, (e) => (f(), i("button", {
206
218
  id: p(e.value),
207
219
  key: e.value,
@@ -211,23 +223,23 @@ var j = /* @__PURE__ */ c({
211
223
  "aria-controls": v(e.value),
212
224
  "aria-selected": e.value === d.value,
213
225
  tabindex: e.value === d.value ? 0 : -1,
214
- onClick: (t) => x(e.value),
215
- onFocus: (t) => x(e.value)
216
- }, _(e.label), 41, H))), 128))], 40, V)) : r("", !0), (f(!0), i(e, null, h(n.items, (e) => (f(), i("section", {
226
+ onClick: (t) => w(e.value),
227
+ onFocus: (t) => w(e.value)
228
+ }, _(e.label), 41, W))), 128))], 40, U)) : r("", !0), (f(!0), i(e, null, h(n.items, (e) => (f(), i("section", {
217
229
  id: v(e.value),
218
230
  key: e.value,
219
231
  class: "fg-tabs__panel",
220
232
  role: l.value ? "tabpanel" : void 0,
221
- "aria-labelledby": l.value ? p(e.value) : b(e.value),
233
+ "aria-labelledby": l.value ? p(e.value) : S(e.value),
222
234
  hidden: l.value && e.value !== d.value,
223
235
  tabindex: l.value ? 0 : void 0
224
236
  }, [a("h3", {
225
- id: b(e.value),
237
+ id: S(e.value),
226
238
  class: "fg-tabs__heading",
227
239
  hidden: l.value
228
- }, _(e.label), 9, W), g(t.$slots, "panel", { value: e.value })], 8, U))), 128))]));
240
+ }, _(e.label), 9, K), g(t.$slots, "panel", { value: e.value })], 8, G))), 128))]));
229
241
  }
230
- }), K = ({ targetIds: e }) => {
242
+ }), J = ({ targetIds: e }) => {
231
243
  let t = m(e[0]), n, r, i;
232
244
  return u(() => {
233
245
  let a = e.flatMap((e) => {
@@ -252,4 +264,4 @@ var j = /* @__PURE__ */ c({
252
264
  }), p(t);
253
265
  };
254
266
  //#endregion
255
- export { M as a, I as i, G as n, j as o, z as r, K as t };
267
+ export { P as a, R as i, q as n, N as o, V as r, J as t };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@filipgutica/ui",
3
- "version": "1.8.0",
3
+ "version": "1.8.1",
4
4
  "description": "A compact Vue design system with Tailwind CSS and VS Code theme support",
5
5
  "type": "module",
6
6
  "sideEffects": [
@@ -32,7 +32,7 @@
32
32
 
33
33
  .fg-tabs__tab:hover { color: var(--color-text); }
34
34
  .fg-tabs__tab[aria-selected="true"] {
35
- border-bottom-color: var(--color-link);
35
+ border-bottom-color: var(--color-accent);
36
36
  color: var(--color-text);
37
37
  }
38
38
 
@@ -44,10 +44,15 @@
44
44
  font-weight: var(--weight-medium);
45
45
  }
46
46
 
47
- .fg-tabs__tab:focus-visible, .fg-tabs__panel:focus-visible {
47
+ /* Inset: the tab list scrolls sideways, which would clip an outer ring. */
48
+ .fg-tabs__tab:focus-visible {
48
49
  outline: 2px solid var(--color-focus);
49
50
  outline-offset: -2px;
50
51
  }
52
+ .fg-tabs__panel:focus-visible {
53
+ outline: 2px solid var(--color-focus);
54
+ outline-offset: 2px;
55
+ }
51
56
 
52
57
  @media (max-width: 640px) {
53
58
  .fg-tabs__tab { min-height: var(--control-lg); }
@@ -51,7 +51,9 @@
51
51
  }
52
52
  .fg-code-block__copy:hover { background: color-mix(in srgb, currentColor 12%, transparent); }
53
53
  .fg-code-block__copy svg { width: 1rem; height: 1rem; }
54
- .fg-code-block__copy:focus-visible, .fg-code-block__pre:focus-visible, .fg-figure__plate:focus-visible { outline: 2px solid var(--color-focus); outline-offset: -2px; }
54
+ /* Inset: the code block clips its content. */
55
+ .fg-code-block__copy:focus-visible, .fg-code-block__pre:focus-visible { outline: 2px solid var(--color-focus); outline-offset: -2px; }
56
+ .fg-figure__plate:focus-visible { outline: 2px solid var(--color-focus); outline-offset: 2px; }
55
57
  .fg-code-block__pre { margin: 0; padding: 1rem; overflow: auto; max-height: 32rem; tab-size: 2; }
56
58
  .fg-code-block__pre, .fg-code-block__pre code { font-family: var(--font-mono); font-size: 0.8125rem; line-height: 1.65; }
57
59
  .fg-code-block__pre code { display: block; color: inherit; }
@@ -323,9 +325,11 @@
323
325
  cursor: pointer;
324
326
  }
325
327
  .fg-select:not([multiple]):not([size]) option:checked { color: var(--color-row-selected-text); background: var(--color-row-selected); }
326
- .fg-select:not([multiple]):not([size]) option:hover:not(:disabled), .fg-select:not([multiple]):not([size]) option:focus-visible { background: var(--color-row-hover); outline: 2px solid var(--color-focus); outline-offset: -2px; }
328
+ /* Pointer hover only fills the row; the focus ring marks the keyboard position. Inset: the picker scrolls. */
329
+ .fg-select:not([multiple]):not([size]) option:hover:not(:disabled) { background: var(--color-row-hover); }
330
+ .fg-select:not([multiple]):not([size]) option:focus-visible { background: var(--color-row-hover); outline: 2px solid var(--color-focus); outline-offset: -2px; }
327
331
  .fg-select:not([multiple]):not([size]) option:disabled { color: var(--color-muted); opacity: 0.58; cursor: not-allowed; }
328
- .fg-select:not([multiple]):not([size]) option::checkmark { order: 1; margin-left: auto; color: var(--color-link); }
332
+ .fg-select:not([multiple]):not([size]) option::checkmark { order: 1; margin-left: auto; color: var(--color-accent); }
329
333
  @media (pointer: coarse) {
330
334
  .fg-select:not([multiple]):not([size]) option { min-height: var(--control-lg); }
331
335
  }
@@ -474,9 +478,10 @@
474
478
  border-color: var(--color-border);
475
479
  }
476
480
 
481
+ /* Checked choices use the accent, as the checkbox does; blue stays for links and focus. */
477
482
  .fg-radio-card[data-state="checked"] {
478
- border-color: var(--color-link);
479
- outline: 1px solid var(--color-link);
483
+ border-color: var(--color-accent);
484
+ outline: 1px solid var(--color-accent);
480
485
  outline-offset: -2px;
481
486
  }
482
487
 
@@ -506,7 +511,7 @@
506
511
  }
507
512
 
508
513
  .fg-radio-card[data-state="checked"] .fg-radio-card__indicator {
509
- border: 0.3rem solid var(--color-link);
514
+ border: 0.3rem solid var(--color-accent);
510
515
  }
511
516
 
512
517
  .fg-badge {
package/src/site.css CHANGED
@@ -129,10 +129,12 @@
129
129
  .fg-site-appearance__choice:hover { color: var(--color-text); background: var(--color-row-hover); }
130
130
  .fg-site-appearance__choice[aria-pressed="true"] { color: var(--color-text); background: var(--color-row-selected); }
131
131
 
132
+ /* Focus is a 2px outline outside the control, which forced-colors mode keeps. An
133
+ * inset ring is used only where a scrolling ancestor would clip an outer one. */
132
134
  .fg-site-header a:focus-visible,
133
135
  .fg-site-projects__trigger:focus-visible,
134
136
  .fg-site-appearance__choice:focus-visible { outline: 2px solid var(--color-focus); outline-offset: 2px; }
135
- .fg-site-appearance__choice:focus-visible { outline-offset: -2px; }
137
+ /* Inset: the desktop rail and the phone drawer scroll. */
136
138
  .fg-site-nav a:focus-visible { outline: 2px solid var(--color-focus); outline-offset: -2px; }
137
139
 
138
140
  /* Section rail (UiSiteNav). The page grid places it; on desktop it stays in view
@@ -178,8 +180,9 @@
178
180
  }
179
181
  .fg-capture .fg-tabs__list { width: 100%; margin-block-end: 0; padding-inline: var(--space-2); }
180
182
  .fg-capture .fg-tabs__tab { min-height: var(--control-lg); }
181
- /* The Full size control sits at the end of the tab row; the list reserves its width. */
182
- .fg-capture[data-enhanced="true"] .fg-tabs__list { padding-inline-end: calc(var(--space-2) + 6.5rem); }
183
+ /* The Full size control sits at the end of the tab row. The list's margin, not its
184
+ * padding, reserves the control's width, so the scroll area and divider end before it. */
185
+ .fg-capture[data-enhanced="true"] .fg-tabs__list { width: auto; margin-inline-end: calc(var(--space-2) + 6.5rem); }
183
186
  .fg-capture__full {
184
187
  position: absolute;
185
188
  top: 0;
@@ -198,6 +201,7 @@
198
201
  }
199
202
  .fg-capture__full:hover { color: var(--color-text); }
200
203
  .fg-capture__full svg { width: 14px; height: 14px; flex: none; }
204
+ /* Inset: the capture preview below would cover an outset ring. */
201
205
  .fg-capture__full:focus-visible { outline: 2px solid var(--color-focus); outline-offset: -2px; }
202
206
  /* Without JavaScript every capture is listed under its own heading. */
203
207
  .fg-capture .fg-tabs__heading { margin: 0; padding: var(--space-3) var(--space-4); }
@@ -240,8 +244,9 @@
240
244
  line-height: var(--leading);
241
245
  }
242
246
  .fg-capture__caption > code { color: var(--color-text); font-family: var(--font-mono); font-size: var(--text-sm); overflow-wrap: anywhere; }
243
- .fg-capture__screen:focus-visible { outline: 2px solid var(--color-focus); outline-offset: -2px; }
244
- .fg-capture__expand:focus-visible { outline: 2px solid var(--color-focus); outline-offset: -4px; }
247
+ /* Inset: the enhanced panel clips its content. */
248
+ .fg-capture__screen:focus-visible,
249
+ .fg-capture__expand:focus-visible { outline: 2px solid var(--color-focus); outline-offset: -2px; }
245
250
  .fg-dialog__content.fg-capture-viewer {
246
251
  width: calc(100vw - var(--space-8));
247
252
  max-width: var(--page-max);
@@ -299,6 +304,7 @@
299
304
  }
300
305
  .fg-command-list__copy:hover { color: var(--color-text); background: var(--color-row-hover); }
301
306
  .fg-command-list__copy svg { width: 1rem; height: 1rem; }
307
+ /* Inset: the table scrolls sideways on larger screens. */
302
308
  .fg-command-list__copy:focus-visible { outline: 2px solid var(--color-focus); outline-offset: -2px; }
303
309
  .fg-command-list__status:not(:empty) { display: block; padding-top: var(--space-2); font-size: var(--text-sm); }
304
310
 
@@ -421,7 +427,7 @@
421
427
  }
422
428
  .fg-site-rail__summary::-webkit-details-marker { display: none; }
423
429
  .fg-site-rail__summary:hover { color: var(--color-text); background: var(--color-row-hover); }
424
- .fg-site-rail__summary:focus-visible { outline: 2px solid var(--color-focus); outline-offset: -2px; }
430
+ .fg-site-rail__summary:focus-visible { outline: 2px solid var(--color-focus); outline-offset: 2px; }
425
431
  .fg-site-rail__menu > .fg-site-rail__content { padding-block: var(--space-4); }
426
432
  .fg-site-rail__menu .fg-site-rail__group { grid-template-columns: repeat(2, minmax(0, 1fr)); }
427
433
  .fg-site-rail__menu .fg-site-rail__group-label { grid-column: 1 / -1; }
@@ -430,7 +436,7 @@
430
436
  @media (max-width: 640px) {
431
437
  .fg-dialog__content.fg-capture-viewer { width: 100%; max-width: none; }
432
438
  /* Phones keep the icon; the accessible name still says Full size. */
433
- .fg-capture[data-enhanced="true"] .fg-tabs__list { padding-inline-end: calc(var(--space-2) + var(--control-lg)); }
439
+ .fg-capture[data-enhanced="true"] .fg-tabs__list { margin-inline-end: calc(var(--space-2) + var(--control-lg)); }
434
440
  .fg-capture__full { min-width: var(--control-lg); justify-content: center; }
435
441
  .fg-capture__full-label { display: none; }
436
442
  .fg-command-list__table,