@filipgutica/ui 1.17.0 → 1.18.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 +2 -2
- package/dist/index.js +1 -1
- package/dist/site.js +1 -1
- package/dist/{use-active-section-DMbpGE2f.js → use-active-section-DpcSUhA9.js} +23 -4
- package/package.json +1 -1
- package/src/components.css +36 -7
- package/src/site.css +17 -8
- package/src/theme.css +7 -0
package/README.md
CHANGED
|
@@ -186,9 +186,9 @@ import read from './captures/read.html?raw'
|
|
|
186
186
|
|
|
187
187
|
`UiSiteNav` renders a labelled list of route rows in optional `groups`. Links to
|
|
188
188
|
`#id` sections track the reader's position with `useActiveSection` and get
|
|
189
|
-
`aria-current="location"`, an accent
|
|
189
|
+
`aria-current="location"`, an accent pip without a fill. An item with
|
|
190
190
|
`current: true` gets
|
|
191
|
-
`aria-current="page"`,
|
|
191
|
+
`aria-current="page"`, a quiet filled row with a slight lift and accent text; `code: true` shows the
|
|
192
192
|
label in the monospace font. The page grid places the rail, which stays in view
|
|
193
193
|
on desktop. At 800px and below it is a native disclosure until the page is
|
|
194
194
|
interactive, then a drawer. The disclosure, the drawer button, and the drawer
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as e, i as t, n, o as r, r as i, s as a, t as o } from "./use-active-section-
|
|
1
|
+
import { a as e, i as t, n, o as r, r as i, s as a, t as o } from "./use-active-section-DpcSUhA9.js";
|
|
2
2
|
import { Fragment as s, computed as c, createBlock as l, createCommentVNode as u, createElementBlock as d, createElementVNode as f, createTextVNode as p, createVNode as m, defineComponent as h, inject as g, mergeModels as _, mergeProps as v, normalizeStyle as y, openBlock as b, provide as x, ref as S, renderList as C, renderSlot as w, resolveDynamicComponent as T, shallowRef as ee, toDisplayString as E, unref as D, useAttrs as te, useId as O, useModel as ne, useSlots as k, watch as re, withCtx as A } from "vue";
|
|
3
3
|
import { CheckboxIndicator as j, CheckboxRoot as M, Primitive as N, ProgressIndicator as P, ProgressRoot as F, RadioGroupItem as I, RadioGroupRoot as L, SliderRange as R, SliderRoot as z, SliderThumb as B, SliderTrack as V, Toggle as H } 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, s as a, t as o } from "./use-active-section-
|
|
1
|
+
import { a as e, i as t, n, o as r, r as i, s as a, t as o } from "./use-active-section-DpcSUhA9.js";
|
|
2
2
|
import { Fragment as s, computed as c, createBlock as l, createCommentVNode as u, createElementBlock as d, createElementVNode as f, createTextVNode as p, createVNode as m, defineComponent as h, mergeModels as g, nextTick as _, normalizeClass as v, normalizeStyle as y, onBeforeUnmount as b, onMounted as x, onUnmounted as S, openBlock as C, ref as w, renderList as T, renderSlot as E, shallowRef as D, toDisplayString as O, unref as k, useId as A, useModel as j, useSlots as M, useTemplateRef as N, watch as P, withCtx as F } from "vue";
|
|
3
3
|
import { tryOnMounted as I, usePreferredDark as L, useStorage as R } from "@vueuse/core";
|
|
4
4
|
//#region src/site/appearance.ts
|
|
@@ -185,13 +185,32 @@ var N = /* @__PURE__ */ c({
|
|
|
185
185
|
},
|
|
186
186
|
emits: ["update:modelValue"],
|
|
187
187
|
setup(t, { emit: n }) {
|
|
188
|
-
let a = n;
|
|
189
|
-
|
|
188
|
+
let a = n, o = b("root"), s = !1;
|
|
189
|
+
u(() => requestAnimationFrame(() => {
|
|
190
|
+
s = !0;
|
|
191
|
+
}));
|
|
192
|
+
let c = () => {
|
|
193
|
+
o.value && (delete o.value.dataset.slide, o.value.style.removeProperty("--fg-segmented-slide-left"), o.value.style.removeProperty("--fg-segmented-slide-right"));
|
|
194
|
+
};
|
|
195
|
+
return x(() => t.modelValue, (e, n) => {
|
|
196
|
+
let r = o.value;
|
|
197
|
+
if (!r) return;
|
|
198
|
+
let i = Array.from(r.children).filter((e) => e instanceof HTMLElement), a = i[t.items.findIndex((e) => e.value === n)], l = i[t.items.findIndex((t) => t.value === e)];
|
|
199
|
+
if (!s || !a || !l || matchMedia("(prefers-reduced-motion: reduce)").matches) {
|
|
200
|
+
c();
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
let u = getComputedStyle(a, "::before"), d = a.getBoundingClientRect(), f = d.left + parseFloat(u.left), p = d.right - parseFloat(u.right), m = l.getBoundingClientRect();
|
|
204
|
+
c(), r.style.setProperty("--fg-segmented-slide-left", `${f - m.left}px`), r.style.setProperty("--fg-segmented-slide-right", `${m.right - p}px`), r.dataset.slide = "";
|
|
205
|
+
}, { flush: "pre" }), (n, s) => (f(), i("div", {
|
|
206
|
+
ref_key: "root",
|
|
207
|
+
ref: o,
|
|
190
208
|
class: "fg-segmented",
|
|
191
209
|
role: "group",
|
|
192
210
|
"aria-label": t.label,
|
|
193
211
|
"data-size": t.size,
|
|
194
|
-
"data-icon-only": t.iconOnly ? "true" : void 0
|
|
212
|
+
"data-icon-only": t.iconOnly ? "true" : void 0,
|
|
213
|
+
onAnimationend: s[0] ||= (e) => e.animationName === "fg-segmented-slide" && c()
|
|
195
214
|
}, [(f(!0), i(e, null, h(t.items, (e) => (f(), i("button", {
|
|
196
215
|
key: e.value,
|
|
197
216
|
type: "button",
|
|
@@ -204,7 +223,7 @@ var N = /* @__PURE__ */ c({
|
|
|
204
223
|
}, [g(n.$slots, "icon", {
|
|
205
224
|
item: e,
|
|
206
225
|
pressed: t.modelValue === e.value
|
|
207
|
-
}), t.iconOnly ? r("", !0) : (f(), i("span", W, _(e.label), 1))], 8, U))), 128))],
|
|
226
|
+
}), t.iconOnly ? r("", !0) : (f(), i("span", W, _(e.label), 1))], 8, U))), 128))], 40, H));
|
|
208
227
|
}
|
|
209
228
|
}), K = { class: "fg-tabs" }, q = ["aria-label", "data-clipped"], J = [
|
|
210
229
|
"id",
|
package/package.json
CHANGED
package/src/components.css
CHANGED
|
@@ -147,13 +147,15 @@
|
|
|
147
147
|
}
|
|
148
148
|
|
|
149
149
|
.fg-button {
|
|
150
|
+
--fg-button-shadow: var(--shadow-raise);
|
|
150
151
|
display: inline-flex;
|
|
151
152
|
align-items: center;
|
|
152
153
|
justify-content: center;
|
|
153
154
|
gap: var(--space-2);
|
|
154
155
|
color: var(--color-button-text);
|
|
155
156
|
background: var(--color-button-bg);
|
|
156
|
-
box-shadow:
|
|
157
|
+
box-shadow: var(--fg-button-shadow);
|
|
158
|
+
transition: background-color var(--duration-fast) var(--ease-out), box-shadow var(--duration-fast) var(--ease-out);
|
|
157
159
|
font-weight: 500;
|
|
158
160
|
letter-spacing: -0.01em;
|
|
159
161
|
line-height: 1.25;
|
|
@@ -164,8 +166,8 @@
|
|
|
164
166
|
background: var(--color-button-hover);
|
|
165
167
|
}
|
|
166
168
|
|
|
167
|
-
.fg-button:active:not(:disabled) {
|
|
168
|
-
|
|
169
|
+
.fg-button:where(:not([data-variant]), [data-variant="primary"]):is(:active, [aria-pressed="true"]):not(:disabled) {
|
|
170
|
+
background: color-mix(in srgb, var(--color-button-bg) 80%, var(--color-bg));
|
|
169
171
|
}
|
|
170
172
|
|
|
171
173
|
.fg-button:disabled,
|
|
@@ -241,12 +243,12 @@
|
|
|
241
243
|
|
|
242
244
|
.fg-button[data-variant="secondary"] {
|
|
243
245
|
color: var(--color-text);
|
|
244
|
-
background: var(--color-
|
|
246
|
+
background: var(--color-control-thumb);
|
|
245
247
|
}
|
|
246
248
|
|
|
247
249
|
.fg-button[data-variant="secondary"]:hover:not(:disabled) {
|
|
248
250
|
color: var(--color-text);
|
|
249
|
-
background: color-mix(in srgb, var(--color-text) 8%, var(--color-
|
|
251
|
+
background: color-mix(in srgb, var(--color-text) 8%, var(--color-control-thumb));
|
|
250
252
|
}
|
|
251
253
|
|
|
252
254
|
.fg-button[data-variant="ghost"]:hover:not(:disabled) {
|
|
@@ -255,12 +257,14 @@
|
|
|
255
257
|
}
|
|
256
258
|
|
|
257
259
|
.fg-button[data-variant="ghost"] {
|
|
260
|
+
--fg-button-shadow: 0 0 0 0 transparent;
|
|
258
261
|
color: var(--color-muted);
|
|
259
262
|
background: transparent;
|
|
260
263
|
box-shadow: none;
|
|
261
264
|
}
|
|
262
265
|
|
|
263
266
|
.fg-button[data-variant="danger"] {
|
|
267
|
+
--fg-button-shadow: 0 0 0 0 transparent;
|
|
264
268
|
color: var(--color-status-text, var(--color-error));
|
|
265
269
|
background: var(--color-error-surface);
|
|
266
270
|
box-shadow: none;
|
|
@@ -274,6 +278,7 @@
|
|
|
274
278
|
}
|
|
275
279
|
|
|
276
280
|
.fg-button[data-variant="text"] {
|
|
281
|
+
--fg-button-shadow: 0 0 0 0 transparent;
|
|
277
282
|
height: auto;
|
|
278
283
|
min-height: auto;
|
|
279
284
|
padding: var(--space-1);
|
|
@@ -289,6 +294,14 @@
|
|
|
289
294
|
background: transparent;
|
|
290
295
|
}
|
|
291
296
|
|
|
297
|
+
/* Keep press specificity level with focus so its halo can wrap the sink shadow. */
|
|
298
|
+
.fg-button:where(:not([data-variant="danger"], [data-variant="text"])):is(:active, [aria-pressed="true"]):not(:disabled) {
|
|
299
|
+
--fg-button-shadow: var(--shadow-sink);
|
|
300
|
+
box-shadow: var(--fg-button-shadow);
|
|
301
|
+
}
|
|
302
|
+
.fg-button[data-variant="secondary"]:is(:active, [aria-pressed="true"]):not(:disabled) { background: var(--color-well); }
|
|
303
|
+
.fg-button:disabled { box-shadow: none; }
|
|
304
|
+
|
|
292
305
|
.fg-input,
|
|
293
306
|
.fg-select {
|
|
294
307
|
width: 100%;
|
|
@@ -759,13 +772,14 @@
|
|
|
759
772
|
border-radius: var(--radius-control);
|
|
760
773
|
background: var(--color-well);
|
|
761
774
|
box-shadow: var(--shadow-sink);
|
|
775
|
+
/* Thumbs stack on the track, under every label, so a sliding thumb never covers its neighbour's. */
|
|
776
|
+
isolation: isolate;
|
|
762
777
|
}
|
|
763
778
|
:root.dark .fg-segmented { --fg-segmented-thumb: color-mix(in srgb, var(--color-surface-raised) 88%, white); }
|
|
764
779
|
.fg-segmented[data-size="sm"] { --fg-segmented-item: var(--control-sm); font-size: var(--text-sm); }
|
|
765
780
|
.fg-segmented[data-size="lg"] { --fg-segmented-item: var(--control-lg); font-size: var(--text-lg); }
|
|
766
781
|
.fg-segmented__item {
|
|
767
782
|
position: relative;
|
|
768
|
-
isolation: isolate;
|
|
769
783
|
display: inline-flex;
|
|
770
784
|
gap: var(--space-2);
|
|
771
785
|
min-height: var(--fg-segmented-item);
|
|
@@ -781,7 +795,7 @@
|
|
|
781
795
|
}
|
|
782
796
|
.fg-segmented[data-icon-only="true"] .fg-segmented__item { width: var(--fg-segmented-item); padding: 0; }
|
|
783
797
|
/* The thumb is inset inside each full-size button, so it floats on the track and the hit target keeps its size. */
|
|
784
|
-
.fg-segmented__item::before { content: ""; position: absolute; inset: 2px; z-index: -
|
|
798
|
+
.fg-segmented__item::before { content: ""; position: absolute; inset: 2px; z-index: -2; border-radius: var(--radius-control); pointer-events: none; }
|
|
785
799
|
.fg-segmented__item:hover:not(:disabled, [aria-pressed="true"]) { color: var(--color-text); }
|
|
786
800
|
.fg-segmented__item:hover:not(:disabled, [aria-pressed="true"])::before { background: color-mix(in srgb, var(--fg-segmented-thumb) 45%, transparent); }
|
|
787
801
|
/* The accent label or icon marks the pressed choice at 3:1 against its thumb. */
|
|
@@ -789,9 +803,20 @@
|
|
|
789
803
|
/* Drawn inside the button, the ring hugs the thumb instead of reaching the next choice. */
|
|
790
804
|
.fg-segmented .fg-segmented__item:focus-visible { outline-offset: -2px; }
|
|
791
805
|
.fg-segmented__item[aria-pressed="true"]::before {
|
|
806
|
+
z-index: -1;
|
|
792
807
|
background: var(--fg-segmented-thumb);
|
|
793
808
|
box-shadow: var(--shadow-raise);
|
|
794
809
|
}
|
|
810
|
+
/* Motion answers only the reader: hover, and a change of choice, which the script marks
|
|
811
|
+
* with the thumb's start over the previous option. A theme change recolours at once. */
|
|
812
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
813
|
+
.fg-segmented__item:hover:not(:disabled, [aria-pressed="true"]) { transition: color var(--duration-fast) var(--ease-out); }
|
|
814
|
+
.fg-segmented[data-slide] .fg-segmented__item { transition: color var(--duration) var(--ease-out); }
|
|
815
|
+
.fg-segmented[data-slide] .fg-segmented__item[aria-pressed="true"]::before { animation: fg-segmented-slide var(--duration) var(--ease-out); }
|
|
816
|
+
}
|
|
817
|
+
@keyframes fg-segmented-slide {
|
|
818
|
+
from { left: var(--fg-segmented-slide-left); right: var(--fg-segmented-slide-right); }
|
|
819
|
+
}
|
|
795
820
|
|
|
796
821
|
.fg-switch { display: inline-flex; align-items: center; gap: var(--space-2); min-width: 0; }
|
|
797
822
|
.fg-switch__control {
|
|
@@ -1024,6 +1049,10 @@
|
|
|
1024
1049
|
box-shadow: 0 0 0 6px color-mix(in srgb, var(--color-accent) 18%, transparent);
|
|
1025
1050
|
}
|
|
1026
1051
|
|
|
1052
|
+
.fg-button:focus-visible:not(:disabled) {
|
|
1053
|
+
box-shadow: var(--fg-button-shadow), 0 0 0 6px color-mix(in srgb, var(--color-accent) 18%, transparent);
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1027
1056
|
.fg-input:focus-visible,
|
|
1028
1057
|
.fg-select:focus-visible,
|
|
1029
1058
|
.fg-radio-card[data-state="checked"]:focus-visible,
|
package/src/site.css
CHANGED
|
@@ -201,30 +201,39 @@
|
|
|
201
201
|
|
|
202
202
|
/* Route rows. Hover is neutral, and the current page keeps its selected fill on
|
|
203
203
|
* hover. An in-page section marker (aria-current="location") must look different
|
|
204
|
-
* from the current page, so it
|
|
204
|
+
* from the current page, so it uses an accent pip without the raised fill. */
|
|
205
205
|
.fg-site-nav a {
|
|
206
206
|
display: flex;
|
|
207
207
|
min-height: var(--row);
|
|
208
208
|
align-items: center;
|
|
209
209
|
padding-inline: var(--space-3);
|
|
210
|
-
border-radius: var(--radius-
|
|
210
|
+
border-radius: var(--radius-field);
|
|
211
211
|
color: var(--color-muted);
|
|
212
212
|
font-family: var(--font-mono);
|
|
213
213
|
font-size: var(--text-sm);
|
|
214
214
|
text-decoration: none;
|
|
215
|
-
transition: background-color var(--duration-fast) ease, color var(--duration-fast) ease;
|
|
215
|
+
transition: background-color var(--duration-fast) ease, color var(--duration-fast) ease, box-shadow var(--duration-fast) ease;
|
|
216
216
|
}
|
|
217
217
|
.fg-site-nav a:hover { background: var(--color-row-hover); color: var(--color-text); }
|
|
218
218
|
.fg-site-nav a[aria-current="page"] {
|
|
219
|
-
background: var(--color-
|
|
220
|
-
box-shadow:
|
|
221
|
-
color: var(--color-
|
|
219
|
+
background: var(--color-nav-current);
|
|
220
|
+
box-shadow: var(--shadow-nav-current);
|
|
221
|
+
color: var(--color-accent);
|
|
222
222
|
font-weight: var(--weight-medium);
|
|
223
223
|
}
|
|
224
|
-
.fg-site-nav a[aria-current="location"] {
|
|
224
|
+
.fg-site-nav a[aria-current="location"] { position: relative; color: var(--color-text); font-weight: var(--weight-medium); }
|
|
225
|
+
.fg-site-nav a[aria-current="location"]::before {
|
|
226
|
+
content: "";
|
|
227
|
+
position: absolute;
|
|
228
|
+
inset-inline-start: var(--space-1);
|
|
229
|
+
width: 3px;
|
|
230
|
+
height: 14px;
|
|
231
|
+
border-radius: var(--radius-pill);
|
|
232
|
+
background: var(--color-accent);
|
|
233
|
+
}
|
|
225
234
|
.fg-site-nav a code { font-family: var(--font-mono); font-size: var(--text-sm); }
|
|
226
235
|
|
|
227
|
-
/* Forced colors drop
|
|
236
|
+
/* Forced colors drop shadows, so the current link takes a system border. */
|
|
228
237
|
@media (forced-colors: active) {
|
|
229
238
|
.fg-site-nav a[aria-current] { border-inline-start: 2px solid Highlight; }
|
|
230
239
|
}
|
package/src/theme.css
CHANGED
|
@@ -38,6 +38,9 @@
|
|
|
38
38
|
--color-surface-raised: #fbfbfc;
|
|
39
39
|
--color-surface-muted: #e2e5e8;
|
|
40
40
|
--color-well: var(--color-surface-muted);
|
|
41
|
+
--color-control-thumb: var(--color-surface-raised);
|
|
42
|
+
--color-nav-current: var(--color-surface-raised);
|
|
43
|
+
--shadow-nav-current: 0 1px 3px rgb(130 142 158 / 40%);
|
|
41
44
|
--color-text: #101315;
|
|
42
45
|
--color-muted: #50575e;
|
|
43
46
|
--color-link: #0b5f7c;
|
|
@@ -82,6 +85,7 @@
|
|
|
82
85
|
--radius-inline: 6px;
|
|
83
86
|
--radius-control: 999px;
|
|
84
87
|
--radius-panel: 16px;
|
|
88
|
+
--radius-field: 10px;
|
|
85
89
|
--radius-pill: 999px;
|
|
86
90
|
--duration-fast: 120ms;
|
|
87
91
|
--duration: 180ms;
|
|
@@ -110,6 +114,9 @@
|
|
|
110
114
|
--color-surface-raised: #181d21;
|
|
111
115
|
--color-surface-muted: #20262b;
|
|
112
116
|
--color-well: color-mix(in srgb, var(--color-bg) 87.5%, black);
|
|
117
|
+
--color-control-thumb: color-mix(in srgb, var(--color-surface-raised) 88%, white);
|
|
118
|
+
--color-nav-current: color-mix(in srgb, var(--color-text) 7%, transparent);
|
|
119
|
+
--shadow-nav-current: 0 1px 2px rgb(0 0 0 / 25%);
|
|
113
120
|
--color-text: #e6e9eb;
|
|
114
121
|
--color-muted: #9aa3ab;
|
|
115
122
|
--color-link: #67c1df;
|