vintage-frames 0.13.0 → 0.14.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.
Files changed (79) hide show
  1. package/README.md +16 -0
  2. package/custom-elements.json +184 -20
  3. package/dist/components/vf-checkbox.d.ts +6 -5
  4. package/dist/components/vf-checkbox.js +41 -37
  5. package/dist/components/vf-container.d.ts +14 -14
  6. package/dist/components/vf-container.js +58 -28
  7. package/dist/components/vf-desktop.d.ts +114 -12
  8. package/dist/components/vf-desktop.js +119 -58
  9. package/dist/components/vf-fieldset.js +30 -22
  10. package/dist/components/vf-icon-field.d.ts +1 -1
  11. package/dist/components/vf-icon.js +111 -104
  12. package/dist/components/vf-list.js +30 -29
  13. package/dist/components/vf-menu-bar.d.ts +5 -0
  14. package/dist/components/vf-menu-bar.js +67 -43
  15. package/dist/components/vf-menu.js +24 -22
  16. package/dist/components/vf-number-field.js +32 -30
  17. package/dist/components/vf-progress-bar.js +28 -29
  18. package/dist/components/vf-radio.js +3 -4
  19. package/dist/components/vf-scroll-area.js +27 -26
  20. package/dist/components/vf-select.js +108 -90
  21. package/dist/components/vf-separator.d.ts +5 -5
  22. package/dist/components/vf-separator.js +36 -10
  23. package/dist/components/vf-swatch.js +47 -45
  24. package/dist/components/vf-text-area.js +37 -35
  25. package/dist/components/vf-text-field.js +20 -18
  26. package/dist/components/vf-window.d.ts +13 -0
  27. package/dist/components/vf-window.js +101 -72
  28. package/dist/index.d.ts +12 -1
  29. package/dist/index.js +61 -59
  30. package/dist/pattern-fill.d.ts +11 -8
  31. package/dist/pattern-fill.js +2 -4
  32. package/dist/position.d.ts +25 -7
  33. package/dist/position.js +6 -3
  34. package/dist/shell/alert.d.ts +32 -0
  35. package/dist/shell/alert.js +44 -0
  36. package/dist/shell/catalog.d.ts +168 -0
  37. package/dist/shell/catalog.js +278 -0
  38. package/dist/shell/clock.d.ts +16 -0
  39. package/dist/shell/clock.js +36 -0
  40. package/dist/shell/filing.d.ts +39 -0
  41. package/dist/shell/filing.js +68 -0
  42. package/dist/shell/finder.d.ts +116 -0
  43. package/dist/shell/finder.js +564 -0
  44. package/dist/shell/geometry.d.ts +196 -0
  45. package/dist/shell/geometry.js +241 -0
  46. package/dist/shell/index.d.ts +29 -0
  47. package/dist/shell/index.js +11 -0
  48. package/dist/shell/shell.d.ts +150 -0
  49. package/dist/shell/shell.js +213 -0
  50. package/dist/shell/state.d.ts +88 -0
  51. package/dist/shell/state.js +103 -0
  52. package/dist/shell/storage.d.ts +14 -0
  53. package/dist/shell/storage.js +41 -0
  54. package/dist/shell/windows.d.ts +215 -0
  55. package/dist/shell/windows.js +343 -0
  56. package/dist/styles/base.d.ts +3 -1
  57. package/dist/styles/recipes/field.d.ts +10 -6
  58. package/dist/styles/recipes/field.js +8 -7
  59. package/dist/styles/recipes/focus.d.ts +7 -3
  60. package/dist/styles/recipes/rule.d.ts +11 -8
  61. package/dist/styles/recipes/rule.js +31 -13
  62. package/dist/styles/recipes/scroll-rail.d.ts +7 -6
  63. package/dist/styles/recipes/scroll-rail.js +18 -18
  64. package/dist/styles/recipes/shadow.d.ts +10 -6
  65. package/dist/styles/recipes/shadow.js +4 -2
  66. package/dist/styles/recipes/stroke.d.ts +85 -0
  67. package/dist/styles/recipes/stroke.js +53 -0
  68. package/dist/styles/recipes/surface.d.ts +8 -4
  69. package/dist/styles/recipes/surface.js +23 -17
  70. package/dist/styles/recipes/title-bar.d.ts +7 -0
  71. package/dist/styles/recipes/title-bar.js +25 -22
  72. package/dist/styles/recipes/toggle.js +3 -3
  73. package/dist/window-chrome.d.ts +43 -0
  74. package/dist/window-chrome.js +13 -0
  75. package/dist/zoom.d.ts +9 -9
  76. package/docs/SPEC.md +34 -32
  77. package/editor/vscode.html-custom-data.json +12 -7
  78. package/editor/web-types.json +50 -14
  79. package/package.json +10 -2
@@ -0,0 +1,343 @@
1
+ import { snapSys as e, systemPxQuantum as t } from "../scale.js";
2
+ import { VfWindow as n } from "../components/vf-window.js";
3
+ import "../index.js";
4
+ import { cascadeFrom as r, cascadedBox as i, frameOf as a, nearBox as o, pinOf as s, pinTo as c, windowOrigin as l } from "./geometry.js";
5
+ //#region src/shell/windows.ts
6
+ var u = (e, t) => e.set(t, (e.get(t) ?? 0) + 1), d = (e, t) => {
7
+ let n = e.get(t) ?? 1;
8
+ n > 1 ? e.set(t, n - 1) : e.delete(t);
9
+ }, f = (e, t, n) => Math.min(Math.max(e, t), n), p = (e) => ({
10
+ left: e.left ?? 0,
11
+ top: e.top ?? 0,
12
+ width: e.width ?? 0,
13
+ height: e.height ?? 0
14
+ });
15
+ function m() {
16
+ let e = document.activeElement;
17
+ for (; e?.shadowRoot?.activeElement;) e = e.shadowRoot.activeElement;
18
+ return e;
19
+ }
20
+ var h = (e) => {
21
+ let t = document.activeElement;
22
+ return !!t && (t === e || e.contains(t));
23
+ };
24
+ async function g(e, { when: t } = {}) {
25
+ let n = [...e.querySelectorAll("*")];
26
+ if (await Promise.all([e, ...n].map((e) => e.updateComplete)), !e.isConnected || t && !t()) return !1;
27
+ for (let t of n) if (!(t.closest("[hidden]") || !t.checkVisibility()) && !(t.tabIndex < 0 && !t.localName.startsWith("vf-")) && (t.focus({ preventScroll: !0 }), h(e))) return !0;
28
+ return !1;
29
+ }
30
+ function _(m, _) {
31
+ let { defaultApp: v, savedPin: ee = () => null } = _, y = [], b = (e, t, n) => {
32
+ e.addEventListener(t, n), y.push(() => e.removeEventListener(t, n));
33
+ }, x = () => ({
34
+ width: m.width,
35
+ height: m.height
36
+ }), S = () => m.windowArea, C = /* @__PURE__ */ new Map(), w = /* @__PURE__ */ new Map(), T = /* @__PURE__ */ new Map(), E = /* @__PURE__ */ new Map(), D = /* @__PURE__ */ new Map(), O = /* @__PURE__ */ new WeakMap(), k = {}, A = () => a(S().top, k), j = /* @__PURE__ */ new Set(), M = !1, N = () => {
37
+ if (!M) for (let e of [...j]) e();
38
+ }, P = /* @__PURE__ */ new Set(), F = () => {
39
+ for (let e of [...P]) e();
40
+ }, I = /* @__PURE__ */ new Set(), L = /* @__PURE__ */ new Set(), R = /* @__PURE__ */ new Set(), z = () => null, B = () => !1;
41
+ function V(n, r) {
42
+ let i = S(), a = t(n), o = (e) => Math.floor(e / a) * a, s = Math.ceil(i.top / a) * a, c = i.left + i.width, l = i.top + i.height, { width: u, height: d } = r;
43
+ n.resizable && (u != null && Number.isFinite(u) && (u = Math.min(u, o(i.width))), d != null && Number.isFinite(d) && (d = Math.min(d, o(Math.max(0, l - s)))));
44
+ let p = u ?? n.width ?? 0, m = d ?? n.height ?? 0;
45
+ return {
46
+ left: f(e(r.left ?? i.left, n), i.left, Math.max(i.left, o(c - p))),
47
+ top: f(e(r.top ?? s, n), s, Math.max(s, o(l - m))),
48
+ width: u,
49
+ height: d
50
+ };
51
+ }
52
+ let H = (t, n) => {
53
+ t.left = e(n.left, t), t.top = e(n.top, t), n.width != null && Number.isFinite(n.width) && (t.width = n.width), n.height != null && Number.isFinite(n.height) && (t.height = n.height), O.delete(t);
54
+ }, U = (e) => {
55
+ let t = C.get(e)?.place;
56
+ return t ? V(e, t(S())) : null;
57
+ }, W = (e, t) => {
58
+ let n = C.get(e)?.policy;
59
+ if (n) return n(t);
60
+ if (!e.resizable) return { size: {
61
+ width: t.width,
62
+ height: t.height
63
+ } };
64
+ let { minWidth: r, minHeight: i } = e.sizeLimits;
65
+ return { min: {
66
+ width: r,
67
+ height: i
68
+ } };
69
+ }, G = (e, t, n = W(e, p(e))) => V(e, c(t, x(), A(), n)), te = (e, t) => {
70
+ let n = p(e);
71
+ return [
72
+ "left",
73
+ "top",
74
+ "width",
75
+ "height"
76
+ ].every((e) => {
77
+ let r = t[e];
78
+ return r == null || !Number.isFinite(r) || n[e] === r;
79
+ });
80
+ }, ne = (e) => {
81
+ let t = [...C].filter(([t, n]) => t !== e && !t.hidden && !n.palette).map(([e]) => p(e));
82
+ return r(l(S()), t).slot;
83
+ }, K = v, q = (e) => e instanceof n && C.get(e)?.app || v;
84
+ function J() {
85
+ let e = !1;
86
+ for (let [t, n] of C) {
87
+ if (!n.palette) continue;
88
+ let r = n.app === K && n.palette();
89
+ t.hidden !== !r && (t.hidden = !r, r && m.bringToFront(t), e = !0);
90
+ }
91
+ e && N();
92
+ }
93
+ let re = (e) => {
94
+ let t = document.activeElement?.closest("vf-window");
95
+ !(t instanceof n) || t === e || !C.has(t) || C.get(t).palette || (e ? Y(e) : B(null, null));
96
+ }, Y = (e) => void g(e, { when: () => m.activeWindow === e }), X = (e) => {
97
+ re(e);
98
+ for (let t of [...L]) t(e);
99
+ let t = q(e);
100
+ if (t !== K) {
101
+ K = t, J();
102
+ for (let e of [...R]) e(K);
103
+ }
104
+ };
105
+ b(m, "vf-activate", (e) => X(e.detail.window)), b(m, "vf-close", (e) => {
106
+ let t = e.target;
107
+ t instanceof n && C.has(t) && $.requestClose(t);
108
+ });
109
+ let Z = 0, Q = (e) => {
110
+ !(e.target instanceof n) || !C.has(e.target) || Z || (Z = window.setTimeout(() => {
111
+ Z = 0, N();
112
+ }, 0));
113
+ };
114
+ m.addEventListener("vf-placement-change", Q), y.push(() => m.removeEventListener("vf-placement-change", Q)), b(m, "vf-resize", (e) => {
115
+ e.detail?.commit && N();
116
+ });
117
+ let ie = (e) => {
118
+ let t = C.get(e);
119
+ t && (C.delete(e), O.delete(e), t.palette || F(), N());
120
+ }, ae = (n, r, i, a) => {
121
+ let l = p(n), u = C.get(n);
122
+ if (u?.keep && o(l, u.keep(a))) {
123
+ H(n, V(n, u.keep(S())));
124
+ return;
125
+ }
126
+ let d = O.get(n), f = !!d && n.resizable && (d.width !== l.width || d.height !== l.height), m = !d || d.left !== l.left || d.top !== l.top || f || !d ? s(l, r, A()) : d.pin, h = c(m, i, A(), W(n, l));
127
+ if (n.left = e(h.left, n), n.top = e(h.top, n), n.resizable) {
128
+ let r = t(n), i = S(), a = Math.ceil(i.top / r) * r, o = Math.floor(i.width / r) * r, s = Math.floor(Math.max(0, i.top + i.height - a) / r) * r;
129
+ n.width = Math.min(e(h.width, n), o), n.height = Math.min(e(h.height, n), s);
130
+ }
131
+ O.set(n, {
132
+ pin: m,
133
+ ...p(n)
134
+ });
135
+ }, $ = {
136
+ get area() {
137
+ return S();
138
+ },
139
+ get front() {
140
+ return K;
141
+ },
142
+ adopt(e, t) {
143
+ let n = t.palette;
144
+ if (C.set(e, {
145
+ app: t.app,
146
+ item: t.item ?? null,
147
+ place: t.place ?? null,
148
+ policy: t.policy ?? null,
149
+ keep: t.keep ?? null,
150
+ palette: n ? n === !0 ? () => !0 : n : null,
151
+ close: t.close ?? null
152
+ }), t.pin) H(e, G(e, t.pin));
153
+ else {
154
+ let t = U(e);
155
+ t && H(e, t);
156
+ }
157
+ if (n) {
158
+ let t = C.get(e);
159
+ e.hidden = !(t.app === K && t.palette());
160
+ }
161
+ m.activeWindow === e && X(e), n || F(), N();
162
+ },
163
+ open(e) {
164
+ let t = e.item == null ? null : $.windowFor(e.item);
165
+ if (t) return m.bringToFront(t), Y(t), t;
166
+ let n = e.create(), r = {
167
+ width: n.width ?? 0,
168
+ height: n.height ?? 0
169
+ }, a = ne();
170
+ m.append(n);
171
+ let o = e.item ?? null;
172
+ return $.adopt(n, {
173
+ app: e.app,
174
+ item: o,
175
+ place: e.place ?? ((e) => i(e, r, l(e), a)),
176
+ pin: o == null ? null : T.get(o)?.pin ?? ee(o),
177
+ policy: e.policy ?? null,
178
+ keep: e.keep ?? null,
179
+ close: e.close ?? null
180
+ }), o != null && T.delete(o), m.bringToFront(n), e.from && n.show({ from: e.from }), Y(n), n;
181
+ },
182
+ close(e, { to: t } = {}) {
183
+ let n = C.get(e), r = n?.item ?? null, i = n?.app ?? null, a = t === void 0 ? i == null ? null : z(r, i) : t, o = m.activeWindow === e || h(e);
184
+ r != null && i != null && T.set(r, {
185
+ app: i,
186
+ pin: s(p(e), x(), A())
187
+ });
188
+ let c = e.hide({ to: a });
189
+ r != null && u(E, r), i != null && !n?.palette && u(D, i);
190
+ let l = () => {
191
+ r != null && d(E, r), i != null && !n?.palette && d(D, i), F();
192
+ };
193
+ return c.then(l, l), ie(e), e.remove(), o && queueMicrotask(() => {
194
+ let e = m.activeWindow;
195
+ e ? Y(e) : B(r, i) || B(null, null);
196
+ }), c;
197
+ },
198
+ requestClose(e) {
199
+ let t = C.get(e)?.close;
200
+ t ? t(e) : $.close(e);
201
+ },
202
+ windowFor(e) {
203
+ for (let [t, n] of C) if (n.item === e) return t;
204
+ return null;
205
+ },
206
+ windowsOf(e) {
207
+ return m.stackingOrder.filter((t) => t instanceof n && C.get(t)?.app === e);
208
+ },
209
+ itemOf(e) {
210
+ return e instanceof n ? C.get(e)?.item ?? null : null;
211
+ },
212
+ appOf(e) {
213
+ return e instanceof n ? C.get(e)?.app ?? null : null;
214
+ },
215
+ isPalette(e) {
216
+ return e instanceof n && !!C.get(e)?.palette;
217
+ },
218
+ setItem(e, t) {
219
+ let n = C.get(e);
220
+ !n || n.item === t || (n.item = t, F());
221
+ },
222
+ isOpen(e) {
223
+ return E.has(e) || $.windowFor(e) !== null;
224
+ },
225
+ hasWindows(e) {
226
+ if (D.has(e)) return !0;
227
+ for (let [, t] of C) if (t.app === e && !t.palette) return !0;
228
+ return !1;
229
+ },
230
+ palettesChanged() {
231
+ J();
232
+ },
233
+ write(e, t) {
234
+ H(e, t), N();
235
+ },
236
+ clamped(e, t) {
237
+ return V(e, t);
238
+ },
239
+ placed(e) {
240
+ return U(e);
241
+ },
242
+ pinOf(e) {
243
+ return s(p(e), x(), A());
244
+ },
245
+ fromPin(e, t, n) {
246
+ return G(e, t, n);
247
+ },
248
+ setFrameBands(e) {
249
+ k = { ...e }, O = /* @__PURE__ */ new WeakMap();
250
+ },
251
+ arrangeWith(e, t) {
252
+ return w.set(e, t), N(), () => {
253
+ w.get(e) === t && w.delete(e), N();
254
+ };
255
+ },
256
+ arrange() {
257
+ M = !0;
258
+ try {
259
+ for (let e of w.values()) e.arrange();
260
+ for (let e of C.keys()) {
261
+ let t = U(e);
262
+ t && H(e, t);
263
+ }
264
+ } finally {
265
+ M = !1;
266
+ }
267
+ N();
268
+ },
269
+ arranged() {
270
+ for (let e of w.values()) if (!e.arranged()) return !1;
271
+ for (let e of C.keys()) {
272
+ let t = U(e);
273
+ if (t && !e.hidden && !te(e, t)) return !1;
274
+ }
275
+ return !0;
276
+ },
277
+ resized(e) {
278
+ let t = x();
279
+ if (e.width === t.width && e.height === t.height) return;
280
+ let n = S(), r = {
281
+ left: n.left,
282
+ top: n.top,
283
+ width: e.width - n.left,
284
+ height: Math.max(0, e.height - n.top)
285
+ };
286
+ for (let n of C.keys()) ae(n, e, t, r);
287
+ for (let n of [...I]) n(e, t);
288
+ N();
289
+ },
290
+ snapshot() {
291
+ let e = {};
292
+ for (let [t, n] of T) e[t] = { ...n };
293
+ let t = m.stackingOrder;
294
+ for (let [n, r] of C) {
295
+ if (r.item == null) continue;
296
+ let i = s(p(n), x(), A());
297
+ e[r.item] = r.palette ? {
298
+ app: r.app,
299
+ pin: i
300
+ } : {
301
+ app: r.app,
302
+ pin: i,
303
+ z: t.indexOf(n)
304
+ };
305
+ }
306
+ return e;
307
+ },
308
+ rememberedPin(e) {
309
+ return T.get(e)?.pin ?? null;
310
+ },
311
+ layoutChanged() {
312
+ N();
313
+ },
314
+ setHome(e, t) {
315
+ return z = e, B = t, () => {
316
+ z = () => null, B = () => !1;
317
+ };
318
+ },
319
+ onLayout(e) {
320
+ return j.add(e), () => void j.delete(e);
321
+ },
322
+ onWindows(e) {
323
+ return P.add(e), () => void P.delete(e);
324
+ },
325
+ onRaster(e) {
326
+ return I.add(e), () => void I.delete(e);
327
+ },
328
+ beforeFront(e) {
329
+ return L.add(e), () => void L.delete(e);
330
+ },
331
+ onFront(e) {
332
+ return R.add(e), () => void R.delete(e);
333
+ },
334
+ dispose() {
335
+ clearTimeout(Z);
336
+ for (let e of y) e();
337
+ j.clear(), P.clear(), I.clear(), L.clear(), R.clear(), w.clear(), C.clear();
338
+ }
339
+ };
340
+ return K = q(m.activeWindow), $;
341
+ }
342
+ //#endregion
343
+ export { _ as createWindowManager, m as deepActiveElement, g as focusInto };
@@ -27,7 +27,9 @@ export { vfDisplay, vfDisplayDecls } from './recipes/display-face.js';
27
27
  export { vfBase } from './recipes/host.js';
28
28
  export { vfStaticText } from './recipes/static-text.js';
29
29
  export { vfDots, vfStripes } from './recipes/pattern.js';
30
- export { vfHardShadowDecls } from './recipes/shadow.js';
30
+ export { vfHardShadow, vfHardShadowDecls } from './recipes/shadow.js';
31
+ export { vfStrokeDecls, vfStrokeInset, vfStrokeShadow } from './recipes/stroke.js';
32
+ export type { VfStrokeOptions } from './recipes/stroke.js';
31
33
  export { vfChromeFrame, vfModalFrame, vfPanel } from './recipes/surface.js';
32
34
  export { RULE_EDGES, parseRule, ruleClasses, vfRule } from './recipes/rule.js';
33
35
  export type { RuleEdge } from './recipes/rule.js';
@@ -1,13 +1,17 @@
1
1
  /**
2
2
  * The System 7 editable-field skin (SPEC §1/§5): a white well with a 1px black
3
- * border, no corner radius, Chicago-style display type, and the kit's dashed
3
+ * frame, no corner radius, Chicago-style display type, and the kit's dashed
4
4
  * keyboard-focus rule under the well (no dotted ring). Add the `vf-field`
5
5
  * class to the inner native `<input>`/`<textarea>` and wrap it in a
6
- * `.vf-field-well` element; the host supplies layout (width, height, padding)
7
- * around it, and `VfTextControlBase` supplies the `.vf-focus-rule` class that
8
- * turns the rule on. Shared by vf-text-field, vf-text-area and
9
- * vf-number-field so the well, focus and disabled treatment stay identical
10
- * across all three.
6
+ * `.vf-field-well` element; the host supplies layout (width, height) around
7
+ * it, and `VfTextControlBase` supplies the `.vf-focus-rule` class that turns
8
+ * the rule on. Shared by vf-text-field, vf-text-area and vf-number-field so
9
+ * the well, focus and disabled treatment stay identical across all three.
10
+ *
11
+ * The frame is a stroke ({@link vfStrokeDecls}), so the recipe owns the
12
+ * input's padding and box-shadow: a host that insets the text restates the
13
+ * padding with `vfStrokeInset` (`padding: ${vfStrokeInset(0)}
14
+ * ${vfStrokeInset(6)}`) in a rule that outranks `.vf-field`.
11
15
  *
12
16
  * Why the wrapper: the rule is a pseudo-element, and a replaced element
13
17
  * generates none — neither `<input>` nor `<textarea>` can draw its own
@@ -1,11 +1,12 @@
1
1
  import { vfDisplayDecls as e } from "./display-face.js";
2
- import { vfFocusUnderline as t } from "./focus.js";
3
- import { css as n } from "lit";
2
+ import { vfStrokeDecls as t } from "./stroke.js";
3
+ import { vfFocusUnderline as n } from "./focus.js";
4
+ import { css as r } from "lit";
4
5
  //#region src/styles/recipes/field.ts
5
- var r = n`
6
+ var i = r`
6
7
  .vf-field {
7
8
  background: var(--vf-white, #fff);
8
- border: calc(var(--vf-scale, 1) * 1px) solid var(--vf-black, #000);
9
+ ${t()}
9
10
  border-radius: 0;
10
11
  /* Editable text is set in the Chicago-style display face. */
11
12
  ${e}
@@ -36,7 +37,7 @@ var r = n`
36
37
  VfTextControlBase adds .vf-focus-rule for a keyboard focus only. */
37
38
  .vf-field-well.vf-focus-rule::after {
38
39
  --vf-focus-underline-offset: -2px;
39
- ${t}
40
+ ${n}
40
41
  }
41
42
  /* Its own token, deliberately not --vf-disabled: the disabled rule below is
42
43
  an inactive control and exempt from contrast requirements, but a
@@ -50,7 +51,7 @@ var r = n`
50
51
  font-weight: inherit;
51
52
  opacity: 1;
52
53
  }
53
- /* Disabled: the text dims to gray; the solid black box border stays. The
54
+ /* Disabled: the text dims to gray; the solid black frame stays. The
54
55
  cursor returns to the arrow — a well that can't take input isn't text. */
55
56
  .vf-field:disabled {
56
57
  color: var(--vf-disabled, #c0c0c0);
@@ -65,4 +66,4 @@ var r = n`
65
66
  }
66
67
  `;
67
68
  //#endregion
68
- export { r as vfField };
69
+ export { i as vfField };
@@ -64,13 +64,17 @@ export declare const vfFocus: import("lit").CSSResult;
64
64
  * the box continues 6px past it (2px half-leading from vfBase's 1.25 line box
65
65
  * over the 16px em, plus the face's 4px descent): 6 − 2 = 4.
66
66
  * - `-2px` is a well whose ink runs to its own bottom edge — the radio's circle,
67
- * a field's wrapper — putting the rule in the second row below it.
68
- * - `-3px` adds a 1px border the pseudo-element sits inside of (vf-checkbox).
67
+ * a field's wrapper, a box framed by a stroke (vf-checkbox), whose line is
68
+ * inside the padding box — putting the rule in the second row below it.
69
69
  * - past that, the rule also clears a hard shadow, which is ink no box the
70
- * pseudo-element can size to contains: `-4px` for vf-select's 1px one, and
70
+ * pseudo-element can size to contains: `-3px` for vf-select's 1px one, and
71
71
  * for vf-swatch an offset that composes `--vf-shadow-offset` rather than
72
72
  * hard-coding a depth its consumer can retheme.
73
73
  *
74
+ * Under forced colors a stroke is a border again and the padding box sits
75
+ * inside it, so a stroked carrier states its offset and sides with
76
+ * `vfStrokeInset`, which adds the stroke back there.
77
+ *
74
78
  * Drawn in `currentColor`, so it inverts with the label on a pressed face.
75
79
  */
76
80
  export declare const vfFocusUnderline: import("lit").CSSResult;
@@ -9,15 +9,18 @@ export type RuleEdge = (typeof RULE_EDGES)[number];
9
9
  * floor is `.vf-rule-bottom`, a window's status strip's ceiling is
10
10
  * `.vf-rule-top`, and `vf-container rule="…"` takes the edges by name.
11
11
  *
12
- * A border, not a pseudo-element or a painted line, so it lives inside the
13
- * box's own `border-box` size (vfBase) and insets the padding box the way
14
- * FrameRect insets a rectangle's interior: content, percentage fills and
15
- * children placed against the box all begin inside the rule. Scales with
16
- * `--vf-scale`, so it is one system px at every density, and paints in
17
- * `--vf-black`, so it remaps under forced colors with the rest of the ink.
12
+ * A stroke (see {@link vfStrokeDecls}), inside the box's own `border-box`
13
+ * size (vfBase): the rule's row is padding and an inset shadow paints it
14
+ * black, so it is one system px at every density and zoom, and in-flow
15
+ * content begins inside it. Children placed against the box, and percentage
16
+ * fills, measure from the box's outer edge; a component whose contract puts
17
+ * them inside the rule gives them an inner box to lay out in (vf-container
18
+ * uses its slot). Paints in `--vf-black`, and under forced colors turns back
19
+ * into a border, so it remaps with the rest of the ink.
18
20
  *
19
- * One declaration per edge, so the kit cannot grow two rules — the
20
- * {@link vfHardShadowDecls} principle, applied to the line.
21
+ * The classes compose on one element: each edge's shadow goes through its
22
+ * own private property and one rule paints all four, so the classes own the
23
+ * element's `box-shadow`, and its padding on their edges.
21
24
  *
22
25
  * `npm run verify:rule` asserts a container's rule against the menu bar's.
23
26
  */
@@ -1,32 +1,50 @@
1
- import { css as e, unsafeCSS as t } from "lit";
1
+ import { STROKE_FORCED_COLORS as e, strokeBorder as t, strokeEdgeShadow as n, strokePadding as r } from "./stroke.js";
2
+ import { css as i, unsafeCSS as a } from "lit";
2
3
  //#region src/styles/recipes/rule.ts
3
- var n = [
4
+ var o = [
4
5
  "top",
5
6
  "right",
6
7
  "bottom",
7
8
  "left"
8
- ], r = t("calc(var(--vf-scale, 1) * 1px) solid var(--vf-black, #000)"), i = e`
9
+ ], s = (e) => a(`
10
+ --_vf-rule-${e}: ${n(e, 1)};
11
+ padding-${e}: ${r(0, 1)};
12
+ border-${e}: ${t(1)};
13
+ `), c = i`
14
+ .vf-rule-top,
15
+ .vf-rule-right,
16
+ .vf-rule-bottom,
17
+ .vf-rule-left {
18
+ --_vf-stroke-ink: var(--vf-black, #000);
19
+ --_vf-rule-top: 0 0 transparent;
20
+ --_vf-rule-right: 0 0 transparent;
21
+ --_vf-rule-bottom: 0 0 transparent;
22
+ --_vf-rule-left: 0 0 transparent;
23
+ box-shadow: var(--_vf-rule-top), var(--_vf-rule-right), var(--_vf-rule-bottom),
24
+ var(--_vf-rule-left);
25
+ ${a(e)}
26
+ }
9
27
  .vf-rule-top {
10
- border-top: ${r};
28
+ ${s("top")}
11
29
  }
12
30
  .vf-rule-right {
13
- border-right: ${r};
31
+ ${s("right")}
14
32
  }
15
33
  .vf-rule-bottom {
16
- border-bottom: ${r};
34
+ ${s("bottom")}
17
35
  }
18
36
  .vf-rule-left {
19
- border-left: ${r};
37
+ ${s("left")}
20
38
  }
21
39
  `;
22
- function a(e) {
40
+ function l(e) {
23
41
  if (e == null) return [];
24
- let t = e.trim().split(/\s+/).filter(Boolean), r = new Set(t);
25
- for (let e of r) if (!n.includes(e)) return null;
26
- return n.filter((e) => r.has(e));
42
+ let t = e.trim().split(/\s+/).filter(Boolean), n = new Set(t);
43
+ for (let e of n) if (!o.includes(e)) return null;
44
+ return o.filter((e) => n.has(e));
27
45
  }
28
- function o(e) {
46
+ function u(e) {
29
47
  return e.map((e) => `vf-rule-${e}`).join(" ");
30
48
  }
31
49
  //#endregion
32
- export { n as RULE_EDGES, a as parseRule, o as ruleClasses, i as vfRule };
50
+ export { o as RULE_EDGES, l as parseRule, u as ruleClasses, c as vfRule };
@@ -28,16 +28,17 @@ export declare const TROUGH: import("../../patterns.js").PatternMotif;
28
28
  *
29
29
  * Geometry (system px) — the classic 16px cell, of which the outermost line
30
30
  * is the component's own 1px frame: the rail element is the 15 inside it —
31
- * a 1px divider on the content side (the rail's own border) plus the 14px
31
+ * a 1px divider on the content side (the rail's own stroke) plus the 14px
32
32
  * channel. Arrow cells are 15 long (14 interior + the 1px divider facing the
33
33
  * track); their glyphs are the 16-unit sprites windowed to the 14×14
34
34
  * interior (`viewBox="1 1 14 14"`), since the sprite's outer ring is exactly
35
- * the frame/divider lines the borders draw. The thumb is a fixed 16px white
36
- * box spanning the channel, its border reading as inset 1px from each
35
+ * the frame/divider lines the strokes draw. The thumb is a fixed 16px white
36
+ * box spanning the channel, its stroke reading as inset 1px from each
37
37
  * channel rail over its extent. The component supplies the outer frame as a
38
- * real border and lays the rail out as a sibling of the scroller — AFTER it,
39
- * so the state selectors below can reach the rail from the scroller's
40
- * attributes.
38
+ * stroke of its own and lays the rail out as a sibling of the scroller —
39
+ * AFTER it, so the state selectors below can reach the rail from the
40
+ * scroller's attributes. Every line here is a stroke (vfStrokeDecls):
41
+ * padding holds it and an inset shadow paints it.
41
42
  *
42
43
  * States, keyed off the attributes `ScrollStateController` writes on the
43
44
  * scroller (now styling real elements, so there is no scrollbar-pseudo
@@ -1,9 +1,10 @@
1
1
  import { tileImage as e, tileRects as t, vfTileMaskSize as n } from "./tile.js";
2
2
  import { vfRepeatTileSize as r } from "../../tile-grid.js";
3
3
  import { PATTERNS as i, patternMotif as a } from "../../patterns.js";
4
- import { css as o, unsafeCSS as s } from "lit";
4
+ import { vfStrokeDecls as o } from "./stroke.js";
5
+ import { css as s, unsafeCSS as c } from "lit";
5
6
  //#region src/styles/recipes/scroll-rail.ts
6
- var c = a(i["gray-25"]), l = e(c.width, c.height, t(c.rects)), u = o`
7
+ var l = a(i["gray-25"]), u = e(l.width, l.height, t(l.rects)), d = s`
7
8
  /* Hide the native bar, keep the native scrolling. Both lines ship:
8
9
  scrollbar-width reaches Chromium 121+/Firefox/Safari 18.2+, the pseudo
9
10
  covers older WebKit — the last scrollbar pseudo left in the kit.
@@ -37,7 +38,7 @@ var c = a(i["gray-25"]), l = e(c.width, c.height, t(c.rects)), u = o`
37
38
  calc(var(--vf-scale, 1) * 15px)
38
39
  1fr
39
40
  calc(var(--vf-scale, 1) * 15px);
40
- border-left: calc(var(--vf-scale, 1) * 1px) solid var(--vf-black, #000);
41
+ ${o({ edges: ["left"] })}
41
42
  }
42
43
  .vf-rail--horizontal {
43
44
  height: calc(var(--vf-scale, 1) * 15px);
@@ -45,7 +46,7 @@ var c = a(i["gray-25"]), l = e(c.width, c.height, t(c.rects)), u = o`
45
46
  calc(var(--vf-scale, 1) * 15px)
46
47
  1fr
47
48
  calc(var(--vf-scale, 1) * 15px);
48
- border-top: calc(var(--vf-scale, 1) * 1px) solid var(--vf-black, #000);
49
+ ${o({ edges: ["top"] })}
49
50
  }
50
51
  /* Explicit placement, so hiding the arrows (inactive-window/degenerate
51
52
  states) never reflows the track into an arrow cell — its box holds still
@@ -70,11 +71,11 @@ var c = a(i["gray-25"]), l = e(c.width, c.height, t(c.rects)), u = o`
70
71
  }
71
72
 
72
73
  /* Arrow cells: white boxes carrying only the 1px divider that faces the
73
- track — the frame edges are the component's border, the content edge the
74
- rail's own divider — so no line ever doubles to 2px. Each cell's padding
74
+ track — the frame edges are the component's stroke, the content edge the
75
+ rail's own divider — so no line ever doubles to 2px. Each cell's content
75
76
  box is by construction the sprite's 14×14 interior, wherever the cell
76
77
  sits (flush to the frame, or against the corner cell), so the glyph
77
- lands at the padding origin with no per-variant offsets. */
78
+ lands at the content origin with no per-variant offsets. */
78
79
  .vf-rail-button {
79
80
  display: grid;
80
81
  place-items: start;
@@ -82,16 +83,16 @@ var c = a(i["gray-25"]), l = e(c.width, c.height, t(c.rects)), u = o`
82
83
  overflow: hidden;
83
84
  }
84
85
  .vf-rail--vertical .vf-rail-button--decrement {
85
- border-bottom: calc(var(--vf-scale, 1) * 1px) solid var(--vf-black, #000);
86
+ ${o({ edges: ["bottom"] })}
86
87
  }
87
88
  .vf-rail--vertical .vf-rail-button--increment {
88
- border-top: calc(var(--vf-scale, 1) * 1px) solid var(--vf-black, #000);
89
+ ${o({ edges: ["top"] })}
89
90
  }
90
91
  .vf-rail--horizontal .vf-rail-button--decrement {
91
- border-right: calc(var(--vf-scale, 1) * 1px) solid var(--vf-black, #000);
92
+ ${o({ edges: ["right"] })}
92
93
  }
93
94
  .vf-rail--horizontal .vf-rail-button--increment {
94
- border-left: calc(var(--vf-scale, 1) * 1px) solid var(--vf-black, #000);
95
+ ${o({ edges: ["left"] })}
95
96
  }
96
97
  .vf-rail-arrow {
97
98
  display: block;
@@ -130,7 +131,7 @@ var c = a(i["gray-25"]), l = e(c.width, c.height, t(c.rects)), u = o`
130
131
  pointer-events: none;
131
132
  }
132
133
  /* The fixed System 7 thumb (never proportional): a white 16px box spanning
133
- the channel. Its border reads as inset 1px from each channel rail — the
134
+ the channel. Its stroke reads as inset 1px from each channel rail — the
134
135
  divider and the frame — over its extent; the controller writes its
135
136
  travel as a whole-system-px translate. */
136
137
  .vf-rail-thumb {
@@ -138,7 +139,7 @@ var c = a(i["gray-25"]), l = e(c.width, c.height, t(c.rects)), u = o`
138
139
  top: 0;
139
140
  left: 0;
140
141
  background: var(--vf-scrollbar-thumb, #ffffff);
141
- border: calc(var(--vf-scale, 1) * 1px) solid var(--vf-black, #000);
142
+ ${o()}
142
143
  }
143
144
  .vf-rail--vertical .vf-rail-thumb {
144
145
  width: 100%;
@@ -161,8 +162,7 @@ var c = a(i["gray-25"]), l = e(c.width, c.height, t(c.rects)), u = o`
161
162
  width: calc(var(--vf-scale, 1) * 15px);
162
163
  height: calc(var(--vf-scale, 1) * 15px);
163
164
  background: var(--vf-white, #fff);
164
- border-top: calc(var(--vf-scale, 1) * 1px) solid var(--vf-black, #000);
165
- border-left: calc(var(--vf-scale, 1) * 1px) solid var(--vf-black, #000);
165
+ ${o({ edges: ["top", "left"] })}
166
166
  }
167
167
 
168
168
  /* ── Always-a-rail (driven by ScrollStateController) ─────────────────────
@@ -208,10 +208,10 @@ var c = a(i["gray-25"]), l = e(c.width, c.height, t(c.rects)), u = o`
208
208
  .vf-rail-trough {
209
209
  background-image: none;
210
210
  background-color: var(--vf-black, #000);
211
- mask-image: ${s(l)};
212
- ${n(c.width, c.height)}
211
+ mask-image: ${c(u)};
212
+ ${n(l.width, l.height)}
213
213
  }
214
214
  }
215
215
  `;
216
216
  //#endregion
217
- export { c as TROUGH, u as vfScrollRail };
217
+ export { l as TROUGH, d as vfScrollRail };