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,94 @@
1
+ /**
2
+ * Which input modality last drove the page — the signal a control consults to
3
+ * decide whether an incoming focus deserves a visible focus mark.
4
+ *
5
+ * `:focus-visible` answers that question for every control in the kit except
6
+ * the editable fields. The selector is specified to match ANY focus of an
7
+ * element that takes keyboard input, so a text field clicked with the mouse
8
+ * matches it exactly as a tabbed-to one does — unlike a button, which matches
9
+ * only from the keyboard (`npm run verify:focus` asserts both halves). That is
10
+ * the right default for a browser drawing a ring nobody asked for on a field
11
+ * with no other focus affordance. It is the wrong one here, where a click has
12
+ * already put the insertion point on screen and the kit draws its own mark.
13
+ *
14
+ * Why the document, rather than latching the field's own `pointerdown` the way
15
+ * `vf-slider` does for its ring: the pointer event that lands focus in a field
16
+ * is routinely not on the field. A `vf-label` `for` caption focuses its control
17
+ * from a click on the caption, and all the field sees is a programmatic
18
+ * `focus()` — locally indistinguishable from Tab. Watching the page catches
19
+ * that, and any other control that focuses a field on the user's behalf.
20
+ *
21
+ * The default is `'keyboard'`: focus that arrives with no preceding input at
22
+ * all is assistive tech, an `autofocus`, or a script, and all three should be
23
+ * marked. Only an observed pointer suppresses the mark.
24
+ */
25
+ import type { LitElement, ReactiveController } from 'lit';
26
+ export type FocusModality = 'keyboard' | 'pointer';
27
+ /** The modality that last drove the page. `'keyboard'` until a pointer says otherwise. */
28
+ export declare function focusModality(): FocusModality;
29
+ /**
30
+ * Start watching the page's input modality; returns the release function.
31
+ *
32
+ * The two listeners are shared by every watcher and refcounted, so a page full
33
+ * of fields adds one capture-phase `pointerdown` and one `keydown` to the
34
+ * document, not one pair each. Both are passive and read-only — they set a
35
+ * variable and nothing else. Releasing the last watcher removes them, so a kit
36
+ * torn off a page leaves nothing behind (calling the release twice is a no-op).
37
+ *
38
+ * Capture phase, because the modality has to be recorded before the focus it
39
+ * explains: a `pointerdown` that a control stops from bubbling still moved the
40
+ * focus, and a `keydown` on the element being tabbed away from is the thing
41
+ * that makes the next focus a keyboard one.
42
+ *
43
+ * Both handlers assign one variable and do nothing else — no DOM reads, no
44
+ * `preventDefault` (a passive listener could not anyway), no
45
+ * `stopPropagation` — so a host page's own handlers see every event exactly as
46
+ * they would without the kit. The one way a page can defeat the tracker is to
47
+ * stop these events before the document sees them, e.g. a window-capture
48
+ * listener calling `stopPropagation()`. The modality then goes stale and a
49
+ * field may wear its mark after a click: the failure mode is a focus rule
50
+ * shown too eagerly, never an exception or a swallowed event.
51
+ */
52
+ export declare function trackFocusModality(): () => void;
53
+ /**
54
+ * Whether a control should be wearing the kit's dashed focus rule right now:
55
+ * {@link focusModality} resolved against the host's own focus, as one reactive
56
+ * flag.
57
+ *
58
+ * Four controls need this rather than `:focus-visible`, for two different
59
+ * reasons (SPEC §4). The editable fields, because that selector is specified to
60
+ * match *any* focus of an element which takes keyboard input, so it is already
61
+ * true for a clicked text field. And the three that suppress the browser's own
62
+ * mouse focus so a press-drag gesture can own the pointer — `vf-select`,
63
+ * `vf-menu`, `vf-slider` — then call `focus()` themselves, which Blink reads as
64
+ * a *visible* focus, making `:focus-visible` true after a pure mouse gesture.
65
+ * One controller so the answer can't drift between them.
66
+ *
67
+ * `focusin`/`focusout`, not `focus`/`blur`: the focus usually lands on an
68
+ * element inside the shadow root, and only the bubbling, composed pair crosses
69
+ * that boundary to reach the host. Focus moving *within* the component — a
70
+ * `vf-select` pill handing off to its option rows, a `vf-menu` title to its
71
+ * dropped panel — is not leaving, so the mark survives it and the host takes it
72
+ * back when the panel closes.
73
+ */
74
+ export declare class FocusRuleController implements ReactiveController {
75
+ #private;
76
+ private readonly host;
77
+ constructor(host: LitElement);
78
+ /** True while the host's focus should be marked. */
79
+ get marked(): boolean;
80
+ hostConnected(): void;
81
+ hostDisconnected(): void;
82
+ /**
83
+ * Force the mark on: the keyboard just drove the control, whatever put the
84
+ * focus there. `vf-slider` calls this from a handled arrow key, so a slider
85
+ * clicked and then nudged with the keys starts showing its rule.
86
+ */
87
+ reveal(): void;
88
+ /**
89
+ * Force the mark off. A pointer press on an ALREADY-focused control moves no
90
+ * focus and so fires no `focusin` — the one pointer route this controller
91
+ * cannot see for itself.
92
+ */
93
+ suppress(): void;
94
+ }
@@ -0,0 +1,56 @@
1
+ //#region src/focus-modality.ts
2
+ var e = "keyboard", t = 0, n = () => {
3
+ e = "pointer";
4
+ }, r = () => {
5
+ e = "keyboard";
6
+ };
7
+ function i() {
8
+ return e;
9
+ }
10
+ function a() {
11
+ t++ === 0 && (document.addEventListener("pointerdown", n, {
12
+ capture: !0,
13
+ passive: !0
14
+ }), document.addEventListener("keydown", r, {
15
+ capture: !0,
16
+ passive: !0
17
+ }));
18
+ let e = !1;
19
+ return () => {
20
+ e || (e = !0, --t === 0 && (document.removeEventListener("pointerdown", n, { capture: !0 }), document.removeEventListener("keydown", r, { capture: !0 })));
21
+ };
22
+ }
23
+ var o = class {
24
+ #e = !1;
25
+ #t;
26
+ constructor(e) {
27
+ this.host = e, e.addController(this);
28
+ }
29
+ get marked() {
30
+ return this.#e;
31
+ }
32
+ hostConnected() {
33
+ this.#t = a(), this.host.addEventListener("focusin", this.#r), this.host.addEventListener("focusout", this.#i);
34
+ }
35
+ hostDisconnected() {
36
+ this.host.removeEventListener("focusin", this.#r), this.host.removeEventListener("focusout", this.#i), this.#t?.(), this.#t = void 0, this.#n(!1);
37
+ }
38
+ reveal() {
39
+ this.#n(!0);
40
+ }
41
+ suppress() {
42
+ this.#n(!1);
43
+ }
44
+ #n(e) {
45
+ e !== this.#e && (this.#e = e, this.host.requestUpdate());
46
+ }
47
+ #r = () => {
48
+ this.#n(e === "keyboard");
49
+ };
50
+ #i = (e) => {
51
+ let t = e.relatedTarget;
52
+ t instanceof Node && (this.host.contains(t) || this.host.renderRoot.contains(t)) || this.#n(!1);
53
+ };
54
+ };
55
+ //#endregion
56
+ export { o as FocusRuleController, i as focusModality, a as trackFocusModality };
@@ -0,0 +1,247 @@
1
+ import { LitElement, nothing, type PropertyValues } from 'lit';
2
+ /**
3
+ * Shared base for the kit's form-associated controls (SPEC §4).
4
+ *
5
+ * Owns the boilerplate every one of them repeated: the `ElementInternals`
6
+ * handle, the reflected `disabled` property, the `formDisabled` state fed by
7
+ * `formDisabledCallback` (an ancestor `<fieldset disabled>`), the resolved
8
+ * {@link isDisabled} getter, the form-reset default latch
9
+ * ({@link latchFormDefault} / {@link formDefault}), and — crucially — the
10
+ * disabled-guarded {@link syncFormValue} funnel every subclass routes its
11
+ * `setFormValue` through, so a disabled control can never leak a value into
12
+ * submission.
13
+ *
14
+ * Subclasses supply their own value semantics, ARIA and (where needed) a
15
+ * `formResetCallback` restoring their captured default.
16
+ */
17
+ export declare class VfFormControl extends LitElement {
18
+ #private;
19
+ /** Participates in native forms via ElementInternals. */
20
+ static formAssociated: boolean;
21
+ /** Form + ARIA internals; attached once here for every subclass. */
22
+ protected readonly internals: ElementInternals;
23
+ /**
24
+ * Disables the control: it stops responding and submits no value. Each
25
+ * control dims per SPEC §1 (typically the label greys while the 1-bit chrome
26
+ * stays black).
27
+ */
28
+ disabled: boolean;
29
+ /** True while an ancestor `<fieldset disabled>` disables this control. */
30
+ protected formDisabled: boolean;
31
+ /** Effective disabled state: the `disabled` prop OR an ancestor fieldset. */
32
+ get isDisabled(): boolean;
33
+ /**
34
+ * Form-associated lifecycle: the browser calls this when this control's
35
+ * disabled state changes — an ancestor `<fieldset disabled>`, or the
36
+ * element's OWN `disabled` attribute (the browser counts both).
37
+ *
38
+ * Also mirrored as the `form-disabled` custom state: `disabled` reflects as
39
+ * an attribute a stylesheet can see, but a `<fieldset disabled>` ancestor
40
+ * lands here in otherwise-private state — `:state(form-disabled)` is the
41
+ * one selector that lets consumer CSS style that case. Optional-chained for
42
+ * engines without `CustomStateSet`.
43
+ */
44
+ formDisabledCallback(disabled: boolean): void;
45
+ /**
46
+ * Form-associated lifecycle: the browser hands back state it stored for
47
+ * this control — a bfcache/session restore, or a browser autofill pass.
48
+ * The kit's controls all submit through single-argument `setFormValue`, so
49
+ * the stored state IS the last submitted string. Without this callback,
50
+ * every native input in the form repopulates on restore while the `vf-*`
51
+ * controls silently keep their defaults.
52
+ */
53
+ formStateRestoreCallback(state: string | File | FormData | null, _mode: 'restore' | 'autofill'): void;
54
+ /**
55
+ * Maps stored form state back onto the control's own value semantics. The
56
+ * default covers the string-valued majority (the fields, `vf-select`,
57
+ * `vf-radio-group`); `vf-checkbox` (a checked flag) and `vf-slider` (a
58
+ * number) override.
59
+ */
60
+ protected applyFormState(state: string): void;
61
+ /**
62
+ * Submit `value` under the control's `name` — unless the control is disabled,
63
+ * in which case nothing is submitted (SPEC §4). The single guarded funnel for
64
+ * every subclass's form value, so the disabled contract holds in one place.
65
+ */
66
+ protected syncFormValue(value: string | File | FormData | null): void;
67
+ /**
68
+ * Requires a value before the associated form submits (SPEC §4): an empty
69
+ * control fails constraint validation with `valueMissing`, exactly like a
70
+ * native `required`. What "empty" means is each control's own
71
+ * {@link valueMissing}; a control with no required semantics (a slider
72
+ * always has a value, a swatch submits nothing) never fails it, the way a
73
+ * native range input never does.
74
+ */
75
+ required: boolean;
76
+ /**
77
+ * Installs a custom validity message — the native channel: a non-empty
78
+ * string makes the control invalid with exactly that message, `''` clears
79
+ * it.
80
+ */
81
+ setCustomValidity(message: string): void;
82
+ /** The control's current `ValidityState`, as on a native control. */
83
+ get validity(): ValidityState;
84
+ /** The message of the currently failing constraint, `''` while valid. */
85
+ get validationMessage(): string;
86
+ /**
87
+ * Whether the control is a candidate for constraint validation — false
88
+ * while disabled or readonly, per HTML's barring rules (the browser
89
+ * computes this from the reflected attributes).
90
+ */
91
+ get willValidate(): boolean;
92
+ /** True when the control satisfies its constraints; fires `invalid` if not. */
93
+ checkValidity(): boolean;
94
+ /** {@link checkValidity} plus the browser's own error UI on failure. */
95
+ reportValidity(): boolean;
96
+ /**
97
+ * Whether the control is empty for `required`'s purposes. Overridden by the
98
+ * controls a value can be missing from — `value === ''` on the fields, the
99
+ * select and the radio group, unchecked on the checkbox. The default never
100
+ * fails, which is what makes a bare `required` inert on the rest.
101
+ */
102
+ protected get valueMissing(): boolean;
103
+ /** The `valueMissing` message; subclasses match their native counterpart's. */
104
+ protected get valueMissingMessage(): string;
105
+ /**
106
+ * The single funnel every validity write goes through — the
107
+ * {@link syncFormValue} shape, one place for the contract: the flags are
108
+ * computed from the live properties (`required` × {@link valueMissing},
109
+ * plus any custom error) and installed with `setValidity`, so `:invalid`
110
+ * matches on the host and `form.reportValidity()` blocks exactly as it
111
+ * would on a native control. Runs from {@link willUpdate}, before render,
112
+ * so the same update's template reads fresh validity. Also mirrors
113
+ * `aria-required`/`aria-invalid` through internals for the controls whose
114
+ * role sits on the host — internals lose to a consumer's own host
115
+ * attribute, which is the correct precedence direction.
116
+ */
117
+ protected syncValidity(): void;
118
+ protected willUpdate(changed: PropertyValues): void;
119
+ /**
120
+ * Capture the control's form-reset default. Only the first call latches, so
121
+ * lifecycle paths that can run more than once — reconnects, repeated
122
+ * slotchanges — may call it unconditionally. WHEN to call it is each
123
+ * control's own contract: most latch on first connect, `vf-select` waits
124
+ * until options exist, `vf-radio-group` lets a pre-checked radio adopted on
125
+ * slotchange claim an unauthored default.
126
+ */
127
+ protected latchFormDefault(value: unknown): void;
128
+ /**
129
+ * The latched form-reset default, or `fallback` while nothing has latched.
130
+ * The cast is sound because a control only ever latches its own value type.
131
+ */
132
+ protected formDefault<T>(fallback: T): T;
133
+ /**
134
+ * True when this update changed the *resolved* disabled state — the gate a
135
+ * subclass's `updated()` should use before re-running its form value, ARIA
136
+ * or tab-stop writes.
137
+ *
138
+ * It exists because {@link formDisabled} is protected, so `keyof this` can't
139
+ * name it and a `PropertyValues<this>` gate silently can't test for it: a
140
+ * `changed.has('disabled')` check compiles, reads as complete, and misses
141
+ * every ancestor `<fieldset disabled>` — which is the path that must clear a
142
+ * control's submitted value. One named predicate keeps that trap in one
143
+ * place instead of in each subclass's gate.
144
+ */
145
+ protected disabledChanged(changed: PropertyValues): boolean;
146
+ }
147
+ /**
148
+ * {@link VfFormControl} plus the name/description bridge, for the controls
149
+ * whose **role sits on a shadow-internal node** — `vf-button`, `vf-select`,
150
+ * `vf-swatch`, and the three fields via `VfTextControlBase` (SPEC §4).
151
+ *
152
+ * That one structural fact is the whole membership rule. On those controls the
153
+ * host is a generic wrapper AccName never consults, and a host-level IDREF
154
+ * cannot reach into a shadow tree, so the platform's own words — `aria-label`,
155
+ * `aria-labelledby`, `<label for>`, `aria-describedby` — land on nothing unless
156
+ * something resolves them to text and hands them inward. This class is that
157
+ * something.
158
+ *
159
+ * The controls whose role sits on the **host** (`vf-checkbox`,
160
+ * `vf-radio-group`, `vf-slider`) deliberately stay on the plain base: the
161
+ * platform reads their host attributes directly, so a bridge there would
162
+ * duplicate a working channel — and an inherited property that renders nothing
163
+ * is exactly the "advertised API that silently does nothing" this split exists
164
+ * to make impossible. `verify:manifest` holds the line: a tag whose manifest
165
+ * carries `description` must call {@link renderDescription} in its own source.
166
+ */
167
+ export declare class VfShadowRoleControl extends VfFormControl {
168
+ /**
169
+ * The host-level ARIA attributes the bridge mirrors inward, observed so a
170
+ * consumer writing one after upgrade re-renders the control. They are
171
+ * deliberately not reactive properties: each has an IDL accessor on
172
+ * `Element` already, and a Lit `@property` would shadow the platform member
173
+ * (the kit's `align`/`draggable` trap) — so they are observed by name and
174
+ * read at render time, the `forwardedAttributes` shape.
175
+ */
176
+ private static readonly bridgedAriaAttributes;
177
+ static get observedAttributes(): string[];
178
+ attributeChangedCallback(name: string, old: string | null, value: string | null): void;
179
+ /**
180
+ * Form-associated lifecycle: the association changed, so the `<label for>`
181
+ * set feeding {@link hostLabel} may have too — re-render whatever mirrors it.
182
+ */
183
+ formAssociatedCallback(_form: HTMLFormElement | null): void;
184
+ /**
185
+ * The accessible name the *host* carries. A consumer's `aria-label`,
186
+ * `aria-labelledby` or `<label for>` used to be silently inert on these
187
+ * controls, so the bridge resolves them to text for the control to hand to
188
+ * its inner focusable element. The explicit `label` property still wins:
189
+ * templates read `this.label || this.hostLabel`.
190
+ *
191
+ * Precedence is html-aam's — `aria-labelledby`, then `aria-label`, then the
192
+ * associated `<label for>` elements (`internals.labels`). Referenced text is
193
+ * flattened at render time, so an edit to a referenced element's *text*
194
+ * lands on the control's next render rather than instantly — the one
195
+ * divergence from a native control, recorded in SPEC §4.
196
+ */
197
+ protected get hostLabel(): string;
198
+ /**
199
+ * The ARIA half of {@link hostLabel} — `aria-labelledby`, then `aria-label`,
200
+ * in html-aam's order — without the `<label for>` leg.
201
+ *
202
+ * Split out because that leg does not apply to every control. A `<button>`
203
+ * is not a labelable element, so no caption names a native one and none
204
+ * should name a `vf-button` either (`verify:names` asserts that a `vf-label
205
+ * for=` leaves a button's own name alone). What a button DOES need is the
206
+ * other half: on a control whose role sits on a shadow-internal node, a
207
+ * consumer's `aria-label` is otherwise inert, because it lands on a generic
208
+ * host AccName never consults — so an icon button labelled the ordinary way
209
+ * was announced by its glyph.
210
+ */
211
+ protected get hostAriaLabel(): string;
212
+ /**
213
+ * Description for the control — hint text, a format, a unit. A host-level
214
+ * `aria-describedby` cannot reach a focusable element inside a shadow root,
215
+ * so there was structurally no way to describe a field; this property is
216
+ * that channel. It renders as a hidden span in the control's own shadow root
217
+ * with the inner control's `aria-describedby` pointing at it — the
218
+ * shadow-internal IDREF idiom `vf-dialog`'s title patch already uses. A
219
+ * host-level `aria-describedby` is bridged into the same span when this
220
+ * property is empty, and a failing constraint's {@link validationMessage}
221
+ * joins it too, so AT hears the error where it hears the hint.
222
+ *
223
+ * Host-role controls get neither half: their `aria-describedby` already
224
+ * works, and their validation message reaches AT the way a native control's
225
+ * does — `aria-invalid` plus the browser's own validation UI, not AccName.
226
+ */
227
+ description: string;
228
+ /**
229
+ * What {@link renderDescription}'s span carries: the current validation
230
+ * message while the control is invalid, then the description (the property,
231
+ * or the bridged host `aria-describedby` text).
232
+ */
233
+ protected get descriptionText(): string;
234
+ /**
235
+ * `aria-describedby` value for the inner control — set only while the span
236
+ * has something to say, so an idle control isn't announced as described by
237
+ * nothing.
238
+ */
239
+ protected get describedBy(): string | typeof nothing;
240
+ /**
241
+ * The hidden span the inner control's `aria-describedby` points at.
242
+ * `hidden` keeps it out of the page; AccName still resolves `display: none`
243
+ * reference targets (the fact vf-window's utility title patch leans on), so
244
+ * the text reaches AT without painting.
245
+ */
246
+ protected renderDescription(): import("lit-html").TemplateResult<1> | typeof nothing;
247
+ }
@@ -0,0 +1,127 @@
1
+ import e from "./_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.js";
2
+ import { LitElement as t, html as n, nothing as r } from "lit";
3
+ import { property as i, state as a } from "lit/decorators.js";
4
+ //#region src/form-control.ts
5
+ function o(e, t) {
6
+ let n = e.getAttribute(t);
7
+ if (!n) return "";
8
+ let r = e.getRootNode();
9
+ return r instanceof Document || r instanceof ShadowRoot ? n.split(/\s+/).filter(Boolean).map((e) => r.getElementById(e)?.textContent?.trim() ?? "").filter(Boolean).join(" ") : "";
10
+ }
11
+ var s = class extends t {
12
+ constructor(...e) {
13
+ super(...e), this.internals = this.attachInternals(), this.disabled = !1, this.formDisabled = !1, this.required = !1, this.#e = "", this.#n = !1;
14
+ }
15
+ static {
16
+ this.formAssociated = !0;
17
+ }
18
+ get isDisabled() {
19
+ return this.disabled || this.formDisabled;
20
+ }
21
+ formDisabledCallback(e) {
22
+ let t = this.formDisabled !== e;
23
+ this.formDisabled = e, e ? this.internals.states?.add("form-disabled") : this.internals.states?.delete("form-disabled"), t && this.isUpdatePending && queueMicrotask(() => this.requestUpdate());
24
+ }
25
+ formStateRestoreCallback(e, t) {
26
+ typeof e == "string" && this.applyFormState(e);
27
+ }
28
+ applyFormState(e) {
29
+ this.value = e;
30
+ }
31
+ syncFormValue(e) {
32
+ this.internals.setFormValue(this.isDisabled ? null : e);
33
+ }
34
+ #e;
35
+ setCustomValidity(e) {
36
+ this.#e = e, this.syncValidity(), this.requestUpdate();
37
+ }
38
+ get validity() {
39
+ return this.internals.validity;
40
+ }
41
+ get validationMessage() {
42
+ return this.internals.validationMessage;
43
+ }
44
+ get willValidate() {
45
+ return this.internals.willValidate;
46
+ }
47
+ checkValidity() {
48
+ return this.internals.checkValidity();
49
+ }
50
+ reportValidity() {
51
+ return this.internals.reportValidity();
52
+ }
53
+ get valueMissing() {
54
+ return !1;
55
+ }
56
+ get valueMissingMessage() {
57
+ return "Please fill out this field.";
58
+ }
59
+ syncValidity() {
60
+ let e = this.required && this.valueMissing, t = this.#e || (e ? this.valueMissingMessage : "");
61
+ this.internals.setValidity({
62
+ customError: this.#e !== "",
63
+ valueMissing: e
64
+ }, t || void 0), this.internals.ariaRequired = this.required ? "true" : null, this.internals.ariaInvalid = this.internals.validity.valid ? null : "true";
65
+ }
66
+ willUpdate(e) {
67
+ super.willUpdate(e), this.syncValidity();
68
+ }
69
+ #t;
70
+ #n;
71
+ latchFormDefault(e) {
72
+ this.#n || (this.#n = !0, this.#t = e);
73
+ }
74
+ formDefault(e) {
75
+ return this.#n ? this.#t : e;
76
+ }
77
+ disabledChanged(e) {
78
+ return e.has("disabled") || e.has("formDisabled");
79
+ }
80
+ };
81
+ e([i({
82
+ type: Boolean,
83
+ reflect: !0
84
+ })], s.prototype, "disabled", void 0), e([a()], s.prototype, "formDisabled", void 0), e([i({
85
+ type: Boolean,
86
+ reflect: !0
87
+ })], s.prototype, "required", void 0);
88
+ var c = class e extends s {
89
+ constructor(...e) {
90
+ super(...e), this.description = "";
91
+ }
92
+ static {
93
+ this.bridgedAriaAttributes = [
94
+ "aria-label",
95
+ "aria-labelledby",
96
+ "aria-describedby"
97
+ ];
98
+ }
99
+ static get observedAttributes() {
100
+ return [...super.observedAttributes, ...e.bridgedAriaAttributes];
101
+ }
102
+ attributeChangedCallback(t, n, r) {
103
+ super.attributeChangedCallback(t, n, r), e.bridgedAriaAttributes.includes(t) && this.requestUpdate();
104
+ }
105
+ formAssociatedCallback(e) {
106
+ this.requestUpdate();
107
+ }
108
+ get hostLabel() {
109
+ return this.hostAriaLabel || [...this.internals.labels].map((e) => e.textContent?.trim() ?? "").filter(Boolean).join(" ");
110
+ }
111
+ get hostAriaLabel() {
112
+ return o(this, "aria-labelledby") || this.getAttribute("aria-label")?.trim() || "";
113
+ }
114
+ get descriptionText() {
115
+ return [this.internals.validity.valid ? "" : this.internals.validationMessage, this.description || o(this, "aria-describedby")].filter(Boolean).join(" ");
116
+ }
117
+ get describedBy() {
118
+ return this.descriptionText ? "description" : r;
119
+ }
120
+ renderDescription() {
121
+ let e = this.descriptionText;
122
+ return e ? n`<span id="description" hidden>${e}</span>` : r;
123
+ }
124
+ };
125
+ e([i()], c.prototype, "description", void 0);
126
+ //#endregion
127
+ export { s as VfFormControl, c as VfShadowRoleControl };
@@ -0,0 +1,117 @@
1
+ import type { TemplateResult } from 'lit';
2
+ /**
3
+ * Pixel-exact 1-bit glyphs. Each glyph's `d` is a run-length-merged fill path
4
+ * on the System 7 pixel grid, one rectangle per run of ink. Rendered at its
5
+ * native pixel size (or an integer multiple) with `shape-rendering: crispEdges`
6
+ * it is pixel-exact — so consumers MUST size the SVG to `w`×`h` px (or a whole
7
+ * multiple), never a fractional scale, or the 1-bit edges snap unevenly to the
8
+ * device grid. It inherits `currentColor` (fully themeable) and ships as inline
9
+ * SVG — no external raster assets, matching the library's zero-CSS,
10
+ * `var(--vf-*)`-themeable design (see SPEC §1).
11
+ *
12
+ * This file is the authoritative source for these marks (the glyph-sprites note
13
+ * at the top of SPEC.md). They replace the earlier anti-aliased approximations
14
+ * (the checkbox ✕ was a 1.5px SVG stroke; the radio was a `border-radius: 50%`
15
+ * box; the menu ✓ was a font-dependent Unicode character; the popup ▼ was a
16
+ * CSS-border triangle) with the exact System 7 pixel shapes.
17
+ */
18
+ export interface Glyph {
19
+ /** Native grid width (viewBox units = source-sprite pixels). */
20
+ readonly w: number;
21
+ /** Native grid height. */
22
+ readonly h: number;
23
+ /** SVG path data (a union of 1×N pixel-run rectangles). */
24
+ readonly d: string;
25
+ }
26
+ /** Checkbox ✕ — the interior corner-to-corner cross (`check Selected=True`). */
27
+ export declare const CHECKBOX_X: Glyph;
28
+ /** Radio solid disc — the white control face under the ring (`radio` outline, flood-filled). */
29
+ export declare const RADIO_FACE: Glyph;
30
+ /** Radio ring — the 1px pixel circle outline (`radio Selected=False`). */
31
+ export declare const RADIO_RING: Glyph;
32
+ /** Radio ring, pressed — the 2px-thick outline (`radio State=Press`). */
33
+ export declare const RADIO_RING_PRESSED: Glyph;
34
+ /** Radio dot — the centered pixel disc shown when selected (`radio Selected=True` minus ring). */
35
+ export declare const RADIO_DOT: Glyph;
36
+ /** Menu / popup ✓ checkmark (`Symbols/Check.png`). */
37
+ export declare const CHECKMARK: Glyph;
38
+ /**
39
+ * Popup-menu ▼ caret (`Symbols/Caret Down.png`).
40
+ *
41
+ * Also the *scroll* arrow a clipped popup shows in its bottom row slot: a
42
+ * screenshot of a real System 7 popup overflowing the screen (Find File's
43
+ * criteria menu, captured under Infinite Mac at 2×) puts a 22×12 device-pixel
44
+ * triangle there, which is this exact 11×6 shape — the popup's own ▼
45
+ * vocabulary, reused rather than a second arrow art.
46
+ */
47
+ export declare const CARET_DOWN: Glyph;
48
+ /**
49
+ * The ▲ a clipped popup shows in its *top* row slot — {@link CARET_DOWN}
50
+ * mirrored vertically (the shape is left-right symmetric, so the mirror is its
51
+ * six rows in reverse order). Synthesized rather than traced: the reference
52
+ * sheets carry no scrolling menu, and the overflow screenshot above happened to
53
+ * catch a list clipped only at the bottom. If a top-clipped reference turns up,
54
+ * retrace both. Same synthesis convention as {@link STEPPER_UP_FILL}.
55
+ */
56
+ export declare const CARET_UP: Glyph;
57
+ /**
58
+ * The "little arrows" stepper at rest — a rounded 1-bit frame enclosing hollow
59
+ * up/down arrows (`Little arrows.png`), used by `vf-number-field`.
60
+ */
61
+ export declare const STEPPER: Glyph;
62
+ /**
63
+ * The horizontal slider thumb — a shield-shaped handle with three vertical grip
64
+ * lines and a pointed bottom (`Slider Handle.png`), used by `vf-slider`. Traced
65
+ * pixel-exact from the sprite: an 11×12 hollow 1-bit frame (flat top, tapering
66
+ * to a point) enclosing the three grip strokes. The grip lines sit at columns
67
+ * 3/5/7 so the middle stroke (x5) marks the thumb's value point.
68
+ */
69
+ export declare const SLIDER_THUMB: Glyph;
70
+ /**
71
+ * Solid white face for the slider thumb — the filled shield silhouette drawn
72
+ * *behind* {@link SLIDER_THUMB} so the handle is opaque (the rail passes behind
73
+ * it, not through it). Same 11×12 grid, flood-filled to the outline's extent on
74
+ * each row. Analogous to {@link RADIO_FACE} under the radio ring.
75
+ */
76
+ export declare const SLIDER_THUMB_FACE: Glyph;
77
+ /**
78
+ * Solid up-arrow fill for the pressed state — overlaid on {@link STEPPER} while
79
+ * the up arrow is held. Synthesized from the rest sprite (the only reference we
80
+ * have) by filling the hollow arrow solid, matching the kit's scroll-arrow
81
+ * hollow→solid press convention.
82
+ */
83
+ export declare const STEPPER_UP_FILL: Glyph;
84
+ /** Solid down-arrow fill for the pressed state (mirror of {@link STEPPER_UP_FILL}). */
85
+ export declare const STEPPER_DOWN_FILL: Glyph;
86
+ /**
87
+ * Authentic System 7 scroll arrows (arrow + stem), traced from the Classic
88
+ * Macintosh UI Kit sprites — a hollow outline at rest that fills solid black
89
+ * while the button is pressed (the `*_FILL` variants). Each is stated on the
90
+ * scrollbar's full 16-unit cell (same convention as {@link STEPPER}): the
91
+ * outermost ring of that cell is the frame and divider lines, which the DOM
92
+ * rail draws as real borders, so the rail renders these through a
93
+ * `viewBox="1 1 14 14"` window onto the interior (src/scroll-rail.ts) — the
94
+ * ink never reaches the outer ring, so nothing is cropped.
95
+ */
96
+ export declare const SCROLL_ARROW_UP: Glyph;
97
+ /** {@link SCROLL_ARROW_UP}, pressed — the arrow flood-filled solid. */
98
+ export declare const SCROLL_ARROW_UP_FILL: Glyph;
99
+ /** The ▼ scroll arrow (vertical increment). */
100
+ export declare const SCROLL_ARROW_DOWN: Glyph;
101
+ /** {@link SCROLL_ARROW_DOWN}, pressed. */
102
+ export declare const SCROLL_ARROW_DOWN_FILL: Glyph;
103
+ /** The ◀ scroll arrow (horizontal decrement). */
104
+ export declare const SCROLL_ARROW_LEFT: Glyph;
105
+ /** {@link SCROLL_ARROW_LEFT}, pressed. */
106
+ export declare const SCROLL_ARROW_LEFT_FILL: Glyph;
107
+ /** The ▶ scroll arrow (horizontal increment). */
108
+ export declare const SCROLL_ARROW_RIGHT: Glyph;
109
+ /** {@link SCROLL_ARROW_RIGHT}, pressed. */
110
+ export declare const SCROLL_ARROW_RIGHT_FILL: Glyph;
111
+ /**
112
+ * Render a single-color glyph as a crisp, theme-colored inline SVG.
113
+ *
114
+ * Size and color come from CSS on the passed `className` (the SVG fills with
115
+ * `currentColor`), so consumers theme it via `var(--vf-*)` like everything else.
116
+ */
117
+ export declare function glyphSvg(g: Glyph, className: string): TemplateResult;