@filipgutica/ui 1.4.1 → 1.6.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 +65 -1
- package/dist/index.js +167 -404
- package/dist/site/CaptureScreen.vue.d.ts +9 -0
- package/dist/site/SiteNavLinks.vue.d.ts +14 -0
- package/dist/site/UiCapture.vue.d.ts +40 -0
- package/dist/site/UiCommandList.vue.d.ts +26 -0
- package/dist/site/UiSiteNav.vue.d.ts +21 -0
- package/dist/site/index.d.ts +6 -0
- package/dist/site/projects.d.ts +5 -0
- package/dist/site.js +390 -59
- package/dist/use-active-section-CiflgGBI.js +255 -0
- package/dist/use-clipboard-copy.d.ts +17 -0
- package/package.json +1 -1
- package/src/site.css +284 -2
|
@@ -0,0 +1,255 @@
|
|
|
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";
|
|
4
|
+
//#endregion
|
|
5
|
+
//#region src/components/UiButton.vue
|
|
6
|
+
var j = /* @__PURE__ */ c({
|
|
7
|
+
__name: "UiButton",
|
|
8
|
+
props: {
|
|
9
|
+
disabled: {
|
|
10
|
+
type: Boolean,
|
|
11
|
+
default: !1
|
|
12
|
+
},
|
|
13
|
+
loading: {
|
|
14
|
+
type: Boolean,
|
|
15
|
+
default: !1
|
|
16
|
+
},
|
|
17
|
+
size: { default: "md" },
|
|
18
|
+
type: { default: "button" },
|
|
19
|
+
variant: { default: "primary" }
|
|
20
|
+
},
|
|
21
|
+
setup(e) {
|
|
22
|
+
return (t, r) => (f(), n(v(k), {
|
|
23
|
+
as: "button",
|
|
24
|
+
class: "fg-button",
|
|
25
|
+
"data-size": e.size,
|
|
26
|
+
"data-variant": e.variant,
|
|
27
|
+
"aria-busy": e.loading ? "true" : void 0,
|
|
28
|
+
disabled: e.disabled || e.loading,
|
|
29
|
+
type: e.type
|
|
30
|
+
}, {
|
|
31
|
+
default: b(() => [g(t.$slots, "default")]),
|
|
32
|
+
_: 3
|
|
33
|
+
}, 8, [
|
|
34
|
+
"data-size",
|
|
35
|
+
"data-variant",
|
|
36
|
+
"aria-busy",
|
|
37
|
+
"disabled",
|
|
38
|
+
"type"
|
|
39
|
+
]));
|
|
40
|
+
}
|
|
41
|
+
}), M = ({ unavailableMessage: e, failureMessage: t }) => {
|
|
42
|
+
let n = m(!1), r = m(!1), i = m(""), { start: a } = A(() => {
|
|
43
|
+
n.value = !1;
|
|
44
|
+
}, 1500, { immediate: !1 });
|
|
45
|
+
return {
|
|
46
|
+
copied: p(n),
|
|
47
|
+
pending: p(r),
|
|
48
|
+
error: p(i),
|
|
49
|
+
copy: async (o) => {
|
|
50
|
+
if (i.value = "", n.value = !1, typeof navigator > "u" || !navigator.clipboard?.writeText) {
|
|
51
|
+
i.value = e;
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
r.value = !0;
|
|
55
|
+
try {
|
|
56
|
+
await navigator.clipboard.writeText(o), n.value = !0, a();
|
|
57
|
+
} catch {
|
|
58
|
+
i.value = t;
|
|
59
|
+
} finally {
|
|
60
|
+
r.value = !1;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
}, N = { class: "fg-dialog__header" }, P = { class: "fg-dialog__body" }, F = {
|
|
65
|
+
key: 0,
|
|
66
|
+
class: "fg-dialog__footer"
|
|
67
|
+
}, I = /* @__PURE__ */ c({
|
|
68
|
+
inheritAttrs: !1,
|
|
69
|
+
__name: "UiDialog",
|
|
70
|
+
props: {
|
|
71
|
+
description: { default: () => void 0 },
|
|
72
|
+
open: { type: Boolean },
|
|
73
|
+
title: {}
|
|
74
|
+
},
|
|
75
|
+
emits: ["update:open"],
|
|
76
|
+
setup(e, { emit: t }) {
|
|
77
|
+
let c = t;
|
|
78
|
+
return (t, u) => (f(), n(v(E), {
|
|
79
|
+
open: e.open,
|
|
80
|
+
"onUpdate:open": u[0] ||= (e) => c("update:open", e)
|
|
81
|
+
}, {
|
|
82
|
+
default: b(() => [s(v(T), null, {
|
|
83
|
+
default: b(() => [s(v(w), { class: "fg-dialog__overlay" }), s(v(S), l({
|
|
84
|
+
class: "fg-dialog__content",
|
|
85
|
+
"aria-modal": "true"
|
|
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)]),
|
|
90
|
+
_: 1
|
|
91
|
+
}), e.description ? (f(), n(v(C), {
|
|
92
|
+
key: 0,
|
|
93
|
+
class: "fg-dialog__description"
|
|
94
|
+
}, {
|
|
95
|
+
default: b(() => [o(_(e.description), 1)]),
|
|
96
|
+
_: 1
|
|
97
|
+
})) : r("", !0)]), s(v(x), {
|
|
98
|
+
class: "fg-dialog__close",
|
|
99
|
+
"aria-label": "Close dialog"
|
|
100
|
+
}, {
|
|
101
|
+
default: b(() => [...u[1] ||= [o(" × ", -1)]]),
|
|
102
|
+
_: 1
|
|
103
|
+
})]),
|
|
104
|
+
a("div", P, [g(t.$slots, "default")]),
|
|
105
|
+
t.$slots.footer ? (f(), i("div", F, [g(t.$slots, "footer")])) : r("", !0)
|
|
106
|
+
]),
|
|
107
|
+
_: 3
|
|
108
|
+
}, 16)]),
|
|
109
|
+
_: 3
|
|
110
|
+
})]),
|
|
111
|
+
_: 3
|
|
112
|
+
}, 8, ["open"]));
|
|
113
|
+
}
|
|
114
|
+
}), L = { class: "fg-drawer__header" }, R = { class: "fg-drawer__body" }, z = /* @__PURE__ */ c({
|
|
115
|
+
inheritAttrs: !1,
|
|
116
|
+
__name: "UiDrawer",
|
|
117
|
+
props: {
|
|
118
|
+
open: { type: Boolean },
|
|
119
|
+
title: {}
|
|
120
|
+
},
|
|
121
|
+
emits: ["update:open", "closeAutoFocus"],
|
|
122
|
+
setup(e, { emit: t }) {
|
|
123
|
+
let i = t;
|
|
124
|
+
return (t, c) => (f(), n(v(E), {
|
|
125
|
+
open: e.open,
|
|
126
|
+
"onUpdate:open": c[1] ||= (e) => i("update:open", e)
|
|
127
|
+
}, {
|
|
128
|
+
default: b(() => [t.$slots.trigger ? (f(), n(v(O), {
|
|
129
|
+
key: 0,
|
|
130
|
+
"as-child": ""
|
|
131
|
+
}, {
|
|
132
|
+
default: b(() => [g(t.$slots, "trigger")]),
|
|
133
|
+
_: 3
|
|
134
|
+
})) : r("", !0), s(v(T), null, {
|
|
135
|
+
default: b(() => [s(v(w), { class: "fg-drawer__overlay" }), s(v(S), l({
|
|
136
|
+
class: "fg-drawer__content",
|
|
137
|
+
"aria-modal": "true",
|
|
138
|
+
"aria-describedby": void 0
|
|
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)]),
|
|
142
|
+
_: 1
|
|
143
|
+
}), s(v(x), {
|
|
144
|
+
class: "fg-drawer__close",
|
|
145
|
+
"aria-label": "Close drawer"
|
|
146
|
+
}, {
|
|
147
|
+
default: b(() => [...c[2] ||= [o(" × ", -1)]]),
|
|
148
|
+
_: 1
|
|
149
|
+
})]), a("div", R, [g(t.$slots, "default")])]),
|
|
150
|
+
_: 3
|
|
151
|
+
}, 16)]),
|
|
152
|
+
_: 3
|
|
153
|
+
})]),
|
|
154
|
+
_: 3
|
|
155
|
+
}, 8, ["open"]));
|
|
156
|
+
}
|
|
157
|
+
}), B = { class: "fg-tabs" }, V = ["aria-label"], H = [
|
|
158
|
+
"id",
|
|
159
|
+
"aria-controls",
|
|
160
|
+
"aria-selected",
|
|
161
|
+
"tabindex",
|
|
162
|
+
"onClick",
|
|
163
|
+
"onFocus"
|
|
164
|
+
], U = [
|
|
165
|
+
"id",
|
|
166
|
+
"role",
|
|
167
|
+
"aria-labelledby",
|
|
168
|
+
"hidden",
|
|
169
|
+
"tabindex"
|
|
170
|
+
], W = ["id", "hidden"], G = /* @__PURE__ */ c({
|
|
171
|
+
__name: "UiTabs",
|
|
172
|
+
props: {
|
|
173
|
+
items: {},
|
|
174
|
+
label: {},
|
|
175
|
+
modelValue: {}
|
|
176
|
+
},
|
|
177
|
+
emits: ["update:modelValue"],
|
|
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)}`;
|
|
180
|
+
u(() => {
|
|
181
|
+
l.value = !0;
|
|
182
|
+
});
|
|
183
|
+
let x = (e) => {
|
|
184
|
+
e !== d.value && s("update:modelValue", e);
|
|
185
|
+
}, S = (e) => {
|
|
186
|
+
if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey || ![
|
|
187
|
+
"ArrowLeft",
|
|
188
|
+
"ArrowRight",
|
|
189
|
+
"Home",
|
|
190
|
+
"End"
|
|
191
|
+
].includes(e.key)) return;
|
|
192
|
+
let t = e.currentTarget, n = e.target;
|
|
193
|
+
if (!(t instanceof HTMLElement) || !(n instanceof HTMLButtonElement)) return;
|
|
194
|
+
let r = [...t.querySelectorAll("[role=\"tab\"]")], i = r.indexOf(n);
|
|
195
|
+
if (i < 0 || r.length === 0) return;
|
|
196
|
+
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());
|
|
198
|
+
};
|
|
199
|
+
return (t, o) => (f(), i("div", B, [l.value ? (f(), i("div", {
|
|
200
|
+
key: 0,
|
|
201
|
+
class: "fg-tabs__list",
|
|
202
|
+
role: "tablist",
|
|
203
|
+
"aria-label": n.label,
|
|
204
|
+
onKeydown: S
|
|
205
|
+
}, [(f(!0), i(e, null, h(n.items, (e) => (f(), i("button", {
|
|
206
|
+
id: p(e.value),
|
|
207
|
+
key: e.value,
|
|
208
|
+
type: "button",
|
|
209
|
+
class: "fg-tabs__tab",
|
|
210
|
+
role: "tab",
|
|
211
|
+
"aria-controls": v(e.value),
|
|
212
|
+
"aria-selected": e.value === d.value,
|
|
213
|
+
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", {
|
|
217
|
+
id: v(e.value),
|
|
218
|
+
key: e.value,
|
|
219
|
+
class: "fg-tabs__panel",
|
|
220
|
+
role: l.value ? "tabpanel" : void 0,
|
|
221
|
+
"aria-labelledby": l.value ? p(e.value) : b(e.value),
|
|
222
|
+
hidden: l.value && e.value !== d.value,
|
|
223
|
+
tabindex: l.value ? 0 : void 0
|
|
224
|
+
}, [a("h3", {
|
|
225
|
+
id: b(e.value),
|
|
226
|
+
class: "fg-tabs__heading",
|
|
227
|
+
hidden: l.value
|
|
228
|
+
}, _(e.label), 9, W), g(t.$slots, "panel", { value: e.value })], 8, U))), 128))]));
|
|
229
|
+
}
|
|
230
|
+
}), K = ({ targetIds: e }) => {
|
|
231
|
+
let t = m(e[0]), n, r, i;
|
|
232
|
+
return u(() => {
|
|
233
|
+
let a = e.flatMap((e) => {
|
|
234
|
+
let t = document.getElementById(e);
|
|
235
|
+
return t ? [{
|
|
236
|
+
id: e,
|
|
237
|
+
element: t
|
|
238
|
+
}] : [];
|
|
239
|
+
}), o = () => {
|
|
240
|
+
n = void 0;
|
|
241
|
+
let e = document.documentElement, r = Number.parseFloat(getComputedStyle(e).scrollPaddingTop) || 0, i = a[0]?.id;
|
|
242
|
+
for (let e of a) e.element.getBoundingClientRect().top <= r + 1 && (i = e.id);
|
|
243
|
+
window.scrollY > 0 && window.scrollY + window.innerHeight >= e.scrollHeight - 2 && (i = a.at(-1)?.id), t.value = i;
|
|
244
|
+
}, s = () => {
|
|
245
|
+
n === void 0 && (n = requestAnimationFrame(o));
|
|
246
|
+
};
|
|
247
|
+
window.addEventListener("scroll", s, { passive: !0 }), window.addEventListener("resize", s), window.addEventListener("pageshow", s), document.body.addEventListener("transitionend", s), typeof ResizeObserver < "u" && (r = new ResizeObserver(s), r.observe(document.body)), i = () => {
|
|
248
|
+
window.removeEventListener("scroll", s), window.removeEventListener("resize", s), window.removeEventListener("pageshow", s), document.body.removeEventListener("transitionend", s);
|
|
249
|
+
}, o();
|
|
250
|
+
}), d(() => {
|
|
251
|
+
i?.(), r?.disconnect(), n !== void 0 && cancelAnimationFrame(n);
|
|
252
|
+
}), p(t);
|
|
253
|
+
};
|
|
254
|
+
//#endregion
|
|
255
|
+
export { M as a, I as i, G as n, j as o, z as r, K as t };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Ref } from 'vue';
|
|
2
|
+
export interface ClipboardCopyOptions {
|
|
3
|
+
/** Shown when the browser has no clipboard API. */
|
|
4
|
+
unavailableMessage: string;
|
|
5
|
+
/** Shown when the browser rejects the write. */
|
|
6
|
+
failureMessage: string;
|
|
7
|
+
}
|
|
8
|
+
export interface ClipboardCopy {
|
|
9
|
+
/** True for a short time after a successful copy. */
|
|
10
|
+
copied: Readonly<Ref<boolean>>;
|
|
11
|
+
pending: Readonly<Ref<boolean>>;
|
|
12
|
+
/** The last failure message, cleared when a new copy starts. */
|
|
13
|
+
error: Readonly<Ref<string>>;
|
|
14
|
+
copy: (text: string) => Promise<void>;
|
|
15
|
+
}
|
|
16
|
+
/** Copies text to the clipboard and reports the outcome for a live status message. */
|
|
17
|
+
export declare const useClipboardCopy: ({ unavailableMessage, failureMessage }: ClipboardCopyOptions) => ClipboardCopy;
|
package/package.json
CHANGED
package/src/site.css
CHANGED
|
@@ -81,8 +81,8 @@
|
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
/* Route rows. Hover is neutral, and the current page keeps its selected fill on
|
|
84
|
-
* hover.
|
|
85
|
-
*
|
|
84
|
+
* hover. An in-page section marker (aria-current="location") must look different
|
|
85
|
+
* from the current page, so it only strengthens the text. */
|
|
86
86
|
.fg-site-nav a {
|
|
87
87
|
display: flex;
|
|
88
88
|
min-height: var(--row);
|
|
@@ -101,6 +101,8 @@
|
|
|
101
101
|
color: var(--color-text);
|
|
102
102
|
font-weight: var(--weight-medium);
|
|
103
103
|
}
|
|
104
|
+
.fg-site-nav a[aria-current="location"] { color: var(--color-text); font-weight: var(--weight-medium); }
|
|
105
|
+
.fg-site-nav a code { font-family: var(--font-mono); font-size: var(--text-sm); }
|
|
104
106
|
|
|
105
107
|
.fg-site-appearance {
|
|
106
108
|
display: inline-flex;
|
|
@@ -133,6 +135,224 @@
|
|
|
133
135
|
.fg-site-appearance__choice:focus-visible { outline-offset: -2px; }
|
|
134
136
|
.fg-site-nav a:focus-visible { outline: 2px solid var(--color-focus); outline-offset: -2px; }
|
|
135
137
|
|
|
138
|
+
/* Section rail (UiSiteNav). The page grid places it; on desktop it stays in view
|
|
139
|
+
* beside the article. At 800px and below it becomes a Menu disclosure, replaced
|
|
140
|
+
* by a drawer once the page is interactive. */
|
|
141
|
+
.fg-site-rail {
|
|
142
|
+
position: sticky;
|
|
143
|
+
top: var(--space-6);
|
|
144
|
+
align-self: start;
|
|
145
|
+
min-width: 0;
|
|
146
|
+
max-height: calc(100svh - var(--space-12));
|
|
147
|
+
overflow-y: auto;
|
|
148
|
+
padding-block: var(--space-4);
|
|
149
|
+
color: var(--color-text);
|
|
150
|
+
font-family: var(--font-ui);
|
|
151
|
+
font-size: var(--text-md);
|
|
152
|
+
line-height: var(--leading);
|
|
153
|
+
}
|
|
154
|
+
.fg-site-rail__summary { display: none; }
|
|
155
|
+
.fg-site-rail__nav { display: grid; gap: var(--space-2); }
|
|
156
|
+
.fg-site-rail__label,
|
|
157
|
+
.fg-site-rail__group-label {
|
|
158
|
+
margin: 0;
|
|
159
|
+
padding-inline: var(--space-3);
|
|
160
|
+
color: var(--color-muted);
|
|
161
|
+
font-size: var(--text-sm);
|
|
162
|
+
font-weight: var(--weight-regular);
|
|
163
|
+
}
|
|
164
|
+
.fg-site-rail__group { display: grid; gap: 2px; }
|
|
165
|
+
.fg-site-rail__group + .fg-site-rail__group { margin-top: var(--space-3); }
|
|
166
|
+
.fg-site-rail__group-label { margin-block-end: var(--space-1); }
|
|
167
|
+
|
|
168
|
+
/* Capture panel (UiCapture): tabs in the header, the capture as the body, and the
|
|
169
|
+
* command and caption as the footer. The site owns terminal colors and sets the
|
|
170
|
+
* screen background with --site-capture-bg on :root, which also reaches the
|
|
171
|
+
* full-size viewer. Nothing clips the panel, so focus rings inside it stay whole. */
|
|
172
|
+
.fg-capture {
|
|
173
|
+
min-width: 0;
|
|
174
|
+
border: 1px solid var(--color-border-subtle);
|
|
175
|
+
border-radius: var(--radius-panel);
|
|
176
|
+
background: var(--color-surface);
|
|
177
|
+
}
|
|
178
|
+
.fg-capture .fg-tabs__list { width: 100%; margin-block-end: 0; padding-inline: var(--space-2); }
|
|
179
|
+
.fg-capture .fg-tabs__tab { min-height: var(--control-lg); }
|
|
180
|
+
/* Without JavaScript every capture is listed under its own heading. */
|
|
181
|
+
.fg-capture .fg-tabs__heading { margin: 0; padding: var(--space-3) var(--space-4); }
|
|
182
|
+
.fg-capture:not([data-enhanced="true"]) .fg-tabs__panel + .fg-tabs__panel { border-top: 1px solid var(--color-border-subtle); }
|
|
183
|
+
.fg-capture[data-enhanced="true"] .fg-tabs__panel,
|
|
184
|
+
.fg-capture .fg-tabs__panel:last-child {
|
|
185
|
+
overflow: clip;
|
|
186
|
+
border-end-start-radius: calc(var(--radius-panel) - 1px);
|
|
187
|
+
border-end-end-radius: calc(var(--radius-panel) - 1px);
|
|
188
|
+
}
|
|
189
|
+
.fg-capture__figure { display: grid; min-width: 0; margin: 0; }
|
|
190
|
+
.fg-capture__preview { position: relative; min-width: 0; }
|
|
191
|
+
.fg-capture__screen {
|
|
192
|
+
min-width: 0;
|
|
193
|
+
max-width: 100%;
|
|
194
|
+
overflow-x: auto;
|
|
195
|
+
background: var(--site-capture-bg, var(--color-code));
|
|
196
|
+
}
|
|
197
|
+
.fg-capture__screen > * { width: max-content; margin: 0; }
|
|
198
|
+
.fg-capture__screen[data-fit] { overflow: hidden; }
|
|
199
|
+
.fg-capture__screen[data-fit] > * { transform: scale(var(--capture-scale, 1)); transform-origin: top left; }
|
|
200
|
+
.fg-capture__expand {
|
|
201
|
+
position: absolute;
|
|
202
|
+
inset: 0;
|
|
203
|
+
width: 100%;
|
|
204
|
+
padding: 0;
|
|
205
|
+
border: 0;
|
|
206
|
+
border-radius: var(--radius-control);
|
|
207
|
+
background: transparent;
|
|
208
|
+
cursor: zoom-in;
|
|
209
|
+
}
|
|
210
|
+
.fg-capture__caption {
|
|
211
|
+
display: grid;
|
|
212
|
+
justify-items: start;
|
|
213
|
+
gap: var(--space-1);
|
|
214
|
+
padding: var(--space-3) var(--space-4);
|
|
215
|
+
border-top: 1px solid var(--color-border-subtle);
|
|
216
|
+
color: var(--color-muted);
|
|
217
|
+
font-size: var(--text-sm);
|
|
218
|
+
line-height: var(--leading);
|
|
219
|
+
}
|
|
220
|
+
.fg-capture__caption > code { color: var(--color-text); font-family: var(--font-mono); font-size: var(--text-sm); overflow-wrap: anywhere; }
|
|
221
|
+
.fg-capture__screen:focus-visible { outline: 2px solid var(--color-focus); outline-offset: -2px; }
|
|
222
|
+
.fg-capture__expand:focus-visible { outline: 2px solid var(--color-focus); outline-offset: -4px; }
|
|
223
|
+
.fg-dialog__content.fg-capture-viewer {
|
|
224
|
+
width: calc(100vw - var(--space-8));
|
|
225
|
+
max-width: var(--page-max);
|
|
226
|
+
max-height: calc(100dvh - var(--space-8));
|
|
227
|
+
}
|
|
228
|
+
.fg-capture-viewer .fg-capture__screen {
|
|
229
|
+
max-height: calc(100dvh - var(--space-16) * 3);
|
|
230
|
+
overflow: auto;
|
|
231
|
+
border-radius: var(--radius-control);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/* Command table (UiCommandList). Wide commands scroll inside it on larger
|
|
235
|
+
* screens; at 640px and below each command stacks above its description. */
|
|
236
|
+
.fg-command-list {
|
|
237
|
+
position: relative;
|
|
238
|
+
min-width: 0;
|
|
239
|
+
max-width: 100%;
|
|
240
|
+
overflow-x: auto;
|
|
241
|
+
color: var(--color-text);
|
|
242
|
+
font-family: var(--font-ui);
|
|
243
|
+
font-size: var(--text-md);
|
|
244
|
+
line-height: var(--leading);
|
|
245
|
+
}
|
|
246
|
+
.fg-command-list__table { width: 100%; border-collapse: collapse; }
|
|
247
|
+
.fg-command-list th {
|
|
248
|
+
padding: 0 var(--space-3) var(--space-2) 0;
|
|
249
|
+
color: var(--color-muted);
|
|
250
|
+
font-size: var(--text-sm);
|
|
251
|
+
font-weight: var(--weight-medium);
|
|
252
|
+
text-align: start;
|
|
253
|
+
}
|
|
254
|
+
.fg-command-list td {
|
|
255
|
+
padding: var(--space-3) var(--space-3) var(--space-3) 0;
|
|
256
|
+
border-top: 1px solid var(--color-border-subtle);
|
|
257
|
+
vertical-align: top;
|
|
258
|
+
}
|
|
259
|
+
.fg-command-list thead td { padding: 0; border: 0; }
|
|
260
|
+
.fg-command-list tbody tr:last-child td { border-bottom: 1px solid var(--color-border-subtle); }
|
|
261
|
+
.fg-command-list__command { width: 1%; white-space: nowrap; }
|
|
262
|
+
.fg-command-list__command code { font-family: var(--font-mono); font-size: var(--text-sm); }
|
|
263
|
+
.fg-command-list__description { color: var(--color-muted); }
|
|
264
|
+
.fg-command-list td.fg-command-list__action { width: 1%; padding: var(--space-1) 0; }
|
|
265
|
+
.fg-command-list__copy {
|
|
266
|
+
display: inline-grid;
|
|
267
|
+
width: var(--control-lg);
|
|
268
|
+
height: var(--control-lg);
|
|
269
|
+
padding: 0;
|
|
270
|
+
place-items: center;
|
|
271
|
+
border: 0;
|
|
272
|
+
border-radius: var(--radius-control);
|
|
273
|
+
color: var(--color-muted);
|
|
274
|
+
background: transparent;
|
|
275
|
+
font: inherit;
|
|
276
|
+
cursor: pointer;
|
|
277
|
+
}
|
|
278
|
+
.fg-command-list__copy:hover { color: var(--color-text); background: var(--color-row-hover); }
|
|
279
|
+
.fg-command-list__copy svg { width: 1rem; height: 1rem; }
|
|
280
|
+
.fg-command-list__copy:focus-visible { outline: 2px solid var(--color-focus); outline-offset: -2px; }
|
|
281
|
+
.fg-command-list__status:not(:empty) { display: block; padding-top: var(--space-2); font-size: var(--text-sm); }
|
|
282
|
+
|
|
283
|
+
/* Page helpers. Steps number a real sequence; facts are short label and value
|
|
284
|
+
* pairs on one line; definitions pair terms with short meanings. Definitions
|
|
285
|
+
* take dt and dd as direct children. */
|
|
286
|
+
.fg-site-steps { display: grid; gap: var(--space-5); margin: 0; padding: 0; list-style: none; counter-reset: fg-site-step; }
|
|
287
|
+
.fg-site-steps > li {
|
|
288
|
+
position: relative;
|
|
289
|
+
min-width: 0;
|
|
290
|
+
padding-inline-start: calc(var(--space-6) + var(--space-3));
|
|
291
|
+
counter-increment: fg-site-step;
|
|
292
|
+
}
|
|
293
|
+
.fg-site-steps > li::before {
|
|
294
|
+
content: counter(fg-site-step);
|
|
295
|
+
position: absolute;
|
|
296
|
+
top: 0;
|
|
297
|
+
inset-inline-start: 0;
|
|
298
|
+
display: grid;
|
|
299
|
+
width: var(--space-6);
|
|
300
|
+
height: var(--space-6);
|
|
301
|
+
place-items: center;
|
|
302
|
+
border: 1px solid var(--color-border);
|
|
303
|
+
border-radius: var(--radius-pill);
|
|
304
|
+
color: var(--color-muted);
|
|
305
|
+
font-size: var(--text-xs);
|
|
306
|
+
font-variant-numeric: tabular-nums;
|
|
307
|
+
line-height: 1;
|
|
308
|
+
}
|
|
309
|
+
.fg-site-facts {
|
|
310
|
+
display: flex;
|
|
311
|
+
flex-wrap: wrap;
|
|
312
|
+
gap: var(--space-2) var(--space-8);
|
|
313
|
+
margin: 0;
|
|
314
|
+
color: var(--color-muted);
|
|
315
|
+
font-size: var(--text-sm);
|
|
316
|
+
}
|
|
317
|
+
.fg-site-facts > div { display: flex; gap: var(--space-2); }
|
|
318
|
+
.fg-site-facts dd { margin: 0; color: var(--color-text); }
|
|
319
|
+
.fg-site-definitions {
|
|
320
|
+
display: grid;
|
|
321
|
+
grid-template-columns: 9rem minmax(0, 1fr);
|
|
322
|
+
gap: var(--space-3) var(--space-6);
|
|
323
|
+
max-width: var(--measure);
|
|
324
|
+
margin: 0;
|
|
325
|
+
font-size: var(--text-md);
|
|
326
|
+
line-height: var(--leading);
|
|
327
|
+
}
|
|
328
|
+
.fg-site-definitions dt { font-weight: var(--weight-medium); }
|
|
329
|
+
.fg-site-definitions dd { margin: 0; color: var(--color-muted); }
|
|
330
|
+
|
|
331
|
+
.fg-site-sr-only {
|
|
332
|
+
position: absolute;
|
|
333
|
+
width: 1px;
|
|
334
|
+
height: 1px;
|
|
335
|
+
margin: -1px;
|
|
336
|
+
padding: 0;
|
|
337
|
+
overflow: hidden;
|
|
338
|
+
clip-path: inset(50%);
|
|
339
|
+
white-space: nowrap;
|
|
340
|
+
border: 0;
|
|
341
|
+
}
|
|
342
|
+
.fg-site-skip {
|
|
343
|
+
position: absolute;
|
|
344
|
+
z-index: 60;
|
|
345
|
+
top: -100%;
|
|
346
|
+
left: var(--space-4);
|
|
347
|
+
padding: var(--space-2) var(--space-3);
|
|
348
|
+
border-radius: var(--radius-control);
|
|
349
|
+
color: var(--color-text);
|
|
350
|
+
background: var(--color-surface-raised);
|
|
351
|
+
box-shadow: var(--shadow-dialog);
|
|
352
|
+
}
|
|
353
|
+
.fg-site-skip:focus { top: var(--space-4); }
|
|
354
|
+
.fg-site-skip:focus-visible { outline: 2px solid var(--color-focus); outline-offset: 2px; }
|
|
355
|
+
|
|
136
356
|
/* Type roles for article content. Prose reads at --text-reading. Components set
|
|
137
357
|
* their own size, and controls and dense content such as tables belong at --text-md.
|
|
138
358
|
* Zero-specificity heading selectors keep component and page styles in charge. */
|
|
@@ -152,6 +372,66 @@
|
|
|
152
372
|
.fg-site-prose :where(p a, li a, dd a) { text-decoration: underline; text-underline-offset: 0.18em; }
|
|
153
373
|
.fg-site-meta { color: var(--color-muted); font-size: var(--text-sm); }
|
|
154
374
|
|
|
375
|
+
/* No-JavaScript desktop: the rail's disclosure stays expanded. */
|
|
376
|
+
@media (min-width: 801px) {
|
|
377
|
+
.fg-site-rail__menu::details-content { content-visibility: visible; }
|
|
378
|
+
.fg-site-rail__menu:not([open]) > .fg-site-rail__content { display: block; }
|
|
379
|
+
}
|
|
380
|
+
/* Keep in sync with MOBILE_QUERY in UiSiteNav.vue. */
|
|
381
|
+
@media (max-width: 800px) {
|
|
382
|
+
.fg-site-rail {
|
|
383
|
+
position: static;
|
|
384
|
+
max-height: none;
|
|
385
|
+
overflow: visible;
|
|
386
|
+
padding-block: var(--space-2);
|
|
387
|
+
}
|
|
388
|
+
.fg-site-rail__summary {
|
|
389
|
+
display: flex;
|
|
390
|
+
width: max-content;
|
|
391
|
+
min-height: var(--control-lg);
|
|
392
|
+
align-items: center;
|
|
393
|
+
padding-inline: var(--space-3);
|
|
394
|
+
border-radius: var(--radius-control);
|
|
395
|
+
color: var(--color-muted);
|
|
396
|
+
font-weight: var(--weight-medium);
|
|
397
|
+
list-style: none;
|
|
398
|
+
cursor: pointer;
|
|
399
|
+
}
|
|
400
|
+
.fg-site-rail__summary::-webkit-details-marker { display: none; }
|
|
401
|
+
.fg-site-rail__summary:hover { color: var(--color-text); background: var(--color-row-hover); }
|
|
402
|
+
.fg-site-rail__summary:focus-visible { outline: 2px solid var(--color-focus); outline-offset: -2px; }
|
|
403
|
+
.fg-site-rail__menu > .fg-site-rail__content { padding-block: var(--space-4); }
|
|
404
|
+
.fg-site-rail__menu .fg-site-rail__group { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
|
405
|
+
.fg-site-rail__menu .fg-site-rail__group-label { grid-column: 1 / -1; }
|
|
406
|
+
.fg-site-rail__nav a { min-height: var(--control-lg); }
|
|
407
|
+
}
|
|
408
|
+
@media (max-width: 640px) {
|
|
409
|
+
.fg-dialog__content.fg-capture-viewer { width: 100%; max-width: none; }
|
|
410
|
+
.fg-command-list__table,
|
|
411
|
+
.fg-command-list tbody { display: block; }
|
|
412
|
+
.fg-command-list thead {
|
|
413
|
+
position: absolute;
|
|
414
|
+
width: 1px;
|
|
415
|
+
height: 1px;
|
|
416
|
+
overflow: hidden;
|
|
417
|
+
clip-path: inset(50%);
|
|
418
|
+
white-space: nowrap;
|
|
419
|
+
}
|
|
420
|
+
.fg-command-list tbody tr {
|
|
421
|
+
display: grid;
|
|
422
|
+
grid-template-columns: minmax(0, 1fr) auto;
|
|
423
|
+
column-gap: var(--space-2);
|
|
424
|
+
padding-block: var(--space-3);
|
|
425
|
+
border-top: 1px solid var(--color-border-subtle);
|
|
426
|
+
}
|
|
427
|
+
.fg-command-list tbody tr:last-child { border-bottom: 1px solid var(--color-border-subtle); }
|
|
428
|
+
.fg-command-list td,
|
|
429
|
+
.fg-command-list tbody tr:last-child td,
|
|
430
|
+
.fg-command-list td.fg-command-list__action { width: auto; padding: 0; border: 0; }
|
|
431
|
+
.fg-command-list__command { white-space: normal; overflow-wrap: anywhere; }
|
|
432
|
+
.fg-command-list td.fg-command-list__description { grid-column: 1; padding-top: var(--space-1); }
|
|
433
|
+
.fg-command-list td.fg-command-list__action { grid-column: 2; grid-row: 1 / span 2; }
|
|
434
|
+
}
|
|
155
435
|
@media (min-width: 681px) {
|
|
156
436
|
.fg-site-frame,
|
|
157
437
|
.fg-site-header__inner { padding-inline: var(--site-gutter, var(--space-6)); }
|
|
@@ -161,6 +441,8 @@
|
|
|
161
441
|
.fg-site-header__end { gap: var(--space-2) var(--space-4); }
|
|
162
442
|
.fg-site-header__links { gap: var(--space-4); }
|
|
163
443
|
.fg-site-projects__menu a { min-height: var(--control-lg); }
|
|
444
|
+
.fg-site-definitions { grid-template-columns: minmax(0, 1fr); row-gap: var(--space-1); }
|
|
445
|
+
.fg-site-definitions dd + dt { margin-top: var(--space-3); }
|
|
164
446
|
}
|
|
165
447
|
@media (prefers-reduced-motion: reduce) {
|
|
166
448
|
.fg-site-projects__chevron,
|