vintage-frames 0.1.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.
Files changed (163) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +262 -0
  3. package/custom-elements.json +19807 -0
  4. package/dist/_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.js +9 -0
  5. package/dist/chrome.d.ts +73 -0
  6. package/dist/chrome.js +94 -0
  7. package/dist/components/vf-button-group.d.ts +50 -0
  8. package/dist/components/vf-button-group.js +68 -0
  9. package/dist/components/vf-button.d.ts +169 -0
  10. package/dist/components/vf-button.js +234 -0
  11. package/dist/components/vf-checkbox.d.ts +60 -0
  12. package/dist/components/vf-checkbox.js +135 -0
  13. package/dist/components/vf-container.d.ts +95 -0
  14. package/dist/components/vf-container.js +86 -0
  15. package/dist/components/vf-desktop.d.ts +243 -0
  16. package/dist/components/vf-desktop.js +310 -0
  17. package/dist/components/vf-dialog.d.ts +105 -0
  18. package/dist/components/vf-dialog.js +280 -0
  19. package/dist/components/vf-fieldset.d.ts +34 -0
  20. package/dist/components/vf-fieldset.js +76 -0
  21. package/dist/components/vf-grid.d.ts +124 -0
  22. package/dist/components/vf-grid.js +133 -0
  23. package/dist/components/vf-icon.d.ts +356 -0
  24. package/dist/components/vf-icon.js +507 -0
  25. package/dist/components/vf-img.d.ts +63 -0
  26. package/dist/components/vf-img.js +65 -0
  27. package/dist/components/vf-label.d.ts +117 -0
  28. package/dist/components/vf-label.js +151 -0
  29. package/dist/components/vf-list-item.d.ts +48 -0
  30. package/dist/components/vf-list-item.js +98 -0
  31. package/dist/components/vf-list.d.ts +77 -0
  32. package/dist/components/vf-list.js +256 -0
  33. package/dist/components/vf-menu-bar.d.ts +64 -0
  34. package/dist/components/vf-menu-bar.js +236 -0
  35. package/dist/components/vf-menu-item.d.ts +91 -0
  36. package/dist/components/vf-menu-item.js +252 -0
  37. package/dist/components/vf-menu.d.ts +95 -0
  38. package/dist/components/vf-menu.js +346 -0
  39. package/dist/components/vf-number-field.d.ts +60 -0
  40. package/dist/components/vf-number-field.js +248 -0
  41. package/dist/components/vf-option.d.ts +49 -0
  42. package/dist/components/vf-option.js +119 -0
  43. package/dist/components/vf-paragraph.d.ts +73 -0
  44. package/dist/components/vf-paragraph.js +66 -0
  45. package/dist/components/vf-progress-bar.d.ts +69 -0
  46. package/dist/components/vf-progress-bar.js +272 -0
  47. package/dist/components/vf-radio-group.d.ts +109 -0
  48. package/dist/components/vf-radio-group.js +105 -0
  49. package/dist/components/vf-radio.d.ts +59 -0
  50. package/dist/components/vf-radio.js +135 -0
  51. package/dist/components/vf-scroll-area.d.ts +75 -0
  52. package/dist/components/vf-scroll-area.js +124 -0
  53. package/dist/components/vf-select.d.ts +334 -0
  54. package/dist/components/vf-select.js +658 -0
  55. package/dist/components/vf-separator.d.ts +38 -0
  56. package/dist/components/vf-separator.js +55 -0
  57. package/dist/components/vf-slider.d.ts +80 -0
  58. package/dist/components/vf-slider.js +255 -0
  59. package/dist/components/vf-stack.d.ts +188 -0
  60. package/dist/components/vf-stack.js +168 -0
  61. package/dist/components/vf-swatch.d.ts +98 -0
  62. package/dist/components/vf-swatch.js +201 -0
  63. package/dist/components/vf-text-area.d.ts +77 -0
  64. package/dist/components/vf-text-area.js +121 -0
  65. package/dist/components/vf-text-field.d.ts +53 -0
  66. package/dist/components/vf-text-field.js +79 -0
  67. package/dist/components/vf-window.d.ts +157 -0
  68. package/dist/components/vf-window.js +377 -0
  69. package/dist/cursor-art.d.ts +37 -0
  70. package/dist/cursor-art.js +41 -0
  71. package/dist/cursor.d.ts +49 -0
  72. package/dist/cursor.js +171 -0
  73. package/dist/define.d.ts +45 -0
  74. package/dist/define.js +15 -0
  75. package/dist/document-listeners.d.ts +62 -0
  76. package/dist/document-listeners.js +29 -0
  77. package/dist/drag.d.ts +49 -0
  78. package/dist/drag.js +30 -0
  79. package/dist/events.d.ts +79 -0
  80. package/dist/events.js +25 -0
  81. package/dist/focus-modality.d.ts +94 -0
  82. package/dist/focus-modality.js +56 -0
  83. package/dist/form-control.d.ts +247 -0
  84. package/dist/form-control.js +127 -0
  85. package/dist/glyphs.d.ts +117 -0
  86. package/dist/glyphs.js +100 -0
  87. package/dist/grid-snap.d.ts +73 -0
  88. package/dist/grid-snap.js +135 -0
  89. package/dist/index.d.ts +145 -0
  90. package/dist/index.js +71 -0
  91. package/dist/menu-press.d.ts +60 -0
  92. package/dist/menu-press.js +97 -0
  93. package/dist/modal-dialog.d.ts +148 -0
  94. package/dist/modal-dialog.js +155 -0
  95. package/dist/motion.d.ts +72 -0
  96. package/dist/motion.js +22 -0
  97. package/dist/number.d.ts +24 -0
  98. package/dist/number.js +12 -0
  99. package/dist/open-art.d.ts +40 -0
  100. package/dist/open-art.js +35 -0
  101. package/dist/pixel-frame.d.ts +102 -0
  102. package/dist/pixel-frame.js +77 -0
  103. package/dist/popup-overflow.d.ts +157 -0
  104. package/dist/popup-overflow.js +45 -0
  105. package/dist/position.d.ts +187 -0
  106. package/dist/position.js +74 -0
  107. package/dist/scale.d.ts +259 -0
  108. package/dist/scale.js +133 -0
  109. package/dist/scroll-rail.d.ts +112 -0
  110. package/dist/scroll-rail.js +194 -0
  111. package/dist/scroll-state.d.ts +113 -0
  112. package/dist/scroll-state.js +55 -0
  113. package/dist/size.d.ts +47 -0
  114. package/dist/size.js +30 -0
  115. package/dist/styles/base.d.ts +38 -0
  116. package/dist/styles/body-font.d.ts +7 -0
  117. package/dist/styles/body-font.js +9 -0
  118. package/dist/styles/display-font.d.ts +7 -0
  119. package/dist/styles/display-font.js +9 -0
  120. package/dist/styles/recipes/body-face.d.ts +14 -0
  121. package/dist/styles/recipes/body-face.js +6 -0
  122. package/dist/styles/recipes/display-face.d.ts +23 -0
  123. package/dist/styles/recipes/display-face.js +10 -0
  124. package/dist/styles/recipes/field.d.ts +20 -0
  125. package/dist/styles/recipes/field.js +68 -0
  126. package/dist/styles/recipes/focus.d.ts +76 -0
  127. package/dist/styles/recipes/focus.js +39 -0
  128. package/dist/styles/recipes/host.d.ts +5 -0
  129. package/dist/styles/recipes/host.js +62 -0
  130. package/dist/styles/recipes/pattern.d.ts +80 -0
  131. package/dist/styles/recipes/pattern.js +82 -0
  132. package/dist/styles/recipes/scroll-rail.d.ts +69 -0
  133. package/dist/styles/recipes/scroll-rail.js +222 -0
  134. package/dist/styles/recipes/shadow.d.ts +13 -0
  135. package/dist/styles/recipes/shadow.js +5 -0
  136. package/dist/styles/recipes/static-text.d.ts +24 -0
  137. package/dist/styles/recipes/static-text.js +17 -0
  138. package/dist/styles/recipes/surface.d.ts +28 -0
  139. package/dist/styles/recipes/surface.js +27 -0
  140. package/dist/styles/recipes/tile.d.ts +122 -0
  141. package/dist/styles/recipes/tile.js +40 -0
  142. package/dist/styles/recipes/title-bar.d.ts +49 -0
  143. package/dist/styles/recipes/title-bar.js +134 -0
  144. package/dist/styles/recipes/toggle.d.ts +10 -0
  145. package/dist/styles/recipes/toggle.js +20 -0
  146. package/dist/styles/register-embedded-font.d.ts +64 -0
  147. package/dist/styles/register-embedded-font.js +30 -0
  148. package/dist/text-control.d.ts +131 -0
  149. package/dist/text-control.js +83 -0
  150. package/dist/tile-grid.d.ts +111 -0
  151. package/dist/tile-grid.js +53 -0
  152. package/dist/toggle-control.d.ts +60 -0
  153. package/dist/toggle-control.js +41 -0
  154. package/dist/track-width.d.ts +42 -0
  155. package/dist/track-width.js +30 -0
  156. package/dist/type-ahead.d.ts +35 -0
  157. package/dist/type-ahead.js +20 -0
  158. package/dist/zoom.d.ts +204 -0
  159. package/dist/zoom.js +105 -0
  160. package/docs/SPEC.md +2471 -0
  161. package/editor/vscode.html-custom-data.json +1320 -0
  162. package/editor/web-types.json +3237 -0
  163. package/package.json +118 -0
@@ -0,0 +1,194 @@
1
+ import { effectiveScale as e, sysLength as t } from "./scale.js";
2
+ import { TROUGH_RECTS as n } from "./styles/recipes/scroll-rail.js";
3
+ import { TileRasterCache as r } from "./tile-grid.js";
4
+ import "./motion.js";
5
+ import { SCROLL_ARROW_DOWN as i, SCROLL_ARROW_DOWN_FILL as a, SCROLL_ARROW_LEFT as o, SCROLL_ARROW_LEFT_FILL as s, SCROLL_ARROW_RIGHT as c, SCROLL_ARROW_RIGHT_FILL as l, SCROLL_ARROW_UP as u, SCROLL_ARROW_UP_FILL as d } from "./glyphs.js";
6
+ import { html as f } from "lit";
7
+ //#region src/scroll-rail.ts
8
+ var p = 16, m = 16, h = 14, g = 66, _ = class {
9
+ constructor(t, n) {
10
+ this.host = t, this.opts = n, this.scroller = null, this.press = null, this.troughCache = {
11
+ vertical: new r(),
12
+ horizontal: new r()
13
+ }, this.onScroll = () => this.sync(), this.onPointerDown = (t) => {
14
+ if (t.button !== 0 || this.press !== null) return;
15
+ let n = t.currentTarget, r = this.opts.getScroll();
16
+ if (!r) return;
17
+ let i = v(n), a = i === "vertical" ? "data-overflow-y" : "data-overflow-x";
18
+ if (r.getAttribute(a) === "false" || r.hasAttribute("data-window-inactive") || n.getAttribute("data-degenerate") === "rail") return;
19
+ let o = t.target, s = i === "vertical" ? t.clientY : t.clientX, c = e(this.host) || 1, l = o.closest(".vf-rail-thumb"), u = o.closest(".vf-rail-button--decrement"), d = o.closest(".vf-rail-button--increment");
20
+ if (l) {
21
+ let e = n.querySelector(".vf-rail-track");
22
+ if (!e) return;
23
+ let a = e.getBoundingClientRect(), o = i === "vertical" ? a.height : a.width;
24
+ this.press = {
25
+ pointerId: t.pointerId,
26
+ kind: "thumb",
27
+ axis: i,
28
+ rail: n,
29
+ dir: 1,
30
+ button: null,
31
+ startCoord: s,
32
+ startScroll: i === "vertical" ? r.scrollTop : r.scrollLeft,
33
+ travelCss: o - m * c,
34
+ lastCoord: s
35
+ };
36
+ } else if (u || d) {
37
+ let e = u ?? d, r = u ? -1 : 1;
38
+ e.setAttribute("data-pressed", ""), this.press = {
39
+ pointerId: t.pointerId,
40
+ kind: "step",
41
+ axis: i,
42
+ rail: n,
43
+ dir: r,
44
+ button: e,
45
+ startCoord: s,
46
+ startScroll: 0,
47
+ travelCss: 0,
48
+ lastCoord: s
49
+ }, this.stepBy(r, i), this.armRepeat(() => this.stepBy(r, i));
50
+ } else if (o.closest(".vf-rail-track")) {
51
+ let e = n.querySelector(".vf-rail-thumb")?.getBoundingClientRect(), r = s < ((e && (i === "vertical" ? e.top : e.left)) ?? s) ? -1 : 1;
52
+ this.press = {
53
+ pointerId: t.pointerId,
54
+ kind: "page",
55
+ axis: i,
56
+ rail: n,
57
+ dir: r,
58
+ button: null,
59
+ startCoord: s,
60
+ startScroll: 0,
61
+ travelCss: 0,
62
+ lastCoord: s
63
+ }, this.pageBy(r, i), this.armRepeat(() => this.pageTowardPointer());
64
+ } else return;
65
+ n.setPointerCapture(t.pointerId), t.preventDefault();
66
+ }, this.onPointerMove = (e) => {
67
+ let t = this.press;
68
+ if (!t || e.pointerId !== t.pointerId || (t.lastCoord = t.axis === "vertical" ? e.clientY : e.clientX, t.kind !== "thumb")) return;
69
+ let n = this.opts.getScroll();
70
+ if (!n || t.travelCss <= 0) return;
71
+ let r = this.scrollRange(n, t.axis);
72
+ if (r <= 0) return;
73
+ let i = t.startScroll + (t.lastCoord - t.startCoord) / t.travelCss * r;
74
+ t.axis === "vertical" ? n.scrollTop = i : n.scrollLeft = i;
75
+ }, this.onPointerUp = (e) => {
76
+ let t = this.press;
77
+ !t || e.pointerId !== t.pointerId || (this.press = null, this.clearRepeat(), t.button?.removeAttribute("data-pressed"), t.rail.hasPointerCapture(e.pointerId) && t.rail.releasePointerCapture(e.pointerId));
78
+ }, t.addController(this);
79
+ }
80
+ hostUpdated() {
81
+ this.wire(), this.sync();
82
+ }
83
+ hostDisconnected() {
84
+ this.clearRepeat(), this.press = null, this.unwire();
85
+ }
86
+ wire() {
87
+ let e = this.opts.getScroll() ?? null;
88
+ if (e !== this.scroller && (this.unwire(), this.scroller = e, e && (e.addEventListener("scroll", this.onScroll, { passive: !0 }), typeof ResizeObserver < "u"))) {
89
+ this.resizeObserver = new ResizeObserver(() => this.sync()), this.resizeObserver.observe(e);
90
+ let t = this.opts.getContent?.();
91
+ t && this.resizeObserver.observe(t);
92
+ }
93
+ }
94
+ unwire() {
95
+ this.scroller?.removeEventListener("scroll", this.onScroll), this.scroller = null, this.resizeObserver?.disconnect(), this.resizeObserver = void 0;
96
+ }
97
+ sync() {
98
+ let e = this.scroller ?? this.opts.getScroll(), t = this.host.renderRoot;
99
+ if (!(!e || !t)) for (let n of t.querySelectorAll(".vf-rail")) this.syncRail(n, e);
100
+ }
101
+ syncRail(r, i) {
102
+ let a = v(r), o = r.querySelector(".vf-rail-track"), s = r.querySelector(".vf-rail-thumb");
103
+ if (!o || !s) return;
104
+ let c = e(this.host) || 1, l = o.getBoundingClientRect(), u = (a === "vertical" ? l.height : l.width) / c, d = u < 1 ? "rail" : u < m ? "thumb" : null;
105
+ if (d ? r.setAttribute("data-degenerate", d) : r.removeAttribute("data-degenerate"), d === "rail") return;
106
+ let f = this.scrollRange(i, a), p = a === "vertical" ? i.scrollTop : i.scrollLeft, g = u - m, _ = f > 0 && g > 0 ? Math.max(0, Math.min(Math.floor(g), Math.round(p / f * g))) : 0;
107
+ s.style.translate = a === "vertical" ? `0 ${t(_)}` : `${t(_)} 0`;
108
+ let y = r.querySelector(".vf-rail-trough-art");
109
+ if (y) {
110
+ let e = Math.ceil((u + (a === "vertical" ? 2 : 4)) / (a === "vertical" ? 2 : 4)) * (a === "vertical" ? 2 : 4), r = a === "vertical" ? h : e, i = a === "vertical" ? e : h;
111
+ y.style.width = t(r), y.style.height = t(i), y.style.backgroundImage = this.troughCache[a].for(4, 2, n, r, i);
112
+ }
113
+ }
114
+ scrollRange(e, t) {
115
+ return t === "vertical" ? e.scrollHeight - e.clientHeight : e.scrollWidth - e.clientWidth;
116
+ }
117
+ stepBy(t, n) {
118
+ let r = this.opts.getScroll();
119
+ if (!r) return;
120
+ let i = t * p * (e(this.host) || 1);
121
+ n === "vertical" ? r.scrollTop += i : r.scrollLeft += i;
122
+ }
123
+ pageBy(t, n) {
124
+ let r = this.opts.getScroll();
125
+ if (!r) return;
126
+ let i = p * (e(this.host) || 1), a = n === "vertical" ? r.clientHeight : r.clientWidth, o = Math.max(i, a - i);
127
+ n === "vertical" ? r.scrollTop += t * o : r.scrollLeft += t * o;
128
+ }
129
+ pageTowardPointer() {
130
+ let e = this.press;
131
+ if (!e || e.kind !== "page") return;
132
+ let t = e.rail.querySelector(".vf-rail-thumb");
133
+ if (t) {
134
+ let n = t.getBoundingClientRect(), r = e.axis === "vertical" ? n.top : n.left, i = e.axis === "vertical" ? n.bottom : n.right;
135
+ if (e.dir > 0 ? i > e.lastCoord : r < e.lastCoord) return;
136
+ }
137
+ this.pageBy(e.dir, e.axis);
138
+ }
139
+ armRepeat(e) {
140
+ this.clearRepeat(), this.holdTimer = window.setTimeout(() => {
141
+ this.repeatTimer = window.setInterval(e, g);
142
+ }, 200);
143
+ }
144
+ clearRepeat() {
145
+ this.holdTimer !== void 0 && window.clearTimeout(this.holdTimer), this.repeatTimer !== void 0 && window.clearInterval(this.repeatTimer), this.holdTimer = void 0, this.repeatTimer = void 0;
146
+ }
147
+ }, v = (e) => e.classList.contains("vf-rail--horizontal") ? "horizontal" : "vertical", y = (e, t) => f`<svg
148
+ class="vf-rail-arrow vf-rail-arrow--${t}"
149
+ viewBox="1 1 14 14"
150
+ fill="currentColor"
151
+ shape-rendering="crispEdges"
152
+ aria-hidden="true"
153
+ >
154
+ <path d=${e.d}></path>
155
+ </svg>`;
156
+ function b(e, t) {
157
+ let [n, r, p, m] = t === "vertical" ? [
158
+ u,
159
+ d,
160
+ i,
161
+ a
162
+ ] : [
163
+ o,
164
+ s,
165
+ c,
166
+ l
167
+ ];
168
+ return f`
169
+ <div
170
+ class="vf-rail vf-rail--${t}"
171
+ aria-hidden="true"
172
+ @pointerdown=${e.onPointerDown}
173
+ @pointermove=${e.onPointerMove}
174
+ @pointerup=${e.onPointerUp}
175
+ @pointercancel=${e.onPointerUp}
176
+ @lostpointercapture=${e.onPointerUp}
177
+ >
178
+ <div class="vf-rail-button vf-rail-button--decrement">
179
+ ${y(n, "rest")}${y(r, "fill")}
180
+ </div>
181
+ <div class="vf-rail-track">
182
+ <div class="vf-rail-trough">
183
+ <div class="vf-rail-trough-art"></div>
184
+ </div>
185
+ <div class="vf-rail-thumb"></div>
186
+ </div>
187
+ <div class="vf-rail-button vf-rail-button--increment">
188
+ ${y(p, "rest")}${y(m, "fill")}
189
+ </div>
190
+ </div>
191
+ `;
192
+ }
193
+ //#endregion
194
+ export { _ as ScrollRailController, b as renderScrollRail };
@@ -0,0 +1,113 @@
1
+ import type { ReactiveController, ReactiveControllerHost } from 'lit';
2
+ /**
3
+ * Rail-state tracking for System 7 "always-a-rail" scrollbars.
4
+ *
5
+ * System 7 draws a scrollable frame's scroll bar as a permanent part of its
6
+ * chrome: an empty white rail sits in the reserved channel even when there is
7
+ * nothing to scroll, and it only fills in — dither track, draggable thumb,
8
+ * arrow buttons — once the content actually overflows. Native `overflow` can't
9
+ * express that: `auto` is all-or-nothing (no rail when it fits) and `scroll`
10
+ * shows the whole bar always (arrows + a full-height thumb even when it fits),
11
+ * and there is no CSS selector for "is this element overflowing".
12
+ *
13
+ * This controller supplies the missing signal. It measures scroll vs. client
14
+ * size on both axes and writes `data-overflow-x` / `data-overflow-y`
15
+ * (`"true"` | `"false"`) onto the scroll element. The shared `vfScrollRail`
16
+ * recipe keys the drawn rail's dither, thumb and arrows off those attributes,
17
+ * so a rail is a bare white channel at `"false"` and the full System 7
18
+ * scrollbar at `"true"`.
19
+ *
20
+ * It also supplies the HIG's inactive-window treatment: a window that isn't
21
+ * frontmost must not display interactive scroll UX, so System 7 blanked a
22
+ * deactivated window's scrollbars back to the empty rail (and its List
23
+ * Manager/TextEdit did the same for scrollbars *inside* the window). The
24
+ * controller finds the nearest `vf-window` up the composed tree, watches its
25
+ * reflected `active` attribute, and toggles `data-window-inactive`
26
+ * (presence-only) on the scroll element; the recipe empties the
27
+ * dither/thumb/arrows while it is present, regardless of overflow. A scroller
28
+ * with no `vf-window` ancestor never carries the attribute — `vf-dialog` has
29
+ * no inactive state, and a bare scroll component on a page always draws live.
30
+ *
31
+ * The controller reports BOTH axes always; each component decides which rails
32
+ * it *reserves* by which rail elements it renders (`renderScrollRail`,
33
+ * src/scroll-rail.ts). An unreserved axis still scrolls natively but draws no
34
+ * rail, so the idle rules never touch it. The attributes exist only on
35
+ * managed elements, so a plain `.vf-scroll` used without a controller is
36
+ * untouched.
37
+ *
38
+ * Re-measures on every signal that can change overflow: the viewport resizing
39
+ * (host size, `--vf-scale`), the content resizing (a passed content element —
40
+ * slotted rows growing, media or late bitmap-font load), the host re-rendering,
41
+ * and an imperative {@link measure} (a `<textarea>`'s own scrollHeight changes
42
+ * on input without any box resizing).
43
+ *
44
+ * FUTURE: once `@container scroll-state(scrollable)` container queries reach
45
+ * baseline support, slotted-content components could drop this JS and gate the
46
+ * same recipe rules with a pure-CSS `@container` query instead (a native
47
+ * `<textarea>` changing scrollHeight on input would still need the imperative
48
+ * path). The window-activity half has the same trajectory via `@container
49
+ * style()` queries: vf-window cascades a custom property under
50
+ * `:host(:not([active]))` and the recipe gates on
51
+ * `style(--vf-window-active: false)`, retiring {@link observeWindow}'s
52
+ * MutationObserver and composed-tree walk. The rails being ordinary DOM,
53
+ * either migration is a plain selector swap — the old scrollbar-pseudo
54
+ * re-resolution caveat retired with `::-webkit-scrollbar`.
55
+ */
56
+ export declare class ScrollStateController implements ReactiveController {
57
+ private readonly host;
58
+ private readonly getScroll;
59
+ private readonly getContent?;
60
+ /**
61
+ * Invoked whenever the measured overflow state changes (including the
62
+ * first measurement), with the per-axis result. Components that gate
63
+ * *rendered* state on scrollability — vf-scroll-area's conditional tab
64
+ * stop — hang a reactive property off this; the data attributes alone
65
+ * are written outside Lit's render and would never re-render the host.
66
+ */
67
+ private readonly onOverflowChange?;
68
+ private resizeObserver?;
69
+ private windowObserver?;
70
+ private vfWindow;
71
+ private wired;
72
+ constructor(host: ReactiveControllerHost & HTMLElement, getScroll: () => HTMLElement | null | undefined, getContent?: (() => Element | null | undefined) | undefined,
73
+ /**
74
+ * Invoked whenever the measured overflow state changes (including the
75
+ * first measurement), with the per-axis result. Components that gate
76
+ * *rendered* state on scrollability — vf-scroll-area's conditional tab
77
+ * stop — hang a reactive property off this; the data attributes alone
78
+ * are written outside Lit's render and would never re-render the host.
79
+ */
80
+ onOverflowChange?: ((overflow: {
81
+ x: boolean;
82
+ y: boolean;
83
+ }) => void) | undefined);
84
+ hostConnected(): void;
85
+ hostUpdated(): void;
86
+ hostDisconnected(): void;
87
+ /** (Re-)attach the ResizeObserver to the viewport and content boxes. */
88
+ private wire;
89
+ /**
90
+ * Find the containing `vf-window` (checked by tag name, not instanceof — an
91
+ * import here would pull the whole window component into every scroller's
92
+ * graph) and watch its `active` attribute. The walk crosses shadow
93
+ * boundaries, so it reaches the window whether the scroller is slotted into
94
+ * its body (light-DOM ancestor) or is the window's own edge scroll area
95
+ * (shadow-DOM ancestor). Reconnect re-runs it, so a reparented scroller
96
+ * re-resolves its window.
97
+ */
98
+ private observeWindow;
99
+ /**
100
+ * Reflect the containing window's `active` state as a presence-only
101
+ * `data-window-inactive` on the scroll element. A window that hasn't
102
+ * upgraded yet has no `active` attribute (its default reflects on first
103
+ * update, which precedes any scroller rendering inside it); the observer
104
+ * catches the reflection either way.
105
+ */
106
+ private reflectWindowState;
107
+ /**
108
+ * Measure overflow and reflect it as `data-overflow-{x,y}` on the scroll
109
+ * element. Safe to call any time; components hit it imperatively when content
110
+ * changes without a box resize (e.g. a `<textarea>` on input).
111
+ */
112
+ measure(): void;
113
+ }
@@ -0,0 +1,55 @@
1
+ import { sys as e } from "./scale.js";
2
+ //#region src/scroll-state.ts
3
+ var t = 1, n = 2, r = class {
4
+ constructor(e, t, n, r) {
5
+ this.host = e, this.getScroll = t, this.getContent = n, this.onOverflowChange = r, this.vfWindow = null, this.wired = !1, e.addController(this);
6
+ }
7
+ hostConnected() {
8
+ this.wired = !1, this.wire(), this.observeWindow(), typeof document < "u" && document.fonts && document.fonts.ready.then(() => this.measure());
9
+ }
10
+ hostUpdated() {
11
+ this.wire(), this.measure(), this.reflectWindowState();
12
+ }
13
+ hostDisconnected() {
14
+ this.resizeObserver?.disconnect(), this.resizeObserver = void 0, this.windowObserver?.disconnect(), this.windowObserver = void 0, this.vfWindow = null, this.wired = !1;
15
+ }
16
+ wire() {
17
+ if (this.wired || typeof ResizeObserver > "u") return;
18
+ let e = this.getScroll();
19
+ if (!e) return;
20
+ this.resizeObserver ||= new ResizeObserver(() => this.measure()), this.resizeObserver.observe(e);
21
+ let t = this.getContent?.();
22
+ t && this.resizeObserver.observe(t), this.wired = !0;
23
+ }
24
+ observeWindow() {
25
+ let e = this.host;
26
+ for (this.vfWindow = null; e;) {
27
+ if (e instanceof HTMLElement && e.localName === "vf-window") {
28
+ this.vfWindow = e;
29
+ break;
30
+ }
31
+ e = e.parentNode ?? (e instanceof ShadowRoot ? e.host : null);
32
+ }
33
+ !this.vfWindow || typeof MutationObserver > "u" || (this.windowObserver ??= new MutationObserver(() => this.reflectWindowState()), this.windowObserver.observe(this.vfWindow, {
34
+ attributes: !0,
35
+ attributeFilter: ["active"]
36
+ }));
37
+ }
38
+ reflectWindowState() {
39
+ let e = this.getScroll();
40
+ if (!e) return;
41
+ let t = this.vfWindow !== null && !this.vfWindow.hasAttribute("active");
42
+ e.hasAttribute("data-window-inactive") !== t && e.toggleAttribute("data-window-inactive", t);
43
+ }
44
+ measure() {
45
+ let r = this.getScroll();
46
+ if (!r) return;
47
+ let i = r.scrollHeight - r.clientHeight > e(n, r) + t, a = r.scrollWidth - r.clientWidth > t, o = r.getAttribute("data-overflow-y") !== String(i) || r.getAttribute("data-overflow-x") !== String(a);
48
+ r.setAttribute("data-overflow-y", String(i)), r.setAttribute("data-overflow-x", String(a)), o && this.onOverflowChange?.({
49
+ x: a,
50
+ y: i
51
+ });
52
+ }
53
+ };
54
+ //#endregion
55
+ export { r as ScrollStateController };
package/dist/size.d.ts ADDED
@@ -0,0 +1,47 @@
1
+ import type { LitElement } from 'lit';
2
+ type Constructor<T = object> = new (...args: any[]) => T;
3
+ /**
4
+ * The mixin's surface, as a `declare class` for the same TS4094 reason
5
+ * {@link VfPositionedInterface} states: TypeScript cannot name the
6
+ * anonymous class a mixin returns when emitting declarations. Keep it in sync
7
+ * with the implementation below.
8
+ */
9
+ export declare abstract class VfSizedInterface extends LitElement {
10
+ width?: number | null;
11
+ height?: number | null;
12
+ }
13
+ /**
14
+ * A declared box — `width`/`height` in whole system px, the size half of
15
+ * {@link VfPositioned}'s DITL story: a resource stated each item's rectangle,
16
+ * position *and* extent, in the window's own coordinates.
17
+ *
18
+ * It goes only on the components whose box is *stated* rather than governed
19
+ * by content: the two static-text elements (`vf-paragraph`, where width is
20
+ * the measure the copy wraps to; `vf-label`, a caption column's shared
21
+ * width), `vf-stack` (a panel's declared box) and `vf-window` (the WIND
22
+ * rect). The rest of the kit is content-governed on purpose — a push button
23
+ * is as wide as its label, a popup hugs its widest option (docs/LAYOUT.md, "the
24
+ * content governs the box") — and the components that *transform* a declared
25
+ * size keep their own properties: `vf-desktop` adds the bezel around it,
26
+ * `vf-dialog` sizes the top-layer box, `vf-img` negotiates with the art's
27
+ * natural size.
28
+ *
29
+ * Mechanics, mirroring the position mixin: each declared dimension goes on
30
+ * the host's own inline style as `calc(var(--vf-scale, 1) * Npx)` — live
31
+ * against the display, resolved at paint time — and a whole number keeps the
32
+ * box on the device-pixel grid by construction (the size half of layout
33
+ * contract rule 3, the one grid snapping deliberately doesn't cover).
34
+ * Unsetting a dimension removes the declaration and that axis returns to
35
+ * layout.
36
+ *
37
+ * The writing rides a {@link ReactiveController} for the lifecycle reason
38
+ * position.ts records (components override `updated()` without calling
39
+ * `super.updated()`, which would shadow a mixin-level override), and it
40
+ * re-applies **only when the property values changed** — the `vf-window`
41
+ * grow-box rule: a resize writes resolved px into the same inline
42
+ * `width`/`height` without touching these properties, and re-asserting the
43
+ * authored size on an unrelated render would snap a resized window back.
44
+ * Setting the property again is the deliberate way to re-size it.
45
+ */
46
+ export declare const VfSized: <T extends Constructor<LitElement>>(Base: T) => Constructor<VfSizedInterface> & T;
47
+ export {};
package/dist/size.js ADDED
@@ -0,0 +1,30 @@
1
+ import { sysLength as e } from "./scale.js";
2
+ import t from "./_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.js";
3
+ import { property as n } from "lit/decorators.js";
4
+ //#region src/size.ts
5
+ var r = (e) => {
6
+ class r extends e {
7
+ constructor(...e) {
8
+ super(...e), new i(this);
9
+ }
10
+ }
11
+ return t([n({ type: Number })], r.prototype, "width", void 0), t([n({ type: Number })], r.prototype, "height", void 0), r;
12
+ }, i = class {
13
+ #e;
14
+ #t = {
15
+ width: null,
16
+ height: null
17
+ };
18
+ constructor(e) {
19
+ this.#e = e, e.addController(this);
20
+ }
21
+ hostUpdated() {
22
+ this.#n("width"), this.#n("height");
23
+ }
24
+ #n(t) {
25
+ let n = this.#e[t] ?? null;
26
+ n !== this.#t[t] && (this.#t[t] = n, n === null ? this.#e.style.removeProperty(t) : this.#e.style[t] = e(n));
27
+ }
28
+ };
29
+ //#endregion
30
+ export { r as VfSized };
@@ -0,0 +1,38 @@
1
+ /**
2
+ * The 1-bit style recipes — the barrel every component and the package root
3
+ * import from. Each recipe lives in its own module under `./recipes/`; this
4
+ * file re-exports them so `import { vfBase, vfField } from '../styles/base.js'`
5
+ * keeps working and the public API is one name list.
6
+ *
7
+ * Why the recipes are split across modules rather than declared here: a bundler
8
+ * cannot drop an unused `export const x = css\`…\`` from within a module. The
9
+ * `/* @__PURE__ *\/` annotation that would let it is honored on *call*
10
+ * expressions only — Lit's `css` is a *tagged template*, and esbuild (like
11
+ * Rollup) ignores the annotation there. Whole-module elimination is the one
12
+ * granularity every bundler handles, so a module boundary is the only place a
13
+ * recipe can be dropped at. Before the split, importing a single recipe pulled
14
+ * all 24 KB of them plus both font files; a `vf-button` now carries the four it
15
+ * composes.
16
+ *
17
+ * That is also why the two `@font-face` registrations sit beside the two face
18
+ * recipes (`./recipes/body-face.ts`, `./recipes/display-face.ts`) instead of at
19
+ * the top of this file — a component that sets no chrome type should not ship
20
+ * the Chicago face.
21
+ *
22
+ * Adding a recipe: give it a module here (or add it to the one whose recipes it
23
+ * is always composed with), then re-export it below and from `src/index.ts`.
24
+ */
25
+ export { vfBodyDecls } from './recipes/body-face.js';
26
+ export { vfDisplay, vfDisplayDecls } from './recipes/display-face.js';
27
+ export { vfBase } from './recipes/host.js';
28
+ export { vfStaticText } from './recipes/static-text.js';
29
+ export { vfDots, vfStripes } from './recipes/pattern.js';
30
+ export { vfHardShadowDecls } from './recipes/shadow.js';
31
+ export { vfChromeFrame, vfModalFrame, vfPanel } from './recipes/surface.js';
32
+ export { vfTitleBar, vfWindowWidgets } from './recipes/title-bar.js';
33
+ export { vfFocus, vfFocusRing, vfFocusUnderline } from './recipes/focus.js';
34
+ export { vfToggle } from './recipes/toggle.js';
35
+ export { vfField } from './recipes/field.js';
36
+ export { vfScrollRail } from './recipes/scroll-rail.js';
37
+ export { TILE_LATTICE, tileImage, tileRaster, tileRects, tileSpan, vfTileMaskSize, vfTileSize, } from './recipes/tile.js';
38
+ export type { TileRect } from './recipes/tile.js';
@@ -0,0 +1,7 @@
1
+ /**
2
+ * The CSS `font-family` name the face is registered under — the default of
3
+ * `--vf-font-family`, and the `familyName` inside the woff2 itself.
4
+ */
5
+ export declare const VF_BODY_FAMILY = "VF Body";
6
+ /** Register the body face on the document once (idempotent). */
7
+ export declare function registerBodyFace(): void;
@@ -0,0 +1,9 @@
1
+ import { PIXEL_GRID_METRICS as e, registerEmbeddedFont as t } from "./register-embedded-font.js";
2
+ //#region src/styles/body-font.ts
3
+ var n = "VF Body", r = "d09GMgABAAAAABFQAAoAAAAAhTgAABEBAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAABmAAhUQKgd9wgbJ9ATYCJAOIDAuECAAEIAWCQQeTIhsJbFVGho0DCGS50ogkg9QYFWWLk7L+/7YgriF1Bl415eLh4VkNXUMMLQfgIgCqVGYyrdrXPPJ2+C+pjuS63F4tUbEbZuaLnuF/d8wcA+5wzic8j2Payy+BGiu2rObihm7CTKmxInSAeIi6lOZYEarM/zqPToNCOdVJr7OeMPwblJeK6kJHCxu63EcW4SE1t8m28NeYaZuLePhbven+HaeEOQcxr8uaNOeEK+D4Pr+0AyQ9lhpYOINTc/scZuaYDf8QOWLa1NwW6kJdjRraShBX4BGx61AK8Dx2e/+u3RYG2k7aEm0LKLTADzQAPgh0cGP72Iqts/Vg3LyB0/Cfm3gvF8z/lGrLsi4VJxAIxG6jaV2FP4FZ/b3qv1Rr9DUf34SqF7Vs0jeFfwM6DMVKBw5YACzjncIWleb/N+0Vz/cZpzUYZJhGgEtFLAHM2t2550dfEnKpKKiz0vfpvqcdaUZyaQ0lHaBwHAjDA/3a783uwS/4dPEQ2hq2+/DGEM0riWZSIqlSEi3/0PM/NZ0LfcrrhIww3Ymcbl5Ot143WOGgT0xrat02zIOu8wi6NuJ/RyAQCERER0REREREBO2ezKmBX8uT76KBA8hrHJk5pgJeCl71+sWHl4vKLoyg+A5g/sXPhL7aUzxxQHhQ9VksRdQD5JRDZTH5/dd+XfyE6fCyYtuuM0+ZMi8/Tq3N/vXzCWDEARMhxEDwUEQFbUzxFojwxEsA8RefSKijjU4KUYwytCSbAonKW7LMyJP88AJvec9HvvOT7/Ejfs6v+aPn9y/f8p23m+IeiGJ3K9bs2HfhGVvpw6kVhpR8Ay4igF1GS+7JX/VR69ebjz5L+ix1Ru6NoxzpbgEAYXALbt7NutZP/v1/a//bf/azHQUA+9rCetaxsoWzx6wI2QvI/MW2AaYGM4xvZEOZhZlm/pmlzDtzO0Nmyql1apRkO6nua+upFFBqTykQiS/xTWQDH9+rd5rr0Wf8KPuuYNnHzuMcbteOb24oRn1hC/zjOJZW1n4YHUDm444+Uwc4LN7QiM0xNjE1M+daWEqkMitriI1cobS1dyA5OpGdKS4qjdrNw90VAfb19weRalCasZ5G3wcdbViqM9scrHSSXcQkYJsmc4Qp4BI7d5YDpgH5MAMHSjQQkaWXxok0SXAdKmoK7fsLPvsKpWMQ+yRrUAtH4EYzaoAkPJmLI9hfvvTC7YHQYj0jlAV0TP/RNkxWqe9UTCSmEK4XSrm9NCHRKmT0k24bZklwsEQ/gKpRiAp0wT+OThMkmprrnfMre4RgcmC11Cix8bFQy1cawE7zy+2LNu0GRJ2slRynmVbGzdy7HABgYafYyiggISXvSGrdeLzLFW6Nikrg37j7B2ykdbcK7uGS2giVDo7BvX4PgT8EeMdNy0r4BDXQkQQ9we9fp/W0el+HQsm4Yzp38kWQRTsyAtDrxIeftUvoy3iFdKyj1i4tLypT4BcicVHQkUjLcJf8Ql5xqASI+AMKpPWAugwqlVRzAgHa+IhwV0nIM4KpNDvSW9FiAF9DPBr/QAS3w35LZzNKnqTah7GKHiiYpImVY3tbrODZlnxyDIWUICcDDViUmSOwy9WrO7lYqJoaYYFuQ629d8oHQWgJEsFmHq4sOQ385dLxNFto80I0bpahVfaxqAQLVl5ynOu6oIlIEzzUhthAyZIWW6IGy7Ih2GEINmU1cOo1eUIPAOEZs4UXCeptonJpZ6Sri1E5yoTKBHeJ3AOIgc4JEyKx1K6w9rZLwS4DXmVf5PQR9sG8l2r02CaFwQawylSpeZthcg7qGUUg3aGSjLRX5CRaIfiIEuA4yheKQrOY2EYmczlLs36k9ngnXBg6dtaNe0+sjcBuzAjjcUf5EiVsxMgbOZUpiXNgkW2386F1M3VAk83NjFx7BHbDBuQOfAKo/MqdvtCK6VQ+tttaNeG0X4BTPoCBWUNEW9e2yk3cSrrzFNOORscG9DduwoO93VrJ6B8TgctGcEAEyOkGRotJjomF3BdO5HgZ6tautdWsKFFLFnIz+gJosLj/N3G7fz4XUyXJz3qC//gngTII5u0jp5mbpEzs0R6tkoKmGWdjOwCEJjal8cgsZ4URy3hlUR3Iy/wC13FKs25zC24glkL2Cw+LhgFmZ5jgJHuCZMrg+LSe4m2mMJuIabpDa2zd7C0q49Oay1ySUzacMy6AS1BlSXxiR0EchU7UOZqnk7J1wwu0u+bIhQSYY194mbD5E1JWn8blNwVkTIoaXiShCT0g+0rt9pJAFmyAxVihbIOt2WAuGBZqAluk6JxUBBL6LN+6qGlZL/ygN9CD0e2454ALq5VPLX+bXA4XTEDtSoANG6Zoi865IazQNPYTMVSTxrWI8d0729QCJMquoYl9gLDbtFpfhGYkz3H/Pkt0ggv9tzUvqhuuIq22ZM8iUUWpQAPjfC6MnPhw1ySz1kYgf7Nkzpaa36pyS78jT4qG9cFD59TBWw4zZNaqGTMg05aAOyPcLl+YeGnO/l8TFqNqzLa4KGpa/k1BcNTUdW2xdcIsucwgcL+wTC/d0y7nd/rtlbDLcfcSOatEIhLXvTQdaqupF+kjyZlTcZY8/dpE3dOTzJ88o6onWPKJtfcognWgrO/TWTV9gty1uzlWLsSET9e9ZL9a97O86PnDz7rMLz5vaLoD9Bel2SzDrHIoOY+ap8PDEMB7wdDljLmNywXyLN3E1Ds2aMQyWxD+JP8NNCfPde3HAANF5nusPMc+gE/ZenWIikgBi2zr/M+dECnQLHRvgMeQHNvqYobGPvxIyRLIQqS81uh6b3BYZ7n8OMuAgXIH57whHGA6bTjpjFAJMuJA8wEzxYFaA9OWZ7JWwEZKPR3VMRkVpHRZkEcNQ1JB6xkGPjFw83PXK+8sRoOnSJOeK2rQzGjasqVc2rhpBkh8i2l5aQIDP4Z0YhHM6qZ3Iszwcf5/jpwjtXLGwIPUjDekMQvFceYS+RXpOIt4Toj55LbNrWENTge2os6EVh2bBIzujrvl4eGysTKdol7LzuzyhnLNnnOSAaZxPYAw1CHdYI6qVxQdKDpGxgYXC+yom+zdiUoqL9cSRXPb20tSb11aH71IYNEGmUVjwyIn1tXyWEM31fb2JHKKZXe8WKBFnkZm5nr3Y30yCeck5OedN7Vyqa705vlTuh6rvZAmCGrt+MXAIxOqCoi9G/mjW/ZUgHc6WXcVTK4UXdfg4ohgrElX5Nw3Vn10CtRCDrvBkTu1c/NTRDZqiEguPE7vVtjAaGY5mAsGNyG1TwKpMYR4qU3o4KcyJjdn/4aLsGuzAPqT2sLGoSqniBD1HxDVqNdV1l02FVz2cpuIt0qYXKwUEZkOA7e97wKkioN+6KQ20RFb3cx2jeSYGAcefkbL/iItfBUjnJm/lkDquhBZJPgziqia55c7wgx5H05oAi+/84G4RBfnpIZBZ5+Rso4n10lP/Gu3qki3V+dx1xMujYzpKpxbSljfHlWdpCWC5AxbfRhBZ57+44o4IdEqXr4ElRGTwkGxaOuRl9+Ktezhol3Wt7/kADqprYc78T/2vUlYwMnsCoRt/V3arP3oHzPhrRDUG4bD/iQ+rUHgHUhLpuC98mEZdeUFPbRvX5ALCCvniWptdNcvI798Y5lcW8AC2pnfNkL7AjMH1ipCcjYSRA1dGXHtvXEM+xg1fVReAAjEXBhu4fOL8UT6pQGapHI9yfeWS3S1CvDJr7/ZdD4oUhNaPxdyN1IgW2DjMQyD46hD8iVUApbdJ4dozBMZ/HVGzQb4K0WoYoQgV86e+8nY2e8rTZtcyM7PlikVrSFZE/tqbk4gneeLcvhWW32mr7ISxjhhhRk5+ptUWpTSne7yJgKtZFXcDs+gtgjuBFXypZbDO8xnhsZBbvAGKz45BT+yr3HIhqFCWA3A+fv5UesBn+0m1pQBqMz+zkVI7ZrnvnsrkCnjEUbZ3y6cf4KaLOOtc7gqOanstJH3xf94H/FxNYjs8H647QHg7qGW5yQfFKXgV+61rUraydnhpPlqtziPVw3JcYkxTxRMGDo8vO1qoIPU1S2XcftM0KmeAldq0DMn/XYk3BTi7zknApxIjf3zsv3dffQadYa3oC2HOhd98RIZg/Ay1f5R48k5hP3TJPo58JPuOFH203m0Ll23IQxGH9ugJdroM1IGVQAbjvwe8vRKgIx9ze5j6VN4Y/2sLJ4X5FhREn7Kb4qt55TfLD1f46S/ZmgspAyo6MtmWWp2IHGhArdbU5QOci+1691nkQu6B91vgM80dW8tIlHyfDIYfTwg2iLzfwW15rMP0311/iYEpvd6EBCsxwRds4xn4BCQFfaHOo7Bv1hGLRECDZLu3ytu9qkc7whGdkvN2UWWO0HORjW8uA71x2qBsKbLVgqzLPqIAlpsUQL50YUCNGG4GRbXiIa0Kr0yecIX8cdqQXAjUA+vw04i5IsMRcV9O20Gnfnq6gr7/FxDXal/B0bQq4zTltXxBIKZHzZ6CdWF/6RnQw+WiPyDniYAH0fdSf2r65JnRQXCg6SEAckd++2hVovz/PIOvAFCuGULDKLnsgId1/APKNAhKmigtHACunxlV25o7wsFbOuO05s1QmC/IlhAOpx+A3Sp1UK+wp1WqNGLygjuRenRBT8PDh4LsAF9xIz0I0QLiVLzgQXq4hzSbcd3dIaGe4pO1zqdOyR0BJRt489CvFfTPaDu8Abgv7ULBOeyqwQ82PibWgJr8S5ASBEhlkoEQ0EWbb+hK2juwBpQ0jH8mQXIx4ARQWnNM9QFMcoDAQzavvwKtWvwdv4NCpQn7XIgQMgtiTJNcakIgG4XgVkP+YTH3spZHzEq1vzWWm2tEeUtB3HqZbtomPVQaf7YK2d9FLnd9RNn5vnNnBXLTL13SjYHJwrUySkO9vVaSTHW1ImZLxaLpkP9E2hhrKYP8F1NBCJHK46dk8TGQa5F1qQqtFtb85/26mq+9sXvO3sJYKPGpDGS1NGtvgX5x7d7/ieLWqPV6Q2K0WS2WG12Gp3BZLE5XEEhYRFRMXEgECmBeXxJKWkZWTl5BUUlZRVVNXUNTS1tHV09fQNDJAqNweLwBCKJTKHS6Awmi83h8vgCoUgskcrkCqVKrdHq9AajycraxtbO3sHRydnF1c3s7uHp5e3jC6IxWByeQCSRKVQancFksTlcHl8gFIklUplcoVSpNVqd3sDQCDA2MTUzt7C0sraxtbN3cHRyzrdcubrlK9bHDz/98tuf3PLIAzwG4bZrj1BoMpFKojBpdAaXxRbw+KHQGCwOTyCSyBQqjc5gxonF5nB5fIFQJJZIZXKFUqXux1+yHoBAQYQJZVxIpY114SMDRJhQxoVU2lgXPgpAhAllXEiljXXhowJEmFDGhVTaWBc+GkCECWVcSKWNdeGjA0SYUMaFVNrYcLEAIky5kEqbcLEBEsqUDh8HMKGMh4yLlAkTPh5AhAllXEiljXXh4yPChDIupNKm8ydQKfCAR5VWxaTTCc0KRYQ0YSMB6rQaPsIkv1AsfERU4QaoBySiAenUQ51CkVOClfQoVulkVOvYNGolPmFZxY5Vlek7dLnbgfMmFLKzbFyqkjzdO2ZF6RhVz4jjbu1e6NGr0mm9Snyir4oYgatGToFVEQdyk4/XlTZdcRN/5eNxrVKeyu0kuUlLrvkBgGUWNXJKt5LtoFhLTclOlVXozMon5mp6wJc3AlYjjzJy/3BRptnKFjq3tYmusg6fyi9pZ0/Fq2+OWWka/Smf/oR7taS7TN/CkbwFm4nBPsXXQ8JcZ8aEy28rdrSwbGqwkp7dXH6STuvWCA==";
4
+ function i() {
5
+ t(n, r, e);
6
+ }
7
+ i();
8
+ //#endregion
9
+ export { n as VF_BODY_FAMILY, i as registerBodyFace };
@@ -0,0 +1,7 @@
1
+ /**
2
+ * The CSS `font-family` name the face is registered under — the default of
3
+ * `--vf-font-family-display`, and the `familyName` inside the woff2 itself.
4
+ */
5
+ export declare const VF_DISPLAY_FAMILY = "VF Display";
6
+ /** Register the display face on the document once (idempotent). */
7
+ export declare function registerDisplayFace(): void;
@@ -0,0 +1,9 @@
1
+ import { PIXEL_GRID_METRICS as e, registerEmbeddedFont as t } from "./register-embedded-font.js";
2
+ //#region src/styles/display-font.ts
3
+ var n = "VF Display", r = "d09GMgABAAAAABIAAAoAAAAAnDwAABGvAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAABmAAhUQKgpkcgd9zATYCJAOIDAuECAAEIAWCXAeHYxvbfEUHctg4AFB8NUVRLlbJyECwcQAV4BXj/3tyS0bgLlBtVf2TnMhqd3t09h7swdW7hULVvqlQVTPo7Dbn5ax78Nz1xE2zqsM0Xi2n+BMkiIQUi2XyguMLJZhEEnuHJmNgmH6bMit+o73wnP9ocAY63hGaHHk3Iaq17Jl5pkShI4VyjYrQMSq1FQWogDWiYuNYxRGq879f22PTV+Xsfbg3selMxyMkSjKPTCcRI6X8P7Aunw7p4g9MYLq0BMUDUQcmIqkuSZFNdRdj89GSOXIWEZuIJzHI87TBW258b8U3SaHaIripiKhYgUCcgOdp5/vVlYb5TAY0gREkMprpUGfQzcYEwRADm5jIXPrrOirgWmpqnJ/JWOsUGHthqEnlfzhAloyEgGlXhk+ubBvbju0DKIGBANxgbH4DIwfmmfNAp2j8ifb/26+JXjTiiTxpE9bZrQnemz/I+4PZsA3zJjaoJUInNYskUuJ0T3RqoHYLseP3P+0Vv+do05HdWQgIb99OwR3Bv5Jmji1LqY3VTjW6mnnSW0nrUjelNMBLwzwQLtzQEJSpNnN2a0aG7VD8KFk4AkQhqMUMOO+3/2vnS06TUv0zSsyd9qDCy+t+DzgIBsEgCIIgGARBMBgEg4d/KoTyK1NghFjVPZic8P4idB3jd4vcwP1qBMAI664GPkCBgVL4/7wkVHYARmhgJQw4VNTQMnbo3L13/4IYNaOrtmrnDSEECQkGHBCiyKOEJrr4hPeuVVt6HznUaNAigxxK0ZDGtGBBtlmEpdj2s0huYGRizUakjYyRLXJF3mgQu2JyTP0P/jQQNHgrKmvqmDpy6cmnIMwKHClCgwWUFtHAKW9RM6PnsbKsNc/PkEdkda4CAFIIXnt2DKub/9/jzz8+M7UA4HFzj+sWPN6MPxMWPJxW5KFgG6c4suD+wRrmIWIaE+bfVObRtCYxgBopkBQOqf6Prck/i5tDcfInavEmGDbhe48rDjLwAXY2R5FbekbAHgC7A03wjliHDaFvXODF4jzZxyIQPdjFJXt14Aif6sMNIZ3BZPki4/j24JGC55bKn2cv1E6UnaqoOlNzrq5py6uFN0sfXg1sW/MNkAqgHZIb6Fg+X+fwAUALHwAQE5GRVcELcpeiMJUCOBAmRhAMwWEGTqFBHBDMZRooE8MEiTEGzCOq0Ego0v1kwaWKoFTQXKIIBHOiY3iTMccIIl13jnsnEqFYykIk4H2Xg5cQnGyNxsl0N+pb7n7j40TI63DLNTHxjYC7wAjNgJqLLEAikorIOUzQjh0Y25QEYwAUgo5KOyUyezFVtIhTWtRMY6PNFdp0qWQimLIi9acafzlpp9iKGWgvE7kr08T1hsjsZseJwKgp9cc6ChEUMJD5+foZACjWVduksAVgY0XpJSAzI99mHJhHIsxEk5xbuscKdeqa70dbGBNtjKbfZB9MaNVjW/BBHZhRqTukeuJq62sep+g2hAaXsuQSV4tP+DaWrOQdIyBtWT5kaoS0BU8YkP1IUXPNmJRjxfcvcoJwN2KHCItJDGtxG10mJJlJuGGaBkMU3LqWSOJYVAO707RG1/2KHIzID5FTlkQWEpLMOof+pK4Z3BhlK6rlyAlzUl0i6UXVuz1bQsUlSdzB0B6n1krerfb+0RPMuiqDENdmZ0hjZAOqjm6xnOX+u6evtK5rP+Ywx4DXgMLyBAQdbhj5DuaBs61p0kxo9tIcX8PBwMfiqnYqfnydf/ZqOJRgsQt/1g6+q0kIj9XSOhqyL/bxqppJHCyrZIwwCHO+6CnLntjDSbTZIslWTkTjOkq2e0eUDJYTKoLfvF2N5wv1KSOxZJuSPo7BlbGsnyyWstoUtbsHMkXwkCeNMeu1poy3YbwgAo1tTMooKEi4Yltvva3zbjr3Y12yPXVyzS1UoGVeXGZgNVx9zoZpxzTMHZKDuRXwJSGibntHVGrQN0onPKQHJuEcQDF62W0FgAHIewOfSDQMbIygVETbJ8fBVzSal7wbUkqHvNVamksldt8A5lkgUSpk1U3FBK0brKUBtBJGLcotwWmRAnBe6l3k8g1PDk/ExoPS39kUpG2s/WN5QYjtTPbY+1ZoXmZd6rWZE/p6DE8UWgi05jBBiBnJ2N9+Jtocq1nFUEqs59MuLo32FPsXZ9OKViRmkPDy9KFxXvW5AxHr4vW2LLgpr+lki2f9OI+5jqDTKHCSUExsGvtnWlmqldSRhSBMvvg0BiLZv2Xm7m/ypFacjSBfqZCUZS1S8Cdo2bU+HmS7HXmeLyKBYtWlTxYSpslPMsMRmspTMUZXQK/OuCApAPaz94la3zJzq/TCW7OnTCk/81gWfmKr3Fp5H2TTim+g7TeZjqUE9MVTlBEeXEb5jIPK01eQ7zycmc1BbqIyvc3XlNjSLpDDGrSua2ocYA4IyrY9lQFpulP5CAt5gdAyMXvU9VbGYoO2cK1T4ZOLpk9OFNLoS4gjjHxLYveckzcpKjx6Dxecd5JKlmTD26TtB96FvIcp6O05GgkkgZudlETVBE3wlTUVx0BP65WWJv7Ymk62Ehv0b4kwo/8HyezqVJkBphESiui0IdfVEv62mcpXB3H1U8MZLh99AYUuIZ7azPRVE75y7E94aCouifBlEV43PDlJpa3JDbjHV47w9S42sqUj+OVdpUMC/9DQ8ZY7SXs4jwnOQANpgukNScTfPfgb12avZbIsBJyouFr9xSWNsXzp4hMBzO7pRu8TEAcR7d+Q0ovo7pUE94yxDJVJol65pTAHDBIN9RQz2TmKqVVslt4PzWZwtHUa9cbz6lPHo5tBnRH2H59gZuacn6WGyS7d6Xlu4jofEl/0If5o/qqCeyxVz3MuN2I1ra+n6CCn9pff9W9dmVn3UmjIkGJRgq9n3Z+JEsqRc0JRYYkhaff+WOT14LrfCM0ktpEf1+KbRaIAvqoJgR5yRka08YujFNxPJLfKDJoTsqMI8FzKnaNaeqa5eSL9iGQTc4yRApxpjT7XhAB+Yivr5SPvUnPymvQUGYXYH4XUqbVJzDLX4icLbXFXSTz00CVVDwOkUc2GvjeqjXzizBsDhVKNoJFbn+y7xG/cjhwsSwH0O4zjd/Z3kIWjdzB/ASGRmOC5774JnalVj1bSy3Ix9JhHrz7QlPTJb5IOp5MkhVaQDTvVpPJkWlcj+u1Rbh5S5xPIO70EkW2nlPpvZDOGREJ7qBigIhd/CbXRPlfzjuN+a4FwSDvsyOIYgQPAtuVqln0ri6H6dpKpoGzhasYpB9Aiw9C2mcFd3S4kFf9A/VX59dBdYQ+ooYJdk/XFJtMEuR6mLL2FzqCa1aT6+OfQgVi9irXxHNXEjMig4LYPj327F/q6xjRpgnrmY/1oiN3ykCEHUuLAkI6c/7A53GU6DlbvCX/W3JT+PCIGzHxkDniHR4oRHjjqVrFdwbL7N+aQIKu/i66N6HrIvKAoPvBWLI+FnwzxyDv5mRXtPtTemjih2yfGuONvrFZ1Iy1XRcLRkP+vnJYNoMUOpaixbbKTXy4EDi1aVnZJm/LTlHZim+pvBCTTLhVK76+fNK4ey7oVaDNvlVnObS1CTejGMPjWGL7B6EImziDgZxLAhNHTuz5P5I7oW7HdLRTse8Hv0Ttw4aQtgsd1qT8H+xdE+j7enPGza81V8A3EbO/oWiU6U6W/xBZW67/R9ThlM24mJ8+5/WEOCrfEc1vbzHYMwXKXimfBPG/9Lv8m2xLd4ooeG15watrAADU3M+HUd+7yEnEi4E5bOYGR+6AAxcyKy/3YJfRYrC3tAyJW06AFQPgVQmrqqatdlN5s+y56xe2Mu4Pirs56dGFOPL/crIkettFGnPKT34/Ijaj5qgnfPMS6NdWzw9b5Lte2maA3N/K6E/ahoz/mARC8q841AElRf+2mWGU/iCwlTN6c30Jou3h/B9s7nNUDN9Fkeu6svpJJ//vjbvJ02jPjJDOhZjzkcmIMbjKURLYT6p00i4w5f9Y33v6VE3UZETFgdCoX3rcZotH1ZrrPwkQBj7n9eZwktmx/ZqLoeZ9+IgNjeUn3tEERVAU6oh7WKUKBtp8Mzu4QuMvMuE5wdy4683ObBLunFqgpb0vApXfyGRI34GV93P+ACSY+vxtE0TsOLaLK4raFUaOew/pOQV6kFsCcJ1VvXweZQAWkUfQZFf3TQklpO6ef+KJ8mYyzCta7MOWfjXXS8pn/hQS/VVMiQNe/kg85CJzHVpOw46si102dDiKPm6Pn/O+YmSR5h/8I83+ODIEstB0f1TvfT80Ljk3rgpf/B+fXJk7rowhFbHYV6XEdWbBPOAhl8by1bVKptTCzBR+HHHBwR0z58h+yPZtMMvHOr29kdOYylHILFVnLi2j+15x+39JBE2mrIDt2UURfh/7IW98frDaS7G5J8Pa0cIT+fb9y3f9/+iFKztscYkyfebVmNUmGAgWkR3uG204pLJlber0tBXNSlICOjWXBTQIbnklBAr9WVk1MEvCQfzJAhjbzFIuX/H7SsUdg+N9bqfjMwyILVcICsaNRPB6fX3GbrZdls6Fi3gSIrirx+E2+6WdlWL5vXrJ28E8qL2De8eULDEhZ6aBLu3PSoIQt2ctc5Zy/m/fbeptx/UfTmzRpkl5e9IMM9KnPsc2v7o1Vsmkh08zTYZK1M289+n4ytIJ/hyD8CJoTX8ilGDEwnEU26esRG5mgpnyOZN16UiznEXlt/0nYXbia/MeNrs5Fnnuoh1O67S9q9ecb73IuKmD/m0v5ATsVsXqqlOMDD69YAgKUHuzT6skEpSreq/T/W/vE3f8Si3dvLM4F22gmo7zWsQrMLReURpRPlNNFckzUZCJffFXRCSxAgUc+B+3uO4J0ZhpZE4+cTNcI68IiXhls5XLnAcMk87IyNToHdMpFScVmmOcX5T29opRSOPe5Xry6MoEpq9b1Eta7PEM8K0l0rVZ13miyWtXdSTaOB0ccFfIf2V/ByZ7vN7IQAE+ztEj5xcA0AEMx8v/GLLzAKoeMcIfd6w89L+dWPSelxvcrL8c5ikkjAKfbj/NbdjP5wEKfjNun0cNDLroLdRcbQyTsWUQNJaU7lN+D+GY+N6kAGDQlka273eITV3gBp6DRKqWguzxnZzFoRVhuxncKmIA15Cp5dw7h0qoDPLQma0vI3rB/5AAJJE3KZgQ1I9+2xKuD8GPtqGFl7noBaHAzAR4grtHwfTJ9h6zVMweWb76E78NeE6zvuW7mzr5FvBPM+BOABxwgIEY/9QCKdVeKaQQAIQFQKVESyqifG783FK3C0lrvjkjoFSQtjihpI6Uy+pTpNN8bQiqs3qsxEhH/XuDdU4MHDyzRKV6FCKxFJPmS0gP/4NoDvYVsnqgVyGl5UGQCCNBFUYhYEMsIDEFzLJTpKDwVlidgWizJ4K5W9EAxTFwvU1AEPEBBVp5wYoyWTTZ+s3M7bkJ8pIi5hKSmKU3/ejyBSCJTqDQ6g8lic7jAQ5iwiKiYuISklLSMrJy8ggMIEwqMC6m0sc4DgCAwBAqDI5AoNAaLwxOIJDKFSqMzmCw2h8vjC6IOJVIbmU5t5+WqEpu1tgp7b2c/f6HI0EYEGNwFshUUM2goDA6Lt4dNIpIpNCrfbp/NUr9DHtelLDVfSGumo2uuJC2up29kbGBIx6xuzYpVDhEVMVFFPgllbgHBmIQ4TVgivDnzbtunD5++fPtJVGJc27Vlz4ZNh/YdO3Dk3IlTZ65duHTnxm1iSUhSUpKWjGQlBx4CREiQoUCFBh0GTFiw4cAF4IHAEEYEUcQQR4I2PNWeb+VBWWepUC+udKpFtUJpAuzq95P07GFekJvhvCwky4Lx4CZWWXxb0kqnJvqSEPcBZXHxDjPL000M5pLl6yYsSX7zMEU7btOi32HqF7I0Qzl1DAt855qBfDCjQnD7ukEKnCmSgsDMTvItg2fjSV4LIEdoHvIUD5lXj9r0v8061awJivOtblt54ivX3l91U939ZecFmhhMm7G/WGxfIddwdbXm6c/PkNZH6DblBO/1aJjnQ2f2c8ne3lbLxFyfDAAA";
4
+ function i() {
5
+ t(n, r, e);
6
+ }
7
+ i();
8
+ //#endregion
9
+ export { n as VF_DISPLAY_FAMILY, i as registerDisplayFace };
@@ -0,0 +1,14 @@
1
+ import '../body-font.js';
2
+ /**
3
+ * The three declarations that put text on the *body* face — the family via
4
+ * --vf-font-family, 16px so its 1024-upm pixel grid lands exactly, and the
5
+ * body smoothing token. The mirror image of {@link vfDisplayDecls}, and the
6
+ * body face's single definition: {@link vfBase} applies it to every host, and
7
+ * the places that switch *back* to it from the display face
8
+ * (vf-label/vf-paragraph's `face="body"`) compose this rather than repeating
9
+ * the family stack.
10
+ *
11
+ * 'VF Body' is the embedded face (../body-font.ts); 'Geneva' after it is the
12
+ * installed one a real Mac already has. See {@link vfDisplayDecls}.
13
+ */
14
+ export declare const vfBodyDecls: import("lit").CSSResult;
@@ -0,0 +1,6 @@
1
+ import "../body-font.js";
2
+ import { unsafeCSS as e } from "lit";
3
+ //#region src/styles/recipes/body-face.ts
4
+ var t = e("\n font-family: var(\n --vf-font-family,\n 'VF Body',\n 'Geneva',\n 'Helvetica Neue',\n Helvetica,\n Arial,\n sans-serif\n );\n font-size: calc(var(--vf-scale, 1) * var(--vf-font-size, 16px));\n -webkit-font-smoothing: var(--vf-font-smoothing, antialiased);\n");
5
+ //#endregion
6
+ export { t as vfBodyDecls };
@@ -0,0 +1,23 @@
1
+ import '../display-font.js';
2
+ /**
3
+ * The three declarations that switch text to the display face — the family via
4
+ * --vf-font-family-display, 16px so the 1024-upm pixel grid lands exactly, and
5
+ * grayscale smoothing off for crisp 1-bit edges. Each is tokenized for
6
+ * retheming. Compose onto a rule for one chrome element, or use
7
+ * {@link vfDisplay} to apply to the whole host.
8
+ *
9
+ * 'VF Display' is the embedded face (../display-font.ts). Everything after it
10
+ * is an *installed* face the reader might have — a real Mac has Chicago and
11
+ * Charcoal — so the stack degrades toward the same shapes on a machine that
12
+ * already owns them, and toward the platform sans on one that doesn't.
13
+ */
14
+ export declare const vfDisplayDecls: import("lit").CSSResult;
15
+ /**
16
+ * Display type applied to the whole host. Compose into any
17
+ * component whose text is entirely "chrome": buttons, menus, menu items,
18
+ * checkbox/radio labels, popup menus. Components that mix a chrome title with
19
+ * body content (windows, dialogs, fieldsets) instead apply {@link vfDisplayDecls}
20
+ * to just their title/legend element, leaving slotted body copy on the vfBase
21
+ * body face.
22
+ */
23
+ export declare const vfDisplay: import("lit").CSSResult;
@@ -0,0 +1,10 @@
1
+ import "../display-font.js";
2
+ import { css as e, unsafeCSS as t } from "lit";
3
+ //#region src/styles/recipes/display-face.ts
4
+ var n = t("\n font-family: var(\n --vf-font-family-display,\n 'VF Display',\n 'Chicago',\n 'ChicagoFLF',\n 'Charcoal',\n 'Geneva',\n 'Helvetica Neue',\n Helvetica,\n Arial,\n sans-serif\n );\n font-size: calc(var(--vf-scale, 1) * var(--vf-font-size-display, 16px));\n -webkit-font-smoothing: var(--vf-font-smoothing-display, none);\n"), r = e`
5
+ :host {
6
+ ${n}
7
+ }
8
+ `;
9
+ //#endregion
10
+ export { r as vfDisplay, n as vfDisplayDecls };
@@ -0,0 +1,20 @@
1
+ /**
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
4
+ * keyboard-focus rule under the well (no dotted ring). Add the `vf-field`
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.
11
+ *
12
+ * Why the wrapper: the rule is a pseudo-element, and a replaced element
13
+ * generates none — neither `<input>` nor `<textarea>` can draw its own
14
+ * `::after`. Hanging it off the host instead would leave it behind when grid
15
+ * snapping moves the field, since the correction lands on `.vf-snap` inside the
16
+ * shadow root and never on the host. The wrapper is both anchors at once: it
17
+ * carries `vf-snap`, and it boxes the well exactly, so the rule spans the well
18
+ * and measures its offset from the well's own bottom edge.
19
+ */
20
+ export declare const vfField: import("lit").CSSResult;