@filipgutica/ui 1.7.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
 
@@ -0,0 +1,16 @@
1
+ export interface UiFigureProps {
2
+ number?: number;
3
+ }
4
+ type __VLS_Slots = {
5
+ caption?(): unknown;
6
+ default?(): unknown;
7
+ };
8
+ declare const __VLS_base: import("vue").DefineComponent<UiFigureProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<UiFigureProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
9
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
10
+ declare const _default: typeof __VLS_export;
11
+ export default _default;
12
+ type __VLS_WithSlots<T, S> = T & {
13
+ new (): {
14
+ $slots: S;
15
+ };
16
+ };
@@ -0,0 +1,7 @@
1
+ export interface UiScoreProps {
2
+ max?: number;
3
+ score: number;
4
+ }
5
+ declare const __VLS_export: import("vue").DefineComponent<UiScoreProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<UiScoreProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
6
+ declare const _default: typeof __VLS_export;
7
+ export default _default;
@@ -0,0 +1,23 @@
1
+ import type { UiControlSize } from '../control.js';
2
+ export interface UiToggleButtonProps {
3
+ disabled?: boolean;
4
+ pressed?: boolean;
5
+ size?: UiControlSize;
6
+ }
7
+ type __VLS_Slots = {
8
+ default?(): unknown;
9
+ description?(): unknown;
10
+ };
11
+ declare const __VLS_base: import("vue").DefineComponent<UiToggleButtonProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
12
+ "update:pressed": (value: boolean) => any;
13
+ }, string, import("vue").PublicProps, Readonly<UiToggleButtonProps> & Readonly<{
14
+ "onUpdate:pressed"?: (value: boolean) => any;
15
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
16
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
17
+ declare const _default: typeof __VLS_export;
18
+ export default _default;
19
+ type __VLS_WithSlots<T, S> = T & {
20
+ new (): {
21
+ $slots: S;
22
+ };
23
+ };
package/dist/index.d.ts CHANGED
@@ -16,6 +16,8 @@ export { default as UiDrawer } from './components/UiDrawer.vue';
16
16
  export type { UiDrawerProps } from './components/UiDrawer.vue';
17
17
  export { default as UiField } from './components/UiField.vue';
18
18
  export type { UiFieldProps } from './components/UiField.vue';
19
+ export { default as UiFigure } from './components/UiFigure.vue';
20
+ export type { UiFigureProps } from './components/UiFigure.vue';
19
21
  export { default as UiInput } from './components/UiInput.vue';
20
22
  export type { UiInputProps } from './components/UiInput.vue';
21
23
  export { default as UiProgress } from './components/UiProgress.vue';
@@ -24,12 +26,16 @@ export { default as UiRadioCard } from './components/UiRadioCard.vue';
24
26
  export type { UiRadioCardProps } from './components/UiRadioCard.vue';
25
27
  export { default as UiRadioCardGroup } from './components/UiRadioCardGroup.vue';
26
28
  export type { UiRadioCardGroupProps } from './components/UiRadioCardGroup.vue';
29
+ export { default as UiScore } from './components/UiScore.vue';
30
+ export type { UiScoreProps } from './components/UiScore.vue';
27
31
  export { default as UiSelect } from './components/UiSelect.vue';
28
32
  export type { UiSelectProps } from './components/UiSelect.vue';
29
33
  export { default as UiSlider } from './components/UiSlider.vue';
30
34
  export type { UiSliderProps } from './components/UiSlider.vue';
31
35
  export { default as UiTabs } from './components/UiTabs.vue';
32
36
  export type { UiTabItem, UiTabsProps } from './components/UiTabs.vue';
37
+ export { default as UiToggleButton } from './components/UiToggleButton.vue';
38
+ export type { UiToggleButtonProps } from './components/UiToggleButton.vue';
33
39
  /** @deprecated Use UiCard for structured content. */
34
40
  export { default as UiSurface } from './components/UiSurface.vue';
35
41
  export type { UiSurfaceProps } from './components/UiSurface.vue';
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
- import { a as e, i as t, n, o as r, r as i, t as a } from "./use-active-section-CiflgGBI.js";
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 x, renderList as S, renderSlot as C, resolveDynamicComponent as w, shallowRef as T, toDisplayString as E, unref as D, useModel as ee, useSlots as te, watch as O, withCtx as k } from "vue";
3
- import { CheckboxIndicator as A, CheckboxRoot as j, Primitive as M, ProgressIndicator as N, ProgressRoot as P, RadioGroupItem as F, RadioGroupRoot as I, SliderRange as L, SliderRoot as R, SliderThumb as z, SliderTrack as B } from "reka-ui";
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
+ 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
+ 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
5
5
  var V = { class: "fg-card" }, H = {
6
6
  key: 0,
@@ -21,17 +21,17 @@ var V = { class: "fg-card" }, H = {
21
21
  titleTag: { default: "h3" }
22
22
  },
23
23
  setup(e) {
24
- let t = te();
24
+ let t = D();
25
25
  return (n, r) => (y(), u("div", V, [
26
- e.title || t.title || t.actions ? (y(), u("header", H, [e.title || t.title ? (y(), c(w(e.titleTag), {
26
+ e.title || t.title || t.actions ? (y(), u("header", H, [e.title || t.title ? (y(), c(C(e.titleTag), {
27
27
  key: 0,
28
28
  class: "fg-card__title"
29
29
  }, {
30
- default: k(() => [C(n.$slots, "title", {}, () => [f(E(e.title), 1)])]),
30
+ default: O(() => [S(n.$slots, "title", {}, () => [f(w(e.title), 1)])]),
31
31
  _: 3
32
- })) : l("", !0), t.actions ? (y(), u("div", U, [C(n.$slots, "actions")])) : l("", !0)])) : l("", !0),
33
- t.default ? (y(), u("div", W, [C(n.$slots, "default")])) : l("", !0),
34
- t.footer ? (y(), u("footer", G, [C(n.$slots, "footer")])) : l("", !0)
32
+ })) : l("", !0), t.actions ? (y(), u("div", U, [S(n.$slots, "actions")])) : l("", !0)])) : l("", !0),
33
+ t.default ? (y(), u("div", W, [S(n.$slots, "default")])) : l("", !0),
34
+ t.footer ? (y(), u("footer", G, [S(n.$slots, "footer")])) : l("", !0)
35
35
  ]));
36
36
  }
37
37
  }), q = [
@@ -47,20 +47,20 @@ var V = { class: "fg-card" }, H = {
47
47
  }, X = {
48
48
  key: 1,
49
49
  class: "fg-code-block__language"
50
- }, Z = { class: "fg-code-block__actions" }, ne = ["disabled", "title"], re = {
50
+ }, ie = { class: "fg-code-block__actions" }, ae = ["disabled", "title"], oe = {
51
51
  viewBox: "0 0 24 24",
52
52
  fill: "none",
53
53
  stroke: "currentColor",
54
54
  "stroke-width": "1.5",
55
55
  "aria-hidden": "true"
56
- }, ie = {
56
+ }, se = {
57
57
  key: 0,
58
58
  d: "m5 12 4 4L19 6"
59
- }, ae = { key: 0 }, oe = ["aria-label"], se = {
59
+ }, ce = { key: 0 }, le = ["aria-label"], ue = {
60
60
  key: 0,
61
61
  class: "fg-code-block__number",
62
62
  "aria-hidden": "true"
63
- }, ce = { class: "fg-code-block__text" }, le = ["data-state"], ue = /* @__PURE__ */ m({
63
+ }, de = { class: "fg-code-block__text" }, fe = ["data-state"], pe = /* @__PURE__ */ m({
64
64
  __name: "UiCodeBlock",
65
65
  props: {
66
66
  code: {},
@@ -81,7 +81,7 @@ var V = { class: "fg-card" }, H = {
81
81
  variant: { default: "default" }
82
82
  },
83
83
  setup(t) {
84
- let n = T(), r = x(""), i = s(() => n.value ?? t.code.split("\n").map((e) => [{ content: e }])), a = {
84
+ let n = te(), r = ee(""), i = s(() => n.value ?? t.code.split("\n").map((e) => [{ content: e }])), a = {
85
85
  vue: "vue",
86
86
  ts: "typescript",
87
87
  typescript: "typescript",
@@ -94,7 +94,7 @@ var V = { class: "fg-card" }, H = {
94
94
  sh: "bash",
95
95
  shell: "bash"
96
96
  };
97
- O(() => [t.code, t.language], async ([e, t], i, o) => {
97
+ re(() => [t.code, t.language], async ([e, t], i, o) => {
98
98
  let s = !1;
99
99
  o(() => {
100
100
  s = !0;
@@ -120,7 +120,7 @@ var V = { class: "fg-card" }, H = {
120
120
  let { copied: c, pending: p, error: m, copy: h } = e({
121
121
  unavailableMessage: "Clipboard unavailable. Select the code to copy it.",
122
122
  failureMessage: "Could not copy. Select the code to copy it."
123
- }), g = s(() => c.value && !m.value), _ = s(() => t.variant === "compact" && g.value && !r.value), b = s(() => m.value ? m.value : t.variant === "compact" && r.value ? c.value ? `Code copied to clipboard. ${r.value}` : r.value : c.value ? "Code copied to clipboard." : r.value), w = () => h(t.code);
123
+ }), g = s(() => c.value && !m.value), _ = s(() => t.variant === "compact" && g.value && !r.value), b = s(() => m.value ? m.value : t.variant === "compact" && r.value ? c.value ? `Code copied to clipboard. ${r.value}` : r.value : c.value ? "Code copied to clipboard." : r.value), C = () => h(t.code);
124
124
  return (e, n) => (y(), u("div", {
125
125
  class: "fg-code-block",
126
126
  "data-wrap": t.wrap,
@@ -128,70 +128,70 @@ var V = { class: "fg-card" }, H = {
128
128
  "data-variant": t.variant
129
129
  }, [
130
130
  t.variant === "default" || t.copyable || e.$slots.actions ? (y(), u("div", J, [
131
- t.variant === "default" ? (y(), u("span", Y, E(t.title || t.language), 1)) : l("", !0),
132
- t.title && t.variant === "default" ? (y(), u("span", X, E(t.language), 1)) : l("", !0),
133
- d("div", Z, [C(e.$slots, "actions"), t.copyable ? (y(), u("button", {
131
+ t.variant === "default" ? (y(), u("span", Y, w(t.title || t.language), 1)) : l("", !0),
132
+ t.title && t.variant === "default" ? (y(), u("span", X, w(t.language), 1)) : l("", !0),
133
+ d("div", ie, [S(e.$slots, "actions"), t.copyable ? (y(), u("button", {
134
134
  key: 0,
135
135
  type: "button",
136
136
  class: "fg-code-block__copy",
137
- disabled: D(p),
137
+ disabled: T(p),
138
138
  "aria-label": "Copy code",
139
139
  title: t.variant === "compact" ? g.value ? "Copied" : "Copy code" : void 0,
140
- onClick: w
141
- }, [(y(), u("svg", re, [t.variant === "compact" && g.value ? (y(), u("path", ie)) : (y(), u(o, { key: 1 }, [n[0] ||= d("rect", {
140
+ onClick: C
141
+ }, [(y(), u("svg", oe, [t.variant === "compact" && g.value ? (y(), u("path", se)) : (y(), u(o, { key: 1 }, [n[0] ||= d("rect", {
142
142
  x: "8",
143
143
  y: "8",
144
144
  width: "12",
145
145
  height: "13",
146
146
  rx: "2"
147
- }, null, -1), n[1] ||= d("path", { d: "M16 8V5a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h3" }, null, -1)], 64))])), t.variant === "default" ? (y(), u("span", ae, E(g.value ? "Copied" : "Copy"), 1)) : l("", !0)], 8, ne)) : l("", !0)])
147
+ }, null, -1), n[1] ||= d("path", { d: "M16 8V5a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h3" }, null, -1)], 64))])), t.variant === "default" ? (y(), u("span", ce, w(g.value ? "Copied" : "Copy"), 1)) : l("", !0)], 8, ae)) : l("", !0)])
148
148
  ])) : l("", !0),
149
149
  d("pre", {
150
150
  class: "fg-code-block__pre",
151
151
  tabindex: "0",
152
152
  "aria-label": t.title || `${t.language} code`
153
- }, [d("code", null, [(y(!0), u(o, null, S(i.value, (e, n) => (y(), u("span", {
153
+ }, [d("code", null, [(y(!0), u(o, null, x(i.value, (e, n) => (y(), u("span", {
154
154
  key: n,
155
155
  class: "fg-code-block__line"
156
- }, [t.lineNumbers ? (y(), u("span", se, E(n + 1), 1)) : l("", !0), d("span", ce, [(y(!0), u(o, null, S(e, (e, t) => (y(), u("span", {
156
+ }, [t.lineNumbers ? (y(), u("span", ue, w(n + 1), 1)) : l("", !0), d("span", de, [(y(!0), u(o, null, x(e, (e, t) => (y(), u("span", {
157
157
  key: t,
158
158
  style: v({ color: e.color })
159
- }, E(e.content), 5))), 128)), f(E(n < i.value.length - 1 ? "\n" : ""), 1)])]))), 128))])], 8, oe),
159
+ }, w(e.content), 5))), 128)), f(w(n < i.value.length - 1 ? "\n" : ""), 1)])]))), 128))])], 8, le),
160
160
  d("span", {
161
161
  class: "fg-code-block__status",
162
162
  role: "status",
163
163
  "data-state": _.value ? "success" : void 0
164
- }, E(b.value), 9, le)
164
+ }, w(b.value), 9, fe)
165
165
  ], 8, q));
166
166
  }
167
- }), de = /* @__PURE__ */ m({
167
+ }), me = /* @__PURE__ */ m({
168
168
  __name: "UiAlert",
169
169
  props: { tone: { default: "neutral" } },
170
170
  setup(e) {
171
- return (t, n) => (y(), c(D(M), {
171
+ return (t, n) => (y(), c(T(j), {
172
172
  as: "div",
173
173
  class: "fg-alert",
174
174
  "data-tone": e.tone,
175
175
  role: e.tone === "danger" || e.tone === "error" ? "alert" : "status"
176
176
  }, {
177
- default: k(() => [C(t.$slots, "default")]),
177
+ default: O(() => [S(t.$slots, "default")]),
178
178
  _: 3
179
179
  }, 8, ["data-tone", "role"]));
180
180
  }
181
- }), fe = /* @__PURE__ */ m({
181
+ }), he = /* @__PURE__ */ m({
182
182
  __name: "UiBadge",
183
183
  props: { tone: { default: "neutral" } },
184
184
  setup(e) {
185
- return (t, n) => (y(), c(D(M), {
185
+ return (t, n) => (y(), c(T(j), {
186
186
  as: "span",
187
187
  class: "fg-badge",
188
188
  "data-tone": e.tone
189
189
  }, {
190
- default: k(() => [C(t.$slots, "default")]),
190
+ default: O(() => [S(t.$slots, "default")]),
191
191
  _: 3
192
192
  }, 8, ["data-tone"]));
193
193
  }
194
- }), pe = ["data-size", "data-disabled"], me = { class: "fg-checkbox__label" }, he = /* @__PURE__ */ m({
194
+ }), ge = ["data-size", "data-disabled"], _e = { class: "fg-checkbox__label" }, ve = /* @__PURE__ */ m({
195
195
  __name: "UiCheckbox",
196
196
  props: {
197
197
  disabled: {
@@ -213,20 +213,20 @@ var V = { class: "fg-card" }, H = {
213
213
  class: "fg-checkbox",
214
214
  "data-size": e.size,
215
215
  "data-disabled": e.disabled ? "true" : void 0
216
- }, [p(D(j), {
216
+ }, [p(T(A), {
217
217
  class: "fg-checkbox__control",
218
218
  disabled: e.disabled,
219
219
  "model-value": e.modelValue,
220
220
  "onUpdate:modelValue": r
221
221
  }, {
222
- default: k(() => [p(D(A), { class: "fg-checkbox__indicator" }, {
223
- default: k(() => [...n[0] ||= [f("✓", -1)]]),
222
+ default: O(() => [p(T(k), { class: "fg-checkbox__indicator" }, {
223
+ default: O(() => [...n[0] ||= [f("✓", -1)]]),
224
224
  _: 1
225
225
  })]),
226
226
  _: 1
227
- }, 8, ["disabled", "model-value"]), d("span", me, [C(t.$slots, "default")])], 8, pe));
227
+ }, 8, ["disabled", "model-value"]), d("span", _e, [S(t.$slots, "default")])], 8, ge));
228
228
  }
229
- }), Q = Symbol("UiFieldContext"), ge = ["data-invalid"], _e = ["for"], ve = ["id"], ye = ["id"], be = /* @__PURE__ */ m({
229
+ }), Z = Symbol("UiFieldContext"), ye = ["data-invalid"], be = ["for"], xe = ["id"], Se = ["id"], Ce = /* @__PURE__ */ m({
230
230
  __name: "UiField",
231
231
  props: {
232
232
  controlId: {},
@@ -236,7 +236,7 @@ var V = { class: "fg-card" }, H = {
236
236
  },
237
237
  setup(e) {
238
238
  let t = s(() => e.description && !e.error ? `${e.controlId}-description` : void 0), n = s(() => e.error ? `${e.controlId}-error` : void 0), r = s(() => !!e.error);
239
- return b(Q, {
239
+ return b(Z, {
240
240
  controlId: s(() => e.controlId),
241
241
  descriptionId: t,
242
242
  errorId: n,
@@ -248,8 +248,8 @@ var V = { class: "fg-card" }, H = {
248
248
  d("label", {
249
249
  class: "fg-field__label",
250
250
  for: e.controlId
251
- }, E(e.label), 9, _e),
252
- C(r.$slots, "default", {
251
+ }, w(e.label), 9, be),
252
+ S(r.$slots, "default", {
253
253
  descriptionId: t.value,
254
254
  errorId: n.value
255
255
  }),
@@ -258,16 +258,40 @@ var V = { class: "fg-card" }, H = {
258
258
  id: t.value,
259
259
  class: "fg-field__description",
260
260
  "data-field-description": ""
261
- }, E(e.description), 9, ve)) : l("", !0),
261
+ }, w(e.description), 9, xe)) : l("", !0),
262
262
  e.error ? (y(), u("p", {
263
263
  key: 1,
264
264
  id: n.value,
265
265
  class: "fg-field__error",
266
266
  "data-field-error": ""
267
- }, E(e.error), 9, ye)) : l("", !0)
268
- ], 8, ge));
267
+ }, w(e.error), 9, Se)) : l("", !0)
268
+ ], 8, ye));
269
269
  }
270
- }), $ = /* @__PURE__ */ m({
270
+ }), we = { class: "fg-figure" }, Te = ["aria-labelledby", "aria-label"], Ee = ["id"], De = {
271
+ key: 0,
272
+ class: "fg-figure__number"
273
+ }, Oe = /* @__PURE__ */ m({
274
+ __name: "UiFigure",
275
+ props: { number: { default: () => void 0 } },
276
+ setup(e) {
277
+ let t = D(), n = E(), r = () => e.number !== void 0 || !!t.caption;
278
+ return (t, i) => (y(), u("figure", we, [d("div", {
279
+ class: "fg-figure__plate",
280
+ role: "region",
281
+ tabindex: "0",
282
+ "aria-labelledby": r() ? T(n) : void 0,
283
+ "aria-label": r() ? void 0 : "Figure"
284
+ }, [S(t.$slots, "default")], 8, Te), r() ? (y(), u("figcaption", {
285
+ key: 0,
286
+ id: T(n),
287
+ class: "fg-figure__caption"
288
+ }, [
289
+ e.number === void 0 ? l("", !0) : (y(), u("span", De, "Figure " + w(e.number) + ".", 1)),
290
+ i[0] ||= f(),
291
+ S(t.$slots, "caption")
292
+ ], 8, Ee)) : l("", !0)]));
293
+ }
294
+ }), ke = /* @__PURE__ */ m({
271
295
  __name: "UiInput",
272
296
  props: {
273
297
  disabled: {
@@ -285,16 +309,16 @@ var V = { class: "fg-card" }, H = {
285
309
  },
286
310
  emits: ["update:modelValue"],
287
311
  setup(e, { emit: t }) {
288
- let n = h(Q, void 0), r = t, i = (e) => {
312
+ let n = h(Z, void 0), r = t, i = (e) => {
289
313
  e.currentTarget instanceof HTMLInputElement && r("update:modelValue", e.currentTarget.value);
290
314
  };
291
- return (t, r) => (y(), c(D(M), {
292
- id: D(n)?.controlId.value ?? e.id,
315
+ return (t, r) => (y(), c(T(j), {
316
+ id: T(n)?.controlId.value ?? e.id,
293
317
  as: "input",
294
318
  class: "fg-input",
295
- "aria-describedby": D(n)?.descriptionId.value,
296
- "aria-errormessage": D(n)?.errorId.value,
297
- "aria-invalid": e.invalid || D(n)?.invalid.value ? "true" : void 0,
319
+ "aria-describedby": T(n)?.descriptionId.value,
320
+ "aria-errormessage": T(n)?.errorId.value,
321
+ "aria-invalid": e.invalid || T(n)?.invalid.value ? "true" : void 0,
298
322
  "data-size": e.size,
299
323
  disabled: e.disabled,
300
324
  type: e.type,
@@ -311,7 +335,7 @@ var V = { class: "fg-card" }, H = {
311
335
  "value"
312
336
  ]));
313
337
  }
314
- }), xe = 100, Se = /* @__PURE__ */ m({
338
+ }), Ae = 100, je = /* @__PURE__ */ m({
315
339
  __name: "UiProgress",
316
340
  props: {
317
341
  label: {},
@@ -320,19 +344,19 @@ var V = { class: "fg-card" }, H = {
320
344
  },
321
345
  setup(e) {
322
346
  let t = s(() => {
323
- let t = Number.isFinite(e.max) && e.max > 0 ? e.max : xe;
347
+ let t = Number.isFinite(e.max) && e.max > 0 ? e.max : Ae;
324
348
  return {
325
349
  max: t,
326
350
  value: e.value === null || !Number.isFinite(e.value) ? null : Math.min(t, Math.max(0, e.value))
327
351
  };
328
352
  }), n = s(() => t.value.value === null ? 0 : t.value.value / t.value.max * 100);
329
- return (r, i) => (y(), c(D(P), {
353
+ return (r, i) => (y(), c(T(N), {
330
354
  class: "fg-progress",
331
355
  "aria-label": e.label,
332
356
  max: t.value.max,
333
357
  "model-value": t.value.value
334
358
  }, {
335
- default: k(() => [p(D(N), {
359
+ default: O(() => [p(T(M), {
336
360
  class: "fg-progress__indicator",
337
361
  style: v({ transform: `translateX(-${100 - n.value}%)` })
338
362
  }, null, 8, ["style"])]),
@@ -343,7 +367,7 @@ var V = { class: "fg-card" }, H = {
343
367
  "model-value"
344
368
  ]));
345
369
  }
346
- }), Ce = { class: "fg-radio-card__content" }, we = /* @__PURE__ */ m({
370
+ }), Me = { class: "fg-radio-card__content" }, Ne = /* @__PURE__ */ m({
347
371
  __name: "UiRadioCard",
348
372
  props: {
349
373
  disabled: {
@@ -353,19 +377,19 @@ var V = { class: "fg-card" }, H = {
353
377
  value: {}
354
378
  },
355
379
  setup(e) {
356
- return (t, n) => (y(), c(D(F), {
380
+ return (t, n) => (y(), c(T(P), {
357
381
  class: "fg-radio-card",
358
382
  disabled: e.disabled,
359
383
  value: e.value
360
384
  }, {
361
- default: k(() => [n[0] ||= d("span", {
385
+ default: O(() => [n[0] ||= d("span", {
362
386
  class: "fg-radio-card__indicator",
363
387
  "aria-hidden": "true"
364
- }, null, -1), d("span", Ce, [C(t.$slots, "default")])]),
388
+ }, null, -1), d("span", Me, [S(t.$slots, "default")])]),
365
389
  _: 3
366
390
  }, 8, ["disabled", "value"]));
367
391
  }
368
- }), Te = /* @__PURE__ */ m({
392
+ }), Pe = /* @__PURE__ */ m({
369
393
  __name: "UiRadioCardGroup",
370
394
  props: /*@__PURE__*/ g({
371
395
  disabled: {
@@ -383,7 +407,7 @@ var V = { class: "fg-card" }, H = {
383
407
  }),
384
408
  emits: ["update:modelValue"],
385
409
  setup(e) {
386
- let t = ee(e, "modelValue"), n = (t) => {
410
+ let t = ne(e, "modelValue"), n = (t) => {
387
411
  if (t.altKey || t.ctrlKey || t.metaKey || t.shiftKey) return;
388
412
  let n = t.currentTarget, r = t.target;
389
413
  if (!(n instanceof HTMLElement) || !(r instanceof HTMLElement)) return;
@@ -399,14 +423,14 @@ var V = { class: "fg-card" }, H = {
399
423
  let d = o[u];
400
424
  !d || d === r || (t.preventDefault(), d.focus(), d.click());
401
425
  };
402
- return (r, i) => (y(), c(D(I), _({
426
+ return (r, i) => (y(), c(T(F), _({
403
427
  modelValue: t.value,
404
428
  "onUpdate:modelValue": i[0] ||= (e) => t.value = e,
405
429
  class: "fg-radio-card-group",
406
430
  disabled: e.disabled,
407
431
  loop: e.loop
408
432
  }, e.orientation === void 0 ? {} : { orientation: e.orientation }, { onKeydown: n }), {
409
- default: k(() => [C(r.$slots, "default")]),
433
+ default: O(() => [S(r.$slots, "default")]),
410
434
  _: 3
411
435
  }, 16, [
412
436
  "modelValue",
@@ -414,7 +438,30 @@ var V = { class: "fg-card" }, H = {
414
438
  "loop"
415
439
  ]));
416
440
  }
417
- }), Ee = /* @__PURE__ */ m({
441
+ }), Fe = { class: "fg-score" }, Q = {
442
+ class: "fg-score__pips",
443
+ "aria-hidden": "true"
444
+ }, Ie = ["data-filled"], Le = { class: "fg-score__value" }, Re = { class: "fg-score__max" }, $ = 3, ze = /* @__PURE__ */ m({
445
+ __name: "UiScore",
446
+ props: {
447
+ max: { default: () => $ },
448
+ score: {}
449
+ },
450
+ setup(e) {
451
+ let t = s(() => {
452
+ let t = Number.isInteger(e.max) && e.max > 0 ? e.max : $;
453
+ return {
454
+ max: t,
455
+ score: Number.isFinite(e.score) ? Math.min(t, Math.max(0, e.score)) : 0
456
+ };
457
+ });
458
+ return (e, n) => (y(), u("span", Fe, [d("span", Q, [(y(!0), u(o, null, x(t.value.max, (e) => (y(), u("span", {
459
+ key: e,
460
+ class: "fg-score__pip",
461
+ "data-filled": e <= t.value.score ? "true" : void 0
462
+ }, null, 8, Ie))), 128))]), d("span", Le, [f(w(t.value.score), 1), d("span", Re, "/" + w(t.value.max), 1)])]));
463
+ }
464
+ }), Be = /* @__PURE__ */ m({
418
465
  __name: "UiSelect",
419
466
  props: {
420
467
  disabled: {
@@ -431,22 +478,22 @@ var V = { class: "fg-card" }, H = {
431
478
  },
432
479
  emits: ["update:modelValue"],
433
480
  setup(e, { emit: t }) {
434
- let n = h(Q, void 0), r = t, i = (e) => {
481
+ let n = h(Z, void 0), r = t, i = (e) => {
435
482
  e.currentTarget instanceof HTMLSelectElement && r("update:modelValue", e.currentTarget.value);
436
483
  };
437
- return (t, r) => (y(), c(D(M), {
438
- id: D(n)?.controlId.value ?? e.id,
484
+ return (t, r) => (y(), c(T(j), {
485
+ id: T(n)?.controlId.value ?? e.id,
439
486
  as: "select",
440
487
  class: "fg-select",
441
- "aria-describedby": D(n)?.descriptionId.value,
442
- "aria-errormessage": D(n)?.errorId.value,
443
- "aria-invalid": e.invalid || D(n)?.invalid.value ? "true" : void 0,
488
+ "aria-describedby": T(n)?.descriptionId.value,
489
+ "aria-errormessage": T(n)?.errorId.value,
490
+ "aria-invalid": e.invalid || T(n)?.invalid.value ? "true" : void 0,
444
491
  "data-size": e.size,
445
492
  disabled: e.disabled,
446
493
  value: e.modelValue,
447
494
  onChange: i
448
495
  }, {
449
- default: k(() => [C(t.$slots, "default")]),
496
+ default: O(() => [S(t.$slots, "default")]),
450
497
  _: 3
451
498
  }, 8, [
452
499
  "id",
@@ -458,7 +505,7 @@ var V = { class: "fg-card" }, H = {
458
505
  "value"
459
506
  ]));
460
507
  }
461
- }), De = /* @__PURE__ */ m({
508
+ }), Ve = /* @__PURE__ */ m({
462
509
  __name: "UiSlider",
463
510
  props: {
464
511
  disabled: {
@@ -475,7 +522,7 @@ var V = { class: "fg-card" }, H = {
475
522
  },
476
523
  emits: ["update:modelValue"],
477
524
  setup(e, { emit: t }) {
478
- let n = h(Q, void 0), r = s(() => {
525
+ let n = h(Z, void 0), r = s(() => {
479
526
  let t = Number.isFinite(e.min) ? e.min : 0;
480
527
  return {
481
528
  max: Number.isFinite(e.max) && e.max > t ? e.max : t + 100,
@@ -491,7 +538,7 @@ var V = { class: "fg-card" }, H = {
491
538
  e !== void 0 && i("update:modelValue", e);
492
539
  }
493
540
  });
494
- return (t, i) => (y(), c(D(R), {
541
+ return (t, i) => (y(), c(T(L), {
495
542
  modelValue: a.value,
496
543
  "onUpdate:modelValue": i[0] ||= (e) => a.value = e,
497
544
  class: "fg-slider",
@@ -501,15 +548,15 @@ var V = { class: "fg-card" }, H = {
501
548
  min: r.value.min,
502
549
  step: r.value.step
503
550
  }, {
504
- default: k(() => [p(D(B), { class: "fg-slider__track" }, {
505
- default: k(() => [p(D(L), { class: "fg-slider__range" })]),
551
+ default: O(() => [p(T(z), { class: "fg-slider__track" }, {
552
+ default: O(() => [p(T(I), { class: "fg-slider__range" })]),
506
553
  _: 1
507
- }), p(D(z), {
508
- id: D(n)?.controlId.value ?? e.id,
554
+ }), p(T(R), {
555
+ id: T(n)?.controlId.value ?? e.id,
509
556
  class: "fg-slider__thumb",
510
- "aria-describedby": D(n)?.descriptionId.value,
511
- "aria-errormessage": D(n)?.errorId.value,
512
- "aria-invalid": D(n)?.invalid.value ? "true" : void 0,
557
+ "aria-describedby": T(n)?.descriptionId.value,
558
+ "aria-errormessage": T(n)?.errorId.value,
559
+ "aria-invalid": T(n)?.invalid.value ? "true" : void 0,
513
560
  "aria-label": e.label
514
561
  }, null, 8, [
515
562
  "id",
@@ -528,22 +575,61 @@ var V = { class: "fg-card" }, H = {
528
575
  "step"
529
576
  ]));
530
577
  }
531
- }), Oe = /* @__PURE__ */ m({
578
+ }), He = { class: "fg-toggle-button__text" }, Ue = { class: "fg-toggle-button__label" }, We = {
579
+ key: 0,
580
+ class: "fg-toggle-button__description"
581
+ }, Ge = /* @__PURE__ */ m({
582
+ __name: "UiToggleButton",
583
+ props: {
584
+ disabled: {
585
+ type: Boolean,
586
+ default: !1
587
+ },
588
+ pressed: {
589
+ type: Boolean,
590
+ default: !1
591
+ },
592
+ size: { default: "md" }
593
+ },
594
+ emits: ["update:pressed"],
595
+ setup(e, { emit: t }) {
596
+ let n = t, r = D(), i = (e) => {
597
+ n("update:pressed", e);
598
+ };
599
+ return (t, n) => (y(), c(T(B), {
600
+ class: "fg-toggle-button",
601
+ "data-size": e.size,
602
+ disabled: e.disabled,
603
+ "model-value": e.pressed,
604
+ "onUpdate:modelValue": i
605
+ }, {
606
+ default: O(() => [n[0] ||= d("span", {
607
+ class: "fg-toggle-button__switch",
608
+ "aria-hidden": "true"
609
+ }, null, -1), d("span", He, [d("span", Ue, [S(t.$slots, "default")]), r.description ? (y(), u("span", We, [S(t.$slots, "description")])) : l("", !0)])]),
610
+ _: 3
611
+ }, 8, [
612
+ "data-size",
613
+ "disabled",
614
+ "model-value"
615
+ ]));
616
+ }
617
+ }), Ke = /* @__PURE__ */ m({
532
618
  __name: "UiSurface",
533
619
  props: {
534
620
  as: { default: "section" },
535
621
  padding: { default: "default" }
536
622
  },
537
623
  setup(e) {
538
- return (t, n) => (y(), c(D(M), {
624
+ return (t, n) => (y(), c(T(j), {
539
625
  as: e.as,
540
626
  class: "fg-surface",
541
627
  "data-padding": e.padding
542
628
  }, {
543
- default: k(() => [C(t.$slots, "default")]),
629
+ default: O(() => [S(t.$slots, "default")]),
544
630
  _: 3
545
631
  }, 8, ["as", "data-padding"]));
546
632
  }
547
633
  });
548
634
  //#endregion
549
- export { de as UiAlert, fe as UiBadge, r as UiButton, K as UiCard, he as UiCheckbox, ue as UiCodeBlock, t as UiDialog, i as UiDrawer, be as UiField, $ as UiInput, Se as UiProgress, we as UiRadioCard, Te as UiRadioCardGroup, Ee as UiSelect, De as UiSlider, Oe as UiSurface, n as UiTabs, a as useActiveSection };
635
+ export { me as UiAlert, he as UiBadge, r as UiButton, K as UiCard, ve as UiCheckbox, pe as UiCodeBlock, t as UiDialog, i as UiDrawer, Ce as UiField, Oe as UiFigure, ke as UiInput, je as UiProgress, Ne as UiRadioCard, Pe as UiRadioCardGroup, ze as UiScore, Be as UiSelect, Ve as UiSlider, Ke as UiSurface, n as UiTabs, Ge as UiToggleButton, a as useActiveSection };
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.7.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); }
@@ -5,7 +5,7 @@
5
5
  /* Package-owned typography keeps controls consistent across consuming apps. */
6
6
  .fg-button, .fg-input, .fg-select, .fg-checkbox, .fg-field,
7
7
  .fg-radio-card, .fg-badge, .fg-alert, .fg-surface, .fg-card,
8
- .fg-dialog__content, .fg-code-block {
8
+ .fg-dialog__content, .fg-code-block, .fg-toggle-button {
9
9
  font-family: var(--font-ui);
10
10
  font-size: var(--text-md);
11
11
  font-weight: var(--weight-regular);
@@ -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
+ /* Inset: the code block clips its content. */
54
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; }
@@ -141,7 +143,8 @@
141
143
  .fg-button:disabled,
142
144
  .fg-input:disabled,
143
145
  .fg-select:disabled,
144
- .fg-checkbox[data-disabled="true"] {
146
+ .fg-checkbox[data-disabled="true"],
147
+ .fg-toggle-button:disabled {
145
148
  cursor: not-allowed;
146
149
  opacity: 0.58;
147
150
  }
@@ -322,9 +325,11 @@
322
325
  cursor: pointer;
323
326
  }
324
327
  .fg-select:not([multiple]):not([size]) option:checked { color: var(--color-row-selected-text); background: var(--color-row-selected); }
325
- .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; }
326
331
  .fg-select:not([multiple]):not([size]) option:disabled { color: var(--color-muted); opacity: 0.58; cursor: not-allowed; }
327
- .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); }
328
333
  @media (pointer: coarse) {
329
334
  .fg-select:not([multiple]):not([size]) option { min-height: var(--control-lg); }
330
335
  }
@@ -473,9 +478,10 @@
473
478
  border-color: var(--color-border);
474
479
  }
475
480
 
481
+ /* Checked choices use the accent, as the checkbox does; blue stays for links and focus. */
476
482
  .fg-radio-card[data-state="checked"] {
477
- border-color: var(--color-link);
478
- outline: 1px solid var(--color-link);
483
+ border-color: var(--color-accent);
484
+ outline: 1px solid var(--color-accent);
479
485
  outline-offset: -2px;
480
486
  }
481
487
 
@@ -505,7 +511,7 @@
505
511
  }
506
512
 
507
513
  .fg-radio-card[data-state="checked"] .fg-radio-card__indicator {
508
- border: 0.3rem solid var(--color-link);
514
+ border: 0.3rem solid var(--color-accent);
509
515
  }
510
516
 
511
517
  .fg-badge {
@@ -602,6 +608,128 @@
602
608
  font-size: var(--text-sm);
603
609
  }
604
610
 
611
+ .fg-figure {
612
+ display: grid;
613
+ grid-template-columns: minmax(0, 1fr);
614
+ gap: var(--space-3);
615
+ min-width: 0;
616
+ margin: 0;
617
+ }
618
+
619
+ /* Wide content scrolls inside the plate so the page never overflows. */
620
+ .fg-figure__plate {
621
+ min-width: 0;
622
+ overflow-x: auto;
623
+ padding: var(--space-5);
624
+ border: 1px solid var(--color-border-subtle);
625
+ border-radius: var(--radius-panel);
626
+ background: var(--color-surface);
627
+ }
628
+
629
+ .fg-figure__caption {
630
+ max-width: var(--measure);
631
+ color: var(--color-muted);
632
+ font-family: var(--font-ui);
633
+ font-size: var(--text-sm);
634
+ line-height: var(--leading);
635
+ }
636
+
637
+ .fg-figure__number {
638
+ color: var(--color-text);
639
+ font-weight: var(--weight-medium);
640
+ }
641
+
642
+ @media (max-width: 680px) {
643
+ .fg-figure__plate { padding: var(--space-4); }
644
+ }
645
+
646
+ .fg-score {
647
+ display: inline-flex;
648
+ align-items: center;
649
+ gap: var(--space-2);
650
+ font-variant-numeric: tabular-nums;
651
+ white-space: nowrap;
652
+ }
653
+
654
+ .fg-score__pips { display: inline-flex; gap: 0.1875rem; }
655
+
656
+ .fg-score__pip {
657
+ width: 0.5rem;
658
+ height: 0.5rem;
659
+ border: 1px solid var(--color-border);
660
+ border-radius: var(--radius-pill);
661
+ }
662
+
663
+ .fg-score__pip[data-filled="true"] {
664
+ border-color: var(--color-accent);
665
+ background: var(--color-accent);
666
+ }
667
+
668
+ .fg-score__value { font-weight: var(--weight-medium); }
669
+ .fg-score__max { color: var(--color-muted); font-weight: var(--weight-regular); }
670
+
671
+ .fg-toggle-button {
672
+ display: flex;
673
+ align-items: center;
674
+ gap: var(--space-3);
675
+ padding: var(--space-2) var(--space-3);
676
+ border: 1px solid var(--color-border-subtle);
677
+ border-radius: var(--radius-control);
678
+ color: var(--color-text);
679
+ background: var(--color-surface-raised);
680
+ line-height: var(--leading-tight);
681
+ text-align: left;
682
+ cursor: pointer;
683
+ }
684
+
685
+ .fg-toggle-button[data-size="sm"] { min-height: var(--control-sm); font-size: var(--text-sm); }
686
+ .fg-toggle-button[data-size="md"] { min-height: var(--control-md); font-size: var(--text-md); }
687
+ .fg-toggle-button[data-size="lg"] { min-height: var(--control-lg); font-size: var(--text-lg); }
688
+
689
+ .fg-toggle-button:hover:not(:disabled, [aria-pressed="true"]) { border-color: var(--color-border); }
690
+ .fg-toggle-button[aria-pressed="true"] { border-color: var(--color-accent); }
691
+
692
+ /* The thumb's position, not only its color, shows the state. */
693
+ .fg-toggle-button__switch {
694
+ position: relative;
695
+ flex: none;
696
+ width: 1.75rem;
697
+ height: 1rem;
698
+ border: 1px solid var(--color-border);
699
+ border-radius: var(--radius-pill);
700
+ background: var(--color-surface-muted);
701
+ transition: background-color var(--duration) var(--ease-out), border-color var(--duration) var(--ease-out);
702
+ }
703
+
704
+ .fg-toggle-button__switch::after {
705
+ position: absolute;
706
+ top: 0.125rem;
707
+ left: 0.125rem;
708
+ width: 0.625rem;
709
+ height: 0.625rem;
710
+ border-radius: var(--radius-pill);
711
+ background: var(--color-border);
712
+ content: "";
713
+ transition: transform var(--duration) var(--ease-out), background-color var(--duration) var(--ease-out);
714
+ }
715
+
716
+ .fg-toggle-button[aria-pressed="true"] .fg-toggle-button__switch {
717
+ border-color: var(--color-accent);
718
+ background: var(--color-accent);
719
+ }
720
+
721
+ .fg-toggle-button[aria-pressed="true"] .fg-toggle-button__switch::after {
722
+ background: var(--color-button-text);
723
+ transform: translateX(0.75rem);
724
+ }
725
+
726
+ .fg-toggle-button__text { display: grid; min-width: 0; }
727
+
728
+ .fg-toggle-button__description {
729
+ color: var(--color-muted);
730
+ font-size: var(--text-xs);
731
+ }
732
+
605
733
  .fg-progress {
606
734
  position: relative;
607
735
  height: 0.55rem;
@@ -745,7 +873,8 @@
745
873
  .fg-radio-card:focus-visible,
746
874
  .fg-slider__thumb:focus-visible,
747
875
  .fg-dialog__close:focus-visible,
748
- .fg-dialog__content:focus-visible {
876
+ .fg-dialog__content:focus-visible,
877
+ .fg-toggle-button:focus-visible {
749
878
  outline: 2px solid var(--color-focus);
750
879
  outline-offset: 2px;
751
880
  }
@@ -759,7 +888,8 @@
759
888
  :root.high-contrast .fg-surface,
760
889
  :root.high-contrast .fg-card,
761
890
  :root.high-contrast .fg-code-block,
762
- :root.high-contrast .fg-dialog__content {
891
+ :root.high-contrast .fg-dialog__content,
892
+ :root.high-contrast .fg-toggle-button {
763
893
  border-width: 2px;
764
894
  border-color: var(--color-border);
765
895
  }
@@ -783,7 +913,9 @@
783
913
  .fg-radio-card,
784
914
  .fg-slider__range,
785
915
  .fg-dialog__close,
786
- .fg-progress__indicator { transition: none; }
916
+ .fg-progress__indicator,
917
+ .fg-toggle-button__switch,
918
+ .fg-toggle-button__switch::after { transition: none; }
787
919
 
788
920
  .fg-button[aria-busy="true"]::before { animation-duration: 1.4s; }
789
921
  }
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,