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,3237 @@
1
+ {
2
+ "$schema": "https://raw.githubusercontent.com/JetBrains/web-types/master/schema/web-types.json",
3
+ "name": "vintage-frames",
4
+ "version": "0.1.0",
5
+ "description-markup": "markdown",
6
+ "contributions": {
7
+ "html": {
8
+ "elements": [
9
+ {
10
+ "name": "vf-button-group",
11
+ "description": "Lays out a set of `vf-button`s the way a System 7 dialog does: a tidy row\n(or column) with a consistent gap, every button sharing the width of the\nwidest, and all their *faces* on one line.\n\nTwo wrinkles make an ad-hoc flex row get this subtly wrong, and the group\nfixes both:\n\n- **Face alignment.** A `variant=\"default\"` button reserves room for its\n ring with a RING_INSETpx margin, so in a row aligned by any edge\n its *face* drifts from its neighbors' — you line up the ring, not the\n button. The group zeroes that margin (via the inherited\n `--vf-button-ring-margin`) and reserves the ring space itself as padding,\n then centers the cross axis, so the faces — not the margin boxes — align.\n- **Shared width.** Classic dialogs sized grouped buttons to the widest, even\n when a label was short (\"OK\" as wide as \"Cancel\"). The group does this with\n pure CSS — equal `1fr` grid columns resolve to the widest button's\n intrinsic width under the shrink-wrapped `inline-grid` — and stretches each\n button's face to fill (via the inherited `--vf-button-flex`). No measuring.\n `natural` opts out, letting each button size to its own label.\n\nThe group is layout-neutral: it shrink-wraps to its buttons, so position it\nwith the parent (e.g. `justify-self: end` for a bottom-right action row).\n---\n\n\n### **Slots:**\n - _default_ - The `vf-button`s to arrange.\n\n### **CSS Properties:**\n - **--vf-button-group-gap** - Gap between buttons (default 12px). _(default: undefined)_",
12
+ "doc-url": "",
13
+ "attributes": [
14
+ {
15
+ "name": "vertical",
16
+ "description": "Stack the buttons vertically instead of in a row.",
17
+ "value": { "type": "boolean", "default": "false" }
18
+ },
19
+ {
20
+ "name": "natural",
21
+ "description": "Let each button size to its own label instead of every button sharing the\nwidest button's width. Off by default: grouped buttons are uniform width,\nmatching classic System 7 dialogs.",
22
+ "value": { "type": "boolean", "default": "false" }
23
+ },
24
+ {
25
+ "name": "top",
26
+ "description": "Offset from the top of the positioning parent, in whole system px.\nSetting this (or `left`) absolutely positions the element within its\nparent; the coordinate left unset is 0. Remove both to return the\nelement to normal flow.",
27
+ "value": { "type": "number | null | undefined" }
28
+ },
29
+ {
30
+ "name": "left",
31
+ "description": "Offset from the left of the positioning parent, in whole system px.\nSee top.",
32
+ "value": { "type": "number | null | undefined" }
33
+ }
34
+ ],
35
+ "slots": [
36
+ { "name": "", "description": "The `vf-button`s to arrange." }
37
+ ],
38
+ "events": [],
39
+ "js": {
40
+ "properties": [
41
+ {
42
+ "name": "vertical",
43
+ "description": "Stack the buttons vertically instead of in a row.",
44
+ "type": "boolean"
45
+ },
46
+ {
47
+ "name": "natural",
48
+ "description": "Let each button size to its own label instead of every button sharing the\nwidest button's width. Off by default: grouped buttons are uniform width,\nmatching classic System 7 dialogs.",
49
+ "type": "boolean"
50
+ },
51
+ { "name": "role", "type": "string" },
52
+ {
53
+ "name": "top",
54
+ "description": "Offset from the top of the positioning parent, in whole system px.\nSetting this (or `left`) absolutely positions the element within its\nparent; the coordinate left unset is 0. Remove both to return the\nelement to normal flow.",
55
+ "type": "number | null | undefined"
56
+ },
57
+ {
58
+ "name": "left",
59
+ "description": "Offset from the left of the positioning parent, in whole system px.\nSee top.",
60
+ "type": "number | null | undefined"
61
+ }
62
+ ],
63
+ "events": []
64
+ }
65
+ },
66
+ {
67
+ "name": "vf-button",
68
+ "description": "The classic System 7 push button (\"OK\", \"Cancel\", \"Install\", …).\n\nA rounded-rectangle control with a 1px black border and white face that\ninverts to white-on-black while pressed. `variant=\"default\"` draws the\nclassic bold double ring around the button, marking it as the default\naction of a dialog.\n\nThe rounded corners are not `border-radius` arcs: frame, face, and ring are\nstepped `clip-path` silhouettes traced pixel-for-pixel from the kit's button\nreference sheet (see `src/pixel-frame.ts`), so every corner renders as the\nexact 1-bit staircase with no antialiasing.\n\nKeyboard focus — an affordance System 7 didn't have, drawn in its idiom\nanyway — is a 1px dashed rule under the label text (`vfFocusUnderline`)\nrather than a ring around the control.\n\nForm-associated: place it inside a `<form>` and `type=\"submit\"` submits the\nform (contributing its `name`/`value` and any `form*` override to the\nsubmission), `type=\"reset\"` resets it. Enter and Space activate it via the\ninner native button, and the submission runs at the end of the click's\npropagation, so `preventDefault()` on the button cancels it the way it\ncancels a native one.\n\nOne thing the platform will not allow: `event.submitter` cannot BE a\nform-associated custom element. It is the transient native proxy this\nbutton submits through — so read the submitting button as\n`event.submitter.closest('vf-button')`, and its identity from\n`submitter.name`/`.value` rather than by comparing element references.\n\nA host-level `aria-label` / `aria-labelledby` names the inner button, and\n`description` (or a host-level `aria-describedby`) describes it — the same\nbridge the fields use, since the role lives on a shadow-internal node the\nplatform can't deliver those to. A `<label for>` deliberately does not\nname it: a `<button>` is not a labelable element.\n---\n\n\n### **Slots:**\n - _default_ - The button label.\n\n### **CSS Properties:**\n - **--vf-button-height** - `vf-button` face (the default ring's inner box is 80×20) _(default: 20px)_\n\n### **CSS Parts:**\n - **button** - The inner native `<button>` element.",
69
+ "doc-url": "",
70
+ "attributes": [
71
+ {
72
+ "name": "variant",
73
+ "description": "When set to `'default'`, draws the classic double ring that marks the\ndefault button (activated by Return in real System 7 dialogs).",
74
+ "value": { "type": "'default' | undefined" }
75
+ },
76
+ {
77
+ "name": "type",
78
+ "description": "Activation behavior, mirroring native `<button type>`:\n`'submit'` submits the associated form, `'reset'` resets it and\n`'button'` (the default) does nothing beyond the `click` event.\n\nTwo deliberate departures from `<button>`, both pointing the same way — a\n`vf-button` never submits unless it was asked to. HTML's *missing*-value\ndefault is `submit`; here it is `button`, because a custom element that\nsilently submitted the form it happens to sit in is the wrong surprise.\nThe *invalid*-value default follows the missing one rather than HTML's\n(which is also `submit`), so a misspelling does nothing instead of\nsubmitting.",
79
+ "value": {
80
+ "type": "'button' | 'submit' | 'reset'",
81
+ "default": "'button'"
82
+ }
83
+ },
84
+ {
85
+ "name": "name",
86
+ "description": "Form field name; submitted as `name=value` when `type=\"submit\"`.",
87
+ "value": { "type": "string", "default": "''" }
88
+ },
89
+ {
90
+ "name": "value",
91
+ "description": "Value submitted under `name` when `type=\"submit\"`.",
92
+ "value": { "type": "string", "default": "''" }
93
+ },
94
+ {
95
+ "name": "formaction",
96
+ "description": "The submission overrides native `<button>` carries, honored on\n`type=\"submit\"` only, exactly as HTML honors them: each is handed to the\nnative proxy activate submits through, so the *behavior* is the\nplatform's rather than an emulation of it.\n\nOne difference from the native IDL, in the getters only: `formAction`\nreturns the string you set, where `HTMLButtonElement.formAction` returns\nit resolved against the document's base URL. The submission itself\nresolves normally — it is the proxy's `formaction` doing the work.",
97
+ "value": { "type": "string", "default": "''" }
98
+ },
99
+ {
100
+ "name": "formenctype",
101
+ "description": "See formAction. Overrides the form's `enctype`.",
102
+ "value": { "type": "string", "default": "''" }
103
+ },
104
+ {
105
+ "name": "formmethod",
106
+ "description": "See formAction. Overrides the form's `method`.",
107
+ "value": { "type": "string", "default": "''" }
108
+ },
109
+ {
110
+ "name": "formnovalidate",
111
+ "description": "See formAction. Skips the form's constraint validation, so a\n\"Save Draft\" button submits past a failing `required`.",
112
+ "value": { "type": "boolean", "default": "false" }
113
+ },
114
+ {
115
+ "name": "formtarget",
116
+ "description": "See formAction. Overrides the form's `target`.",
117
+ "value": { "type": "string", "default": "''" }
118
+ },
119
+ {
120
+ "name": "top",
121
+ "description": "Offset from the top of the positioning parent, in whole system px.\nSetting this (or `left`) absolutely positions the element within its\nparent; the coordinate left unset is 0. Remove both to return the\nelement to normal flow.",
122
+ "value": { "type": "number | null | undefined" }
123
+ },
124
+ {
125
+ "name": "left",
126
+ "description": "Offset from the left of the positioning parent, in whole system px.\nSee top.",
127
+ "value": { "type": "number | null | undefined" }
128
+ },
129
+ {
130
+ "name": "description",
131
+ "description": "Description for the control — hint text, a format, a unit. A host-level\n`aria-describedby` cannot reach a focusable element inside a shadow root,\nso there was structurally no way to describe a field; this property is\nthat channel. It renders as a hidden span in the control's own shadow root\nwith the inner control's `aria-describedby` pointing at it — the\nshadow-internal IDREF idiom `vf-dialog`'s title patch already uses. A\nhost-level `aria-describedby` is bridged into the same span when this\nproperty is empty, and a failing constraint's validationMessage\njoins it too, so AT hears the error where it hears the hint.\n\nHost-role controls get neither half: their `aria-describedby` already\nworks, and their validation message reaches AT the way a native control's\ndoes — `aria-invalid` plus the browser's own validation UI, not AccName.",
132
+ "value": { "type": "string", "default": "''" }
133
+ },
134
+ {
135
+ "name": "disabled",
136
+ "description": "Disables the control: it stops responding and submits no value. Each\ncontrol dims per SPEC §1 (typically the label greys while the 1-bit chrome\nstays black).",
137
+ "value": { "type": "boolean", "default": "false" }
138
+ },
139
+ {
140
+ "name": "required",
141
+ "description": "Requires a value before the associated form submits (SPEC §4): an empty\ncontrol fails constraint validation with `valueMissing`, exactly like a\nnative `required`. What \"empty\" means is each control's own\nvalueMissing; a control with no required semantics (a slider\nalways has a value, a swatch submits nothing) never fails it, the way a\nnative range input never does.",
142
+ "value": { "type": "boolean", "default": "false" }
143
+ }
144
+ ],
145
+ "slots": [{ "name": "", "description": "The button label." }],
146
+ "events": [],
147
+ "js": {
148
+ "properties": [
149
+ {
150
+ "name": "variant",
151
+ "description": "When set to `'default'`, draws the classic double ring that marks the\ndefault button (activated by Return in real System 7 dialogs).",
152
+ "type": "'default' | undefined"
153
+ },
154
+ {
155
+ "name": "type",
156
+ "description": "Activation behavior, mirroring native `<button type>`:\n`'submit'` submits the associated form, `'reset'` resets it and\n`'button'` (the default) does nothing beyond the `click` event.\n\nTwo deliberate departures from `<button>`, both pointing the same way — a\n`vf-button` never submits unless it was asked to. HTML's *missing*-value\ndefault is `submit`; here it is `button`, because a custom element that\nsilently submitted the form it happens to sit in is the wrong surprise.\nThe *invalid*-value default follows the missing one rather than HTML's\n(which is also `submit`), so a misspelling does nothing instead of\nsubmitting.",
157
+ "type": "'button' | 'submit' | 'reset'"
158
+ },
159
+ {
160
+ "name": "name",
161
+ "description": "Form field name; submitted as `name=value` when `type=\"submit\"`.",
162
+ "type": "string"
163
+ },
164
+ {
165
+ "name": "value",
166
+ "description": "Value submitted under `name` when `type=\"submit\"`.",
167
+ "type": "string"
168
+ },
169
+ {
170
+ "name": "formAction",
171
+ "description": "The submission overrides native `<button>` carries, honored on\n`type=\"submit\"` only, exactly as HTML honors them: each is handed to the\nnative proxy activate submits through, so the *behavior* is the\nplatform's rather than an emulation of it.\n\nOne difference from the native IDL, in the getters only: `formAction`\nreturns the string you set, where `HTMLButtonElement.formAction` returns\nit resolved against the document's base URL. The submission itself\nresolves normally — it is the proxy's `formaction` doing the work.",
172
+ "type": "string"
173
+ },
174
+ {
175
+ "name": "formEnctype",
176
+ "description": "See formAction. Overrides the form's `enctype`.",
177
+ "type": "string"
178
+ },
179
+ {
180
+ "name": "formMethod",
181
+ "description": "See formAction. Overrides the form's `method`.",
182
+ "type": "string"
183
+ },
184
+ {
185
+ "name": "formNoValidate",
186
+ "description": "See formAction. Skips the form's constraint validation, so a\n\"Save Draft\" button submits past a failing `required`.",
187
+ "type": "boolean"
188
+ },
189
+ {
190
+ "name": "formTarget",
191
+ "description": "See formAction. Overrides the form's `target`.",
192
+ "type": "string"
193
+ },
194
+ {
195
+ "name": "top",
196
+ "description": "Offset from the top of the positioning parent, in whole system px.\nSetting this (or `left`) absolutely positions the element within its\nparent; the coordinate left unset is 0. Remove both to return the\nelement to normal flow.",
197
+ "type": "number | null | undefined"
198
+ },
199
+ {
200
+ "name": "left",
201
+ "description": "Offset from the left of the positioning parent, in whole system px.\nSee top.",
202
+ "type": "number | null | undefined"
203
+ },
204
+ {
205
+ "name": "description",
206
+ "description": "Description for the control — hint text, a format, a unit. A host-level\n`aria-describedby` cannot reach a focusable element inside a shadow root,\nso there was structurally no way to describe a field; this property is\nthat channel. It renders as a hidden span in the control's own shadow root\nwith the inner control's `aria-describedby` pointing at it — the\nshadow-internal IDREF idiom `vf-dialog`'s title patch already uses. A\nhost-level `aria-describedby` is bridged into the same span when this\nproperty is empty, and a failing constraint's validationMessage\njoins it too, so AT hears the error where it hears the hint.\n\nHost-role controls get neither half: their `aria-describedby` already\nworks, and their validation message reaches AT the way a native control's\ndoes — `aria-invalid` plus the browser's own validation UI, not AccName.",
207
+ "type": "string"
208
+ },
209
+ {
210
+ "name": "disabled",
211
+ "description": "Disables the control: it stops responding and submits no value. Each\ncontrol dims per SPEC §1 (typically the label greys while the 1-bit chrome\nstays black).",
212
+ "type": "boolean"
213
+ },
214
+ {
215
+ "name": "isDisabled",
216
+ "description": "Effective disabled state: the `disabled` prop OR an ancestor fieldset.",
217
+ "type": "boolean"
218
+ },
219
+ {
220
+ "name": "required",
221
+ "description": "Requires a value before the associated form submits (SPEC §4): an empty\ncontrol fails constraint validation with `valueMissing`, exactly like a\nnative `required`. What \"empty\" means is each control's own\nvalueMissing; a control with no required semantics (a slider\nalways has a value, a swatch submits nothing) never fails it, the way a\nnative range input never does.",
222
+ "type": "boolean"
223
+ },
224
+ {
225
+ "name": "validity",
226
+ "description": "The control's current `ValidityState`, as on a native control.",
227
+ "type": "ValidityState"
228
+ },
229
+ {
230
+ "name": "validationMessage",
231
+ "description": "The message of the currently failing constraint, `''` while valid.",
232
+ "type": "string"
233
+ },
234
+ {
235
+ "name": "willValidate",
236
+ "description": "Whether the control is a candidate for constraint validation — false\nwhile disabled or readonly, per HTML's barring rules (the browser\ncomputes this from the reflected attributes).",
237
+ "type": "boolean"
238
+ }
239
+ ],
240
+ "events": []
241
+ }
242
+ },
243
+ {
244
+ "name": "vf-checkbox",
245
+ "description": "The classic System 7 checkbox: a 13×13 white square with a 1px black\nborder whose checked state is the corner-to-corner ✕ glyph — the pixel-exact\ncross traced from the Classic Macintosh UI Kit sprite. The border \"thickens\"\nwhile pressed, exactly like the original control.\n\nForm-associated: submits `value` under `name` when checked (like a native\ncheckbox) and restores its initial checked state on form reset. Toggles on\nclick and Space.\n---\n\n\n### **Events:**\n - **vf-change** - When toggled by user interaction. `detail: { checked: boolean }`.\n- **input** - Native event, dispatched from the host per user toggle (with `change`, the pair a native checkbox fires). A programmatic `checked` set fires nothing, as on a native checkbox.\n- **change** - Native event, dispatched from the host per user toggle so form delegation and framework bindings hear it.\n\n### **Slots:**\n - _default_ - The label, rendered to the right of the box with a 6px gap.\n\n### **CSS Parts:**\n - **box** - The 13×13 checkbox square.\n- **label** - The label wrapper around the slot.",
246
+ "doc-url": "",
247
+ "attributes": [
248
+ {
249
+ "name": "checked",
250
+ "description": "Whether the checkbox is checked.",
251
+ "value": { "type": "boolean", "default": "false" }
252
+ },
253
+ {
254
+ "name": "name",
255
+ "description": "Form field name used when submitting.",
256
+ "value": { "type": "string", "default": "''" }
257
+ },
258
+ {
259
+ "name": "value",
260
+ "description": "Value submitted with the form while checked.",
261
+ "value": { "type": "string", "default": "'on'" }
262
+ },
263
+ {
264
+ "name": "top",
265
+ "description": "Offset from the top of the positioning parent, in whole system px.\nSetting this (or `left`) absolutely positions the element within its\nparent; the coordinate left unset is 0. Remove both to return the\nelement to normal flow.",
266
+ "value": { "type": "number | null | undefined" }
267
+ },
268
+ {
269
+ "name": "left",
270
+ "description": "Offset from the left of the positioning parent, in whole system px.\nSee top.",
271
+ "value": { "type": "number | null | undefined" }
272
+ },
273
+ {
274
+ "name": "disabled",
275
+ "description": "Disables the control: it stops responding and submits no value. Each\ncontrol dims per SPEC §1 (typically the label greys while the 1-bit chrome\nstays black).",
276
+ "value": { "type": "boolean", "default": "false" }
277
+ },
278
+ {
279
+ "name": "required",
280
+ "description": "Requires a value before the associated form submits (SPEC §4): an empty\ncontrol fails constraint validation with `valueMissing`, exactly like a\nnative `required`. What \"empty\" means is each control's own\nvalueMissing; a control with no required semantics (a slider\nalways has a value, a swatch submits nothing) never fails it, the way a\nnative range input never does.",
281
+ "value": { "type": "boolean", "default": "false" }
282
+ }
283
+ ],
284
+ "slots": [
285
+ {
286
+ "name": "",
287
+ "description": "The label, rendered to the right of the box with a 6px gap."
288
+ }
289
+ ],
290
+ "events": [
291
+ {
292
+ "name": "vf-change",
293
+ "description": "When toggled by user interaction. `detail: { checked: boolean }`."
294
+ },
295
+ {
296
+ "name": "input",
297
+ "description": "Native event, dispatched from the host per user toggle (with `change`, the pair a native checkbox fires). A programmatic `checked` set fires nothing, as on a native checkbox."
298
+ },
299
+ {
300
+ "name": "change",
301
+ "description": "Native event, dispatched from the host per user toggle so form delegation and framework bindings hear it."
302
+ }
303
+ ],
304
+ "js": {
305
+ "properties": [
306
+ {
307
+ "name": "checked",
308
+ "description": "Whether the checkbox is checked.",
309
+ "type": "boolean"
310
+ },
311
+ {
312
+ "name": "name",
313
+ "description": "Form field name used when submitting.",
314
+ "type": "string"
315
+ },
316
+ {
317
+ "name": "value",
318
+ "description": "Value submitted with the form while checked.",
319
+ "type": "string"
320
+ },
321
+ { "name": "role", "type": "string" },
322
+ {
323
+ "name": "top",
324
+ "description": "Offset from the top of the positioning parent, in whole system px.\nSetting this (or `left`) absolutely positions the element within its\nparent; the coordinate left unset is 0. Remove both to return the\nelement to normal flow.",
325
+ "type": "number | null | undefined"
326
+ },
327
+ {
328
+ "name": "left",
329
+ "description": "Offset from the left of the positioning parent, in whole system px.\nSee top.",
330
+ "type": "number | null | undefined"
331
+ },
332
+ {
333
+ "name": "disabled",
334
+ "description": "Disables the control: it stops responding and submits no value. Each\ncontrol dims per SPEC §1 (typically the label greys while the 1-bit chrome\nstays black).",
335
+ "type": "boolean"
336
+ },
337
+ {
338
+ "name": "isDisabled",
339
+ "description": "Effective disabled state: the `disabled` prop OR an ancestor fieldset.",
340
+ "type": "boolean"
341
+ },
342
+ {
343
+ "name": "required",
344
+ "description": "Requires a value before the associated form submits (SPEC §4): an empty\ncontrol fails constraint validation with `valueMissing`, exactly like a\nnative `required`. What \"empty\" means is each control's own\nvalueMissing; a control with no required semantics (a slider\nalways has a value, a swatch submits nothing) never fails it, the way a\nnative range input never does.",
345
+ "type": "boolean"
346
+ },
347
+ {
348
+ "name": "validity",
349
+ "description": "The control's current `ValidityState`, as on a native control.",
350
+ "type": "ValidityState"
351
+ },
352
+ {
353
+ "name": "validationMessage",
354
+ "description": "The message of the currently failing constraint, `''` while valid.",
355
+ "type": "string"
356
+ },
357
+ {
358
+ "name": "willValidate",
359
+ "description": "Whether the control is a candidate for constraint validation — false\nwhile disabled or readonly, per HTML's barring rules (the browser\ncomputes this from the reflected attributes).",
360
+ "type": "boolean"
361
+ }
362
+ ],
363
+ "events": [
364
+ {
365
+ "name": "vf-change",
366
+ "description": "When toggled by user interaction. `detail: { checked: boolean }`."
367
+ },
368
+ {
369
+ "name": "input",
370
+ "description": "Native event, dispatched from the host per user toggle (with `change`, the pair a native checkbox fires). A programmatic `checked` set fires nothing, as on a native checkbox."
371
+ },
372
+ {
373
+ "name": "change",
374
+ "description": "Native event, dispatched from the host per user toggle so form delegation and framework bindings hear it."
375
+ }
376
+ ]
377
+ }
378
+ },
379
+ {
380
+ "name": "vf-container",
381
+ "description": "`<vf-container>` — a box that is nothing but its declared size.\n\nThe kit's positioned-placement story (src/position.ts) ends with one line of\nCSS it can't write for you: children placed with `top`/`left` need a\npositioned ancestor, and while every kit container is one — a window body, a\nstack, a fieldset — a region of *your own* needs `position: relative` in a\nstylesheet. This component is that region as an element: declare `width` and\n`height` in whole system px, slot anything into it, place children against\nits origin. A DITL's enclosing rectangle, with nothing drawn in it.\n\n```html\n<vf-container width=\"200\" height=\"120\">\n <vf-icon label=\"System\" width=\"64\" top=\"8\" left=\"12\">…</vf-icon>\n <vf-icon label=\"Finder\" width=\"64\" top=\"8\" left=\"104\">…</vf-icon>\n</vf-container>\n```\n\nThe rectangle is the whole API — `width`/`height` here, plus the `top`/`left`\npair nearly every component takes (VfPositioned), so a container is\nitself placeable: inside a window, a desktop, or another container, at whole\nsystem px that keep its box on the device-pixel grid by construction.\n\n**It is not a `vf-stack`.** The stack is a flexbox with opinions — it\ndistributes children along an axis, compiles `fill-width`/`fill-height` into\nflex, defaults a cross-axis alignment. This box has no layout opinion at\nall: in-flow children get normal flow, placed children get a coordinate\nsystem, and that is the whole API. Reach for it when the stack's opinions\nare the thing in the way — a field of placed icons, a fixed stage for\nabsolutely-positioned art, a consumer's own composition that brings its\nlayout with it.\n\n**The declared size is the layout.** `width`/`height` land on the host as a\nlive `calc(var(--vf-scale, 1) * Npx)` (VfSized), so the box scales\nwith the display and sits on the device-pixel grid by construction. Content\nthat outgrows the box overflows it rather than growing it — the number is\nthe layout, and content that doesn't fit is a number to raise. Leave a\ndimension off and that axis shrink-wraps: `fit-content`, not the parent's\nwidth, because a layout box that silently claimed a size nobody declared\nwould be inventing one (the `vf-stack` rule, held here too).\n\n**It paints nothing and means nothing.** No border, background, role,\nkeyboard behavior or selection — what it holds decides what it is.\n\n**It holds its box on the device-pixel grid** — with a `GridSnapController`.\nA container's box is itself the consumer's coordinate system, including for\nnon-`vf` content that cannot correct itself, so the box is the thing to\nhold on the grid. The shadow box below owns the `position: relative` anchor\nand the `vf-snap` class together, so under `applyGridSnap()` the correction\nmoves the whole coordinate system — everything placed against it rides\nalong instead of being re-corrected child by child. (`vf-stack` shipped\nwithout a controller on the theory that slotted `vf-*` children correct\ntheir own origins; this component is where that theory's gap — consumer\ncontent — became visible, and the stack has since adopted the same\narrangement.)\n\nLike the stack it is **typographically transparent**: `vfBase`'s chrome\ndress is returned to `inherit` on the host, so wrapping content in a sized\nbox changes nothing about how that content reads.\n\n`fill-width` / `fill-height` work here the way they do everywhere: read\nabout the host (be as big as *its* parent allows), and compiled for slotted\nchildren — `width: 100%` in normal flow, so a child filling the cross of a\ndeclared box needs no stylesheet. A height fill needs a declared `height`\nto resolve against; with none it is inert, not an error.\n---\n\n\n### **Slots:**\n - _default_ - The content. In flow by default; `top`/`left` on a kit child places it against this box's origin. `fill-width` / `fill-height` on a child fills it to the declared box.",
382
+ "doc-url": "",
383
+ "attributes": [
384
+ {
385
+ "name": "width",
386
+ "description": "Width in whole system px. What the number means is the component's own\naffair — the measure a paragraph wraps to, a caption column, a window's\nbox — but the mechanics are shared: the length lands on the host as a\nlive `calc(var(--vf-scale, 1) * Npx)`, so the box scales with the\ndisplay and sits on the device-pixel grid by construction. Remove it\nand the width returns to layout.",
387
+ "value": { "type": "number | null | undefined" }
388
+ },
389
+ {
390
+ "name": "height",
391
+ "description": "Height in whole system px; see width. Content that outgrows a\ndeclared height overflows the box rather than growing it — the number\nis the layout, and content that doesn't fit is a number to raise.",
392
+ "value": { "type": "number | null | undefined" }
393
+ },
394
+ {
395
+ "name": "top",
396
+ "description": "Offset from the top of the positioning parent, in whole system px.\nSetting this (or `left`) absolutely positions the element within its\nparent; the coordinate left unset is 0. Remove both to return the\nelement to normal flow.",
397
+ "value": { "type": "number | null | undefined" }
398
+ },
399
+ {
400
+ "name": "left",
401
+ "description": "Offset from the left of the positioning parent, in whole system px.\nSee top.",
402
+ "value": { "type": "number | null | undefined" }
403
+ }
404
+ ],
405
+ "slots": [
406
+ {
407
+ "name": "",
408
+ "description": "The content. In flow by default; `top`/`left` on a kit child places it against this box's origin. `fill-width` / `fill-height` on a child fills it to the declared box."
409
+ }
410
+ ],
411
+ "events": [],
412
+ "js": {
413
+ "properties": [
414
+ {
415
+ "name": "width",
416
+ "description": "Width in whole system px. What the number means is the component's own\naffair — the measure a paragraph wraps to, a caption column, a window's\nbox — but the mechanics are shared: the length lands on the host as a\nlive `calc(var(--vf-scale, 1) * Npx)`, so the box scales with the\ndisplay and sits on the device-pixel grid by construction. Remove it\nand the width returns to layout.",
417
+ "type": "number | null | undefined"
418
+ },
419
+ {
420
+ "name": "height",
421
+ "description": "Height in whole system px; see width. Content that outgrows a\ndeclared height overflows the box rather than growing it — the number\nis the layout, and content that doesn't fit is a number to raise.",
422
+ "type": "number | null | undefined"
423
+ },
424
+ {
425
+ "name": "top",
426
+ "description": "Offset from the top of the positioning parent, in whole system px.\nSetting this (or `left`) absolutely positions the element within its\nparent; the coordinate left unset is 0. Remove both to return the\nelement to normal flow.",
427
+ "type": "number | null | undefined"
428
+ },
429
+ {
430
+ "name": "left",
431
+ "description": "Offset from the left of the positioning parent, in whole system px.\nSee top.",
432
+ "type": "number | null | undefined"
433
+ }
434
+ ],
435
+ "events": []
436
+ }
437
+ },
438
+ {
439
+ "name": "vf-desktop",
440
+ "description": "`<vf-desktop>` — the full-bleed classic desktop container.\n\nRenders the 50%-dither gray desktop pattern and manages the stacking order\nand `active` state of slotted `vf-window` children: a `pointerdown` or\n`focusin` (keyboard focus) anywhere inside a window brings it to the front\nand makes it the single active window. The windows' light-DOM order is kept\nin step with the stacking order (bottom-most first, at pointer-gesture\nends), so tabbing walks the stack the way the eye does and Shift+Tab is\nits exact mirror.\n\nUtility windows (`vf-window[variant=\"utility\"]`) stack in a floating tier\nabove every document-tier window, restack only among themselves, and stand\noutside the single-active invariant entirely — clicking a palette neither\ndeactivates the active document window nor greys the palette, exactly as\nSystem 7's floating windoids behaved while their application was frontmost.\n\nThe desktop is a raster with an explicit size, always: **`width` and\n`height`**, in system px, the way a WIND resource declared a window's —\nthe host box renders at the declared screen plus `2 × bezel` per axis, a\nwhole number of system pixels by construction (default 512×342, the\ncompact Mac's screen). Pure CSS sizing is not supported; the page sets\nthe numbers — directly, or via VfDesktop.fitWithin on\n`resize`/`onScaleChange` for a viewport-filling desktop — and positions\nthe sized box with its own stylesheet, keeping any sub-system-pixel\nslack on its side. `bezel` (system px) draws the black screen surround —\nthe CRT's unlit margin — around the screen, rounding its top corners\nwith the classic corner mask.\n\nCustom properties:\n- `--vf-desktop-pattern` — the dither's tile art (default a 1-bit 50%\n checker, opaque black-on-white on a 30-system-px tile). Overriding it\n renders the token as a placed tile grid at that same 30-px geometry\n (src/tile-grid.ts); a token swapped at runtime without touching the\n component wants a `requestUpdate()`.\n- `--vf-desktop` — base color painted *under* the pattern layer (default\n `#808080`). The default tile is opaque, so this only becomes visible when\n `--vf-desktop-pattern` is overridden with a tile that has transparent\n cells (or with `none`).\n---\n\n\n### **Slots:**\n - _default_ - Default slot: menu bar, windows, anything.\n\n### **CSS Properties:**\n - **--vf-desktop** - base color under the desktop dither — occluded by the default (opaque) tile, so it only shows through a custom `--vf-desktop-pattern` _(default: #808080)_\n- **--vf-desktop-pattern** - the desktop dither's art — a 50% checker drawn as opaque black-on-white rects, on a 30-system-px tile. Override the whole tile; consumer art renders as a placed tile grid at that same geometry (raster art magnifies nearest-neighbor, the `vf-img` idiom) _(default: undefined)_\n\n### **CSS Parts:**\n - **desktop** - The dithered screen surface — the whole-system-px raster (inset by `bezel` when one is set).",
441
+ "doc-url": "",
442
+ "attributes": [
443
+ {
444
+ "name": "width",
445
+ "description": "Screen width in system px — the raster's own size, the way a WIND\nresource declared a window's. The host box renders at exactly\n`width + 2 × bezel` system px, always whole; a desktop is never sized\nby page CSS — the page sets these numbers (directly or via\nfitWithin) and positions the explicitly sized box with its own\nstylesheet, keeping whatever sub-system-pixel slack its layout has on\nits side of the fence. Defaults to the compact Mac's 512.",
446
+ "value": { "type": "string", "default": "DEFAULT_SCREEN_WIDTH" }
447
+ },
448
+ {
449
+ "name": "height",
450
+ "description": "Screen height in system px; see width. Defaults to 342.",
451
+ "value": { "type": "string", "default": "DEFAULT_SCREEN_HEIGHT" }
452
+ },
453
+ {
454
+ "name": "bezel",
455
+ "description": "Width of the black screen bezel, in system px (`0` = none), added onto\nthe declared screen on every side — a `width=\"502\" bezel=\"5\"` desktop\nrenders a 512-system-px host box. The compact Mac's CRT showed an\nunlit black margin between the desktop's raster and the case; `bezel`\ndraws it around the screen and puts the classic screen-corner mask on\nthe screen's two *top* corners — only the top pair was rounded in the\nframebuffer. Flow, window coordinates and the drag clip all belong to\nthe screen, so windows crop at its edge. Inside a bezeled desktop a\nmenu bar needs no `rounded` of its own — the desktop's mask lands on\nthe same pixels.",
456
+ "value": { "type": "number", "default": "0" }
457
+ },
458
+ {
459
+ "name": "top",
460
+ "description": "Offset from the top of the positioning parent, in whole system px.\nSetting this (or `left`) absolutely positions the element within its\nparent; the coordinate left unset is 0. Remove both to return the\nelement to normal flow.",
461
+ "value": { "type": "number | null | undefined" }
462
+ },
463
+ {
464
+ "name": "left",
465
+ "description": "Offset from the left of the positioning parent, in whole system px.\nSee top.",
466
+ "value": { "type": "number | null | undefined" }
467
+ }
468
+ ],
469
+ "slots": [
470
+ {
471
+ "name": "",
472
+ "description": "Default slot: menu bar, windows, anything."
473
+ }
474
+ ],
475
+ "events": [],
476
+ "js": {
477
+ "properties": [
478
+ {
479
+ "name": "width",
480
+ "description": "Screen width in system px — the raster's own size, the way a WIND\nresource declared a window's. The host box renders at exactly\n`width + 2 × bezel` system px, always whole; a desktop is never sized\nby page CSS — the page sets these numbers (directly or via\nfitWithin) and positions the explicitly sized box with its own\nstylesheet, keeping whatever sub-system-pixel slack its layout has on\nits side of the fence. Defaults to the compact Mac's 512.",
481
+ "type": "number"
482
+ },
483
+ {
484
+ "name": "height",
485
+ "description": "Screen height in system px; see width. Defaults to 342.",
486
+ "type": "number"
487
+ },
488
+ {
489
+ "name": "bezel",
490
+ "description": "Width of the black screen bezel, in system px (`0` = none), added onto\nthe declared screen on every side — a `width=\"502\" bezel=\"5\"` desktop\nrenders a 512-system-px host box. The compact Mac's CRT showed an\nunlit black margin between the desktop's raster and the case; `bezel`\ndraws it around the screen and puts the classic screen-corner mask on\nthe screen's two *top* corners — only the top pair was rounded in the\nframebuffer. Flow, window coordinates and the drag clip all belong to\nthe screen, so windows crop at its edge. Inside a bezeled desktop a\nmenu bar needs no `rounded` of its own — the desktop's mask lands on\nthe same pixels.",
491
+ "type": "number"
492
+ },
493
+ {
494
+ "name": "top",
495
+ "description": "Offset from the top of the positioning parent, in whole system px.\nSetting this (or `left`) absolutely positions the element within its\nparent; the coordinate left unset is 0. Remove both to return the\nelement to normal flow.",
496
+ "type": "number | null | undefined"
497
+ },
498
+ {
499
+ "name": "left",
500
+ "description": "Offset from the left of the positioning parent, in whole system px.\nSee top.",
501
+ "type": "number | null | undefined"
502
+ }
503
+ ],
504
+ "events": []
505
+ }
506
+ },
507
+ {
508
+ "name": "vf-dialog",
509
+ "description": "`<vf-dialog>` — the System 7 modal dialog shell.\n\nTwo chromes, one modal lifecycle (native `<dialog>` for top-layer rendering\nand focus trapping, with a fully transparent backdrop — no dimming):\n\n- **Default:** a striped title bar with a centered title over a white body —\n the movable-modal look. Drag the title bar to move it. `closable` adds the\n standard close box (left of the bar) — the HIG's own figures disagree on\n whether a movable modal carries one (Figure 5-1 says yes, Figure 6-1 and\n the Chapter 6 text say no), so the component enables either reading rather\n than enforcing one.\n- **`frame=\"plain\"`:** the classic dBoxProc modal-dialog frame — 1px outer\n border, 2px gap, 2px inner band, no shadow, no title bar — and immovable,\n like the original. A `heading` renders as a centered display-face heading\n at the top of the body (the reference art's \"Dialog title\"); `closable` is\n ignored, there being no bar to carry the widget.\n\nOpen it with `show()` (or set the `open` attribute/property); close with\n`close()`. Escape closes it and fires `vf-close` with\n`{ reason: 'escape' }`; the close box and programmatic closing fire\n`{ reason: 'close' }`.\n---\n\n\n### **Events:**\n - **vf-close** - Dialog closed. Detail `{ reason: 'escape' | 'close' }`.\n\n### **Slots:**\n - _default_ - Default slot: dialog body content.\n- **buttons** - Optional action buttons. Rendered as a bottom-right `vf-button-group` (equal-width, faces aligned); the footer only takes space when the slot is populated.\n\n### **CSS Properties:**\n - **--vf-dots-pattern** - the windoid bar's dot-grid dither — a 2×2 tile, one black pixel at the origin (`vfDots`; override the whole pattern like `--vf-desktop-pattern`) _(default: undefined)_\n- **--vf-titlebar-height** - window/dialog title bars _(default: undefined)_\n- **--vf-scrollbar-thumb** - scrollbar thumb/elevator (white) _(default: undefined)_\n- **--vf-scrollbar-track** - the scroll trough's base color under the dot-dither (white) _(default: undefined)_\n\n### **CSS Parts:**\n - **frame** - The outer chrome frame (striped-bar or plain).\n- **title-bar** - The striped title bar (default chrome only).\n- **title** - The centered title patch (or the plain-frame heading).\n- **close-box** - The close widget (`closable`, default chrome only).\n- **body** - The white content area.\n- **content** - The scrolling region inside the body (heading + slotted content, not the footer). Inert while the content fits; over-stuffed, it scrolls under a System 7 rail and becomes a keyboard stop.\n- **footer** - The action row wrapping the buttons.\n- **buttons** - The button group inside the footer.",
510
+ "doc-url": "",
511
+ "attributes": [
512
+ {
513
+ "name": "heading",
514
+ "description": "Title text: the bar's centered patch, or the plain frame's heading.",
515
+ "value": { "type": "string", "default": "''" }
516
+ },
517
+ {
518
+ "name": "label",
519
+ "description": "Accessible name for the dialog (`aria-label`), for a dialog with no\n`heading` — an untitled title bar has no text to be named by. Ignored when\nempty and a `heading` is set (the title patch names the dialog then);\ndefaults to `'Dialog'` when neither is given.",
520
+ "value": { "type": "string", "default": "''" }
521
+ },
522
+ {
523
+ "name": "closable",
524
+ "description": "Show the close box (left side of the title bar). Off by default — the\nbare movable-modal bar. Ignored with `frame=\"plain\"` (no bar). Clicking\nit closes the dialog and fires `vf-close` with `{ reason: 'close' }`.",
525
+ "value": { "type": "boolean", "default": "false" }
526
+ },
527
+ {
528
+ "name": "frame",
529
+ "description": "Frame chrome. Omit for the striped title bar (movable modal); `'plain'`\nfor the immovable dBoxProc double frame with no bar (modal dialog box).",
530
+ "value": { "type": "'plain' | undefined" }
531
+ },
532
+ {
533
+ "name": "open",
534
+ "description": "Whether the modal is open. Kept in sync with the native `<dialog>`.",
535
+ "value": { "type": "boolean", "default": "false" }
536
+ },
537
+ {
538
+ "name": "width",
539
+ "description": "Modal width in whole system px — the art's own unit, so the box holds its\nproportion to the chrome inside it at every display density.\n\n**Declare it.** A modal owns its width; it is not a shape its content\nhappens to fall into. The platform's own default is `fit-content` measured\nagainst the space *left over* beside its offsets — and stating an offset is\nexactly how the box is placed and dragged (top), so an undeclared\nmodal squeezes itself and reflows its text as it moves toward an edge.\nUnset, it falls back to MODAL_FALLBACK_WIDTH and says so once in\nthe console.",
540
+ "value": { "type": "number | undefined" }
541
+ },
542
+ {
543
+ "name": "height",
544
+ "description": "Modal height in whole system px.\n\n**Declare it,** with width. A modal is a fixed box in both axes —\nthe same rule `vf-window` follows, for the same reason: the classic shells\ncarried both dimensions in the resource, and a box that grows with its body\nis one whose buttons move under the pointer as its text changes. Unset, the\nbody sizes to its content — the old behavior — and it says so once in the\nconsole.\n\nContent taller than the declared box is clipped at the frame.",
545
+ "value": { "type": "number | undefined" }
546
+ },
547
+ {
548
+ "name": "top",
549
+ "description": "Offset from the top of the **viewport**, in whole system px — the same\n`top`/`left` pair every other component takes (VfPositioned), in\nthe same unit, with one difference the platform forces: `showModal()` puts\nthe box in the top layer, whose containing block is the viewport rather\nthan the nearest positioned ancestor. So these coordinates are screen\ncoordinates, not the parent's.\n\nLeave both unset and the modal is **centered** — recomputed on open, and\nagain whenever its own box or the viewport changes, which is what keeps a\ndialog whose slotted content upgrades after `showModal()` from opening at\nthe offset its smaller first render centered at. Dragging the title bar\nstates the pair; setting either back to `null` returns the modal to\ncentering.",
550
+ "value": { "type": "number | null | undefined" }
551
+ },
552
+ {
553
+ "name": "left",
554
+ "description": "Offset from the left of the viewport, in whole system px. See top.",
555
+ "value": { "type": "number | null | undefined" }
556
+ }
557
+ ],
558
+ "slots": [
559
+ { "name": "", "description": "Default slot: dialog body content." },
560
+ {
561
+ "name": "buttons",
562
+ "description": "Optional action buttons. Rendered as a bottom-right `vf-button-group` (equal-width, faces aligned); the footer only takes space when the slot is populated."
563
+ }
564
+ ],
565
+ "events": [
566
+ {
567
+ "name": "vf-close",
568
+ "description": "Dialog closed. Detail `{ reason: 'escape' | 'close' }`."
569
+ }
570
+ ],
571
+ "js": {
572
+ "properties": [
573
+ {
574
+ "name": "heading",
575
+ "description": "Title text: the bar's centered patch, or the plain frame's heading.",
576
+ "type": "string"
577
+ },
578
+ {
579
+ "name": "label",
580
+ "description": "Accessible name for the dialog (`aria-label`), for a dialog with no\n`heading` — an untitled title bar has no text to be named by. Ignored when\nempty and a `heading` is set (the title patch names the dialog then);\ndefaults to `'Dialog'` when neither is given.",
581
+ "type": "string"
582
+ },
583
+ {
584
+ "name": "closable",
585
+ "description": "Show the close box (left side of the title bar). Off by default — the\nbare movable-modal bar. Ignored with `frame=\"plain\"` (no bar). Clicking\nit closes the dialog and fires `vf-close` with `{ reason: 'close' }`.",
586
+ "type": "boolean"
587
+ },
588
+ {
589
+ "name": "frame",
590
+ "description": "Frame chrome. Omit for the striped title bar (movable modal); `'plain'`\nfor the immovable dBoxProc double frame with no bar (modal dialog box).",
591
+ "type": "'plain' | undefined"
592
+ },
593
+ {
594
+ "name": "open",
595
+ "description": "Whether the modal is open. Kept in sync with the native `<dialog>`.",
596
+ "type": "boolean"
597
+ },
598
+ {
599
+ "name": "width",
600
+ "description": "Modal width in whole system px — the art's own unit, so the box holds its\nproportion to the chrome inside it at every display density.\n\n**Declare it.** A modal owns its width; it is not a shape its content\nhappens to fall into. The platform's own default is `fit-content` measured\nagainst the space *left over* beside its offsets — and stating an offset is\nexactly how the box is placed and dragged (top), so an undeclared\nmodal squeezes itself and reflows its text as it moves toward an edge.\nUnset, it falls back to MODAL_FALLBACK_WIDTH and says so once in\nthe console.",
601
+ "type": "number | undefined"
602
+ },
603
+ {
604
+ "name": "height",
605
+ "description": "Modal height in whole system px.\n\n**Declare it,** with width. A modal is a fixed box in both axes —\nthe same rule `vf-window` follows, for the same reason: the classic shells\ncarried both dimensions in the resource, and a box that grows with its body\nis one whose buttons move under the pointer as its text changes. Unset, the\nbody sizes to its content — the old behavior — and it says so once in the\nconsole.\n\nContent taller than the declared box is clipped at the frame.",
606
+ "type": "number | undefined"
607
+ },
608
+ {
609
+ "name": "top",
610
+ "description": "Offset from the top of the **viewport**, in whole system px — the same\n`top`/`left` pair every other component takes (VfPositioned), in\nthe same unit, with one difference the platform forces: `showModal()` puts\nthe box in the top layer, whose containing block is the viewport rather\nthan the nearest positioned ancestor. So these coordinates are screen\ncoordinates, not the parent's.\n\nLeave both unset and the modal is **centered** — recomputed on open, and\nagain whenever its own box or the viewport changes, which is what keeps a\ndialog whose slotted content upgrades after `showModal()` from opening at\nthe offset its smaller first render centered at. Dragging the title bar\nstates the pair; setting either back to `null` returns the modal to\ncentering.",
611
+ "type": "number | null | undefined"
612
+ },
613
+ {
614
+ "name": "left",
615
+ "description": "Offset from the left of the viewport, in whole system px. See top.",
616
+ "type": "number | null | undefined"
617
+ }
618
+ ],
619
+ "events": [
620
+ {
621
+ "name": "vf-close",
622
+ "description": "Dialog closed. Detail `{ reason: 'escape' | 'close' }`."
623
+ }
624
+ ]
625
+ }
626
+ },
627
+ {
628
+ "name": "vf-fieldset",
629
+ "description": "`<vf-fieldset>` — the classic \"Install Location\" group box.\n\nA 1px black rectangle whose bold legend sits on the top border, punching a\ngap through it with a `var(--vf-surface, ...)` background patch — so it\nmatches whatever surface sits behind it.\n---\n\n\n### **Slots:**\n - _default_ - Group contents.\n- **legend** - Rich legend content; overrides the `legend` attribute.\n\n### **CSS Properties:**\n - **--vf-surface** - bg behind legends/label patches; `vf-window` and `vf-dialog` both set it to white _(default: ...)_\n\n### **CSS Parts:**\n - **fieldset** - The bordered box.\n- **legend** - The legend patch on the top border.",
630
+ "doc-url": "",
631
+ "attributes": [
632
+ {
633
+ "name": "legend",
634
+ "description": "Legend text shown on the top border.",
635
+ "value": { "type": "string", "default": "''" }
636
+ },
637
+ {
638
+ "name": "top",
639
+ "description": "Offset from the top of the positioning parent, in whole system px.\nSetting this (or `left`) absolutely positions the element within its\nparent; the coordinate left unset is 0. Remove both to return the\nelement to normal flow.",
640
+ "value": { "type": "number | null | undefined" }
641
+ },
642
+ {
643
+ "name": "left",
644
+ "description": "Offset from the left of the positioning parent, in whole system px.\nSee top.",
645
+ "value": { "type": "number | null | undefined" }
646
+ }
647
+ ],
648
+ "slots": [
649
+ { "name": "", "description": "Group contents." },
650
+ {
651
+ "name": "legend",
652
+ "description": "Rich legend content; overrides the `legend` attribute."
653
+ }
654
+ ],
655
+ "events": [],
656
+ "js": {
657
+ "properties": [
658
+ {
659
+ "name": "legend",
660
+ "description": "Legend text shown on the top border.",
661
+ "type": "string"
662
+ },
663
+ {
664
+ "name": "top",
665
+ "description": "Offset from the top of the positioning parent, in whole system px.\nSetting this (or `left`) absolutely positions the element within its\nparent; the coordinate left unset is 0. Remove both to return the\nelement to normal flow.",
666
+ "type": "number | null | undefined"
667
+ },
668
+ {
669
+ "name": "left",
670
+ "description": "Offset from the left of the positioning parent, in whole system px.\nSee top.",
671
+ "type": "number | null | undefined"
672
+ }
673
+ ],
674
+ "events": []
675
+ }
676
+ },
677
+ {
678
+ "name": "vf-grid",
679
+ "description": "`<vf-grid>` — a lattice of equal cells separated by 1px rules.\n\nThe reference screens are full of these: the Figure 5-6 tool palette's 3×3\nof desk accessories, a color picker's swatch table, a pattern or icon\nchooser. They are all the same drawing — fixed-size cells butted together\nwith a hairline between them — and every one of them used to be hand-rolled\npage CSS: a `display: grid` with a 1px gap over a black background, so the\nbackground showed through as the rules. That trick only draws *solid*\ninterior rules, has to restate the cell size in three places, and puts the\nkit's own artwork in the consumer's stylesheet.\n\nThis draws it instead. State the cell count and cell size in whole system px\nand slot the cells in; the component owns the track geometry and the rules:\n\n```html\n<vf-grid columns=\"3\" rows=\"3\" cell-width=\"26\" cell-height=\"26\">\n <button>…</button> <!-- nine of them, one per cell -->\n</vf-grid>\n```\n\n**The rules are a masked lattice, not gaps.** The grid's 1px gaps make the\nroom; a single overlay paints `--vf-black` through two tiled masks — one\ncolumn of pixels at the horizontal pitch, one row at the vertical — so the\npen switches from `solid` to `dashed` (1 on, 1 off) without the layout\nmoving a pixel. It paints *under* the cells (`z-index: -1` inside the grid's\nown stacking context, above its surface), so an item that spans two cells\ncovers the boundary it swallowed, and a cell's own ink — a pressed face, a\nfocus rule, a hard shadow — is never crossed by a line.\n\n**Cells are centered in their wells.** What a palette holds is normally\nsmaller than the well it sits in — a 16px icon in a 26px cell — so a slotted\nitem is centered on both axes rather than parked in the corner, which is\nwhere grid's own `stretch` default leaves anything carrying its own size. A\ncell that means to fill its well — a tool button that inverts black when\nselected, a tile whose border merges with the lattice — says\n`place-self: stretch` and gets the whole track. (Under `collapse` that is\nalready the default: those cells exist to meet the lines.)\n\n**The perimeter is part of the lattice**, and drawn by default: the box\ncarries the 1px the outer lines need, and one uninterrupted pitch runs from\nedge to edge. `frameless` drops it, for a grid inside something that already\ndraws that line — the desk-accessory palette is a `vf-window[flush]`, whose\nframe *is* the grid's outer border, and a doubled one would read as 2px.\n\n**`collapse` puts an item's own border on the rule.** A cell that draws its\nown 1px border — a bordered tile, a 16×16 swatch — otherwise sets that border\n*beside* the lattice line, and every boundary reads 2px thick. `collapse`\npulls each cell back one pixel on all four sides, so the two become one line:\nthe `border-collapse: collapse` of a table, in system px. Size the cells at\n*item − 2px* — `cell-width=\"14\"` for a 16px item — and each item then spans\nlattice line to lattice line, sharing one with each neighbor:\n\n```html\n<vf-grid columns=\"8\" rows=\"4\" cell-width=\"14\" cell-height=\"14\" collapse>\n```\n\nThe frame is what gives the outermost borders a line of their own to land on,\nand it ends the box exactly at the last item's outer edge; go `frameless` and\nthey paint a pixel outside the grid's box instead.\n\n`rules=\"none\"` is the pen, not the lattice: nothing is drawn, the gaps close\nup, and the cells butt directly together (there is then no perimeter and\nnothing for `collapse` to collapse onto, so it goes inert).\n\n**Semantics are the consumer's.** The grid is layout — it takes no role, no\nkeyboard behavior and no selection, so what it holds decides what it is: put\n`role=\"group\"`/`aria-label` on the host for a tool palette, `role=\"radiogroup\"`\nfor a picker, or nothing at all for a plain tiling. Slotted cells stay in the\nlight DOM with their own semantics intact.\n---\n\n\n### **Slots:**\n - _default_ - The cells, in order — one element per cell.\n\n### **CSS Properties:**\n - **--vf-surface** - The surface behind the cells (default white). _(default: undefined)_\n\n### **CSS Parts:**\n - **grid** - The grid box itself (surface, tracks, frame padding).\n- **rules** - The lattice overlay painting the rules.",
680
+ "doc-url": "",
681
+ "attributes": [
682
+ {
683
+ "name": "columns",
684
+ "description": "Cells across the x axis.",
685
+ "value": { "type": "number", "default": "1" }
686
+ },
687
+ {
688
+ "name": "rows",
689
+ "description": "Cells down the y axis. Left unset the grid takes as many rows as the\nslotted cells need; set, it reserves that many — an unfilled cell still\ngets its rules, the way a palette keeps its empty wells.",
690
+ "value": { "type": "number | undefined" }
691
+ },
692
+ {
693
+ "name": "cell-width",
694
+ "description": "Cell width in whole system px.",
695
+ "value": { "type": "number", "default": "16" }
696
+ },
697
+ {
698
+ "name": "cell-height",
699
+ "description": "Cell height in whole system px.",
700
+ "value": { "type": "number", "default": "16" }
701
+ },
702
+ {
703
+ "name": "rules",
704
+ "description": "The pen every rule is drawn with: `solid` (default), `dashed` (1 system px\non, 1 off), or `none` — no rules at all, and the cells butt together.",
705
+ "value": { "type": "VfGridRules", "default": "'solid'" }
706
+ },
707
+ {
708
+ "name": "frameless",
709
+ "description": "Drop the outer border, leaving only the boundaries between cells. For a\ngrid inside something that already draws that line — a `vf-window[flush]`,\na `vf-fieldset` — where a second one would read as 2px.",
710
+ "value": { "type": "boolean", "default": "false" }
711
+ },
712
+ {
713
+ "name": "collapse",
714
+ "description": "Collapse each cell's own 1px border onto the grid's rules instead of\nsetting it beside them (see the class doc). Size the cells at *item − 2px*;\nthe frame is what gives the outermost borders a line to land on, so a\n`frameless` grid lets them paint a pixel outside its box.",
715
+ "value": { "type": "boolean", "default": "false" }
716
+ },
717
+ {
718
+ "name": "top",
719
+ "description": "Offset from the top of the positioning parent, in whole system px.\nSetting this (or `left`) absolutely positions the element within its\nparent; the coordinate left unset is 0. Remove both to return the\nelement to normal flow.",
720
+ "value": { "type": "number | null | undefined" }
721
+ },
722
+ {
723
+ "name": "left",
724
+ "description": "Offset from the left of the positioning parent, in whole system px.\nSee top.",
725
+ "value": { "type": "number | null | undefined" }
726
+ }
727
+ ],
728
+ "slots": [
729
+ {
730
+ "name": "",
731
+ "description": "The cells, in order — one element per cell."
732
+ }
733
+ ],
734
+ "events": [],
735
+ "js": {
736
+ "properties": [
737
+ {
738
+ "name": "columns",
739
+ "description": "Cells across the x axis.",
740
+ "type": "number"
741
+ },
742
+ {
743
+ "name": "rows",
744
+ "description": "Cells down the y axis. Left unset the grid takes as many rows as the\nslotted cells need; set, it reserves that many — an unfilled cell still\ngets its rules, the way a palette keeps its empty wells.",
745
+ "type": "number | undefined"
746
+ },
747
+ {
748
+ "name": "cellWidth",
749
+ "description": "Cell width in whole system px.",
750
+ "type": "number"
751
+ },
752
+ {
753
+ "name": "cellHeight",
754
+ "description": "Cell height in whole system px.",
755
+ "type": "number"
756
+ },
757
+ {
758
+ "name": "rules",
759
+ "description": "The pen every rule is drawn with: `solid` (default), `dashed` (1 system px\non, 1 off), or `none` — no rules at all, and the cells butt together.",
760
+ "type": "VfGridRules"
761
+ },
762
+ {
763
+ "name": "frameless",
764
+ "description": "Drop the outer border, leaving only the boundaries between cells. For a\ngrid inside something that already draws that line — a `vf-window[flush]`,\na `vf-fieldset` — where a second one would read as 2px.",
765
+ "type": "boolean"
766
+ },
767
+ {
768
+ "name": "collapse",
769
+ "description": "Collapse each cell's own 1px border onto the grid's rules instead of\nsetting it beside them (see the class doc). Size the cells at *item − 2px*;\nthe frame is what gives the outermost borders a line to land on, so a\n`frameless` grid lets them paint a pixel outside its box.",
770
+ "type": "boolean"
771
+ },
772
+ {
773
+ "name": "top",
774
+ "description": "Offset from the top of the positioning parent, in whole system px.\nSetting this (or `left`) absolutely positions the element within its\nparent; the coordinate left unset is 0. Remove both to return the\nelement to normal flow.",
775
+ "type": "number | null | undefined"
776
+ },
777
+ {
778
+ "name": "left",
779
+ "description": "Offset from the left of the positioning parent, in whole system px.\nSee top.",
780
+ "type": "number | null | undefined"
781
+ }
782
+ ],
783
+ "events": []
784
+ }
785
+ },
786
+ {
787
+ "name": "vf-icon",
788
+ "description": "`<vf-icon>` — a Finder icon: pixel art in a reserved cell with its name on a\nplate below, selectable, movable and renameable in place.\n\nIt is the one composite in the kit whose parts already existed separately —\n`vf-img` draws the art, `vf-list-item` inverts on selection, `vf-window`\ndrags — and the reason to have it anyway is that the *icon* is the unit the\nFinder actually manipulates. A picture and a caption that select together,\nmove together and rename together is a control, not a layout.\n\n### The art arrives by slot\n\nTwo slots, one per resource size — `large` is the 32×32 `ICN#` and `small`\nthe 16×16 `ics#` — each holding a `vf-img` around the consumer's own `<img>`:\n\n```html\n<vf-icon label=\"Macintosh HD\" selectable movable editable>\n <vf-img slot=\"large\"><img src=\"hd-32.png\" alt=\"\"></vf-img>\n <vf-img slot=\"small\"><img src=\"hd-16.png\" alt=\"\"></vf-img>\n</vf-icon>\n```\n\nSlots rather than `src` properties, for the reason `vf-img` exists at all:\nthe kit ships no raster files and never builds an `<img>` on a consumer's\nbehalf, so the graphic stays a real element in the light DOM with its own\n`alt`, `srcset`, loading behavior and asset URLs. A `src` string can express\nnone of that, and it could not hold an inline `<svg>` or a `<canvas>` either.\n`vf-list-item`'s `icon` slot makes the same trade.\n\nThe cost is that both files fetch even though one paints — pay it with two\ndata URIs, or slot only the size that view uses.\n\n### The cell is reserved, the art is registered in it\n\n`size` picks which slot paints *and* the cell it paints in: 32×32 or 16×16,\nheld whether or not there is art for it. A folder is 32×20 of ink and a\ndocument 25×32, and a row of them keeps one baseline only because the cell —\nnot the ink — is the unit, which is what an icon resource always was.\n\n### Everything centered lands on a whole pixel, by parity\n\nThe frame centers two things over one axis — the art cell and the name — and\na centered child sits at `(box − child) / 2`. That is a whole number exactly\nwhen the box and the child have the **same parity**. Half a system pixel is\nwhat fringes 1-bit art: the glyph stems smear across two device columns and\ngo gray, while the plate behind them stays sharp, because backgrounds are\npixel-snapped by the compositor and glyphs are not. A crisp plate under a\ngrey name is the signature of exactly this.\n\nSo the component makes the parities agree instead of correcting afterwards.\nThe cell is 32 or 16, and #measurePlate sizes the name plate to a\nwhole **even** number of system px — measured from the text, rounded up. Both\nchildren are then even, and so is every offset, at every display density and\nwhether or not a `width` is declared. Nothing is snapped, nothing leans on\nthe rasterizer, and `verify:icon` asserts both the geometry and that a field\nof names renders without a single gray pixel — with the kit's normal\nantialiasing left on, since a run on whole pixels gives it nothing to smooth.\n\nYour side of it is one rule: **a declared `width` must be even**, since the\ncomponent cannot round a number you chose. Supply art at the cell size too —\nwhich \"one image pixel is one system pixel\" already implies — because art\ndiffering from its cell by an odd number centers the same way.\n\n### Selection inverts, because the art is a 1-bit mask\n\nA System 7 icon is black ink and opaque white on a transparent surround —\nprecisely an image plus its mask — so inverting it flips ink and fill and\nleaves the surround alone, which is the whole of the classic selected\nappearance. `filter: invert(1)` reproduces it exactly for 1-bit art. Color\nart inverts too, into a photographic negative rather than the darkening\nSystem 7 gave it; that is the case to revisit if the kit ever grows a\nselected-state treatment of its own. The label plate inverts to the\n`--vf-highlight` pair, sharing one selection color with `vf-list-item`.\n\n### Open is derived, not shipped\n\nWith `open`, the art redraws as the Finder's open ghost: the outline held\nin solid black, the interior re-filled with the kit's loose 25% dither (the\nscrollbar trough's lattice), the transparent surround untouched. There is\nno second raster and no second fetch — the same alpha channel that makes\nselection an inversion makes the ghost derivable, and `src/open-art.ts`\nderives it from the slotted art by canvas compositing alone. No pixels are\never read back, so a cross-origin image that taints its canvas still works:\ntaint forbids reading, not drawing or displaying.\n\nThe ghost keeps the shape selection expects — ink and opaque white on a\ntransparent surround — so a selected open icon inverts exactly as a closed\none does, with no second treatment. The slot stays in the tree while the\nghost paints, hidden (it is where the art loads, and re-loads, from), and\nart the pipeline cannot draw — nothing slotted yet, a failed load, an\ninline `<svg>` — keeps rendering as itself rather than vanishing behind a\nstate it cannot show.\n\n### `movable`, not `draggable`\n\n`draggable` is a global HTML attribute *and* an `HTMLElement` accessor, so\ndeclaring it would both shadow a platform member and hand the element to the\nbrowser's own drag-and-drop machinery. This is the `align` trap from\n`vf-stack` in a second costume — `align`, `hidden`, `dir`, `draggable` and\n`title` all carry behavior a custom element never asked for. The kit already\nspells this parameter `movable` on `vf-window`, so the icon does too, and it\nmoves the same way: `DragController` tracks the gesture and\n`PlacementController` writes the result into `left`/`top` in whole system px,\nthe art's own unit — the same pair markup places an icon with, so a moved\nicon is still where it was dropped after a zoom.\n\nDragging is a pointer gesture with no keyboard equivalent, which is the kind\nof gap the kit closes rather than inherits (SPEC §1): a focused movable icon\nalso moves under the arrow keys, one system px at a time and eight with\nShift. Focus is what `selectable` grants, so the keyboard half of `movable`\nand `editable` presupposes it — see the role section below.\n\nOpening gets the same treatment. The double-click is the pointer gesture,\nand its keyboard route is ⌘O / ⌘↓ — the System 7 Open shortcuts, with Ctrl\nstanding in for ⌘ off the Mac. Return is deliberately not one of them: the\nFinder's Return renamed, never opened, so on an editable icon it starts the\nedit and on a non-editable one it does nothing at all.\n\n### The name and the art are one target, and the second click decides\n\nA double-click opens the icon *wherever it lands* — the name is as much the\nicon as the picture is, and the Finder never made you aim at the 32 pixels of\nart. But the name is also where a single click renames, so the two gestures\nbegin with the same press and only the second one tells them apart.\n\nSo the rename waits for it. A press on the plate of an already-selected icon\narms the field rather than opening it, and the next press inside\nRENAME_DELAY_MS calls it off — leaving the double-click to open, with\nno rename box flashing up behind it. Nothing needs to *undo* an edit that\nbegan: the press that starts one and the press that opens are the same\npress, so the only thing that can be got right is not committing early.\n\nThe window is generous in the one direction that is cheap. Reading a lone\nclick as a pair costs a wait before the box appears; reading a pair as a lone\nclick renames when the user asked to open. The same reasoning covers a press\nthat turns into a drag, a press elsewhere, and any key — each calls the\npending rename off, because none of them is the click it is waiting for.\n\n### An icon alone is a picture; an icon in a field is an option\n\n`role=\"option\"` is only meaningful inside a `listbox` that owns it. Written\nunconditionally it is not merely untidy — the browser *drops* it, and\n`aria-selected` with it, so a `selectable` icon announced as a bare generic\nand its selection state reached assistive tech nowhere at all.\n\nSo the role follows the container. Owned, the icon is an `option` that names\nitself from its plate and publishes `aria-selected`. Unowned, it degrades to\n`role=\"img\"` with a name — the same vocabulary the derived open ghost uses,\nand true of what it is. Deliberately not `button`: that would promise Enter\nand Space activate, and here Return *renames* while the open route is ⌘O / ⌘↓.\n\nDeclaring the owner is one attribute on whatever already holds the field, and\nit is what buys the selection state back:\n\n```html\n<div role=\"listbox\" aria-label=\"Desktop\" aria-multiselectable=\"true\">\n <vf-icon label=\"Macintosh HD\" selectable movable editable>…</vf-icon>\n <vf-icon label=\"Trash\" selectable movable editable>…</vf-icon>\n</div>\n```\n\nA `vf-desktop` cannot be that container itself: it also holds windows and a\nmenu bar, and a non-`option` child of a listbox is invalid the same way the\norphaned option was. The plain wrapper above is layout-neutral — placed icons\nanchor to the nearest *positioned* ancestor, which is still the desktop's\nraster. One divergence from the APG listbox is deliberate: its options share\na single roving tab stop, while these stay one stop each, the way a Finder\nicon is reached on its own.\n\n**`selectable` is what makes an icon focusable**, and `movable`/`editable`\npresuppose it. That is the Finder's own model — you cannot move or rename\nwhat you have not selected — and the pointer path already assumed it: the\nrename opens on a press on the plate of an *already-selected* icon. A\n`movable`-only icon is a picture you can drag, not a widget.\n\n### The label is a property, because it is editable\n\nThe caption is `label` rather than slotted content: renaming means the\ncomponent owns the string and hands it back on `vf-change`, and it cannot own\ntext that lives in the consumer's DOM. An empty `label` draws no plate at\nall — that *is* the \"no label\" parameter, in preference to a second attribute\nthat could disagree with it. `editable` then lets a click on the plate of an\nalready-selected icon open the rename box a moment later, as the Finder's\ndoes (see above), with Return committing, Escape reverting, and the plate\nwidening as you type.\n\n### A name is never abbreviated, and never folded\n\nThere is no ellipsis, no clipping and no wrapping: one line, always. System 7\nsolved the long-name problem at the *other* end — HFS capped a filename at 31\ncharacters — so the Finder could afford to always draw the name in full, and\ndid. A name wider than its cell simply overflows it, centered, the way a name\nwider than a 32-pixel icon always did.\n\nStaying on one line is also what keeps the name on the grid: a single run has\na single measured width, so the even-plate rule above is one number rather\nthan one per line, each with its own parity. `width` is therefore the cell —\nthe grid pitch — not a bound on the name.\n\n`maxlength` (31) bounds the rename field rather than the `label` property:\nthe name belongs to the consumer's model, and truncating one handed to us\nwould lose data.\n\nThe box hugs its text while you type it, not only once you are done, which is\nwhat keeps the name from moving when the edit commits: the plate is the same\nwidth either side of it, so the glyphs stay exactly where they were.\n\nA file also has to be called *something*, so a rename committed empty — or\nas nothing but spaces — is refused rather than applied, and the previous name\ncomes back, which is what System 7 did. An empty `label` is still a perfectly\ngood state to *start* in, though: a freshly made icon has no name until it is\ngiven one. Such an icon draws no plate but stays selectable, focusable and\nrenameable — Return opens a field, and an edit with nothing in it yet is the\none time the box stops hugging and reserves a cell's width, since a field you\ncannot see is one you cannot type into.\n---\n\n\n### **Events:**\n - **vf-select** - Selection changed by user interaction. `detail: { selected: boolean }`.\n- **vf-change** - The name was committed. `detail: { label: string, previous: string }`.\n- **vf-open** - The icon was opened — double-clicked anywhere on it, its name included, or ⌘O / ⌘↓ from the keyboard (Ctrl off the Mac), the System 7 shortcuts. Return renames instead, as the Finder's did. `detail: {}`.\n- **vf-name-too-long** - A rename was typed or pasted past `maxlength`, and the field refused the excess. `detail: { attempted, accepted, limit }` — enough to raise the alert System 7 raised rather than drop the characters silently. Not fired for a `label` set from your own code, which is never truncated in the first place.\n- **vf-name-rejected** - A rename was committed with no name in it, so the edit was dropped and the old name put back. `detail: { attempted, kept, reason: 'empty' }`. A `vf-change` is *not* fired alongside it — nothing changed.\n\n### **Slots:**\n - **large** - The 32×32 art, normally a `vf-img` around an `<img>`.\n- **small** - The 16×16 art, shown under `size=\"small\"`.\n\n### **CSS Properties:**\n - **--vf-icon-gap** - Space between the art cell and the name plate _(default: undefined)_\n- **--vf-icon-label-height** - The name plate's line box _(default: undefined)_\n\n### **CSS Parts:**\n - **frame** - The icon and its label plate, stacked.\n- **icon** - The reserved art cell.\n- **label** - The name block; its lines are centered under the art.\n- **plate** - The inked run behind the name, which each wrapped line gets its own of (inverts when selected).\n- **input** - The rename field, while editing.",
789
+ "doc-url": "",
790
+ "attributes": [
791
+ {
792
+ "name": "label",
793
+ "description": "The icon's name. Empty draws no plate — that is the \"no label\" setting.",
794
+ "value": { "type": "string", "default": "''" }
795
+ },
796
+ {
797
+ "name": "size",
798
+ "description": "Which member of the icon family paints, and the cell it is registered in:\n`large` (32×32) or `small` (16×16).",
799
+ "value": { "type": "VfIconSize", "default": "'large'" }
800
+ },
801
+ {
802
+ "name": "selectable",
803
+ "description": "Clicking selects. Set `selected` yourself to drive selection some other way.\n\nThis is also the flag that makes an icon focusable and gives it a role, so\nthe keyboard halves of movable and editable presuppose it —\nas the Finder did. A container carrying `role=\"listbox\"` turns the role from\n`img` into a real `option`; see the class doc.",
804
+ "value": { "type": "boolean", "default": "false" }
805
+ },
806
+ {
807
+ "name": "selected",
808
+ "description": "Whether the icon is selected: the art inverts and the plate goes black.",
809
+ "value": { "type": "boolean", "default": "false" }
810
+ },
811
+ {
812
+ "name": "open",
813
+ "description": "The icon's window is on screen, so the art paints as the Finder's open\nghost — outline held, interior re-filled with the kit's loose dither —\nderived in the client from the slotted art itself (see the class doc).\nSet it when handling `vf-open`, clear it when the window goes away.\nSelection inverts the ghost exactly as it inverts the art.",
814
+ "value": { "type": "boolean", "default": "false" }
815
+ },
816
+ {
817
+ "name": "movable",
818
+ "description": "Drag to move — `movable`, never `draggable`, which is a platform attribute\nand accessor (see the class doc). Arrow keys move a focused icon too, which\nmeans pairing this with selectable: focus is what that grants.",
819
+ "value": { "type": "boolean", "default": "false" }
820
+ },
821
+ {
822
+ "name": "editable",
823
+ "description": "The name can be renamed in place: click a selected plate, or press Return.\nPair with selectable — both routes start from a selected icon.\n\nThe pointer route opens the field once the double-click window has passed\n(RENAME_DELAY_MS), so double-clicking the *name* opens the icon the\nway double-clicking its art does. Return opens it at once — a keypress has\nno second half to wait for.",
824
+ "value": { "type": "boolean", "default": "false" }
825
+ },
826
+ {
827
+ "name": "width",
828
+ "description": "The cell width in whole system px — the grid pitch, and it must be\n**even**: the component rounds its own plate to keep every centered offset\nwhole, but it cannot round a number you chose (see the class doc).\n\nIt is not a bound on the name. Left off, the box is as wide as the wider of\nthe art cell and the name; set, a longer name overflows it, centered.\nDeclaring one is what keeps a field of icons on a single pitch, and what\nkeeps a text-sized box off a fractional origin (contract rule 3).",
829
+ "value": { "type": "number | undefined" }
830
+ },
831
+ {
832
+ "name": "maxlength",
833
+ "description": "The most characters the rename field will accept — 31, the HFS filename\nlimit every System 7 name was cut to, which is also why the Finder could\nafford to always draw the whole name. Going past it fires\n`vf-name-too-long` with what was tried, so a host can say so rather than\nlet characters vanish.\n\nIt bounds *typing* only: a `label` set from your own data is displayed as\ngiven and fires nothing, since the name belongs to your model and silently\ntruncating it would lose data.",
834
+ "value": { "type": "number", "default": "31" }
835
+ },
836
+ {
837
+ "name": "top",
838
+ "description": "Offset from the top of the positioning parent, in whole system px.\nSetting this (or `left`) absolutely positions the element within its\nparent; the coordinate left unset is 0. Remove both to return the\nelement to normal flow.",
839
+ "value": { "type": "number | null | undefined" }
840
+ },
841
+ {
842
+ "name": "left",
843
+ "description": "Offset from the left of the positioning parent, in whole system px.\nSee top.",
844
+ "value": { "type": "number | null | undefined" }
845
+ }
846
+ ],
847
+ "slots": [
848
+ {
849
+ "name": "large",
850
+ "description": "The 32×32 art, normally a `vf-img` around an `<img>`."
851
+ },
852
+ {
853
+ "name": "small",
854
+ "description": "The 16×16 art, shown under `size=\"small\"`."
855
+ }
856
+ ],
857
+ "events": [
858
+ {
859
+ "name": "vf-select",
860
+ "description": "Selection changed by user interaction. `detail: { selected: boolean }`."
861
+ },
862
+ {
863
+ "name": "vf-change",
864
+ "description": "The name was committed. `detail: { label: string, previous: string }`."
865
+ },
866
+ {
867
+ "name": "vf-open",
868
+ "description": "The icon was opened — double-clicked anywhere on it, its name included, or ⌘O / ⌘↓ from the keyboard (Ctrl off the Mac), the System 7 shortcuts. Return renames instead, as the Finder's did. `detail: {}`."
869
+ },
870
+ {
871
+ "name": "vf-name-too-long",
872
+ "description": "A rename was typed or pasted past `maxlength`, and the field refused the excess. `detail: { attempted, accepted, limit }` — enough to raise the alert System 7 raised rather than drop the characters silently. Not fired for a `label` set from your own code, which is never truncated in the first place."
873
+ },
874
+ {
875
+ "name": "vf-name-rejected",
876
+ "description": "A rename was committed with no name in it, so the edit was dropped and the old name put back. `detail: { attempted, kept, reason: 'empty' }`. A `vf-change` is *not* fired alongside it — nothing changed."
877
+ }
878
+ ],
879
+ "js": {
880
+ "properties": [
881
+ {
882
+ "name": "label",
883
+ "description": "The icon's name. Empty draws no plate — that is the \"no label\" setting.",
884
+ "type": "string"
885
+ },
886
+ {
887
+ "name": "size",
888
+ "description": "Which member of the icon family paints, and the cell it is registered in:\n`large` (32×32) or `small` (16×16).",
889
+ "type": "VfIconSize"
890
+ },
891
+ {
892
+ "name": "selectable",
893
+ "description": "Clicking selects. Set `selected` yourself to drive selection some other way.\n\nThis is also the flag that makes an icon focusable and gives it a role, so\nthe keyboard halves of movable and editable presuppose it —\nas the Finder did. A container carrying `role=\"listbox\"` turns the role from\n`img` into a real `option`; see the class doc.",
894
+ "type": "boolean"
895
+ },
896
+ {
897
+ "name": "selected",
898
+ "description": "Whether the icon is selected: the art inverts and the plate goes black.",
899
+ "type": "boolean"
900
+ },
901
+ {
902
+ "name": "open",
903
+ "description": "The icon's window is on screen, so the art paints as the Finder's open\nghost — outline held, interior re-filled with the kit's loose dither —\nderived in the client from the slotted art itself (see the class doc).\nSet it when handling `vf-open`, clear it when the window goes away.\nSelection inverts the ghost exactly as it inverts the art.",
904
+ "type": "boolean"
905
+ },
906
+ {
907
+ "name": "movable",
908
+ "description": "Drag to move — `movable`, never `draggable`, which is a platform attribute\nand accessor (see the class doc). Arrow keys move a focused icon too, which\nmeans pairing this with selectable: focus is what that grants.",
909
+ "type": "boolean"
910
+ },
911
+ {
912
+ "name": "editable",
913
+ "description": "The name can be renamed in place: click a selected plate, or press Return.\nPair with selectable — both routes start from a selected icon.\n\nThe pointer route opens the field once the double-click window has passed\n(RENAME_DELAY_MS), so double-clicking the *name* opens the icon the\nway double-clicking its art does. Return opens it at once — a keypress has\nno second half to wait for.",
914
+ "type": "boolean"
915
+ },
916
+ {
917
+ "name": "width",
918
+ "description": "The cell width in whole system px — the grid pitch, and it must be\n**even**: the component rounds its own plate to keep every centered offset\nwhole, but it cannot round a number you chose (see the class doc).\n\nIt is not a bound on the name. Left off, the box is as wide as the wider of\nthe art cell and the name; set, a longer name overflows it, centered.\nDeclaring one is what keeps a field of icons on a single pitch, and what\nkeeps a text-sized box off a fractional origin (contract rule 3).",
919
+ "type": "number | undefined"
920
+ },
921
+ {
922
+ "name": "maxlength",
923
+ "description": "The most characters the rename field will accept — 31, the HFS filename\nlimit every System 7 name was cut to, which is also why the Finder could\nafford to always draw the whole name. Going past it fires\n`vf-name-too-long` with what was tried, so a host can say so rather than\nlet characters vanish.\n\nIt bounds *typing* only: a `label` set from your own data is displayed as\ngiven and fires nothing, since the name belongs to your model and silently\ntruncating it would lose data.",
924
+ "type": "number"
925
+ },
926
+ {
927
+ "name": "top",
928
+ "description": "Offset from the top of the positioning parent, in whole system px.\nSetting this (or `left`) absolutely positions the element within its\nparent; the coordinate left unset is 0. Remove both to return the\nelement to normal flow.",
929
+ "type": "number | null | undefined"
930
+ },
931
+ {
932
+ "name": "left",
933
+ "description": "Offset from the left of the positioning parent, in whole system px.\nSee top.",
934
+ "type": "number | null | undefined"
935
+ }
936
+ ],
937
+ "events": [
938
+ {
939
+ "name": "vf-select",
940
+ "description": "Selection changed by user interaction. `detail: { selected: boolean }`."
941
+ },
942
+ {
943
+ "name": "vf-change",
944
+ "description": "The name was committed. `detail: { label: string, previous: string }`."
945
+ },
946
+ {
947
+ "name": "vf-open",
948
+ "description": "The icon was opened — double-clicked anywhere on it, its name included, or ⌘O / ⌘↓ from the keyboard (Ctrl off the Mac), the System 7 shortcuts. Return renames instead, as the Finder's did. `detail: {}`."
949
+ },
950
+ {
951
+ "name": "vf-name-too-long",
952
+ "description": "A rename was typed or pasted past `maxlength`, and the field refused the excess. `detail: { attempted, accepted, limit }` — enough to raise the alert System 7 raised rather than drop the characters silently. Not fired for a `label` set from your own code, which is never truncated in the first place."
953
+ },
954
+ {
955
+ "name": "vf-name-rejected",
956
+ "description": "A rename was committed with no name in it, so the edit was dropped and the old name put back. `detail: { attempted, kept, reason: 'empty' }`. A `vf-change` is *not* fired alongside it — nothing changed."
957
+ }
958
+ ]
959
+ }
960
+ },
961
+ {
962
+ "name": "vf-img",
963
+ "description": "`<vf-img>` — a raster image on the kit's pixel grid.\n\nThe reference art is full of small pixel graphics that are *pictures*, not\nglyphs: the Apple menu's logo, desk-accessory and document icons, the\nabout-box machine portrait. This component displays one — a slotted native\n`<img>` — at the kit's own magnification, treating **one image pixel as one\nsystem pixel**:\n\n- the image is sized to `naturalWidth × naturalHeight` system px, multiplied\n by `--vf-scale` in `calc()` like every other metric, so a source pixel\n covers exactly `scale × dpr` device pixels (a whole number, per layout\n contract rule 1);\n- `image-rendering: pixelated` keeps the resampling nearest-neighbor, and on\n a whole-device-pixel box that is bit-exact magnification — no smoothing,\n no half-covered edge rows;\n- its own GridSnapController, so the image's origin holds the device\n grid wherever the page puts it (a fractional origin would slide the whole\n box across device pixels and shear every source pixel's coverage).\n\nThe graphic stays a real `<img>` in the consumer's light DOM — it keeps\nnative loading, `alt` text (use `alt=\"\"` for a decorative icon) and the\nconsumer's own asset URLs; the kit ships no raster files. `width`/`height`\n(in system px, whole numbers) state the box up front so the page doesn't\nshift when the file lands — until a size is known the box is 0×0, never a\nflash of the image at some other scale. They also override the natural size\ndeliberately: a whole multiple (`width=\"32\"` on 16-px art) magnifies on the\nsame grid.\n---\n\n\n### **Slots:**\n - _default_ - The image: a single `<img>` element.\n\n### **CSS Parts:**\n - **frame** - The sized box the image fills.",
964
+ "doc-url": "",
965
+ "attributes": [
966
+ {
967
+ "name": "width",
968
+ "description": "The displayed width in system px — a whole number. Defaults to the slotted\nimage's `naturalWidth` (one image px = one system px); state it explicitly\nto reserve the box before the file loads, or a whole multiple to magnify.",
969
+ "value": { "type": "number | undefined" }
970
+ },
971
+ {
972
+ "name": "height",
973
+ "description": "The displayed height in system px; see width.",
974
+ "value": { "type": "number | undefined" }
975
+ },
976
+ {
977
+ "name": "top",
978
+ "description": "Offset from the top of the positioning parent, in whole system px.\nSetting this (or `left`) absolutely positions the element within its\nparent; the coordinate left unset is 0. Remove both to return the\nelement to normal flow.",
979
+ "value": { "type": "number | null | undefined" }
980
+ },
981
+ {
982
+ "name": "left",
983
+ "description": "Offset from the left of the positioning parent, in whole system px.\nSee top.",
984
+ "value": { "type": "number | null | undefined" }
985
+ }
986
+ ],
987
+ "slots": [
988
+ {
989
+ "name": "",
990
+ "description": "The image: a single `<img>` element."
991
+ }
992
+ ],
993
+ "events": [],
994
+ "js": {
995
+ "properties": [
996
+ {
997
+ "name": "width",
998
+ "description": "The displayed width in system px — a whole number. Defaults to the slotted\nimage's `naturalWidth` (one image px = one system px); state it explicitly\nto reserve the box before the file loads, or a whole multiple to magnify.",
999
+ "type": "number | undefined"
1000
+ },
1001
+ {
1002
+ "name": "height",
1003
+ "description": "The displayed height in system px; see width.",
1004
+ "type": "number | undefined"
1005
+ },
1006
+ {
1007
+ "name": "top",
1008
+ "description": "Offset from the top of the positioning parent, in whole system px.\nSetting this (or `left`) absolutely positions the element within its\nparent; the coordinate left unset is 0. Remove both to return the\nelement to normal flow.",
1009
+ "type": "number | null | undefined"
1010
+ },
1011
+ {
1012
+ "name": "left",
1013
+ "description": "Offset from the left of the positioning parent, in whole system px.\nSee top.",
1014
+ "type": "number | null | undefined"
1015
+ }
1016
+ ],
1017
+ "events": []
1018
+ }
1019
+ },
1020
+ {
1021
+ "name": "vf-label",
1022
+ "description": "`<vf-label>` — a static System 7 caption.\n\nThe \"Name:\" beside a field, the \"Mode\" heading over a group of radios, a\nnumeric readout beside a slider. Text the page was previously setting by hand\nin the display face — this is that, as a component, on the kit's grid:\n\n- the **Chicago-style chrome face** by default (dialog captions are chrome),\n with `face=\"body\"` to switch to Geneva — which is also the kit's fine\n print, as it was System 7's: a dialog's small captions are Geneva 9 at\n its own strike size;\n- a **whole-system-pixel line box at the face's native pitch**\n (`--vf-label-line-height`: 16px — Chicago 12's own line, which is also the\n em; `face=\"body\"` drops to 12px, Geneva 9's), so a column of captions\n accumulates whole offsets instead of pushing what follows off the\n device-pixel grid the way a ratio `line-height` does (docs/SIZING.md\n rule 2);\n- its own GridSnapController, so the bitmap stems stay on the grid\n wherever the page puts it;\n- a **declared `width`** (VfSized) — the shared width of a caption\n column, so a run of label-and-field rows lands every field on one x.\n Left to its text a caption measures whatever its glyphs measure (the\n showcase's Apple menu title came to 32.641 system px) and anything sized\n from it inherits the fraction; a declared width is whole, and so is the\n row built on it (layout contract rule 3). A caption wider than its column\n overflows rather than reflowing the row: the number is the column, and a\n caption that doesn't fit is a number to raise.\n\n`for` points at a control by id, as a native `<label>` does — clicking the\ncaption focuses that control, and the caption text becomes its accessible\nname (see for for how, and for what it deliberately doesn't do).\n\nChrome text, so the host is not selectable (SPEC §1); prose belongs in\n`<vf-paragraph>`, which is.\n---\n\n\n### **Slots:**\n - _default_ - The caption text.\n\n### **CSS Properties:**\n - **--vf-label-line-height** - The caption's own line box, in system px. Unset (the default) the box follows the face tokens below; set, it overrides both faces for labels alone. Keep any override a whole *even* number — a ratio puts every following line off the grid, and an odd value halves into the em's half-leading and puts the baseline on a half pixel. _(default: undefined)_\n- **--vf-line-height** - The body face's native line (default `12px`, Geneva 9's) — the face-level knob a body-face retheme states alongside `--vf-font-family` / `--vf-font-size`. _(default: undefined)_\n- **--vf-line-height-display** - The display face's native line (default `16px`, Chicago 12's) — the display retheme's third number. _(default: undefined)_\n\n### **CSS Parts:**\n - **label** - The inner text box.",
1023
+ "doc-url": "",
1024
+ "attributes": [
1025
+ {
1026
+ "name": "for",
1027
+ "description": "The id of the control this caption names, resolved in the label's own tree\nscope (its document or shadow root), like a native `<label for>`.\n\nTwo things follow from it. **Clicking the caption focuses the control** —\na focus shortcut, not an activation: the kit's toggles carry their own\nlabels, so forwarding a click to one would double up on the label they\nalready have. And **the caption text becomes the control's accessible\nname**, by whichever route reaches it:\n\n- a `vf-*` control's focusable element lives in its shadow root, where an\n `aria-label` on the host cannot reach it — which is why each of them\n exposes a `label` property that lands on the inner control. That property\n is what gets filled in, and only when the consumer left it empty.\n- anything else (a native `<input>`, an element with a role) is in this\n label's own tree scope, so an `aria-labelledby` id reference works; it is\n set only when the target has no name of its own — and a name computed\n from the target's *content* counts: a `vf-checkbox` with slotted text, a\n `vf-button`, a native `<button>` are already named by what they show, and\n the caption declines rather than stamping over it.\n\nThe one target neither route reaches is a control whose focusable element\nis shadow-internal but which exposes no `label` property because it names\nfrom content — `vf-button` is the kit's case. With visible text it needs\nno caption; an icon-only one should carry its name on the art (`alt` on\nthe slotted `<img>`), which name-from-content picks up the same way.\n\nEither way the label puts back what it found when it is removed, the id\nchanges, or the caption text does.\n\nLeft `undefined` rather than `''` so an unset `for` reflects no attribute\nat all: `vf-label[for]` is a selector a page will reasonably write, and an\nempty `for=\"\"` on every plain caption would make it match all of them.",
1028
+ "value": { "type": "string | undefined" }
1029
+ },
1030
+ {
1031
+ "name": "face",
1032
+ "description": "Which embedded face to set the caption in — `'display'` (the Chicago-style\nchrome face, the default) or `'body'` (Geneva). Applied by\n`vfStaticText`; declared here so it types and reflects.",
1033
+ "value": { "type": "'display' | 'body' | undefined" }
1034
+ },
1035
+ {
1036
+ "name": "dim",
1037
+ "description": "Greys the caption to `--vf-disabled`. System 7 dims the label, not the\ncontrol (SPEC §1), so this is what a caption beside a disabled control\nwears — the control keeps its solid black box.",
1038
+ "value": { "type": "boolean", "default": "false" }
1039
+ },
1040
+ {
1041
+ "name": "width",
1042
+ "description": "Width in whole system px. What the number means is the component's own\naffair — the measure a paragraph wraps to, a caption column, a window's\nbox — but the mechanics are shared: the length lands on the host as a\nlive `calc(var(--vf-scale, 1) * Npx)`, so the box scales with the\ndisplay and sits on the device-pixel grid by construction. Remove it\nand the width returns to layout.",
1043
+ "value": { "type": "number | null | undefined" }
1044
+ },
1045
+ {
1046
+ "name": "height",
1047
+ "description": "Height in whole system px; see width. Content that outgrows a\ndeclared height overflows the box rather than growing it — the number\nis the layout, and content that doesn't fit is a number to raise.",
1048
+ "value": { "type": "number | null | undefined" }
1049
+ },
1050
+ {
1051
+ "name": "top",
1052
+ "description": "Offset from the top of the positioning parent, in whole system px.\nSetting this (or `left`) absolutely positions the element within its\nparent; the coordinate left unset is 0. Remove both to return the\nelement to normal flow.",
1053
+ "value": { "type": "number | null | undefined" }
1054
+ },
1055
+ {
1056
+ "name": "left",
1057
+ "description": "Offset from the left of the positioning parent, in whole system px.\nSee top.",
1058
+ "value": { "type": "number | null | undefined" }
1059
+ }
1060
+ ],
1061
+ "slots": [{ "name": "", "description": "The caption text." }],
1062
+ "events": [],
1063
+ "js": {
1064
+ "properties": [
1065
+ {
1066
+ "name": "for",
1067
+ "description": "The id of the control this caption names, resolved in the label's own tree\nscope (its document or shadow root), like a native `<label for>`.\n\nTwo things follow from it. **Clicking the caption focuses the control** —\na focus shortcut, not an activation: the kit's toggles carry their own\nlabels, so forwarding a click to one would double up on the label they\nalready have. And **the caption text becomes the control's accessible\nname**, by whichever route reaches it:\n\n- a `vf-*` control's focusable element lives in its shadow root, where an\n `aria-label` on the host cannot reach it — which is why each of them\n exposes a `label` property that lands on the inner control. That property\n is what gets filled in, and only when the consumer left it empty.\n- anything else (a native `<input>`, an element with a role) is in this\n label's own tree scope, so an `aria-labelledby` id reference works; it is\n set only when the target has no name of its own — and a name computed\n from the target's *content* counts: a `vf-checkbox` with slotted text, a\n `vf-button`, a native `<button>` are already named by what they show, and\n the caption declines rather than stamping over it.\n\nThe one target neither route reaches is a control whose focusable element\nis shadow-internal but which exposes no `label` property because it names\nfrom content — `vf-button` is the kit's case. With visible text it needs\nno caption; an icon-only one should carry its name on the art (`alt` on\nthe slotted `<img>`), which name-from-content picks up the same way.\n\nEither way the label puts back what it found when it is removed, the id\nchanges, or the caption text does.\n\nLeft `undefined` rather than `''` so an unset `for` reflects no attribute\nat all: `vf-label[for]` is a selector a page will reasonably write, and an\nempty `for=\"\"` on every plain caption would make it match all of them.",
1068
+ "type": "string | undefined"
1069
+ },
1070
+ {
1071
+ "name": "face",
1072
+ "description": "Which embedded face to set the caption in — `'display'` (the Chicago-style\nchrome face, the default) or `'body'` (Geneva). Applied by\n`vfStaticText`; declared here so it types and reflects.",
1073
+ "type": "'display' | 'body' | undefined"
1074
+ },
1075
+ {
1076
+ "name": "dim",
1077
+ "description": "Greys the caption to `--vf-disabled`. System 7 dims the label, not the\ncontrol (SPEC §1), so this is what a caption beside a disabled control\nwears — the control keeps its solid black box.",
1078
+ "type": "boolean"
1079
+ },
1080
+ {
1081
+ "name": "width",
1082
+ "description": "Width in whole system px. What the number means is the component's own\naffair — the measure a paragraph wraps to, a caption column, a window's\nbox — but the mechanics are shared: the length lands on the host as a\nlive `calc(var(--vf-scale, 1) * Npx)`, so the box scales with the\ndisplay and sits on the device-pixel grid by construction. Remove it\nand the width returns to layout.",
1083
+ "type": "number | null | undefined"
1084
+ },
1085
+ {
1086
+ "name": "height",
1087
+ "description": "Height in whole system px; see width. Content that outgrows a\ndeclared height overflows the box rather than growing it — the number\nis the layout, and content that doesn't fit is a number to raise.",
1088
+ "type": "number | null | undefined"
1089
+ },
1090
+ {
1091
+ "name": "top",
1092
+ "description": "Offset from the top of the positioning parent, in whole system px.\nSetting this (or `left`) absolutely positions the element within its\nparent; the coordinate left unset is 0. Remove both to return the\nelement to normal flow.",
1093
+ "type": "number | null | undefined"
1094
+ },
1095
+ {
1096
+ "name": "left",
1097
+ "description": "Offset from the left of the positioning parent, in whole system px.\nSee top.",
1098
+ "type": "number | null | undefined"
1099
+ }
1100
+ ],
1101
+ "events": []
1102
+ }
1103
+ },
1104
+ {
1105
+ "name": "vf-list-item",
1106
+ "description": "`<vf-list-item>` — a row inside a `<vf-list>` list box.\n\nA 20px-tall single-line row; when selected the entire row inverts\n(white-on-black), the classic System 7 selection style. Selection and\nkeyboard focus are managed by the parent `<vf-list>`.\n---\n\n\n### **Slots:**\n - _default_ - The row's text/content.\n- **icon** - A leading graphic — usually a `vf-img` holding a 16×16 System 7 small icon, but any element rides here. The row lays it out as the icon gutter: flex-centered vertically (keep the difference between the row height and the icon height even, so the centering offset stays a whole pixel — 16 in a 20px row is a whole 2px), with the reference art's 4px gap to the text. Contributes no text, so first-letter type-ahead still reads the row's words. On a selected row the graphic rides the inverted bar as-is — System 7 left color icons unfiltered on the highlight.\n\n### **CSS Parts:**\n - **text** - The text span beside the icon gutter (ellipsizes).",
1107
+ "doc-url": "",
1108
+ "attributes": [
1109
+ {
1110
+ "name": "value",
1111
+ "description": "The value this row contributes to the list's `value`/`values`.",
1112
+ "value": { "type": "string", "default": "''" }
1113
+ },
1114
+ {
1115
+ "name": "selected",
1116
+ "description": "Whether the row is selected (inverted). Managed by `<vf-list>`.",
1117
+ "value": { "type": "boolean", "default": "false" }
1118
+ },
1119
+ {
1120
+ "name": "disabled",
1121
+ "description": "Disables the row: dimmed text, not selectable or focusable.",
1122
+ "value": { "type": "boolean", "default": "false" }
1123
+ }
1124
+ ],
1125
+ "slots": [
1126
+ { "name": "", "description": "The row's text/content." },
1127
+ {
1128
+ "name": "icon",
1129
+ "description": "A leading graphic — usually a `vf-img` holding a 16×16 System 7 small icon, but any element rides here. The row lays it out as the icon gutter: flex-centered vertically (keep the difference between the row height and the icon height even, so the centering offset stays a whole pixel — 16 in a 20px row is a whole 2px), with the reference art's 4px gap to the text. Contributes no text, so first-letter type-ahead still reads the row's words. On a selected row the graphic rides the inverted bar as-is — System 7 left color icons unfiltered on the highlight."
1130
+ }
1131
+ ],
1132
+ "events": [],
1133
+ "js": {
1134
+ "properties": [
1135
+ {
1136
+ "name": "value",
1137
+ "description": "The value this row contributes to the list's `value`/`values`.",
1138
+ "type": "string"
1139
+ },
1140
+ {
1141
+ "name": "selected",
1142
+ "description": "Whether the row is selected (inverted). Managed by `<vf-list>`.",
1143
+ "type": "boolean"
1144
+ },
1145
+ {
1146
+ "name": "disabled",
1147
+ "description": "Disables the row: dimmed text, not selectable or focusable.",
1148
+ "type": "boolean"
1149
+ },
1150
+ {
1151
+ "name": "listDisabled",
1152
+ "description": "True while the containing `<vf-list>` is disabled. Managed by the list —\nnot intended to be set by consumers. Kept distinct from `disabled` so\nre-enabling the list doesn't clear rows that are disabled in their own\nright. (The dimming already arrives by inheritance: the list host sets\n`color: var(--vf-disabled)` and rows inherit it.) Mirrors\n`vf-radio.groupDisabled`.",
1153
+ "type": "boolean"
1154
+ },
1155
+ { "name": "role", "type": "string" }
1156
+ ],
1157
+ "events": []
1158
+ }
1159
+ },
1160
+ {
1161
+ "name": "vf-list",
1162
+ "description": "`<vf-list>` — the classic System 7 list box.\n\nA white, black-bordered scrolling box of `<vf-list-item>` rows with the\nkit-drawn System 7 scroll rail (dither trough, boxed arrow buttons, fixed\nthumb — the shared `vfScrollRail` subtree, synced to the native scrolling\nby ScrollRailController). The vertical rail is a permanent\nplaceholder — arrows on an empty white channel when the rows fit, the\ndither and thumb filling in only on overflow (driven by\nScrollStateController). Selection inverts rows. Supports single and\nmultiple selection, roving tabindex, arrow-key navigation, and classic Finder\nfirst-letter type-ahead.\n\nMax height defaults to 200px; override with `--vf-list-max-height`.\n---\n\n\n### **Events:**\n - **vf-change** - When the user changes the selection. `detail: { value: string, values: string[] }`.\n\n### **Slots:**\n - _default_ - `vf-list-item` elements.\n\n### **CSS Properties:**\n - **--vf-list-max-height** - `vf-list` max height before its rail takes over (the host adds the 2px frame) _(default: 200px)_\n- **--vf-scrollbar-thumb** - scrollbar thumb/elevator (white) _(default: undefined)_\n- **--vf-scrollbar-track** - the scroll trough's base color under the dot-dither (white) _(default: undefined)_\n\n### **CSS Parts:**\n - **list** - The scrolling viewport around the slotted items.",
1163
+ "doc-url": "",
1164
+ "attributes": [
1165
+ {
1166
+ "name": "multiple",
1167
+ "description": "Allows multiple selection (Shift extends, Cmd/Ctrl toggles).",
1168
+ "value": { "type": "boolean", "default": "false" }
1169
+ },
1170
+ {
1171
+ "name": "value",
1172
+ "description": "Value of the (first) selected item. Settable.",
1173
+ "value": { "type": "string", "default": "''" }
1174
+ },
1175
+ {
1176
+ "name": "disabled",
1177
+ "description": "Disables the whole list: dimmed, no interaction.",
1178
+ "value": { "type": "boolean", "default": "false" }
1179
+ },
1180
+ {
1181
+ "name": "label",
1182
+ "description": "Accessible name for the list, applied as `aria-label` on the host (which\ncarries `role=\"listbox\"`). Without it a caption-less list is announced\nanonymously. A consumer-supplied `aria-label`/`aria-labelledby` attribute\nis left alone.",
1183
+ "value": { "type": "string", "default": "''" }
1184
+ },
1185
+ {
1186
+ "name": "top",
1187
+ "description": "Offset from the top of the positioning parent, in whole system px.\nSetting this (or `left`) absolutely positions the element within its\nparent; the coordinate left unset is 0. Remove both to return the\nelement to normal flow.",
1188
+ "value": { "type": "number | null | undefined" }
1189
+ },
1190
+ {
1191
+ "name": "left",
1192
+ "description": "Offset from the left of the positioning parent, in whole system px.\nSee top.",
1193
+ "value": { "type": "number | null | undefined" }
1194
+ }
1195
+ ],
1196
+ "slots": [{ "name": "", "description": "`vf-list-item` elements." }],
1197
+ "events": [
1198
+ {
1199
+ "name": "vf-change",
1200
+ "description": "When the user changes the selection. `detail: { value: string, values: string[] }`."
1201
+ }
1202
+ ],
1203
+ "js": {
1204
+ "properties": [
1205
+ {
1206
+ "name": "multiple",
1207
+ "description": "Allows multiple selection (Shift extends, Cmd/Ctrl toggles).",
1208
+ "type": "boolean"
1209
+ },
1210
+ {
1211
+ "name": "value",
1212
+ "description": "Value of the (first) selected item. Settable.",
1213
+ "type": "string"
1214
+ },
1215
+ {
1216
+ "name": "values",
1217
+ "description": "Values of all selected items (multiple mode). Settable.",
1218
+ "type": "string[]"
1219
+ },
1220
+ {
1221
+ "name": "disabled",
1222
+ "description": "Disables the whole list: dimmed, no interaction.",
1223
+ "type": "boolean"
1224
+ },
1225
+ {
1226
+ "name": "label",
1227
+ "description": "Accessible name for the list, applied as `aria-label` on the host (which\ncarries `role=\"listbox\"`). Without it a caption-less list is announced\nanonymously. A consumer-supplied `aria-label`/`aria-labelledby` attribute\nis left alone.",
1228
+ "type": "string"
1229
+ },
1230
+ { "name": "role", "type": "string" },
1231
+ {
1232
+ "name": "top",
1233
+ "description": "Offset from the top of the positioning parent, in whole system px.\nSetting this (or `left`) absolutely positions the element within its\nparent; the coordinate left unset is 0. Remove both to return the\nelement to normal flow.",
1234
+ "type": "number | null | undefined"
1235
+ },
1236
+ {
1237
+ "name": "left",
1238
+ "description": "Offset from the left of the positioning parent, in whole system px.\nSee top.",
1239
+ "type": "number | null | undefined"
1240
+ }
1241
+ ],
1242
+ "events": [
1243
+ {
1244
+ "name": "vf-change",
1245
+ "description": "When the user changes the selection. `detail: { value: string, values: string[] }`."
1246
+ }
1247
+ ]
1248
+ }
1249
+ },
1250
+ {
1251
+ "name": "vf-menu-bar",
1252
+ "description": "`<vf-menu-bar>` — the System 7 menu bar: white strip, 1px black bottom\nrule, slotted `<vf-menu>` children laid out from the left. `rounded` adds\nthe classic screen-corner mask to the top corners.\n\nCoordinates its menus: pressing a label opens that menu (and inverts the\nlabel); while any menu is open, hovering another label switches to it;\nEscape, an outside click, or item selection closes. ArrowLeft/ArrowRight\nmove between menus while one is open; ArrowDown/ArrowUp move focus through\nthe open menu's items, Home/End jump to its first/last, and typed letters\nrun the shared Finder first-letter type-ahead (src/type-ahead.ts).\n\nThe bar also owns the **press-drag-release** gesture (see src/menu-press.ts)\n— press a title, slide onto a command, release over it — because one press\nmay travel across several of its menus. `vf-select` drives its popup by the\nsame mechanic.\n---\n\n\n### **Slots:**\n - _default_ - `vf-menu` elements.\n\n### **CSS Properties:**\n - **--vf-menubar-height** - `vf-menu-bar` _(default: 20px)_\n\n### **CSS Parts:**\n - **bar** - The horizontal layout container.",
1253
+ "doc-url": "",
1254
+ "attributes": [
1255
+ {
1256
+ "name": "label",
1257
+ "description": "Accessible name for the bar, applied as `aria-label` on the host (which\ncarries `role=\"menubar\"`). Without it the menubar computes with an empty\nname. A consumer-supplied `aria-label`/`aria-labelledby` attribute is\nleft alone.",
1258
+ "value": { "type": "string", "default": "''" }
1259
+ },
1260
+ {
1261
+ "name": "rounded",
1262
+ "description": "Rounds the bar's top corners the way System 7's were: not a curve of the\nbar itself but the black stair-step mask the compact Mac's ROM painted\nover the CRT's rounded corners, which the menu bar wore by sitting flush\nwith the screen top. Drawn as the traced 5-px staircase in `--vf-black`\nink over the bar's own corners — over any backdrop, as the hardware\nmask was.",
1263
+ "value": { "type": "boolean", "default": "false" }
1264
+ },
1265
+ {
1266
+ "name": "top",
1267
+ "description": "Offset from the top of the positioning parent, in whole system px.\nSetting this (or `left`) absolutely positions the element within its\nparent; the coordinate left unset is 0. Remove both to return the\nelement to normal flow.",
1268
+ "value": { "type": "number | null | undefined" }
1269
+ },
1270
+ {
1271
+ "name": "left",
1272
+ "description": "Offset from the left of the positioning parent, in whole system px.\nSee top.",
1273
+ "value": { "type": "number | null | undefined" }
1274
+ }
1275
+ ],
1276
+ "slots": [{ "name": "", "description": "`vf-menu` elements." }],
1277
+ "events": [],
1278
+ "js": {
1279
+ "properties": [
1280
+ {
1281
+ "name": "label",
1282
+ "description": "Accessible name for the bar, applied as `aria-label` on the host (which\ncarries `role=\"menubar\"`). Without it the menubar computes with an empty\nname. A consumer-supplied `aria-label`/`aria-labelledby` attribute is\nleft alone.",
1283
+ "type": "string"
1284
+ },
1285
+ {
1286
+ "name": "rounded",
1287
+ "description": "Rounds the bar's top corners the way System 7's were: not a curve of the\nbar itself but the black stair-step mask the compact Mac's ROM painted\nover the CRT's rounded corners, which the menu bar wore by sitting flush\nwith the screen top. Drawn as the traced 5-px staircase in `--vf-black`\nink over the bar's own corners — over any backdrop, as the hardware\nmask was.",
1288
+ "type": "boolean"
1289
+ },
1290
+ {
1291
+ "name": "top",
1292
+ "description": "Offset from the top of the positioning parent, in whole system px.\nSetting this (or `left`) absolutely positions the element within its\nparent; the coordinate left unset is 0. Remove both to return the\nelement to normal flow.",
1293
+ "type": "number | null | undefined"
1294
+ },
1295
+ {
1296
+ "name": "left",
1297
+ "description": "Offset from the left of the positioning parent, in whole system px.\nSee top.",
1298
+ "type": "number | null | undefined"
1299
+ }
1300
+ ],
1301
+ "events": []
1302
+ }
1303
+ },
1304
+ {
1305
+ "name": "vf-menu-item",
1306
+ "description": "`<vf-menu-item>` — a single command inside a `<vf-menu>` panel.\n\nRenders the classic System 7 menu row: optional ✓ check in the\n`--vf-select-gutter` left column (16px, shared with vf-select/vf-option),\nlabel, and the keyboard shortcut left-aligned in a right-anchored column,\nso every ⌘ lands at the same x. On activation the item performs the classic\n3-blink inversion (~250ms), then dispatches `vf-menu-select` and asks its\nancestors to close the menu.\n---\n\n\n### **Events:**\n - **vf-menu-select** - After the blink completes. `detail: { value, item }`. Named for the menu rather than plain `vf-select`, which would collide with the `<vf-select>` popup on any delegated ancestor listener (that component commits with `vf-change`).\n\n### **Slots:**\n - _default_ - The item label.\n\n### **CSS Properties:**\n - **--vf-menu-row-height** - `vf-menu-item` row pitch (`Menus.png`; kept separate from `--vf-popup-height` so re-theming the popup pill doesn't move pulldown rows) _(default: 16px)_\n- **--vf-menu-shortcut-column** - `vf-menu-item` shortcut slot, right-anchored with the text left-aligned in it so every ⌘ lands at the same x (`Menus.png`) — the MDEF reserve, ⌘'s 11px advance + the face's widest letter (M/W, 12px); widen it to line up longer shortcuts (\"⌘⇧S\") _(default: 23px)_\n- **--vf-select-gutter** - checkmark column: `vf-select` left inset / `vf-option` + `vf-menu-item` ✓ column (shared so the value doesn't shift on open) _(default: 16px)_\n\n### **CSS Parts:**\n - **item** - The row container.\n- **check** - The ✓ checkmark glyph (rendered when `checked`).\n- **label** - The label wrapper around the default slot.\n- **shortcut** - The shortcut text, left-aligned in the shared column.",
1307
+ "doc-url": "",
1308
+ "attributes": [
1309
+ {
1310
+ "name": "disabled",
1311
+ "description": "Disables the item: dimmed text, no highlight, no activation.",
1312
+ "value": { "type": "boolean", "default": "false" }
1313
+ },
1314
+ {
1315
+ "name": "checked",
1316
+ "description": "Shows the classic ✓ checkmark in the left gutter.",
1317
+ "value": { "type": "boolean", "default": "false" }
1318
+ },
1319
+ {
1320
+ "name": "active",
1321
+ "description": "Transient highlight — the full-row inversion the press-drag gesture paints\non the row under the pointer (`:hover` can't: under touch the pointer is\ncaptured by the title the press started on). Managed by the menu, mirroring\n`vf-option[active]`; not part of the authoring API.",
1322
+ "value": { "type": "boolean", "default": "false" }
1323
+ },
1324
+ {
1325
+ "name": "checkable",
1326
+ "description": "Declares the item a *checkable* toggle up front, so it carries\n`role=\"menuitemcheckbox\"` and `aria-checked=\"false\"` from the first render\nrather than only once it has been checked. Set this on a toggle that starts\n**off** — otherwise it announces as a plain command until the first flip\n(the `checked` attribute can't express \"checkable but off\": for a boolean\nattribute, presence *is* true).",
1327
+ "value": { "type": "boolean", "default": "false" }
1328
+ },
1329
+ {
1330
+ "name": "shortcut",
1331
+ "description": "Keyboard shortcut text, e.g. `\"⌘H\"`, drawn left-aligned in the panel's\nshared shortcut column. Display only in the\nvisual sense — the span is `aria-hidden` so the glyphs never concatenate\ninto the item's accessible name (\"Print… place of interest sign P\") —\nwhile the host mirrors it as `aria-keyshortcuts` (\"Meta+H\"), so AT\nannounces it *as* a shortcut. A consumer's own `aria-keyshortcuts` wins.",
1332
+ "value": { "type": "string", "default": "''" }
1333
+ },
1334
+ {
1335
+ "name": "value",
1336
+ "description": "Value reported in the `vf-menu-select` event detail. Defaults to the item's\ntrimmed text content when unset.",
1337
+ "value": { "type": "string | undefined" }
1338
+ }
1339
+ ],
1340
+ "slots": [{ "name": "", "description": "The item label." }],
1341
+ "events": [
1342
+ {
1343
+ "name": "vf-menu-select",
1344
+ "description": "After the blink completes. `detail: { value, item }`. Named for the menu rather than plain `vf-select`, which would collide with the `<vf-select>` popup on any delegated ancestor listener (that component commits with `vf-change`)."
1345
+ }
1346
+ ],
1347
+ "js": {
1348
+ "properties": [
1349
+ {
1350
+ "name": "disabled",
1351
+ "description": "Disables the item: dimmed text, no highlight, no activation.",
1352
+ "type": "boolean"
1353
+ },
1354
+ {
1355
+ "name": "checked",
1356
+ "description": "Shows the classic ✓ checkmark in the left gutter.",
1357
+ "type": "boolean"
1358
+ },
1359
+ {
1360
+ "name": "active",
1361
+ "description": "Transient highlight — the full-row inversion the press-drag gesture paints\non the row under the pointer (`:hover` can't: under touch the pointer is\ncaptured by the title the press started on). Managed by the menu, mirroring\n`vf-option[active]`; not part of the authoring API.",
1362
+ "type": "boolean"
1363
+ },
1364
+ {
1365
+ "name": "checkable",
1366
+ "description": "Declares the item a *checkable* toggle up front, so it carries\n`role=\"menuitemcheckbox\"` and `aria-checked=\"false\"` from the first render\nrather than only once it has been checked. Set this on a toggle that starts\n**off** — otherwise it announces as a plain command until the first flip\n(the `checked` attribute can't express \"checkable but off\": for a boolean\nattribute, presence *is* true).",
1367
+ "type": "boolean"
1368
+ },
1369
+ {
1370
+ "name": "shortcut",
1371
+ "description": "Keyboard shortcut text, e.g. `\"⌘H\"`, drawn left-aligned in the panel's\nshared shortcut column. Display only in the\nvisual sense — the span is `aria-hidden` so the glyphs never concatenate\ninto the item's accessible name (\"Print… place of interest sign P\") —\nwhile the host mirrors it as `aria-keyshortcuts` (\"Meta+H\"), so AT\nannounces it *as* a shortcut. A consumer's own `aria-keyshortcuts` wins.",
1372
+ "type": "string"
1373
+ },
1374
+ {
1375
+ "name": "value",
1376
+ "description": "Value reported in the `vf-menu-select` event detail. Defaults to the item's\ntrimmed text content when unset.",
1377
+ "type": "string | undefined"
1378
+ }
1379
+ ],
1380
+ "events": [
1381
+ {
1382
+ "name": "vf-menu-select",
1383
+ "description": "After the blink completes. `detail: { value, item }`. Named for the menu rather than plain `vf-select`, which would collide with the `<vf-select>` popup on any delegated ancestor listener (that component commits with `vf-change`)."
1384
+ }
1385
+ ]
1386
+ }
1387
+ },
1388
+ {
1389
+ "name": "vf-menu",
1390
+ "description": "`<vf-menu>` — a pull-down menu: a bar label plus a dropped panel of\n`<vf-menu-item>` / `<vf-separator>` children.\n\nInside a `<vf-menu-bar>` the bar coordinates open state (only one menu open,\nhover-switching, outside-click/Escape dismissal) and owns the pointer\ngesture, which may travel between its menus. Used standalone, the menu\ntoggles itself on label click and manages its own dismissal, its own press\ngesture and item keyboard navigation (ArrowUp/ArrowDown, Home/End, and the\nshared first-letter type-ahead — src/type-ahead.ts) while open.\n\nPointer — the two styles `vf-select` supports, on the same terms (see\nsrc/menu-press.ts): the System 7 press-drag-release (press the title, slide\nonto a command, release over it) and a modern quick tap that leaves the menu\ndropped for a second click.\n---\n\n\n### **Slots:**\n - _default_ - Menu contents: `vf-menu-item` and `vf-separator` elements.\n- **label** - Replaces the `label` text in the bar — e.g. a `vf-img` apple icon for the Apple menu. Keep the `label` attribute set too: it stays the menu's accessible name (the bar item's `aria-label` and the panel's) when the visible title is an image.\n\n### **CSS Properties:**\n - **--vf-menubar-height** - `vf-menu-bar` _(default: 20px)_\n- **--vf-menu-row-height** - `vf-menu-item` row pitch; the panel also spends one of these on every slotted `vf-separator`, the full row the MDEF gave a divider (rule 8px into it — see the panel CSS) _(default: 16px)_\n\n### **CSS Parts:**\n - **label** - The menu title in the bar (inverts while open).\n- **panel** - The dropped `.vf-panel` containing the items.",
1391
+ "doc-url": "",
1392
+ "attributes": [
1393
+ {
1394
+ "name": "label",
1395
+ "description": "The menu title shown in the bar, and the menu's accessible name. Slotted\n`label` content replaces it visually (see the `label` slot) but this text\nkeeps naming the menu for AT.",
1396
+ "value": { "type": "string", "default": "''" }
1397
+ },
1398
+ {
1399
+ "name": "open",
1400
+ "description": "Whether the panel is dropped. Reflected. Managed by the parent\n`vf-menu-bar` when present, otherwise by the menu itself.",
1401
+ "value": { "type": "boolean", "default": "false" }
1402
+ }
1403
+ ],
1404
+ "slots": [
1405
+ {
1406
+ "name": "",
1407
+ "description": "Menu contents: `vf-menu-item` and `vf-separator` elements."
1408
+ },
1409
+ {
1410
+ "name": "label",
1411
+ "description": "Replaces the `label` text in the bar — e.g. a `vf-img` apple icon for the Apple menu. Keep the `label` attribute set too: it stays the menu's accessible name (the bar item's `aria-label` and the panel's) when the visible title is an image."
1412
+ }
1413
+ ],
1414
+ "events": [],
1415
+ "js": {
1416
+ "properties": [
1417
+ {
1418
+ "name": "label",
1419
+ "description": "The menu title shown in the bar, and the menu's accessible name. Slotted\n`label` content replaces it visually (see the `label` slot) but this text\nkeeps naming the menu for AT.",
1420
+ "type": "string"
1421
+ },
1422
+ {
1423
+ "name": "open",
1424
+ "description": "Whether the panel is dropped. Reflected. Managed by the parent\n`vf-menu-bar` when present, otherwise by the menu itself.",
1425
+ "type": "boolean"
1426
+ },
1427
+ {
1428
+ "name": "barTabIndex",
1429
+ "description": "The bar label's tabindex. A parent `vf-menu-bar` owns a roving tabindex\nacross its menus and sets this to 0 on the active menu, -1 on the rest,\nso the whole bar is a single Tab stop. Defaults to 0 so a standalone\nmenu is Tab-focusable on its own.",
1430
+ "type": "number"
1431
+ },
1432
+ {
1433
+ "name": "items",
1434
+ "description": "The enabled `vf-menu-item` children, in document order.",
1435
+ "type": "VfMenuItem[]"
1436
+ },
1437
+ {
1438
+ "name": "allItems",
1439
+ "description": "Every slotted `vf-menu-item`, in document order — **disabled rows\nincluded**, unlike items. The press gesture hit-tests against\nthese: releasing over a disabled row has to cancel, not fall through to\nwhatever the panel covers.",
1440
+ "type": "VfMenuItem[]"
1441
+ },
1442
+ {
1443
+ "name": "labelRect",
1444
+ "description": "Viewport rect of the bar title, or `null` before the first render. The\npress gesture hit-tests by coordinates rather than by event target (see\nsrc/menu-press.ts), so it wants the box, not the element.",
1445
+ "type": "DOMRect | null"
1446
+ }
1447
+ ],
1448
+ "events": []
1449
+ }
1450
+ },
1451
+ {
1452
+ "name": "vf-number-field",
1453
+ "description": "`<vf-number-field>` — a System 7 numeric entry field paired with the classic\n\"little arrows\" stepper.\n\nA form-associated wrapper around a native `<input>` (white well, 1px black\nborder) plus the pixel-exact little-arrows control from the Classic Macintosh\nUI Kit sprite. Clicking (or press-and-holding, with autorepeat) an arrow\nsteps the value by `step`, clamped to `min`/`max`; the held arrow fills solid\nblack, matching the kit's hollow→filled press convention. Keyboard focus\ndraws the kit's dashed rule under the well alone — the stepper is beside the\ntext, not part of it. The field is a `spinbutton`: ArrowUp/ArrowDown step, Home/End\njump to min/max. The shared field skin lives in `vfField`; the value/form\nscaffolding in VfTextControlBase.\n---\n\n\n### **Events:**\n - **vf-input** - On every keystroke. `detail: { value, valueAsNumber }`.\n- **vf-change** - On commit or step. `detail: { value, valueAsNumber }`.\n- **input** - The native event: the inner input's own on a keystroke (composed, crosses the boundary itself), dispatched from the host on a step — a native number input's spinner fires it per click too.\n- **change** - The native commit event, dispatched from the host on a typed commit and on every step, as a native spinner does. The input-behavior attributes — `autocomplete`, `inputmode`, `enterkeyhint`, `maxlength`, `spellcheck`, `autocapitalize` — are forwarded from the host onto the inner input; unset, `inputmode` stays the numeric keypad's `decimal` and `autocomplete` stays `off`.\n\n### **CSS Properties:**\n - **--vf-control-height** - text fields — `vf-text-field`, `vf-text-area`, the `vf-number-field` well _(default: 22px)_\n- **--vf-number-field-width** - width of `vf-number-field`'s input, in its own text (an em, not a system px length — it sizes to the digits) _(default: 4em)_\n- **--vf-field-placeholder** - placeholder text in the editable fields — kept off `--vf-disabled`: a placeholder sits in an *enabled* well and holds AA contrast, where the disabled gray is exempt _(default: #767676)_\n\n### **CSS Parts:**\n - **input** - The inner native `<input>`.\n- **stepper** - The little-arrows control.",
1454
+ "doc-url": "",
1455
+ "attributes": [
1456
+ {
1457
+ "name": "min",
1458
+ "description": "Minimum allowed value (inclusive). Omit for no lower bound.",
1459
+ "value": { "type": "number | undefined" }
1460
+ },
1461
+ {
1462
+ "name": "max",
1463
+ "description": "Maximum allowed value (inclusive). Omit for no upper bound.",
1464
+ "value": { "type": "number | undefined" }
1465
+ },
1466
+ {
1467
+ "name": "step",
1468
+ "description": "Increment applied per step / arrow press. Also sets the value's precision.",
1469
+ "value": { "type": "number", "default": "1" }
1470
+ },
1471
+ {
1472
+ "name": "top",
1473
+ "description": "Offset from the top of the positioning parent, in whole system px.\nSetting this (or `left`) absolutely positions the element within its\nparent; the coordinate left unset is 0. Remove both to return the\nelement to normal flow.",
1474
+ "value": { "type": "number | null | undefined" }
1475
+ },
1476
+ {
1477
+ "name": "left",
1478
+ "description": "Offset from the left of the positioning parent, in whole system px.\nSee top.",
1479
+ "value": { "type": "number | null | undefined" }
1480
+ },
1481
+ {
1482
+ "name": "value",
1483
+ "description": "Current value. Synced on every keystroke and submitted with forms.",
1484
+ "value": { "type": "string", "default": "''" }
1485
+ },
1486
+ {
1487
+ "name": "placeholder",
1488
+ "description": "Placeholder text shown when the field is empty.",
1489
+ "value": { "type": "string", "default": "''" }
1490
+ },
1491
+ {
1492
+ "name": "readonly",
1493
+ "description": "Makes the field read-only (focusable, not editable).",
1494
+ "value": { "type": "boolean", "default": "false" }
1495
+ },
1496
+ {
1497
+ "name": "name",
1498
+ "description": "Form field name used when submitting the associated form.",
1499
+ "value": { "type": "string", "default": "''" }
1500
+ },
1501
+ {
1502
+ "name": "label",
1503
+ "description": "Accessible name, applied as `aria-label` on the inner native control (the\nelement that receives focus and is announced by screen readers — a host\nattribute cannot reach it on its own). Left empty, the name falls back to\nwhatever the host carries — `aria-labelledby`, `aria-label` or an\nassociated `<label for>` — via VfShadowRoleControl.hostLabel, so the\nplatform's own labeling vocabulary works on these controls too.",
1504
+ "value": { "type": "string", "default": "''" }
1505
+ },
1506
+ {
1507
+ "name": "description",
1508
+ "description": "Description for the control — hint text, a format, a unit. A host-level\n`aria-describedby` cannot reach a focusable element inside a shadow root,\nso there was structurally no way to describe a field; this property is\nthat channel. It renders as a hidden span in the control's own shadow root\nwith the inner control's `aria-describedby` pointing at it — the\nshadow-internal IDREF idiom `vf-dialog`'s title patch already uses. A\nhost-level `aria-describedby` is bridged into the same span when this\nproperty is empty, and a failing constraint's validationMessage\njoins it too, so AT hears the error where it hears the hint.\n\nHost-role controls get neither half: their `aria-describedby` already\nworks, and their validation message reaches AT the way a native control's\ndoes — `aria-invalid` plus the browser's own validation UI, not AccName.",
1509
+ "value": { "type": "string", "default": "''" }
1510
+ },
1511
+ {
1512
+ "name": "disabled",
1513
+ "description": "Disables the control: it stops responding and submits no value. Each\ncontrol dims per SPEC §1 (typically the label greys while the 1-bit chrome\nstays black).",
1514
+ "value": { "type": "boolean", "default": "false" }
1515
+ },
1516
+ {
1517
+ "name": "required",
1518
+ "description": "Requires a value before the associated form submits (SPEC §4): an empty\ncontrol fails constraint validation with `valueMissing`, exactly like a\nnative `required`. What \"empty\" means is each control's own\nvalueMissing; a control with no required semantics (a slider\nalways has a value, a swatch submits nothing) never fails it, the way a\nnative range input never does.",
1519
+ "value": { "type": "boolean", "default": "false" }
1520
+ }
1521
+ ],
1522
+ "events": [
1523
+ {
1524
+ "name": "vf-input",
1525
+ "description": "On every keystroke. `detail: { value, valueAsNumber }`."
1526
+ },
1527
+ {
1528
+ "name": "vf-change",
1529
+ "description": "On commit or step. `detail: { value, valueAsNumber }`."
1530
+ },
1531
+ {
1532
+ "name": "input",
1533
+ "description": "The native event: the inner input's own on a keystroke (composed, crosses the boundary itself), dispatched from the host on a step — a native number input's spinner fires it per click too."
1534
+ },
1535
+ {
1536
+ "name": "change",
1537
+ "description": "The native commit event, dispatched from the host on a typed commit and on every step, as a native spinner does. The input-behavior attributes — `autocomplete`, `inputmode`, `enterkeyhint`, `maxlength`, `spellcheck`, `autocapitalize` — are forwarded from the host onto the inner input; unset, `inputmode` stays the numeric keypad's `decimal` and `autocomplete` stays `off`."
1538
+ }
1539
+ ],
1540
+ "js": {
1541
+ "properties": [
1542
+ {
1543
+ "name": "min",
1544
+ "description": "Minimum allowed value (inclusive). Omit for no lower bound.",
1545
+ "type": "number | undefined"
1546
+ },
1547
+ {
1548
+ "name": "max",
1549
+ "description": "Maximum allowed value (inclusive). Omit for no upper bound.",
1550
+ "type": "number | undefined"
1551
+ },
1552
+ {
1553
+ "name": "step",
1554
+ "description": "Increment applied per step / arrow press. Also sets the value's precision.",
1555
+ "type": "number"
1556
+ },
1557
+ {
1558
+ "name": "top",
1559
+ "description": "Offset from the top of the positioning parent, in whole system px.\nSetting this (or `left`) absolutely positions the element within its\nparent; the coordinate left unset is 0. Remove both to return the\nelement to normal flow.",
1560
+ "type": "number | null | undefined"
1561
+ },
1562
+ {
1563
+ "name": "left",
1564
+ "description": "Offset from the left of the positioning parent, in whole system px.\nSee top.",
1565
+ "type": "number | null | undefined"
1566
+ },
1567
+ {
1568
+ "name": "value",
1569
+ "description": "Current value. Synced on every keystroke and submitted with forms.",
1570
+ "type": "string"
1571
+ },
1572
+ {
1573
+ "name": "placeholder",
1574
+ "description": "Placeholder text shown when the field is empty.",
1575
+ "type": "string"
1576
+ },
1577
+ {
1578
+ "name": "readonly",
1579
+ "description": "Makes the field read-only (focusable, not editable).",
1580
+ "type": "boolean"
1581
+ },
1582
+ {
1583
+ "name": "name",
1584
+ "description": "Form field name used when submitting the associated form.",
1585
+ "type": "string"
1586
+ },
1587
+ {
1588
+ "name": "label",
1589
+ "description": "Accessible name, applied as `aria-label` on the inner native control (the\nelement that receives focus and is announced by screen readers — a host\nattribute cannot reach it on its own). Left empty, the name falls back to\nwhatever the host carries — `aria-labelledby`, `aria-label` or an\nassociated `<label for>` — via VfShadowRoleControl.hostLabel, so the\nplatform's own labeling vocabulary works on these controls too.",
1590
+ "type": "string"
1591
+ },
1592
+ {
1593
+ "name": "description",
1594
+ "description": "Description for the control — hint text, a format, a unit. A host-level\n`aria-describedby` cannot reach a focusable element inside a shadow root,\nso there was structurally no way to describe a field; this property is\nthat channel. It renders as a hidden span in the control's own shadow root\nwith the inner control's `aria-describedby` pointing at it — the\nshadow-internal IDREF idiom `vf-dialog`'s title patch already uses. A\nhost-level `aria-describedby` is bridged into the same span when this\nproperty is empty, and a failing constraint's validationMessage\njoins it too, so AT hears the error where it hears the hint.\n\nHost-role controls get neither half: their `aria-describedby` already\nworks, and their validation message reaches AT the way a native control's\ndoes — `aria-invalid` plus the browser's own validation UI, not AccName.",
1595
+ "type": "string"
1596
+ },
1597
+ {
1598
+ "name": "disabled",
1599
+ "description": "Disables the control: it stops responding and submits no value. Each\ncontrol dims per SPEC §1 (typically the label greys while the 1-bit chrome\nstays black).",
1600
+ "type": "boolean"
1601
+ },
1602
+ {
1603
+ "name": "isDisabled",
1604
+ "description": "Effective disabled state: the `disabled` prop OR an ancestor fieldset.",
1605
+ "type": "boolean"
1606
+ },
1607
+ {
1608
+ "name": "required",
1609
+ "description": "Requires a value before the associated form submits (SPEC §4): an empty\ncontrol fails constraint validation with `valueMissing`, exactly like a\nnative `required`. What \"empty\" means is each control's own\nvalueMissing; a control with no required semantics (a slider\nalways has a value, a swatch submits nothing) never fails it, the way a\nnative range input never does.",
1610
+ "type": "boolean"
1611
+ },
1612
+ {
1613
+ "name": "validity",
1614
+ "description": "The control's current `ValidityState`, as on a native control.",
1615
+ "type": "ValidityState"
1616
+ },
1617
+ {
1618
+ "name": "validationMessage",
1619
+ "description": "The message of the currently failing constraint, `''` while valid.",
1620
+ "type": "string"
1621
+ },
1622
+ {
1623
+ "name": "willValidate",
1624
+ "description": "Whether the control is a candidate for constraint validation — false\nwhile disabled or readonly, per HTML's barring rules (the browser\ncomputes this from the reflected attributes).",
1625
+ "type": "boolean"
1626
+ }
1627
+ ],
1628
+ "events": [
1629
+ {
1630
+ "name": "vf-input",
1631
+ "description": "On every keystroke. `detail: { value, valueAsNumber }`."
1632
+ },
1633
+ {
1634
+ "name": "vf-change",
1635
+ "description": "On commit or step. `detail: { value, valueAsNumber }`."
1636
+ },
1637
+ {
1638
+ "name": "input",
1639
+ "description": "The native event: the inner input's own on a keystroke (composed, crosses the boundary itself), dispatched from the host on a step — a native number input's spinner fires it per click too."
1640
+ },
1641
+ {
1642
+ "name": "change",
1643
+ "description": "The native commit event, dispatched from the host on a typed commit and on every step, as a native spinner does. The input-behavior attributes — `autocomplete`, `inputmode`, `enterkeyhint`, `maxlength`, `spellcheck`, `autocapitalize` — are forwarded from the host onto the inner input; unset, `inputmode` stays the numeric keypad's `decimal` and `autocomplete` stays `off`."
1644
+ }
1645
+ ]
1646
+ }
1647
+ },
1648
+ {
1649
+ "name": "vf-option",
1650
+ "description": "`<vf-option>` — a single choice inside a `<vf-select>` popup menu.\n\nA light-DOM child of `<vf-select>` (slotted into the popup panel). Renders\nits slotted label at menu-item metrics (16px row — the pill's content height,\nso a selected option overlays the closed pill exactly; the left checkmark gutter is\n`--vf-select-gutter`, shared with the closed control's left inset so the value\ndoesn't shift on open). The parent select manages `selected` and the transient\n`active` highlight, and slots this element into its popup panel.\n\nThe host carries `role=\"option\"` with `aria-selected`/`aria-disabled`.\n---\n\n\n### **CSS Properties:**\n - **--vf-popup-height** - `vf-select` pill (border box; its 1px hard shadow makes the sheet's 157×19 ink box) _(default: 18px)_\n- **--vf-select-gutter** - checkmark column: `vf-select` left inset / `vf-option` + `vf-menu-item` ✓ column (shared so the value doesn't shift on open) _(default: 16px)_\n\n### **CSS Parts:**\n - **check** - The ✓ checkmark shown in the left gutter when selected.",
1651
+ "doc-url": "",
1652
+ "attributes": [
1653
+ {
1654
+ "name": "value",
1655
+ "description": "Submitted/compared value of this option. Falls back to the trimmed text\ncontent when empty (like a native `<option>`).",
1656
+ "value": { "type": "string", "default": "''" }
1657
+ },
1658
+ {
1659
+ "name": "disabled",
1660
+ "description": "Disables the option: gray text, not selectable.",
1661
+ "value": { "type": "boolean", "default": "false" }
1662
+ },
1663
+ {
1664
+ "name": "selected",
1665
+ "description": "Whether this option is the select's current value. Managed by the parent.",
1666
+ "value": { "type": "boolean", "default": "false" }
1667
+ },
1668
+ {
1669
+ "name": "active",
1670
+ "description": "Transient highlight (hover / keyboard cursor) — full-row inversion.\nManaged by the parent `<vf-select>`; not part of the authoring API.",
1671
+ "value": { "type": "boolean", "default": "false" }
1672
+ }
1673
+ ],
1674
+ "events": [],
1675
+ "js": {
1676
+ "properties": [
1677
+ {
1678
+ "name": "value",
1679
+ "description": "Submitted/compared value of this option. Falls back to the trimmed text\ncontent when empty (like a native `<option>`).",
1680
+ "type": "string"
1681
+ },
1682
+ {
1683
+ "name": "disabled",
1684
+ "description": "Disables the option: gray text, not selectable.",
1685
+ "type": "boolean"
1686
+ },
1687
+ {
1688
+ "name": "selected",
1689
+ "description": "Whether this option is the select's current value. Managed by the parent.",
1690
+ "type": "boolean"
1691
+ },
1692
+ {
1693
+ "name": "active",
1694
+ "description": "Transient highlight (hover / keyboard cursor) — full-row inversion.\nManaged by the parent `<vf-select>`; not part of the authoring API.",
1695
+ "type": "boolean"
1696
+ },
1697
+ { "name": "role", "type": "string" }
1698
+ ],
1699
+ "events": []
1700
+ }
1701
+ },
1702
+ {
1703
+ "name": "vf-paragraph",
1704
+ "description": "`<vf-paragraph>` — a paragraph of copy on the kit's body face and grid.\n\nThe installer's welcome text, a dialog's explanation, an article's prose:\nanything a page would otherwise set with a `<p>` and hope the leading landed\nsomewhere sensible. What it adds over that `<p>`:\n\n- the **Geneva body face** by default (`face=\"display\"` switches to\n the Chicago-style chrome face);\n- a **whole-system-pixel line box at the face's native pitch** —\n `--vf-paragraph-line-height`: 12px for the body face (Geneva 9's own\n strike line) and 16px under `face=\"display\"` (Chicago 12's — ascent 12 +\n descent 3 + leading 1, the pitch a real dialog wrapped its copy on).\n Whole pixels are the point of the component: line boxes are the single\n biggest source of off-grid layout, because a ratio resolves to whatever\n it resolves to (`1.65 × 17px = 28.05px`) and every line of prose nudges\n everything after it further off the device-pixel grid, smearing 1-bit\n borders and bitmap glyph stems (docs/SIZING.md rule 2). A\n whole-pixel line box accumulates whole offsets;\n- its own GridSnapController, so it holds its own origin once the\n page opts in with `applyGridSnap()`;\n- a **declared box** when the layout wants one — `width`/`height` in whole\n system px (VfSized). In flow a paragraph takes its container's\n width, which is usually right; a *placed* one (`top`/`left`) shrink-wraps\n its longest line instead — a fractional glyph-run width, wrapped wherever\n the parent's edge happens to fall — so a DITL-style layout states the\n measure the copy wraps to, whole and on the grid.\n\nThe shadow root renders a real `<p>`, so the copy keeps paragraph semantics\nfor assistive tech, and — unlike the kit's chrome — the text is selectable.\nThere is no margin: per SPEC §2 a component adds nothing outside its own box,\nso paragraph spacing is the page's (a `gap` on the column, or a margin on the\nhost — kept a whole number of pixels, like everything else in the contract).\n---\n\n\n### **Slots:**\n - _default_ - The paragraph copy.\n\n### **CSS Properties:**\n - **--vf-paragraph-line-height** - This paragraph kind's own line box, in system px. Unset (the default) the box follows the face tokens below; set, it overrides both faces for paragraphs alone. Keep any override a whole *even* number — a ratio is what puts a page off the grid in the first place, and half the difference to the 16px em is half-leading, so an odd value lands the baseline on a half pixel. _(default: undefined)_\n- **--vf-line-height** - The body face's native line (default `12px`, Geneva 9's) — the face-level knob: retheming the body face to another strike states this alongside `--vf-font-family` / `--vf-font-size`. _(default: undefined)_\n- **--vf-line-height-display** - The display face's native line (default `16px`, Chicago 12's) — the display retheme's third number. _(default: undefined)_\n\n### **CSS Parts:**\n - **paragraph** - The inner `<p>`.",
1705
+ "doc-url": "",
1706
+ "attributes": [
1707
+ {
1708
+ "name": "face",
1709
+ "description": "Which embedded face to set the copy in — `'body'` (Geneva, the\ndefault) or `'display'` (the Chicago-style chrome face, for copy that is\nreally dialog chrome). Applied by `vfStaticText`; declared here so it types\nand reflects.",
1710
+ "value": { "type": "'display' | 'body' | undefined" }
1711
+ },
1712
+ {
1713
+ "name": "dim",
1714
+ "description": "Greys the copy to `--vf-disabled` — System 7's dimmed static text.",
1715
+ "value": { "type": "boolean", "default": "false" }
1716
+ },
1717
+ {
1718
+ "name": "width",
1719
+ "description": "Width in whole system px. What the number means is the component's own\naffair — the measure a paragraph wraps to, a caption column, a window's\nbox — but the mechanics are shared: the length lands on the host as a\nlive `calc(var(--vf-scale, 1) * Npx)`, so the box scales with the\ndisplay and sits on the device-pixel grid by construction. Remove it\nand the width returns to layout.",
1720
+ "value": { "type": "number | null | undefined" }
1721
+ },
1722
+ {
1723
+ "name": "height",
1724
+ "description": "Height in whole system px; see width. Content that outgrows a\ndeclared height overflows the box rather than growing it — the number\nis the layout, and content that doesn't fit is a number to raise.",
1725
+ "value": { "type": "number | null | undefined" }
1726
+ },
1727
+ {
1728
+ "name": "top",
1729
+ "description": "Offset from the top of the positioning parent, in whole system px.\nSetting this (or `left`) absolutely positions the element within its\nparent; the coordinate left unset is 0. Remove both to return the\nelement to normal flow.",
1730
+ "value": { "type": "number | null | undefined" }
1731
+ },
1732
+ {
1733
+ "name": "left",
1734
+ "description": "Offset from the left of the positioning parent, in whole system px.\nSee top.",
1735
+ "value": { "type": "number | null | undefined" }
1736
+ }
1737
+ ],
1738
+ "slots": [{ "name": "", "description": "The paragraph copy." }],
1739
+ "events": [],
1740
+ "js": {
1741
+ "properties": [
1742
+ {
1743
+ "name": "face",
1744
+ "description": "Which embedded face to set the copy in — `'body'` (Geneva, the\ndefault) or `'display'` (the Chicago-style chrome face, for copy that is\nreally dialog chrome). Applied by `vfStaticText`; declared here so it types\nand reflects.",
1745
+ "type": "'display' | 'body' | undefined"
1746
+ },
1747
+ {
1748
+ "name": "dim",
1749
+ "description": "Greys the copy to `--vf-disabled` — System 7's dimmed static text.",
1750
+ "type": "boolean"
1751
+ },
1752
+ {
1753
+ "name": "width",
1754
+ "description": "Width in whole system px. What the number means is the component's own\naffair — the measure a paragraph wraps to, a caption column, a window's\nbox — but the mechanics are shared: the length lands on the host as a\nlive `calc(var(--vf-scale, 1) * Npx)`, so the box scales with the\ndisplay and sits on the device-pixel grid by construction. Remove it\nand the width returns to layout.",
1755
+ "type": "number | null | undefined"
1756
+ },
1757
+ {
1758
+ "name": "height",
1759
+ "description": "Height in whole system px; see width. Content that outgrows a\ndeclared height overflows the box rather than growing it — the number\nis the layout, and content that doesn't fit is a number to raise.",
1760
+ "type": "number | null | undefined"
1761
+ },
1762
+ {
1763
+ "name": "top",
1764
+ "description": "Offset from the top of the positioning parent, in whole system px.\nSetting this (or `left`) absolutely positions the element within its\nparent; the coordinate left unset is 0. Remove both to return the\nelement to normal flow.",
1765
+ "type": "number | null | undefined"
1766
+ },
1767
+ {
1768
+ "name": "left",
1769
+ "description": "Offset from the left of the positioning parent, in whole system px.\nSee top.",
1770
+ "type": "number | null | undefined"
1771
+ }
1772
+ ],
1773
+ "events": []
1774
+ }
1775
+ },
1776
+ {
1777
+ "name": "vf-progress-bar",
1778
+ "description": "`<vf-progress-bar>` — the System 7 progress indicator.\n\nA 14px white track (`--vf-progress-track`) with a 1px black border.\nDeterminate mode fills from the left in solid black\n(`--vf-progress-fill`) with a 1px black leading edge. Indeterminate\nmode shows chunky, stepped diagonal black/white barber stripes (the classic\n\"busy\" bar) animated with `steps()` timing so movement is deliberately\nsteppy, not smooth.\n\nExposes `role=\"progressbar\"` with `aria-valuemin/max` and, when\ndeterminate, `aria-valuenow`.\n---\n\n\n### **CSS Properties:**\n - **--vf-progress-fill** - determinate progress fill (solid black) _(default: #000000)_\n- **--vf-progress-stripes** - the indeterminate barber stripes — a 12×12 motif drawn as rects so the staircase stays whole system px at any scale, on a 60-system-px tile (override the whole tile — consumer art renders as a placed tile grid at that same geometry) _(default: undefined)_\n- **--vf-progress-track** - progress track (white) _(default: #ffffff)_\n\n### **CSS Parts:**\n - **track** - The outer bordered track.\n- **fill** - The determinate fill or the indeterminate stripe layer.",
1779
+ "doc-url": "",
1780
+ "attributes": [
1781
+ {
1782
+ "name": "value",
1783
+ "description": "Current progress, from 0 to `max`. Clamped for display and ARIA.",
1784
+ "value": { "type": "number", "default": "0" }
1785
+ },
1786
+ {
1787
+ "name": "max",
1788
+ "description": "Maximum value (default 100).",
1789
+ "value": { "type": "number", "default": "100" }
1790
+ },
1791
+ {
1792
+ "name": "indeterminate",
1793
+ "description": "Barber-pole \"busy\" mode; ignores `value` and omits `aria-valuenow`.",
1794
+ "value": { "type": "boolean", "default": "false" }
1795
+ },
1796
+ {
1797
+ "name": "label",
1798
+ "description": "Accessible name for the bar, applied as the host's `aria-label` (which\ncarries `role=\"progressbar\"`). Especially useful in `indeterminate` mode,\nwhere there is no `aria-valuenow` to describe the bar. A consumer-supplied\n`aria-label`/`aria-labelledby` attribute is left alone.",
1799
+ "value": { "type": "string", "default": "''" }
1800
+ },
1801
+ {
1802
+ "name": "top",
1803
+ "description": "Offset from the top of the positioning parent, in whole system px.\nSetting this (or `left`) absolutely positions the element within its\nparent; the coordinate left unset is 0. Remove both to return the\nelement to normal flow.",
1804
+ "value": { "type": "number | null | undefined" }
1805
+ },
1806
+ {
1807
+ "name": "left",
1808
+ "description": "Offset from the left of the positioning parent, in whole system px.\nSee top.",
1809
+ "value": { "type": "number | null | undefined" }
1810
+ }
1811
+ ],
1812
+ "events": [],
1813
+ "js": {
1814
+ "properties": [
1815
+ {
1816
+ "name": "value",
1817
+ "description": "Current progress, from 0 to `max`. Clamped for display and ARIA.",
1818
+ "type": "number"
1819
+ },
1820
+ {
1821
+ "name": "max",
1822
+ "description": "Maximum value (default 100).",
1823
+ "type": "number"
1824
+ },
1825
+ {
1826
+ "name": "indeterminate",
1827
+ "description": "Barber-pole \"busy\" mode; ignores `value` and omits `aria-valuenow`.",
1828
+ "type": "boolean"
1829
+ },
1830
+ {
1831
+ "name": "label",
1832
+ "description": "Accessible name for the bar, applied as the host's `aria-label` (which\ncarries `role=\"progressbar\"`). Especially useful in `indeterminate` mode,\nwhere there is no `aria-valuenow` to describe the bar. A consumer-supplied\n`aria-label`/`aria-labelledby` attribute is left alone.",
1833
+ "type": "string"
1834
+ },
1835
+ { "name": "role", "type": "string" },
1836
+ { "name": "ariaValueMin", "type": "string" },
1837
+ {
1838
+ "name": "top",
1839
+ "description": "Offset from the top of the positioning parent, in whole system px.\nSetting this (or `left`) absolutely positions the element within its\nparent; the coordinate left unset is 0. Remove both to return the\nelement to normal flow.",
1840
+ "type": "number | null | undefined"
1841
+ },
1842
+ {
1843
+ "name": "left",
1844
+ "description": "Offset from the left of the positioning parent, in whole system px.\nSee top.",
1845
+ "type": "number | null | undefined"
1846
+ }
1847
+ ],
1848
+ "events": []
1849
+ }
1850
+ },
1851
+ {
1852
+ "name": "vf-radio-group",
1853
+ "description": "Groups `vf-radio` children, owning selection, keyboard navigation and the\nform value. Renders no chrome of its own; directly slotted radios stack\nvertically with a 6px gap (override with your own layout if needed —\narbitrary markup containing radios also works).\n\nForm-associated: submits `value` under `name`, restores the initial value\non form reset. Keeps children in sync — the child whose `value` matches\nthe group's `value` is checked, all others unchecked.\n\nKeyboard (classic Mac behavior): the group is one tab stop (roving\ntabindex on the selected radio); ArrowUp/ArrowLeft and\nArrowDown/ArrowRight move the selection AND select it, wrapping around\nand skipping disabled radios.\n---\n\n\n### **Events:**\n - **vf-change** - When the selection changes via user interaction. `detail: { value: string }`.\n- **input** - Native event, dispatched from the group (the form-associated surface) per user pick. A programmatic `value` set fires nothing.\n- **change** - Native event, dispatched from the group per user pick so form delegation and framework bindings hear it.\n\n### **Slots:**\n - _default_ - `vf-radio` elements, or arbitrary markup containing them.",
1854
+ "doc-url": "",
1855
+ "attributes": [
1856
+ {
1857
+ "name": "value",
1858
+ "description": "The value of the selected radio. An empty string means no selection\n(and nothing is submitted with the form).",
1859
+ "value": { "type": "string", "default": "''" }
1860
+ },
1861
+ {
1862
+ "name": "name",
1863
+ "description": "Form field name used when submitting.",
1864
+ "value": { "type": "string", "default": "''" }
1865
+ },
1866
+ {
1867
+ "name": "label",
1868
+ "description": "Accessible name for the group, exposed as the `radiogroup`'s `aria-label`.\nWithout it a caption-less group is announced anonymously.",
1869
+ "value": { "type": "string", "default": "''" }
1870
+ },
1871
+ {
1872
+ "name": "top",
1873
+ "description": "Offset from the top of the positioning parent, in whole system px.\nSetting this (or `left`) absolutely positions the element within its\nparent; the coordinate left unset is 0. Remove both to return the\nelement to normal flow.",
1874
+ "value": { "type": "number | null | undefined" }
1875
+ },
1876
+ {
1877
+ "name": "left",
1878
+ "description": "Offset from the left of the positioning parent, in whole system px.\nSee top.",
1879
+ "value": { "type": "number | null | undefined" }
1880
+ },
1881
+ {
1882
+ "name": "disabled",
1883
+ "description": "Disables the control: it stops responding and submits no value. Each\ncontrol dims per SPEC §1 (typically the label greys while the 1-bit chrome\nstays black).",
1884
+ "value": { "type": "boolean", "default": "false" }
1885
+ },
1886
+ {
1887
+ "name": "required",
1888
+ "description": "Requires a value before the associated form submits (SPEC §4): an empty\ncontrol fails constraint validation with `valueMissing`, exactly like a\nnative `required`. What \"empty\" means is each control's own\nvalueMissing; a control with no required semantics (a slider\nalways has a value, a swatch submits nothing) never fails it, the way a\nnative range input never does.",
1889
+ "value": { "type": "boolean", "default": "false" }
1890
+ }
1891
+ ],
1892
+ "slots": [
1893
+ {
1894
+ "name": "",
1895
+ "description": "`vf-radio` elements, or arbitrary markup containing them."
1896
+ }
1897
+ ],
1898
+ "events": [
1899
+ {
1900
+ "name": "vf-change",
1901
+ "description": "When the selection changes via user interaction. `detail: { value: string }`."
1902
+ },
1903
+ {
1904
+ "name": "input",
1905
+ "description": "Native event, dispatched from the group (the form-associated surface) per user pick. A programmatic `value` set fires nothing."
1906
+ },
1907
+ {
1908
+ "name": "change",
1909
+ "description": "Native event, dispatched from the group per user pick so form delegation and framework bindings hear it."
1910
+ }
1911
+ ],
1912
+ "js": {
1913
+ "properties": [
1914
+ {
1915
+ "name": "value",
1916
+ "description": "The value of the selected radio. An empty string means no selection\n(and nothing is submitted with the form).",
1917
+ "type": "string"
1918
+ },
1919
+ {
1920
+ "name": "name",
1921
+ "description": "Form field name used when submitting.",
1922
+ "type": "string"
1923
+ },
1924
+ {
1925
+ "name": "label",
1926
+ "description": "Accessible name for the group, exposed as the `radiogroup`'s `aria-label`.\nWithout it a caption-less group is announced anonymously.",
1927
+ "type": "string"
1928
+ },
1929
+ { "name": "role", "type": "string" },
1930
+ {
1931
+ "name": "top",
1932
+ "description": "Offset from the top of the positioning parent, in whole system px.\nSetting this (or `left`) absolutely positions the element within its\nparent; the coordinate left unset is 0. Remove both to return the\nelement to normal flow.",
1933
+ "type": "number | null | undefined"
1934
+ },
1935
+ {
1936
+ "name": "left",
1937
+ "description": "Offset from the left of the positioning parent, in whole system px.\nSee top.",
1938
+ "type": "number | null | undefined"
1939
+ },
1940
+ {
1941
+ "name": "disabled",
1942
+ "description": "Disables the control: it stops responding and submits no value. Each\ncontrol dims per SPEC §1 (typically the label greys while the 1-bit chrome\nstays black).",
1943
+ "type": "boolean"
1944
+ },
1945
+ {
1946
+ "name": "isDisabled",
1947
+ "description": "Effective disabled state: the `disabled` prop OR an ancestor fieldset.",
1948
+ "type": "boolean"
1949
+ },
1950
+ {
1951
+ "name": "required",
1952
+ "description": "Requires a value before the associated form submits (SPEC §4): an empty\ncontrol fails constraint validation with `valueMissing`, exactly like a\nnative `required`. What \"empty\" means is each control's own\nvalueMissing; a control with no required semantics (a slider\nalways has a value, a swatch submits nothing) never fails it, the way a\nnative range input never does.",
1953
+ "type": "boolean"
1954
+ },
1955
+ {
1956
+ "name": "validity",
1957
+ "description": "The control's current `ValidityState`, as on a native control.",
1958
+ "type": "ValidityState"
1959
+ },
1960
+ {
1961
+ "name": "validationMessage",
1962
+ "description": "The message of the currently failing constraint, `''` while valid.",
1963
+ "type": "string"
1964
+ },
1965
+ {
1966
+ "name": "willValidate",
1967
+ "description": "Whether the control is a candidate for constraint validation — false\nwhile disabled or readonly, per HTML's barring rules (the browser\ncomputes this from the reflected attributes).",
1968
+ "type": "boolean"
1969
+ }
1970
+ ],
1971
+ "events": [
1972
+ {
1973
+ "name": "vf-change",
1974
+ "description": "When the selection changes via user interaction. `detail: { value: string }`."
1975
+ },
1976
+ {
1977
+ "name": "input",
1978
+ "description": "Native event, dispatched from the group (the form-associated surface) per user pick. A programmatic `value` set fires nothing."
1979
+ },
1980
+ {
1981
+ "name": "change",
1982
+ "description": "Native event, dispatched from the group per user pick so form delegation and framework bindings hear it."
1983
+ }
1984
+ ]
1985
+ }
1986
+ },
1987
+ {
1988
+ "name": "vf-radio",
1989
+ "description": "A single System 7 radio button: a 13×13 white circle with the pixel-exact\n1-bit ring and centered dot traced from the Classic Macintosh UI Kit sprite\n(replacing the anti-aliased `border-radius` rendering). The ring thickens\nwhile pressed, exactly like the original control.\n\nRadios are meant to live inside a `vf-radio-group`, which owns selection\nstate, the form value and the roving tabindex. A `vf-radio` is NOT itself\nform-associated. Clicking (or pressing Space on) an unselected radio\ndispatches `vf-change`; the containing group listens, selects it and\nunselects its siblings.\n---\n\n\n### **Events:**\n - **vf-change** - When selected by user interaction. `detail: { value: string }`.\n\n### **Slots:**\n - _default_ - The label, rendered to the right of the circle with a 6px gap.\n\n### **CSS Parts:**\n - **circle** - The 13×13 radio circle.\n- **label** - The label wrapper around the slot.",
1990
+ "doc-url": "",
1991
+ "attributes": [
1992
+ {
1993
+ "name": "checked",
1994
+ "description": "Whether this radio is selected. Managed by the containing group.",
1995
+ "value": { "type": "boolean", "default": "false" }
1996
+ },
1997
+ {
1998
+ "name": "disabled",
1999
+ "description": "Disables this single radio: the label dims to gray; circle and dot stay black.",
2000
+ "value": { "type": "boolean", "default": "false" }
2001
+ },
2002
+ {
2003
+ "name": "value",
2004
+ "description": "The value the containing `vf-radio-group` reports when selected.",
2005
+ "value": { "type": "string", "default": "''" }
2006
+ },
2007
+ {
2008
+ "name": "top",
2009
+ "description": "Offset from the top of the positioning parent, in whole system px.\nSetting this (or `left`) absolutely positions the element within its\nparent; the coordinate left unset is 0. Remove both to return the\nelement to normal flow.",
2010
+ "value": { "type": "number | null | undefined" }
2011
+ },
2012
+ {
2013
+ "name": "left",
2014
+ "description": "Offset from the left of the positioning parent, in whole system px.\nSee top.",
2015
+ "value": { "type": "number | null | undefined" }
2016
+ }
2017
+ ],
2018
+ "slots": [
2019
+ {
2020
+ "name": "",
2021
+ "description": "The label, rendered to the right of the circle with a 6px gap."
2022
+ }
2023
+ ],
2024
+ "events": [
2025
+ {
2026
+ "name": "vf-change",
2027
+ "description": "When selected by user interaction. `detail: { value: string }`."
2028
+ }
2029
+ ],
2030
+ "js": {
2031
+ "properties": [
2032
+ {
2033
+ "name": "checked",
2034
+ "description": "Whether this radio is selected. Managed by the containing group.",
2035
+ "type": "boolean"
2036
+ },
2037
+ {
2038
+ "name": "disabled",
2039
+ "description": "Disables this single radio: the label dims to gray; circle and dot stay black.",
2040
+ "type": "boolean"
2041
+ },
2042
+ {
2043
+ "name": "value",
2044
+ "description": "The value the containing `vf-radio-group` reports when selected.",
2045
+ "type": "string"
2046
+ },
2047
+ {
2048
+ "name": "groupDisabled",
2049
+ "description": "True while the containing `vf-radio-group` is disabled. Managed by the\ngroup — not intended to be set by consumers.",
2050
+ "type": "boolean"
2051
+ },
2052
+ { "name": "role", "type": "string" },
2053
+ {
2054
+ "name": "top",
2055
+ "description": "Offset from the top of the positioning parent, in whole system px.\nSetting this (or `left`) absolutely positions the element within its\nparent; the coordinate left unset is 0. Remove both to return the\nelement to normal flow.",
2056
+ "type": "number | null | undefined"
2057
+ },
2058
+ {
2059
+ "name": "left",
2060
+ "description": "Offset from the left of the positioning parent, in whole system px.\nSee top.",
2061
+ "type": "number | null | undefined"
2062
+ }
2063
+ ],
2064
+ "events": [
2065
+ {
2066
+ "name": "vf-change",
2067
+ "description": "When selected by user interaction. `detail: { value: string }`."
2068
+ }
2069
+ ]
2070
+ }
2071
+ },
2072
+ {
2073
+ "name": "vf-scroll-area",
2074
+ "description": "`<vf-scroll-area>` — a container whose scrollbars look like System 7.\n\nWhite box with a 1px black frame, an inner scrolling viewport, and scroll\nrails the kit draws itself as shadow DOM (the shared `vfScrollRail` recipe):\nboxed arrow buttons at each end, a loose 1-bit dot-dither trough, and the\nclassic fixed 16px thumb. The native scrollbar is hidden, never the native\nscrolling — wheel, trackpad momentum, keyboard, touch and assistive-tech\nscrolling stay the platform's, and ScrollRailController keeps the\nrail in sync while driving the classic interactions (thumb drag, trough\npaging, arrow stepping with auto-repeat). Every engine renders the same\nrail — there is no Firefox fallback skin anymore.\n\nEach reserved scroll rail is a permanent placeholder: arrow buttons on an\nempty white channel sit in the gutter even when the content fits, the\ndither and thumb filling in only once that axis overflows (System 7 drew\nan active window's no-overflow bar as arrows on a bare channel; driven\nby ScrollStateController). Which rails are reserved is set by\naxis; when both are reserved the bottom-right corner joins them.\n\nSize the host (width/height) from the outside; the viewport fills it.\n---\n\n\n### **Slots:**\n - _default_ - Scrollable content.\n\n### **CSS Properties:**\n - **--vf-scrollbar-thumb** - scrollbar thumb/elevator (white) _(default: undefined)_\n- **--vf-scrollbar-track** - the scroll trough's base color under the dot-dither (white) _(default: undefined)_\n\n### **CSS Parts:**\n - **viewport** - The inner scrolling container.",
2075
+ "doc-url": "",
2076
+ "attributes": [
2077
+ {
2078
+ "name": "axis",
2079
+ "description": "Which scroll rails to reserve as permanent placeholders: `vertical`\n(default), `horizontal`, or `both`. Each reserved rail shows an empty white\nchannel until its axis overflows; the unreserved axis still scrolls\nnatively (wheel, keyboard) but draws no rail.",
2080
+ "value": {
2081
+ "type": "'vertical' | 'horizontal' | 'both'",
2082
+ "default": "'vertical'"
2083
+ }
2084
+ },
2085
+ {
2086
+ "name": "label",
2087
+ "description": "Accessible name for the scrolling viewport, applied as its `aria-label`\n(an `aria-label` on the host would not reach into the shadow DOM). The\nviewport is keyboard-focusable while its content overflows, so without a\nname it is announced only as an anonymous scrollable group; setting\n`label` also promotes it to a named `role=\"region\"` landmark. While\n`label` is empty the viewport is a plain `group` when scrollable (an\nunnamed region is inert) and role-less when not.",
2088
+ "value": { "type": "string", "default": "''" }
2089
+ },
2090
+ {
2091
+ "name": "top",
2092
+ "description": "Offset from the top of the positioning parent, in whole system px.\nSetting this (or `left`) absolutely positions the element within its\nparent; the coordinate left unset is 0. Remove both to return the\nelement to normal flow.",
2093
+ "value": { "type": "number | null | undefined" }
2094
+ },
2095
+ {
2096
+ "name": "left",
2097
+ "description": "Offset from the left of the positioning parent, in whole system px.\nSee top.",
2098
+ "value": { "type": "number | null | undefined" }
2099
+ }
2100
+ ],
2101
+ "slots": [{ "name": "", "description": "Scrollable content." }],
2102
+ "events": [],
2103
+ "js": {
2104
+ "properties": [
2105
+ {
2106
+ "name": "axis",
2107
+ "description": "Which scroll rails to reserve as permanent placeholders: `vertical`\n(default), `horizontal`, or `both`. Each reserved rail shows an empty white\nchannel until its axis overflows; the unreserved axis still scrolls\nnatively (wheel, keyboard) but draws no rail.",
2108
+ "type": "'vertical' | 'horizontal' | 'both'"
2109
+ },
2110
+ {
2111
+ "name": "label",
2112
+ "description": "Accessible name for the scrolling viewport, applied as its `aria-label`\n(an `aria-label` on the host would not reach into the shadow DOM). The\nviewport is keyboard-focusable while its content overflows, so without a\nname it is announced only as an anonymous scrollable group; setting\n`label` also promotes it to a named `role=\"region\"` landmark. While\n`label` is empty the viewport is a plain `group` when scrollable (an\nunnamed region is inert) and role-less when not.",
2113
+ "type": "string"
2114
+ },
2115
+ {
2116
+ "name": "top",
2117
+ "description": "Offset from the top of the positioning parent, in whole system px.\nSetting this (or `left`) absolutely positions the element within its\nparent; the coordinate left unset is 0. Remove both to return the\nelement to normal flow.",
2118
+ "type": "number | null | undefined"
2119
+ },
2120
+ {
2121
+ "name": "left",
2122
+ "description": "Offset from the left of the positioning parent, in whole system px.\nSee top.",
2123
+ "type": "number | null | undefined"
2124
+ }
2125
+ ],
2126
+ "events": []
2127
+ }
2128
+ },
2129
+ {
2130
+ "name": "vf-select",
2131
+ "description": "`<vf-select>` — the classic System 7 popup menu control (\"Macintosh HD ▼\").\n\nChildren are `<vf-option>` elements in the default slot. The closed control\nis a white box with a 1px black border, the small 1px hard shadow, the\nselected option's label on the left and a solid black ▼ triangle on the\nright. The open panel uses the shared `.vf-panel` recipe and is positioned\n`position: fixed` (computed from `getBoundingClientRect()`) so it escapes\nclipping containers; when possible the currently-selected item opens\ndirectly over the control, like the real popup menu.\n\nPointer — two interaction styles are supported and disambiguated by the\ngesture itself, resolved at the first pointer release:\n - System 7 press-drag-release: press the pill (the list appears under the\n pointer), drag onto an item, release over it to pick — one continuous\n press. Releasing over the current item or off the list closes with no\n change.\n - Modern click-to-open: a quick in-place click (no drag, released within\n PRESS_HOLD_MS) leaves the list open; a second, independent click\n then picks an item.\nThe two share one opening trigger (pointerdown) and diverge only on how the\npress ends — whether the pointer travelled to another item, and (for an\nin-place release) whether it was a quick tap or a held press. The pull-down\nmenus drive themselves the same way, on the same threshold; that half lives\nin src/menu-press.ts, because one menu press may travel across a whole bar.\n\nA list too tall for the screen is **clipped**, never scrolled: the edge slot\nwith items beyond it shows a solid arrow instead of a row, which rolls the\nlist one row at a time while the pointer rests on it. The clamp is quantized\nto the pill lattice, so a clipped popup still opens with its selected row\nover the closed pill — and the panel keeps every slot the list asked for, so\na box that had to slide to fit the screen opens with *blank rows* at whichever\nend the list no longer reaches: the exact travel it will roll through, in\neither direction. See src/popup-overflow.ts.\n\nKeyboard: Space/Enter/ArrowDown open; while open ArrowUp/ArrowDown move the\nhighlight, Home/End jump, Enter/Space select, Escape cancels. Selecting an\nitem plays the classic inversion blink (~250 ms) before closing. Keyboard\nfocus is marked with the kit's 1px dashed rule under the closed pill\n(`vfFocusUnderline`) rather than a ring around it — keyboard only, which\nhere means the page's input modality rather than `:focus-visible` (the pill\ndrives its own focus, and Blink reads that as visible either way).\n\nForm-associated: submits `value` under `name`.\n---\n\n\n### **Events:**\n - **vf-change** - After a selection commits. `detail: { value: string }`.\n- **input** - Native event, dispatched from the host per committed pick (with `change`, the pair a native `<select>` fires). A programmatic `value` set fires nothing.\n- **change** - Native event, dispatched from the host per committed pick so form delegation and framework bindings hear it.\n\n### **Slots:**\n - _default_ - `<vf-option>` elements.\n\n### **CSS Properties:**\n - **--vf-popup-height** - `vf-select` pill (border box; its 1px hard shadow makes the sheet's 157×19 ink box) _(default: 18px)_\n- **--vf-popup-inset-top** - room a clipped popup panel keeps clear at the TOP screen edge — set it once on `:root` (or the `vf-desktop`) to clear a `vf-menu-bar`: `24px` is the 20px bar plus the default 4 _(default: 4px)_\n- **--vf-popup-inset-bottom** - room a clipped popup panel keeps clear at the BOTTOM screen edge _(default: 4px)_\n- **--vf-select-gutter** - checkmark column: `vf-select` left inset / `vf-option` + `vf-menu-item` ✓ column (shared so the value doesn't shift on open) _(default: 16px)_\n\n### **CSS Parts:**\n - **control** - The closed popup control box.\n- **label** - The selected-option label inside the control.\n- **arrow** - The black ▼ triangle.\n- **panel** - The popup panel (listbox).\n- **scroll-arrow** - Either of the two scroll arrows a clipped panel shows in its edge row slots.",
2132
+ "doc-url": "",
2133
+ "attributes": [
2134
+ {
2135
+ "name": "value",
2136
+ "description": "Value of the selected option. Adopts the first enabled option if unset.",
2137
+ "value": { "type": "string", "default": "''" }
2138
+ },
2139
+ {
2140
+ "name": "name",
2141
+ "description": "Form field name used when submitting the associated form.",
2142
+ "value": { "type": "string", "default": "''" }
2143
+ },
2144
+ {
2145
+ "name": "label",
2146
+ "description": "Accessible name for the combobox control (`aria-label`). Left empty, the\nname falls back to whatever the host carries — `aria-labelledby`,\n`aria-label` or an associated `<label for>` — via\nVfShadowRoleControl.hostLabel; with neither, the control is announced by\nits contents, i.e. the selected value.",
2147
+ "value": { "type": "string", "default": "''" }
2148
+ },
2149
+ {
2150
+ "name": "top",
2151
+ "description": "Offset from the top of the positioning parent, in whole system px.\nSetting this (or `left`) absolutely positions the element within its\nparent; the coordinate left unset is 0. Remove both to return the\nelement to normal flow.",
2152
+ "value": { "type": "number | null | undefined" }
2153
+ },
2154
+ {
2155
+ "name": "left",
2156
+ "description": "Offset from the left of the positioning parent, in whole system px.\nSee top.",
2157
+ "value": { "type": "number | null | undefined" }
2158
+ },
2159
+ {
2160
+ "name": "description",
2161
+ "description": "Description for the control — hint text, a format, a unit. A host-level\n`aria-describedby` cannot reach a focusable element inside a shadow root,\nso there was structurally no way to describe a field; this property is\nthat channel. It renders as a hidden span in the control's own shadow root\nwith the inner control's `aria-describedby` pointing at it — the\nshadow-internal IDREF idiom `vf-dialog`'s title patch already uses. A\nhost-level `aria-describedby` is bridged into the same span when this\nproperty is empty, and a failing constraint's validationMessage\njoins it too, so AT hears the error where it hears the hint.\n\nHost-role controls get neither half: their `aria-describedby` already\nworks, and their validation message reaches AT the way a native control's\ndoes — `aria-invalid` plus the browser's own validation UI, not AccName.",
2162
+ "value": { "type": "string", "default": "''" }
2163
+ },
2164
+ {
2165
+ "name": "disabled",
2166
+ "description": "Disables the control: it stops responding and submits no value. Each\ncontrol dims per SPEC §1 (typically the label greys while the 1-bit chrome\nstays black).",
2167
+ "value": { "type": "boolean", "default": "false" }
2168
+ },
2169
+ {
2170
+ "name": "required",
2171
+ "description": "Requires a value before the associated form submits (SPEC §4): an empty\ncontrol fails constraint validation with `valueMissing`, exactly like a\nnative `required`. What \"empty\" means is each control's own\nvalueMissing; a control with no required semantics (a slider\nalways has a value, a swatch submits nothing) never fails it, the way a\nnative range input never does.",
2172
+ "value": { "type": "boolean", "default": "false" }
2173
+ }
2174
+ ],
2175
+ "slots": [{ "name": "", "description": "`<vf-option>` elements." }],
2176
+ "events": [
2177
+ {
2178
+ "name": "vf-change",
2179
+ "description": "After a selection commits. `detail: { value: string }`."
2180
+ },
2181
+ {
2182
+ "name": "input",
2183
+ "description": "Native event, dispatched from the host per committed pick (with `change`, the pair a native `<select>` fires). A programmatic `value` set fires nothing."
2184
+ },
2185
+ {
2186
+ "name": "change",
2187
+ "description": "Native event, dispatched from the host per committed pick so form delegation and framework bindings hear it."
2188
+ }
2189
+ ],
2190
+ "js": {
2191
+ "properties": [
2192
+ {
2193
+ "name": "value",
2194
+ "description": "Value of the selected option. Adopts the first enabled option if unset.",
2195
+ "type": "string"
2196
+ },
2197
+ {
2198
+ "name": "name",
2199
+ "description": "Form field name used when submitting the associated form.",
2200
+ "type": "string"
2201
+ },
2202
+ {
2203
+ "name": "label",
2204
+ "description": "Accessible name for the combobox control (`aria-label`). Left empty, the\nname falls back to whatever the host carries — `aria-labelledby`,\n`aria-label` or an associated `<label for>` — via\nVfShadowRoleControl.hostLabel; with neither, the control is announced by\nits contents, i.e. the selected value.",
2205
+ "type": "string"
2206
+ },
2207
+ {
2208
+ "name": "top",
2209
+ "description": "Offset from the top of the positioning parent, in whole system px.\nSetting this (or `left`) absolutely positions the element within its\nparent; the coordinate left unset is 0. Remove both to return the\nelement to normal flow.",
2210
+ "type": "number | null | undefined"
2211
+ },
2212
+ {
2213
+ "name": "left",
2214
+ "description": "Offset from the left of the positioning parent, in whole system px.\nSee top.",
2215
+ "type": "number | null | undefined"
2216
+ },
2217
+ {
2218
+ "name": "description",
2219
+ "description": "Description for the control — hint text, a format, a unit. A host-level\n`aria-describedby` cannot reach a focusable element inside a shadow root,\nso there was structurally no way to describe a field; this property is\nthat channel. It renders as a hidden span in the control's own shadow root\nwith the inner control's `aria-describedby` pointing at it — the\nshadow-internal IDREF idiom `vf-dialog`'s title patch already uses. A\nhost-level `aria-describedby` is bridged into the same span when this\nproperty is empty, and a failing constraint's validationMessage\njoins it too, so AT hears the error where it hears the hint.\n\nHost-role controls get neither half: their `aria-describedby` already\nworks, and their validation message reaches AT the way a native control's\ndoes — `aria-invalid` plus the browser's own validation UI, not AccName.",
2220
+ "type": "string"
2221
+ },
2222
+ {
2223
+ "name": "disabled",
2224
+ "description": "Disables the control: it stops responding and submits no value. Each\ncontrol dims per SPEC §1 (typically the label greys while the 1-bit chrome\nstays black).",
2225
+ "type": "boolean"
2226
+ },
2227
+ {
2228
+ "name": "isDisabled",
2229
+ "description": "Effective disabled state: the `disabled` prop OR an ancestor fieldset.",
2230
+ "type": "boolean"
2231
+ },
2232
+ {
2233
+ "name": "required",
2234
+ "description": "Requires a value before the associated form submits (SPEC §4): an empty\ncontrol fails constraint validation with `valueMissing`, exactly like a\nnative `required`. What \"empty\" means is each control's own\nvalueMissing; a control with no required semantics (a slider\nalways has a value, a swatch submits nothing) never fails it, the way a\nnative range input never does.",
2235
+ "type": "boolean"
2236
+ },
2237
+ {
2238
+ "name": "validity",
2239
+ "description": "The control's current `ValidityState`, as on a native control.",
2240
+ "type": "ValidityState"
2241
+ },
2242
+ {
2243
+ "name": "validationMessage",
2244
+ "description": "The message of the currently failing constraint, `''` while valid.",
2245
+ "type": "string"
2246
+ },
2247
+ {
2248
+ "name": "willValidate",
2249
+ "description": "Whether the control is a candidate for constraint validation — false\nwhile disabled or readonly, per HTML's barring rules (the browser\ncomputes this from the reflected attributes).",
2250
+ "type": "boolean"
2251
+ }
2252
+ ],
2253
+ "events": [
2254
+ {
2255
+ "name": "vf-change",
2256
+ "description": "After a selection commits. `detail: { value: string }`."
2257
+ },
2258
+ {
2259
+ "name": "input",
2260
+ "description": "Native event, dispatched from the host per committed pick (with `change`, the pair a native `<select>` fires). A programmatic `value` set fires nothing."
2261
+ },
2262
+ {
2263
+ "name": "change",
2264
+ "description": "Native event, dispatched from the host per committed pick so form delegation and framework bindings hear it."
2265
+ }
2266
+ ]
2267
+ }
2268
+ },
2269
+ {
2270
+ "name": "vf-separator",
2271
+ "description": "`<vf-separator>` — a 1px System 7 rule.\n\nHorizontal by default (1px tall, full width); set the `vertical` attribute\nfor a 1px-wide vertical rule (give it a height, or let a flex parent\nstretch it).\n\nContainers may restyle it via custom properties:\n- `--vf-separator-color` — line color (default `var(--vf-black, #000)`).\n- `--vf-separator-style` — line style, e.g. `dotted` (default `solid`).\n `vf-menu` sets these so slotted separators render as the classic dimmed\n dotted menu rule (see Menus.png).\n---\n\n\n### **CSS Properties:**\n - **--vf-separator-color** - `vf-separator` rule color — `vf-menu` sets it to `--vf-disabled` for the dimmed menu rule _(default: undefined)_\n- **--vf-separator-style** - `vf-separator` rule style — `vf-menu` sets `dotted` (see `Menus.png`) _(default: solid)_",
2272
+ "doc-url": "",
2273
+ "attributes": [
2274
+ {
2275
+ "name": "vertical",
2276
+ "description": "Render as a vertical rule (1px wide) instead of horizontal.",
2277
+ "value": { "type": "boolean", "default": "false" }
2278
+ },
2279
+ {
2280
+ "name": "top",
2281
+ "description": "Offset from the top of the positioning parent, in whole system px.\nSetting this (or `left`) absolutely positions the element within its\nparent; the coordinate left unset is 0. Remove both to return the\nelement to normal flow.",
2282
+ "value": { "type": "number | null | undefined" }
2283
+ },
2284
+ {
2285
+ "name": "left",
2286
+ "description": "Offset from the left of the positioning parent, in whole system px.\nSee top.",
2287
+ "value": { "type": "number | null | undefined" }
2288
+ }
2289
+ ],
2290
+ "events": [],
2291
+ "js": {
2292
+ "properties": [
2293
+ {
2294
+ "name": "vertical",
2295
+ "description": "Render as a vertical rule (1px wide) instead of horizontal.",
2296
+ "type": "boolean"
2297
+ },
2298
+ { "name": "role", "type": "string" },
2299
+ {
2300
+ "name": "top",
2301
+ "description": "Offset from the top of the positioning parent, in whole system px.\nSetting this (or `left`) absolutely positions the element within its\nparent; the coordinate left unset is 0. Remove both to return the\nelement to normal flow.",
2302
+ "type": "number | null | undefined"
2303
+ },
2304
+ {
2305
+ "name": "left",
2306
+ "description": "Offset from the left of the positioning parent, in whole system px.\nSee top.",
2307
+ "type": "number | null | undefined"
2308
+ }
2309
+ ],
2310
+ "events": []
2311
+ }
2312
+ },
2313
+ {
2314
+ "name": "vf-slider",
2315
+ "description": "`<vf-slider>` — a horizontal 1-bit slider.\n\nNot a historically shipped System 7 control: it reverse-adapts the later\nMac OS slider into the library's solid-black, pixel-crisp 1-bit style. A\nrounded capsule rail fills solid black from the left up to a shield-shaped\ndrag handle (the SLIDER_THUMB sprite) and runs hollow after it. The\nrail is regenerated as a whole-pixel SVG on every resize so it stays crisp at\nany width, and the thumb snaps to integer pixels so the sprite never blurs.\n\nForm-associated: submits `value` under `name` and restores it on form reset.\nDrag or click the track to set the value; focus it and use the arrow keys\n(Home/End jump to min/max, PageUp/PageDown move in bigger steps).\n---\n\n\n### **Events:**\n - **vf-input** - On every drag move or key change. `detail: { value: number }`.\n- **vf-change** - On commit (pointer release, or key change). `detail: { value: number }`.\n- **input** - Native event, dispatched from the host on every user value move — a native range input's cadence. A programmatic `value` set fires nothing.\n- **change** - Native event, dispatched from the host on commit (release, or a key change) so form delegation and framework bindings hear it.\n\n### **CSS Parts:**\n - **track** - The full-width rail row (the pointer target).\n- **rail** - The `<svg>` capsule (fill + hollow).\n- **thumb** - The shield-shaped drag handle.",
2316
+ "doc-url": "",
2317
+ "attributes": [
2318
+ {
2319
+ "name": "value",
2320
+ "description": "Current value, clamped to `[min, max]` and snapped to `step`.",
2321
+ "value": { "type": "number", "default": "0" }
2322
+ },
2323
+ {
2324
+ "name": "min",
2325
+ "description": "Minimum value (inclusive).",
2326
+ "value": { "type": "number", "default": "0" }
2327
+ },
2328
+ {
2329
+ "name": "max",
2330
+ "description": "Maximum value (inclusive).",
2331
+ "value": { "type": "number", "default": "100" }
2332
+ },
2333
+ {
2334
+ "name": "step",
2335
+ "description": "Increment the value snaps to (also sets its display precision).",
2336
+ "value": { "type": "number", "default": "1" }
2337
+ },
2338
+ {
2339
+ "name": "name",
2340
+ "description": "Form field name used when submitting the associated form.",
2341
+ "value": { "type": "string", "default": "''" }
2342
+ },
2343
+ {
2344
+ "name": "label",
2345
+ "description": "Accessible name, exposed as the control's `aria-label`.",
2346
+ "value": { "type": "string", "default": "''" }
2347
+ },
2348
+ {
2349
+ "name": "top",
2350
+ "description": "Offset from the top of the positioning parent, in whole system px.\nSetting this (or `left`) absolutely positions the element within its\nparent; the coordinate left unset is 0. Remove both to return the\nelement to normal flow.",
2351
+ "value": { "type": "number | null | undefined" }
2352
+ },
2353
+ {
2354
+ "name": "left",
2355
+ "description": "Offset from the left of the positioning parent, in whole system px.\nSee top.",
2356
+ "value": { "type": "number | null | undefined" }
2357
+ },
2358
+ {
2359
+ "name": "disabled",
2360
+ "description": "Disables the control: it stops responding and submits no value. Each\ncontrol dims per SPEC §1 (typically the label greys while the 1-bit chrome\nstays black).",
2361
+ "value": { "type": "boolean", "default": "false" }
2362
+ },
2363
+ {
2364
+ "name": "required",
2365
+ "description": "Requires a value before the associated form submits (SPEC §4): an empty\ncontrol fails constraint validation with `valueMissing`, exactly like a\nnative `required`. What \"empty\" means is each control's own\nvalueMissing; a control with no required semantics (a slider\nalways has a value, a swatch submits nothing) never fails it, the way a\nnative range input never does.",
2366
+ "value": { "type": "boolean", "default": "false" }
2367
+ }
2368
+ ],
2369
+ "events": [
2370
+ {
2371
+ "name": "vf-input",
2372
+ "description": "On every drag move or key change. `detail: { value: number }`."
2373
+ },
2374
+ {
2375
+ "name": "vf-change",
2376
+ "description": "On commit (pointer release, or key change). `detail: { value: number }`."
2377
+ },
2378
+ {
2379
+ "name": "input",
2380
+ "description": "Native event, dispatched from the host on every user value move — a native range input's cadence. A programmatic `value` set fires nothing."
2381
+ },
2382
+ {
2383
+ "name": "change",
2384
+ "description": "Native event, dispatched from the host on commit (release, or a key change) so form delegation and framework bindings hear it."
2385
+ }
2386
+ ],
2387
+ "js": {
2388
+ "properties": [
2389
+ {
2390
+ "name": "value",
2391
+ "description": "Current value, clamped to `[min, max]` and snapped to `step`.",
2392
+ "type": "number"
2393
+ },
2394
+ {
2395
+ "name": "min",
2396
+ "description": "Minimum value (inclusive).",
2397
+ "type": "number"
2398
+ },
2399
+ {
2400
+ "name": "max",
2401
+ "description": "Maximum value (inclusive).",
2402
+ "type": "number"
2403
+ },
2404
+ {
2405
+ "name": "step",
2406
+ "description": "Increment the value snaps to (also sets its display precision).",
2407
+ "type": "number"
2408
+ },
2409
+ {
2410
+ "name": "name",
2411
+ "description": "Form field name used when submitting the associated form.",
2412
+ "type": "string"
2413
+ },
2414
+ {
2415
+ "name": "label",
2416
+ "description": "Accessible name, exposed as the control's `aria-label`.",
2417
+ "type": "string"
2418
+ },
2419
+ { "name": "role", "type": "string" },
2420
+ { "name": "ariaOrientation", "type": "string" },
2421
+ {
2422
+ "name": "top",
2423
+ "description": "Offset from the top of the positioning parent, in whole system px.\nSetting this (or `left`) absolutely positions the element within its\nparent; the coordinate left unset is 0. Remove both to return the\nelement to normal flow.",
2424
+ "type": "number | null | undefined"
2425
+ },
2426
+ {
2427
+ "name": "left",
2428
+ "description": "Offset from the left of the positioning parent, in whole system px.\nSee top.",
2429
+ "type": "number | null | undefined"
2430
+ },
2431
+ {
2432
+ "name": "disabled",
2433
+ "description": "Disables the control: it stops responding and submits no value. Each\ncontrol dims per SPEC §1 (typically the label greys while the 1-bit chrome\nstays black).",
2434
+ "type": "boolean"
2435
+ },
2436
+ {
2437
+ "name": "isDisabled",
2438
+ "description": "Effective disabled state: the `disabled` prop OR an ancestor fieldset.",
2439
+ "type": "boolean"
2440
+ },
2441
+ {
2442
+ "name": "required",
2443
+ "description": "Requires a value before the associated form submits (SPEC §4): an empty\ncontrol fails constraint validation with `valueMissing`, exactly like a\nnative `required`. What \"empty\" means is each control's own\nvalueMissing; a control with no required semantics (a slider\nalways has a value, a swatch submits nothing) never fails it, the way a\nnative range input never does.",
2444
+ "type": "boolean"
2445
+ },
2446
+ {
2447
+ "name": "validity",
2448
+ "description": "The control's current `ValidityState`, as on a native control.",
2449
+ "type": "ValidityState"
2450
+ },
2451
+ {
2452
+ "name": "validationMessage",
2453
+ "description": "The message of the currently failing constraint, `''` while valid.",
2454
+ "type": "string"
2455
+ },
2456
+ {
2457
+ "name": "willValidate",
2458
+ "description": "Whether the control is a candidate for constraint validation — false\nwhile disabled or readonly, per HTML's barring rules (the browser\ncomputes this from the reflected attributes).",
2459
+ "type": "boolean"
2460
+ }
2461
+ ],
2462
+ "events": [
2463
+ {
2464
+ "name": "vf-input",
2465
+ "description": "On every drag move or key change. `detail: { value: number }`."
2466
+ },
2467
+ {
2468
+ "name": "vf-change",
2469
+ "description": "On commit (pointer release, or key change). `detail: { value: number }`."
2470
+ },
2471
+ {
2472
+ "name": "input",
2473
+ "description": "Native event, dispatched from the host on every user value move — a native range input's cadence. A programmatic `value` set fires nothing."
2474
+ },
2475
+ {
2476
+ "name": "change",
2477
+ "description": "Native event, dispatched from the host on commit (release, or a key change) so form delegation and framework bindings hear it."
2478
+ }
2479
+ ]
2480
+ }
2481
+ },
2482
+ {
2483
+ "name": "vf-stack",
2484
+ "description": "`<vf-stack>` — arrange things inside a window, in system pixels.\n\nA window body is the one place the kit stopped short: every control inside it\nis authored in system px, but the *spaces between* them were the consumer's\nproblem, written by hand as `calc(var(--vf-scale, 1) * 12px)`. This is that\ncalculation, as a component — a flexbox whose `gap`, `pad`, `width` and\n`height` are declared in whole system px and converted internally:\n\n```html\n<vf-stack gap=\"12\"> <!-- a column -->\n <vf-stack fill-width direction=\"row\" gap=\"8\"> <!-- a labeled field -->\n <vf-label width=\"80\" for=\"name\">Name:</vf-label>\n <vf-text-field id=\"name\" fill-width></vf-text-field>\n </vf-stack>\n <vf-stack fill-width place=\"end\"> <!-- an action row -->\n <vf-button-group>\n <vf-button>Cancel</vf-button>\n <vf-button variant=\"default\">Save</vf-button>\n </vf-button-group>\n </vf-stack>\n</vf-stack>\n```\n\n**Why this can't just be page CSS.** Scaling is default-on and *per\ncomponent*: `ScaleController` sets `--vf-scale` on the component's own host,\nnot on the document. So `var(--vf-scale, 1)` in a consumer's stylesheet\nresolves only where the rule's element happens to sit inside a `vf-*`\nancestor and inherit it — true inside a window body, false for a plain\n`<div>` holding two buttons on an ordinary page, where the fallback `1` wins\nand the gap renders at 8px around 3×-sized buttons with no warning. A page\nthat hasn't called `applyScale()` has no way to write \"8 system px\" at all.\nA component always can, because it *is* the scope — which is also why this\none carries a ScaleController of its own: without it a lone stack\nwould resolve its own gap against that same fallback while its children each\nself-scaled around it.\n\n**Whole system px is the only expressible value**, so the gap half of the\nlayout contract (docs/SIZING.md rule 2) stops being a rule to remember. Declaring\n`width`/`height` covers the size half of rule 3 as well.\n\n**The geometry is governed by the content.** A column is as wide as its\nwidest child and a row as tall as its tallest; children keep the size they\ndrew themselves at (`flex: 0 0 auto` — no growing, no shrinking). System 7\nboxes are the size they are: a push button is as wide as its label, a popup\nmenu hugs its widest option, and a window is a fixed box whose overflow is\nclipped at the frame, not a layout that squeezes its controls to fit. The\nstack distributes; it does not resize. That is why the box shrink-wraps\n(`fit-content`) rather than claiming its parent's whole width — a layout box\nthat did would be inventing a size nobody declared.\n\n**`fill-width` / `fill-height` are how a child asks for more**, as bare\nattributes on consumer DOM the way `nosnap` opts an element out of snapping:\n\n```html\n<vf-text-field fill-width></vf-text-field>\n```\n\nEach names the *outcome*, not the axis, so the markup means the same thing\nwherever it lands; the stack does the flexbox translation, which is the whole\nreason to have a component. One rule to learn, about geometry rather than\nvocabulary: **the cross axis always has a size, the main axis only has slack\nif you declared one.** So `fill-width` always works in a column (the width is\nthe widest child's) and needs a declared `width` in a row; `fill-height` is\nexactly the other way round. A fill with nothing to take is inert, not an\nerror. Two children filling along the main axis end up *equal* — the zeroed\nflex basis is what lets them divide the slack rather than keep their natural\nsizes — and a child that declares its own size shouldn't also ask to fill it.\n\nA stack reads the same two attributes about *itself*, for the parents that\naren't stacks: a window body, a fieldset, a scroll well, a grid cell. That is\nwhere a panel's width enters the tree, and from there `fill-width` hands it\ndown a level at a time.\n\n**It paints nothing and means nothing.** No border, no background, no role,\nno keyboard behavior — what it holds decides what it is, as with\nVfGrid.\n\n**It holds its box on the device-pixel grid** — the `vf-container`\narrangement, adopted here after first shipping without it. The original\nreasoning (\"no ink of its own, and slotted `vf-*` children correct their own\norigins\") accounted only for kit children: a stack is a positioned ancestor\nand a layout box for *anything*, and consumer content inside it — a div, an\n`<img>`, a run of text — has no controller of its own. So the flex\ncontainer, the placed-child anchor and the `vf-snap` class live on one\nshadow box, and under `applyGridSnap()` the correction moves the stack's\nwhole coordinate system; children (kit or not) ride it, and the sweep's\noutermost-first order means the kit children then find nothing left to\ncorrect. What this deliberately does not fix: a text-governed child width\nmid-row still shifts later siblings fractionally (their own controllers\ncover that), and centering still can't land on a whole pixel by itself.\n\nIt is also the kit's one **typographically transparent** component: `vfBase`'s\nchrome face, ratio line box, color and non-selectability are all reset to\n`inherit` on the host. Wrapping content in a layout box must not change how\nthat content reads — inside a window it goes on inheriting the window's face\nexactly as before, and on a plain page it leaves the page's typography (and\nits whole-pixel line boxes) alone.\n\nWhat it deliberately does **not** do: equalize its children's widths. A row of\nbuttons still belongs in a `vf-button-group`, which sizes them all to the\nwidest and aligns their *faces* rather than the `variant=\"default\"` ring\nboxes a plain flex row would line up.\n---\n\n\n### **Slots:**\n - _default_ - The children to arrange. `fill-width` / `fill-height` on any of them.",
2485
+ "doc-url": "",
2486
+ "attributes": [
2487
+ {
2488
+ "name": "direction",
2489
+ "description": "Which axis the children run along — `column` (the default) or `row`.\n\nA stack is vertical unless it says otherwise, the way the word is used\neverywhere else; a row is the one that reads as a deliberate choice in the\nmarkup.",
2490
+ "value": { "type": "VfStackDirection", "default": "'column'" }
2491
+ },
2492
+ {
2493
+ "name": "gap",
2494
+ "description": "The space between children, in whole system px. `0` by default: a layout\nbox that spaced things out on its own would be exactly the kind of\nsurprise SPEC §2 rules out when it says a component renders nothing\nunexpected outside its box.",
2495
+ "value": { "type": "number", "default": "0" }
2496
+ },
2497
+ {
2498
+ "name": "pad",
2499
+ "description": "Padding inside the stack, in whole system px — one to four values in the\nusual CSS shorthand order (`pad=\"12\"`, `pad=\"10 12\"`, `pad=\"14 12 10\"`).\n\nLeave it off inside a `vf-window` body, which already carries its own 12px\ninset; a stack that means to own that inset goes inside `<vf-window flush>`.",
2500
+ "value": { "type": "string | number | undefined" }
2501
+ },
2502
+ {
2503
+ "name": "place",
2504
+ "description": "Where the children sit across the stack — `start`, `center` or `end`.\nUnset resolves per direction: `start` down a column, `center` across a row.\n\nNamed `place` rather than `align` for a reason worth keeping in the source:\n`align` is a legacy HTML presentation attribute, and Blink maps it to\n`text-align` on any element, so the cross-axis switch used to re-align every\nrun of copy inside the stack (see the `text-align` reset above).\n\nNote the one thing centering cannot do: land on a whole pixel by itself. A\n16px caption centered in a row set by the 25-system-px `vf-number-field`\nsits at 4.5 system px, and no container can round that — it would have to\nread each child's height. `applyGridSnap()` keeps the caption's own ink\ncrisp regardless (it corrects the origin inside the child's shadow root);\n`place=\"start\"` is the deterministic escape.",
2505
+ "value": { "type": "VfStackPlace | undefined" }
2506
+ },
2507
+ {
2508
+ "name": "width",
2509
+ "description": "Width in whole system px. What the number means is the component's own\naffair — the measure a paragraph wraps to, a caption column, a window's\nbox — but the mechanics are shared: the length lands on the host as a\nlive `calc(var(--vf-scale, 1) * Npx)`, so the box scales with the\ndisplay and sits on the device-pixel grid by construction. Remove it\nand the width returns to layout.",
2510
+ "value": { "type": "number | null | undefined" }
2511
+ },
2512
+ {
2513
+ "name": "height",
2514
+ "description": "Height in whole system px; see width. Content that outgrows a\ndeclared height overflows the box rather than growing it — the number\nis the layout, and content that doesn't fit is a number to raise.",
2515
+ "value": { "type": "number | null | undefined" }
2516
+ },
2517
+ {
2518
+ "name": "top",
2519
+ "description": "Offset from the top of the positioning parent, in whole system px.\nSetting this (or `left`) absolutely positions the element within its\nparent; the coordinate left unset is 0. Remove both to return the\nelement to normal flow.",
2520
+ "value": { "type": "number | null | undefined" }
2521
+ },
2522
+ {
2523
+ "name": "left",
2524
+ "description": "Offset from the left of the positioning parent, in whole system px.\nSee top.",
2525
+ "value": { "type": "number | null | undefined" }
2526
+ }
2527
+ ],
2528
+ "slots": [
2529
+ {
2530
+ "name": "",
2531
+ "description": "The children to arrange. `fill-width` / `fill-height` on any of them."
2532
+ }
2533
+ ],
2534
+ "events": [],
2535
+ "js": {
2536
+ "properties": [
2537
+ {
2538
+ "name": "direction",
2539
+ "description": "Which axis the children run along — `column` (the default) or `row`.\n\nA stack is vertical unless it says otherwise, the way the word is used\neverywhere else; a row is the one that reads as a deliberate choice in the\nmarkup.",
2540
+ "type": "VfStackDirection"
2541
+ },
2542
+ {
2543
+ "name": "gap",
2544
+ "description": "The space between children, in whole system px. `0` by default: a layout\nbox that spaced things out on its own would be exactly the kind of\nsurprise SPEC §2 rules out when it says a component renders nothing\nunexpected outside its box.",
2545
+ "type": "number"
2546
+ },
2547
+ {
2548
+ "name": "pad",
2549
+ "description": "Padding inside the stack, in whole system px — one to four values in the\nusual CSS shorthand order (`pad=\"12\"`, `pad=\"10 12\"`, `pad=\"14 12 10\"`).\n\nLeave it off inside a `vf-window` body, which already carries its own 12px\ninset; a stack that means to own that inset goes inside `<vf-window flush>`.",
2550
+ "type": "string | number | undefined"
2551
+ },
2552
+ {
2553
+ "name": "place",
2554
+ "description": "Where the children sit across the stack — `start`, `center` or `end`.\nUnset resolves per direction: `start` down a column, `center` across a row.\n\nNamed `place` rather than `align` for a reason worth keeping in the source:\n`align` is a legacy HTML presentation attribute, and Blink maps it to\n`text-align` on any element, so the cross-axis switch used to re-align every\nrun of copy inside the stack (see the `text-align` reset above).\n\nNote the one thing centering cannot do: land on a whole pixel by itself. A\n16px caption centered in a row set by the 25-system-px `vf-number-field`\nsits at 4.5 system px, and no container can round that — it would have to\nread each child's height. `applyGridSnap()` keeps the caption's own ink\ncrisp regardless (it corrects the origin inside the child's shadow root);\n`place=\"start\"` is the deterministic escape.",
2555
+ "type": "VfStackPlace | undefined"
2556
+ },
2557
+ {
2558
+ "name": "width",
2559
+ "description": "Width in whole system px. What the number means is the component's own\naffair — the measure a paragraph wraps to, a caption column, a window's\nbox — but the mechanics are shared: the length lands on the host as a\nlive `calc(var(--vf-scale, 1) * Npx)`, so the box scales with the\ndisplay and sits on the device-pixel grid by construction. Remove it\nand the width returns to layout.",
2560
+ "type": "number | null | undefined"
2561
+ },
2562
+ {
2563
+ "name": "height",
2564
+ "description": "Height in whole system px; see width. Content that outgrows a\ndeclared height overflows the box rather than growing it — the number\nis the layout, and content that doesn't fit is a number to raise.",
2565
+ "type": "number | null | undefined"
2566
+ },
2567
+ {
2568
+ "name": "top",
2569
+ "description": "Offset from the top of the positioning parent, in whole system px.\nSetting this (or `left`) absolutely positions the element within its\nparent; the coordinate left unset is 0. Remove both to return the\nelement to normal flow.",
2570
+ "type": "number | null | undefined"
2571
+ },
2572
+ {
2573
+ "name": "left",
2574
+ "description": "Offset from the left of the positioning parent, in whole system px.\nSee top.",
2575
+ "type": "number | null | undefined"
2576
+ }
2577
+ ],
2578
+ "events": []
2579
+ }
2580
+ },
2581
+ {
2582
+ "name": "vf-swatch",
2583
+ "description": "`<vf-swatch>` — a color-swatch button: the color well of a palette cell.\n\nA solid rectangle with a 1px black border and a 1px white inset ring, filled\nedge-to-edge with a solid color. Given no `color` at all it shows the\ntransparency checker instead — the \"this cell holds nothing\" grid every\npaint program draws behind an empty fill.\n\n`width`/`height` state the border box in whole system pixels (the fill is\nwhat remains inside the border and inset), multiplied by `--vf-scale` like\nevery other metric, so the swatch holds the device-pixel grid wherever a\npalette puts it.\n\n`shadow` opts into the kit's hard drop shadow — the shared\n`--vf-shadow-offset` token, the same depth cue as windows, menus and alerts,\npainted outside the box like theirs. It is off by default because the\nswatch's usual home is a table of them (a `vf-grid`, a picker row), where\nevery cell shadowing its neighbour reads as noise rather than depth; a lone\nwell standing in for a current color — the one a dialog asks you to click —\nis the case that wants it.\n\nIt is \"basically a button\": a native `<button>` inside, so `click` retargets\nto the host, Enter/Space activate it, and pressing inverts the white inset\nto black — the inset counterpart of vf-button's face inversion. Keyboard\nfocus is marked with the kit's 1px dashed rule under the box\n(`vfFocusUnderline`) rather than a ring around it. It is form-associated\nfor the *disabled* contract alone — an ancestor `<fieldset disabled>` must\nreach a palette the way it reaches every other control, and only the\nform-associated lifecycle delivers that across the shadow boundary — but it\nsubmits nothing (a palette cell picks, it doesn't submit; no `name`, no\nvalue, no `FormData` entry). `disabled` only stops interaction, dimming\nnothing — the kit dims *labels* when disabled, and a swatch's only label is\nits fill, which must keep reading as its color.\n---\n\n\n### **CSS Properties:**\n - **--vf-swatch-checker** - `vf-swatch`'s no-color transparency checker — a 4×4 motif of 2×2 white/`#c0c0c0` checks, on a 60-system-px tile (override the whole tile like `--vf-desktop-pattern` — consumer art renders as a placed tile grid at that same geometry) _(default: undefined)_\n\n### **CSS Parts:**\n - **button** - The inner native `<button>` (border, inset and, with `shadow`, the drop shadow).\n- **fill** - The color area inside the inset.",
2584
+ "doc-url": "",
2585
+ "attributes": [
2586
+ {
2587
+ "name": "color",
2588
+ "description": "The fill — a CSS color, typically the hex the palette calls for. Unset,\nthe swatch shows the transparency checker; a translucent value (an\n8-digit hex) layers over that same checker, so partial opacity reads as\npartial. An unparseable value falls back to the checker too.",
2589
+ "value": { "type": "string | undefined" }
2590
+ },
2591
+ {
2592
+ "name": "width",
2593
+ "description": "The swatch's border box width in whole system px.",
2594
+ "value": { "type": "number", "default": "24" }
2595
+ },
2596
+ {
2597
+ "name": "height",
2598
+ "description": "The swatch's border box height in whole system px.",
2599
+ "value": { "type": "number", "default": "18" }
2600
+ },
2601
+ {
2602
+ "name": "shadow",
2603
+ "description": "Cast the kit's hard drop shadow (`--vf-shadow-offset`). Off by default:\nthe plain black-bordered well is what a table of swatches wants, and the\ndepth cue is for the lone well that stands in for a current color.",
2604
+ "value": { "type": "boolean", "default": "false" }
2605
+ },
2606
+ {
2607
+ "name": "label",
2608
+ "description": "Accessible name for the inner button — a swatch has no text of its own.\nLeft empty, the name falls back to whatever the host carries\n(`aria-labelledby`, `aria-label`, an associated `<label for>` — see\nVfShadowRoleControl.hostLabel), then to the `color` value (or\n\"transparent\") as a last resort, so a palette is never nameless — but a\nhex literal announced as \"number f f six six zero zero\" is a fallback to\nname past, not a name. `vf-label`'s `for` wiring writes this property,\nlike every `vf-*` control's.",
2609
+ "value": { "type": "string", "default": "''" }
2610
+ },
2611
+ {
2612
+ "name": "disabled",
2613
+ "description": "Disables the control: it stops responding. Nothing dims (see class doc).",
2614
+ "value": { "type": "boolean", "default": "false" }
2615
+ },
2616
+ {
2617
+ "name": "top",
2618
+ "description": "Offset from the top of the positioning parent, in whole system px.\nSetting this (or `left`) absolutely positions the element within its\nparent; the coordinate left unset is 0. Remove both to return the\nelement to normal flow.",
2619
+ "value": { "type": "number | null | undefined" }
2620
+ },
2621
+ {
2622
+ "name": "left",
2623
+ "description": "Offset from the left of the positioning parent, in whole system px.\nSee top.",
2624
+ "value": { "type": "number | null | undefined" }
2625
+ },
2626
+ {
2627
+ "name": "description",
2628
+ "description": "Description for the control — hint text, a format, a unit. A host-level\n`aria-describedby` cannot reach a focusable element inside a shadow root,\nso there was structurally no way to describe a field; this property is\nthat channel. It renders as a hidden span in the control's own shadow root\nwith the inner control's `aria-describedby` pointing at it — the\nshadow-internal IDREF idiom `vf-dialog`'s title patch already uses. A\nhost-level `aria-describedby` is bridged into the same span when this\nproperty is empty, and a failing constraint's validationMessage\njoins it too, so AT hears the error where it hears the hint.\n\nHost-role controls get neither half: their `aria-describedby` already\nworks, and their validation message reaches AT the way a native control's\ndoes — `aria-invalid` plus the browser's own validation UI, not AccName.",
2629
+ "value": { "type": "string", "default": "''" }
2630
+ },
2631
+ {
2632
+ "name": "required",
2633
+ "description": "Requires a value before the associated form submits (SPEC §4): an empty\ncontrol fails constraint validation with `valueMissing`, exactly like a\nnative `required`. What \"empty\" means is each control's own\nvalueMissing; a control with no required semantics (a slider\nalways has a value, a swatch submits nothing) never fails it, the way a\nnative range input never does.",
2634
+ "value": { "type": "boolean", "default": "false" }
2635
+ }
2636
+ ],
2637
+ "events": [],
2638
+ "js": {
2639
+ "properties": [
2640
+ {
2641
+ "name": "color",
2642
+ "description": "The fill — a CSS color, typically the hex the palette calls for. Unset,\nthe swatch shows the transparency checker; a translucent value (an\n8-digit hex) layers over that same checker, so partial opacity reads as\npartial. An unparseable value falls back to the checker too.",
2643
+ "type": "string | undefined"
2644
+ },
2645
+ {
2646
+ "name": "width",
2647
+ "description": "The swatch's border box width in whole system px.",
2648
+ "type": "number"
2649
+ },
2650
+ {
2651
+ "name": "height",
2652
+ "description": "The swatch's border box height in whole system px.",
2653
+ "type": "number"
2654
+ },
2655
+ {
2656
+ "name": "shadow",
2657
+ "description": "Cast the kit's hard drop shadow (`--vf-shadow-offset`). Off by default:\nthe plain black-bordered well is what a table of swatches wants, and the\ndepth cue is for the lone well that stands in for a current color.",
2658
+ "type": "boolean"
2659
+ },
2660
+ {
2661
+ "name": "label",
2662
+ "description": "Accessible name for the inner button — a swatch has no text of its own.\nLeft empty, the name falls back to whatever the host carries\n(`aria-labelledby`, `aria-label`, an associated `<label for>` — see\nVfShadowRoleControl.hostLabel), then to the `color` value (or\n\"transparent\") as a last resort, so a palette is never nameless — but a\nhex literal announced as \"number f f six six zero zero\" is a fallback to\nname past, not a name. `vf-label`'s `for` wiring writes this property,\nlike every `vf-*` control's.",
2663
+ "type": "string"
2664
+ },
2665
+ {
2666
+ "name": "disabled",
2667
+ "description": "Disables the control: it stops responding. Nothing dims (see class doc).",
2668
+ "type": "boolean"
2669
+ },
2670
+ {
2671
+ "name": "top",
2672
+ "description": "Offset from the top of the positioning parent, in whole system px.\nSetting this (or `left`) absolutely positions the element within its\nparent; the coordinate left unset is 0. Remove both to return the\nelement to normal flow.",
2673
+ "type": "number | null | undefined"
2674
+ },
2675
+ {
2676
+ "name": "left",
2677
+ "description": "Offset from the left of the positioning parent, in whole system px.\nSee top.",
2678
+ "type": "number | null | undefined"
2679
+ },
2680
+ {
2681
+ "name": "description",
2682
+ "description": "Description for the control — hint text, a format, a unit. A host-level\n`aria-describedby` cannot reach a focusable element inside a shadow root,\nso there was structurally no way to describe a field; this property is\nthat channel. It renders as a hidden span in the control's own shadow root\nwith the inner control's `aria-describedby` pointing at it — the\nshadow-internal IDREF idiom `vf-dialog`'s title patch already uses. A\nhost-level `aria-describedby` is bridged into the same span when this\nproperty is empty, and a failing constraint's validationMessage\njoins it too, so AT hears the error where it hears the hint.\n\nHost-role controls get neither half: their `aria-describedby` already\nworks, and their validation message reaches AT the way a native control's\ndoes — `aria-invalid` plus the browser's own validation UI, not AccName.",
2683
+ "type": "string"
2684
+ },
2685
+ {
2686
+ "name": "isDisabled",
2687
+ "description": "Effective disabled state: the `disabled` prop OR an ancestor fieldset.",
2688
+ "type": "boolean"
2689
+ },
2690
+ {
2691
+ "name": "required",
2692
+ "description": "Requires a value before the associated form submits (SPEC §4): an empty\ncontrol fails constraint validation with `valueMissing`, exactly like a\nnative `required`. What \"empty\" means is each control's own\nvalueMissing; a control with no required semantics (a slider\nalways has a value, a swatch submits nothing) never fails it, the way a\nnative range input never does.",
2693
+ "type": "boolean"
2694
+ },
2695
+ {
2696
+ "name": "validity",
2697
+ "description": "The control's current `ValidityState`, as on a native control.",
2698
+ "type": "ValidityState"
2699
+ },
2700
+ {
2701
+ "name": "validationMessage",
2702
+ "description": "The message of the currently failing constraint, `''` while valid.",
2703
+ "type": "string"
2704
+ },
2705
+ {
2706
+ "name": "willValidate",
2707
+ "description": "Whether the control is a candidate for constraint validation — false\nwhile disabled or readonly, per HTML's barring rules (the browser\ncomputes this from the reflected attributes).",
2708
+ "type": "boolean"
2709
+ }
2710
+ ],
2711
+ "events": []
2712
+ }
2713
+ },
2714
+ {
2715
+ "name": "vf-text-area",
2716
+ "description": "`<vf-text-area>` — a System 7 multi-line text entry field.\n\nIdentical styling to `<vf-text-field>` but wrapping a native `<textarea>`.\nNo resize grip (`resize: none`) — System 7 fields don't resize. Wrapped\ntext sits on Chicago 12's native 16px line (the single-line well keeps its\n20px box — that is control geometry, not typesetting). The shared field\nskin lives in `vfField`; the value/form scaffolding in\nVfTextControlBase.\n\nThe `.vf-field-well` wrapper is the one `vfField` hangs the focus rule from,\ndoing double duty here: it is the field's framed box — it carries the 1px\nframe the single-line field draws on the well itself, and lays the drawn\nscroll rail beside the borderless inner `<textarea>` — and the `vf-snap`\nelement whose grid-snap offset the field, the frame and the rule ride as\none.\n\nThe vertical scroll rail is a permanent System 7 placeholder: arrows on an\nempty white channel sit in the field even when the text fits, the dither\nand thumb filling in only once the content overflows (driven by\nScrollStateController). The rail is the shared `vfScrollRail`\nsubtree — the kit-drawn rail every scroll surface wears — synced to the\ntextarea's own native scrolling by ScrollRailController; the native\nbar itself is hidden.\n---\n\n\n### **Events:**\n - **vf-input** - On every keystroke. `detail: { value: string }`.\n- **vf-change** - On commit (native `change`). `detail: { value: string }`.\n- **input** - The native keystroke event: the inner textarea's own, composed, so it crosses the shadow boundary and retargets to the host.\n- **change** - The native commit event, re-dispatched from the host (the inner one is `composed: false` and never leaves the shadow root). The input-behavior attributes — `autocomplete`, `inputmode`, `enterkeyhint`, `maxlength`, `spellcheck`, `autocapitalize` — are forwarded from the host onto the inner textarea, where the platform actually honors them (`pattern` is not: only an `<input>` takes it).\n\n### **CSS Properties:**\n - **--vf-line-height-display** - The display face's native line (default `16px`, Chicago 12's) — the pitch wrapped entry text sits on, shared with the static-text components so a display retheme moves them together. _(default: undefined)_\n- **--vf-field-width** - default width of `vf-text-field` / `vf-text-area` _(default: 180px)_\n- **--vf-field-placeholder** - placeholder text in the editable fields — kept off `--vf-disabled`: a placeholder sits in an *enabled* well and holds AA contrast, where the disabled gray is exempt _(default: #767676)_\n- **--vf-scrollbar-thumb** - scrollbar thumb/elevator (white) _(default: undefined)_\n- **--vf-scrollbar-track** - the scroll trough's base color under the dot-dither (white) _(default: undefined)_\n\n### **CSS Parts:**\n - **textarea** - The inner native `<textarea>` element.",
2717
+ "doc-url": "",
2718
+ "attributes": [
2719
+ {
2720
+ "name": "rows",
2721
+ "description": "Number of visible text rows (native `rows`). Default 4.",
2722
+ "value": { "type": "number", "default": "4" }
2723
+ },
2724
+ {
2725
+ "name": "top",
2726
+ "description": "Offset from the top of the positioning parent, in whole system px.\nSetting this (or `left`) absolutely positions the element within its\nparent; the coordinate left unset is 0. Remove both to return the\nelement to normal flow.",
2727
+ "value": { "type": "number | null | undefined" }
2728
+ },
2729
+ {
2730
+ "name": "left",
2731
+ "description": "Offset from the left of the positioning parent, in whole system px.\nSee top.",
2732
+ "value": { "type": "number | null | undefined" }
2733
+ },
2734
+ {
2735
+ "name": "value",
2736
+ "description": "Current value. Synced on every keystroke and submitted with forms.",
2737
+ "value": { "type": "string", "default": "''" }
2738
+ },
2739
+ {
2740
+ "name": "placeholder",
2741
+ "description": "Placeholder text shown when the field is empty.",
2742
+ "value": { "type": "string", "default": "''" }
2743
+ },
2744
+ {
2745
+ "name": "readonly",
2746
+ "description": "Makes the field read-only (focusable, not editable).",
2747
+ "value": { "type": "boolean", "default": "false" }
2748
+ },
2749
+ {
2750
+ "name": "name",
2751
+ "description": "Form field name used when submitting the associated form.",
2752
+ "value": { "type": "string", "default": "''" }
2753
+ },
2754
+ {
2755
+ "name": "label",
2756
+ "description": "Accessible name, applied as `aria-label` on the inner native control (the\nelement that receives focus and is announced by screen readers — a host\nattribute cannot reach it on its own). Left empty, the name falls back to\nwhatever the host carries — `aria-labelledby`, `aria-label` or an\nassociated `<label for>` — via VfShadowRoleControl.hostLabel, so the\nplatform's own labeling vocabulary works on these controls too.",
2757
+ "value": { "type": "string", "default": "''" }
2758
+ },
2759
+ {
2760
+ "name": "description",
2761
+ "description": "Description for the control — hint text, a format, a unit. A host-level\n`aria-describedby` cannot reach a focusable element inside a shadow root,\nso there was structurally no way to describe a field; this property is\nthat channel. It renders as a hidden span in the control's own shadow root\nwith the inner control's `aria-describedby` pointing at it — the\nshadow-internal IDREF idiom `vf-dialog`'s title patch already uses. A\nhost-level `aria-describedby` is bridged into the same span when this\nproperty is empty, and a failing constraint's validationMessage\njoins it too, so AT hears the error where it hears the hint.\n\nHost-role controls get neither half: their `aria-describedby` already\nworks, and their validation message reaches AT the way a native control's\ndoes — `aria-invalid` plus the browser's own validation UI, not AccName.",
2762
+ "value": { "type": "string", "default": "''" }
2763
+ },
2764
+ {
2765
+ "name": "disabled",
2766
+ "description": "Disables the control: it stops responding and submits no value. Each\ncontrol dims per SPEC §1 (typically the label greys while the 1-bit chrome\nstays black).",
2767
+ "value": { "type": "boolean", "default": "false" }
2768
+ },
2769
+ {
2770
+ "name": "required",
2771
+ "description": "Requires a value before the associated form submits (SPEC §4): an empty\ncontrol fails constraint validation with `valueMissing`, exactly like a\nnative `required`. What \"empty\" means is each control's own\nvalueMissing; a control with no required semantics (a slider\nalways has a value, a swatch submits nothing) never fails it, the way a\nnative range input never does.",
2772
+ "value": { "type": "boolean", "default": "false" }
2773
+ }
2774
+ ],
2775
+ "events": [
2776
+ {
2777
+ "name": "vf-input",
2778
+ "description": "On every keystroke. `detail: { value: string }`."
2779
+ },
2780
+ {
2781
+ "name": "vf-change",
2782
+ "description": "On commit (native `change`). `detail: { value: string }`."
2783
+ },
2784
+ {
2785
+ "name": "input",
2786
+ "description": "The native keystroke event: the inner textarea's own, composed, so it crosses the shadow boundary and retargets to the host."
2787
+ },
2788
+ {
2789
+ "name": "change",
2790
+ "description": "The native commit event, re-dispatched from the host (the inner one is `composed: false` and never leaves the shadow root). The input-behavior attributes — `autocomplete`, `inputmode`, `enterkeyhint`, `maxlength`, `spellcheck`, `autocapitalize` — are forwarded from the host onto the inner textarea, where the platform actually honors them (`pattern` is not: only an `<input>` takes it)."
2791
+ }
2792
+ ],
2793
+ "js": {
2794
+ "properties": [
2795
+ {
2796
+ "name": "rows",
2797
+ "description": "Number of visible text rows (native `rows`). Default 4.",
2798
+ "type": "number"
2799
+ },
2800
+ {
2801
+ "name": "top",
2802
+ "description": "Offset from the top of the positioning parent, in whole system px.\nSetting this (or `left`) absolutely positions the element within its\nparent; the coordinate left unset is 0. Remove both to return the\nelement to normal flow.",
2803
+ "type": "number | null | undefined"
2804
+ },
2805
+ {
2806
+ "name": "left",
2807
+ "description": "Offset from the left of the positioning parent, in whole system px.\nSee top.",
2808
+ "type": "number | null | undefined"
2809
+ },
2810
+ {
2811
+ "name": "value",
2812
+ "description": "Current value. Synced on every keystroke and submitted with forms.",
2813
+ "type": "string"
2814
+ },
2815
+ {
2816
+ "name": "placeholder",
2817
+ "description": "Placeholder text shown when the field is empty.",
2818
+ "type": "string"
2819
+ },
2820
+ {
2821
+ "name": "readonly",
2822
+ "description": "Makes the field read-only (focusable, not editable).",
2823
+ "type": "boolean"
2824
+ },
2825
+ {
2826
+ "name": "name",
2827
+ "description": "Form field name used when submitting the associated form.",
2828
+ "type": "string"
2829
+ },
2830
+ {
2831
+ "name": "label",
2832
+ "description": "Accessible name, applied as `aria-label` on the inner native control (the\nelement that receives focus and is announced by screen readers — a host\nattribute cannot reach it on its own). Left empty, the name falls back to\nwhatever the host carries — `aria-labelledby`, `aria-label` or an\nassociated `<label for>` — via VfShadowRoleControl.hostLabel, so the\nplatform's own labeling vocabulary works on these controls too.",
2833
+ "type": "string"
2834
+ },
2835
+ {
2836
+ "name": "description",
2837
+ "description": "Description for the control — hint text, a format, a unit. A host-level\n`aria-describedby` cannot reach a focusable element inside a shadow root,\nso there was structurally no way to describe a field; this property is\nthat channel. It renders as a hidden span in the control's own shadow root\nwith the inner control's `aria-describedby` pointing at it — the\nshadow-internal IDREF idiom `vf-dialog`'s title patch already uses. A\nhost-level `aria-describedby` is bridged into the same span when this\nproperty is empty, and a failing constraint's validationMessage\njoins it too, so AT hears the error where it hears the hint.\n\nHost-role controls get neither half: their `aria-describedby` already\nworks, and their validation message reaches AT the way a native control's\ndoes — `aria-invalid` plus the browser's own validation UI, not AccName.",
2838
+ "type": "string"
2839
+ },
2840
+ {
2841
+ "name": "disabled",
2842
+ "description": "Disables the control: it stops responding and submits no value. Each\ncontrol dims per SPEC §1 (typically the label greys while the 1-bit chrome\nstays black).",
2843
+ "type": "boolean"
2844
+ },
2845
+ {
2846
+ "name": "isDisabled",
2847
+ "description": "Effective disabled state: the `disabled` prop OR an ancestor fieldset.",
2848
+ "type": "boolean"
2849
+ },
2850
+ {
2851
+ "name": "required",
2852
+ "description": "Requires a value before the associated form submits (SPEC §4): an empty\ncontrol fails constraint validation with `valueMissing`, exactly like a\nnative `required`. What \"empty\" means is each control's own\nvalueMissing; a control with no required semantics (a slider\nalways has a value, a swatch submits nothing) never fails it, the way a\nnative range input never does.",
2853
+ "type": "boolean"
2854
+ },
2855
+ {
2856
+ "name": "validity",
2857
+ "description": "The control's current `ValidityState`, as on a native control.",
2858
+ "type": "ValidityState"
2859
+ },
2860
+ {
2861
+ "name": "validationMessage",
2862
+ "description": "The message of the currently failing constraint, `''` while valid.",
2863
+ "type": "string"
2864
+ },
2865
+ {
2866
+ "name": "willValidate",
2867
+ "description": "Whether the control is a candidate for constraint validation — false\nwhile disabled or readonly, per HTML's barring rules (the browser\ncomputes this from the reflected attributes).",
2868
+ "type": "boolean"
2869
+ }
2870
+ ],
2871
+ "events": [
2872
+ {
2873
+ "name": "vf-input",
2874
+ "description": "On every keystroke. `detail: { value: string }`."
2875
+ },
2876
+ {
2877
+ "name": "vf-change",
2878
+ "description": "On commit (native `change`). `detail: { value: string }`."
2879
+ },
2880
+ {
2881
+ "name": "input",
2882
+ "description": "The native keystroke event: the inner textarea's own, composed, so it crosses the shadow boundary and retargets to the host."
2883
+ },
2884
+ {
2885
+ "name": "change",
2886
+ "description": "The native commit event, re-dispatched from the host (the inner one is `composed: false` and never leaves the shadow root). The input-behavior attributes — `autocomplete`, `inputmode`, `enterkeyhint`, `maxlength`, `spellcheck`, `autocapitalize` — are forwarded from the host onto the inner textarea, where the platform actually honors them (`pattern` is not: only an `<input>` takes it)."
2887
+ }
2888
+ ]
2889
+ }
2890
+ },
2891
+ {
2892
+ "name": "vf-text-field",
2893
+ "description": "`<vf-text-field>` — a System 7 single-line text entry field.\n\nA form-associated wrapper around a native `<input>`: white well, 1px solid\nblack border, no corner radius. Keyboard focus draws the kit's dashed rule\none blank row under the well; a click leaves it unmarked (no dotted outline\neither, per SPEC §1/§5). The shared field skin lives in `vfField`; the\nvalue/form scaffolding — and the focus rule's modality gate — in\nVfTextControlBase.\n---\n\n\n### **Events:**\n - **vf-input** - On every keystroke. `detail: { value: string }`.\n- **vf-change** - On commit (native `change`). `detail: { value: string }`.\n- **input** - The native keystroke event: the inner input's own, composed, so it crosses the shadow boundary and retargets to the host by itself.\n- **change** - The native commit event, re-dispatched from the host (the inner one is `composed: false` and never leaves the shadow root), so form delegation and framework bindings hear it. The input-behavior attributes — `autocomplete`, `inputmode`, `enterkeyhint`, `maxlength`, `pattern`, `spellcheck`, `autocapitalize` — are forwarded from the host onto the inner input, where the platform actually honors them.\n\n### **CSS Properties:**\n - **--vf-control-height** - text fields — `vf-text-field`, `vf-text-area`, the `vf-number-field` well _(default: 22px)_\n- **--vf-field-width** - default width of `vf-text-field` / `vf-text-area` _(default: 180px)_\n- **--vf-field-placeholder** - placeholder text in the editable fields — kept off `--vf-disabled`: a placeholder sits in an *enabled* well and holds AA contrast, where the disabled gray is exempt _(default: #767676)_\n\n### **CSS Parts:**\n - **input** - The inner native `<input>` element.",
2894
+ "doc-url": "",
2895
+ "attributes": [
2896
+ {
2897
+ "name": "type",
2898
+ "description": "Input type, passed through to the native input (e.g. `password`).",
2899
+ "value": { "type": "string", "default": "'text'" }
2900
+ },
2901
+ {
2902
+ "name": "top",
2903
+ "description": "Offset from the top of the positioning parent, in whole system px.\nSetting this (or `left`) absolutely positions the element within its\nparent; the coordinate left unset is 0. Remove both to return the\nelement to normal flow.",
2904
+ "value": { "type": "number | null | undefined" }
2905
+ },
2906
+ {
2907
+ "name": "left",
2908
+ "description": "Offset from the left of the positioning parent, in whole system px.\nSee top.",
2909
+ "value": { "type": "number | null | undefined" }
2910
+ },
2911
+ {
2912
+ "name": "value",
2913
+ "description": "Current value. Synced on every keystroke and submitted with forms.",
2914
+ "value": { "type": "string", "default": "''" }
2915
+ },
2916
+ {
2917
+ "name": "placeholder",
2918
+ "description": "Placeholder text shown when the field is empty.",
2919
+ "value": { "type": "string", "default": "''" }
2920
+ },
2921
+ {
2922
+ "name": "readonly",
2923
+ "description": "Makes the field read-only (focusable, not editable).",
2924
+ "value": { "type": "boolean", "default": "false" }
2925
+ },
2926
+ {
2927
+ "name": "name",
2928
+ "description": "Form field name used when submitting the associated form.",
2929
+ "value": { "type": "string", "default": "''" }
2930
+ },
2931
+ {
2932
+ "name": "label",
2933
+ "description": "Accessible name, applied as `aria-label` on the inner native control (the\nelement that receives focus and is announced by screen readers — a host\nattribute cannot reach it on its own). Left empty, the name falls back to\nwhatever the host carries — `aria-labelledby`, `aria-label` or an\nassociated `<label for>` — via VfShadowRoleControl.hostLabel, so the\nplatform's own labeling vocabulary works on these controls too.",
2934
+ "value": { "type": "string", "default": "''" }
2935
+ },
2936
+ {
2937
+ "name": "description",
2938
+ "description": "Description for the control — hint text, a format, a unit. A host-level\n`aria-describedby` cannot reach a focusable element inside a shadow root,\nso there was structurally no way to describe a field; this property is\nthat channel. It renders as a hidden span in the control's own shadow root\nwith the inner control's `aria-describedby` pointing at it — the\nshadow-internal IDREF idiom `vf-dialog`'s title patch already uses. A\nhost-level `aria-describedby` is bridged into the same span when this\nproperty is empty, and a failing constraint's validationMessage\njoins it too, so AT hears the error where it hears the hint.\n\nHost-role controls get neither half: their `aria-describedby` already\nworks, and their validation message reaches AT the way a native control's\ndoes — `aria-invalid` plus the browser's own validation UI, not AccName.",
2939
+ "value": { "type": "string", "default": "''" }
2940
+ },
2941
+ {
2942
+ "name": "disabled",
2943
+ "description": "Disables the control: it stops responding and submits no value. Each\ncontrol dims per SPEC §1 (typically the label greys while the 1-bit chrome\nstays black).",
2944
+ "value": { "type": "boolean", "default": "false" }
2945
+ },
2946
+ {
2947
+ "name": "required",
2948
+ "description": "Requires a value before the associated form submits (SPEC §4): an empty\ncontrol fails constraint validation with `valueMissing`, exactly like a\nnative `required`. What \"empty\" means is each control's own\nvalueMissing; a control with no required semantics (a slider\nalways has a value, a swatch submits nothing) never fails it, the way a\nnative range input never does.",
2949
+ "value": { "type": "boolean", "default": "false" }
2950
+ }
2951
+ ],
2952
+ "events": [
2953
+ {
2954
+ "name": "vf-input",
2955
+ "description": "On every keystroke. `detail: { value: string }`."
2956
+ },
2957
+ {
2958
+ "name": "vf-change",
2959
+ "description": "On commit (native `change`). `detail: { value: string }`."
2960
+ },
2961
+ {
2962
+ "name": "input",
2963
+ "description": "The native keystroke event: the inner input's own, composed, so it crosses the shadow boundary and retargets to the host by itself."
2964
+ },
2965
+ {
2966
+ "name": "change",
2967
+ "description": "The native commit event, re-dispatched from the host (the inner one is `composed: false` and never leaves the shadow root), so form delegation and framework bindings hear it. The input-behavior attributes — `autocomplete`, `inputmode`, `enterkeyhint`, `maxlength`, `pattern`, `spellcheck`, `autocapitalize` — are forwarded from the host onto the inner input, where the platform actually honors them."
2968
+ }
2969
+ ],
2970
+ "js": {
2971
+ "properties": [
2972
+ {
2973
+ "name": "type",
2974
+ "description": "Input type, passed through to the native input (e.g. `password`).",
2975
+ "type": "string"
2976
+ },
2977
+ {
2978
+ "name": "top",
2979
+ "description": "Offset from the top of the positioning parent, in whole system px.\nSetting this (or `left`) absolutely positions the element within its\nparent; the coordinate left unset is 0. Remove both to return the\nelement to normal flow.",
2980
+ "type": "number | null | undefined"
2981
+ },
2982
+ {
2983
+ "name": "left",
2984
+ "description": "Offset from the left of the positioning parent, in whole system px.\nSee top.",
2985
+ "type": "number | null | undefined"
2986
+ },
2987
+ {
2988
+ "name": "value",
2989
+ "description": "Current value. Synced on every keystroke and submitted with forms.",
2990
+ "type": "string"
2991
+ },
2992
+ {
2993
+ "name": "placeholder",
2994
+ "description": "Placeholder text shown when the field is empty.",
2995
+ "type": "string"
2996
+ },
2997
+ {
2998
+ "name": "readonly",
2999
+ "description": "Makes the field read-only (focusable, not editable).",
3000
+ "type": "boolean"
3001
+ },
3002
+ {
3003
+ "name": "name",
3004
+ "description": "Form field name used when submitting the associated form.",
3005
+ "type": "string"
3006
+ },
3007
+ {
3008
+ "name": "label",
3009
+ "description": "Accessible name, applied as `aria-label` on the inner native control (the\nelement that receives focus and is announced by screen readers — a host\nattribute cannot reach it on its own). Left empty, the name falls back to\nwhatever the host carries — `aria-labelledby`, `aria-label` or an\nassociated `<label for>` — via VfShadowRoleControl.hostLabel, so the\nplatform's own labeling vocabulary works on these controls too.",
3010
+ "type": "string"
3011
+ },
3012
+ {
3013
+ "name": "description",
3014
+ "description": "Description for the control — hint text, a format, a unit. A host-level\n`aria-describedby` cannot reach a focusable element inside a shadow root,\nso there was structurally no way to describe a field; this property is\nthat channel. It renders as a hidden span in the control's own shadow root\nwith the inner control's `aria-describedby` pointing at it — the\nshadow-internal IDREF idiom `vf-dialog`'s title patch already uses. A\nhost-level `aria-describedby` is bridged into the same span when this\nproperty is empty, and a failing constraint's validationMessage\njoins it too, so AT hears the error where it hears the hint.\n\nHost-role controls get neither half: their `aria-describedby` already\nworks, and their validation message reaches AT the way a native control's\ndoes — `aria-invalid` plus the browser's own validation UI, not AccName.",
3015
+ "type": "string"
3016
+ },
3017
+ {
3018
+ "name": "disabled",
3019
+ "description": "Disables the control: it stops responding and submits no value. Each\ncontrol dims per SPEC §1 (typically the label greys while the 1-bit chrome\nstays black).",
3020
+ "type": "boolean"
3021
+ },
3022
+ {
3023
+ "name": "isDisabled",
3024
+ "description": "Effective disabled state: the `disabled` prop OR an ancestor fieldset.",
3025
+ "type": "boolean"
3026
+ },
3027
+ {
3028
+ "name": "required",
3029
+ "description": "Requires a value before the associated form submits (SPEC §4): an empty\ncontrol fails constraint validation with `valueMissing`, exactly like a\nnative `required`. What \"empty\" means is each control's own\nvalueMissing; a control with no required semantics (a slider\nalways has a value, a swatch submits nothing) never fails it, the way a\nnative range input never does.",
3030
+ "type": "boolean"
3031
+ },
3032
+ {
3033
+ "name": "validity",
3034
+ "description": "The control's current `ValidityState`, as on a native control.",
3035
+ "type": "ValidityState"
3036
+ },
3037
+ {
3038
+ "name": "validationMessage",
3039
+ "description": "The message of the currently failing constraint, `''` while valid.",
3040
+ "type": "string"
3041
+ },
3042
+ {
3043
+ "name": "willValidate",
3044
+ "description": "Whether the control is a candidate for constraint validation — false\nwhile disabled or readonly, per HTML's barring rules (the browser\ncomputes this from the reflected attributes).",
3045
+ "type": "boolean"
3046
+ }
3047
+ ],
3048
+ "events": [
3049
+ {
3050
+ "name": "vf-input",
3051
+ "description": "On every keystroke. `detail: { value: string }`."
3052
+ },
3053
+ {
3054
+ "name": "vf-change",
3055
+ "description": "On commit (native `change`). `detail: { value: string }`."
3056
+ },
3057
+ {
3058
+ "name": "input",
3059
+ "description": "The native keystroke event: the inner input's own, composed, so it crosses the shadow boundary and retargets to the host by itself."
3060
+ },
3061
+ {
3062
+ "name": "change",
3063
+ "description": "The native commit event, re-dispatched from the host (the inner one is `composed: false` and never leaves the shadow root), so form delegation and framework bindings hear it. The input-behavior attributes — `autocomplete`, `inputmode`, `enterkeyhint`, `maxlength`, `pattern`, `spellcheck`, `autocapitalize` — are forwarded from the host onto the inner input, where the platform actually honors them."
3064
+ }
3065
+ ]
3066
+ }
3067
+ },
3068
+ {
3069
+ "name": "vf-window",
3070
+ "description": "`<vf-window>` — the System 7 desktop-window shell.\n\nStriped title bar with optional close box (left) and zoom box (right), a\nsolid-white frame with a hard offset shadow, an optional grow box for\nresizing, optional edge scroll rails (`scrollbars`), and the slim windoid\nchrome (`variant=\"utility\"`). The HIG's window archetypes are parameter\nrecipes over this shell rather than fixed anatomies — the component enables\nHIG compliance, it doesn't enforce it (see docs/LAYOUT.md \"Window archetypes\"):\nthe full document window is `closable zoomable movable resizable\nscrollbars=\"both\"`, a modeless dialog box is `closable movable`, a utility\nwindow is `variant=\"utility\" movable`. Place inside `<vf-desktop>` to get\nclick-to-front stacking and automatic `active` management (utility windows\nfloat above the document tier).\n\nEvery recipe also declares `width` AND `height` (VfSized), in whole\nsystem px — the art's own unit, so the window keeps its proportions to the\nchrome inside it at every display density (a CSS-px size stays put while\nthe components in it triple). A window is a fixed box in both axes, the way\nthe WIND resource carried it: left to layout it takes whatever its\ncontainer or content hands it, which is how a title bar ends up wider than\nthe screen or a dialog reflows as it moves — and a window that grows with\nits body is one the user can neither predict nor (via the grow box) own.\nContent taller than the declared box is clipped at the frame the way the\nclassic content region was; `scrollbars` lets the user reach the rest.\nUnset, the window still renders — normal block layout, as before — and\nsays so once in the console.\n---\n\n\n### **Events:**\n - **vf-close** - Close box clicked. Detail `{ reason: 'close' }` (shape- compatible with vf-dialog's `vf-close`). The window does NOT remove itself; the consumer decides what closing means.\n- **vf-zoom** - Zoom box clicked. Detail `{}`.\n\n### **Slots:**\n - _default_ - Default slot: window body content.\n\n### **CSS Properties:**\n - **--vf-dots-pattern** - the windoid bar's dot-grid dither — a 2×2 motif, one black pixel at its origin, on a 30-system-px tile (`vfDots`; override the whole tile like `--vf-desktop-pattern` — consumer art renders as a placed tile grid at that same geometry) _(default: undefined)_\n- **--vf-titlebar-height** - window/dialog title bars _(default: undefined)_\n- **--vf-titlebar-height-utility** - the slim `vf-window[variant=\"utility\"]` (windoid) bar — 11px interior + 1px bottom rule, traced from `Windows/utility-window.png` _(default: 12px)_\n\n### **CSS Parts:**\n - **frame** - The outer chrome frame.\n- **title-bar** - The striped (or dithered) title bar.\n- **title** - The centered title patch (hidden on the utility bar).\n- **close-box** - The close widget (left).\n- **zoom-box** - The zoom widget (right).\n- **body** - The content area.\n- **grow-box** - The resize widget (bottom-right, when `resizable`).\n- **viewport** - The built-in scroll area's viewport (when `scrollbars`; re-exported from vf-scroll-area).",
3071
+ "doc-url": "",
3072
+ "attributes": [
3073
+ {
3074
+ "name": "variant",
3075
+ "description": "Chrome variant. Omit for the standard 18px striped bar; `'utility'` for\nthe slim windoid bar (dot-grid dither, 7×7 widgets, no title patch — the\nheading still names the widgets). Inside a `vf-desktop`, utility windows\nfloat above the document tier and never take the single-active state.",
3076
+ "value": { "type": "'utility' | undefined" }
3077
+ },
3078
+ {
3079
+ "name": "heading",
3080
+ "description": "Title text shown centered in the title bar.",
3081
+ "value": { "type": "string", "default": "''" }
3082
+ },
3083
+ {
3084
+ "name": "active",
3085
+ "description": "Whether this is the frontmost (active) window: stripes and widgets show.\nManaged automatically by an enclosing `vf-desktop`.",
3086
+ "value": { "type": "boolean", "default": "true" }
3087
+ },
3088
+ {
3089
+ "name": "closable",
3090
+ "description": "Show the close box (left side of the title bar).",
3091
+ "value": { "type": "boolean", "default": "true" }
3092
+ },
3093
+ {
3094
+ "name": "zoomable",
3095
+ "description": "Show the zoom box (right side of the title bar).",
3096
+ "value": { "type": "boolean", "default": "false" }
3097
+ },
3098
+ {
3099
+ "name": "movable",
3100
+ "description": "Allow dragging the window by its title bar.",
3101
+ "value": { "type": "boolean", "default": "false" }
3102
+ },
3103
+ {
3104
+ "name": "resizable",
3105
+ "description": "Show a grow box at the bottom-right corner for resizing.",
3106
+ "value": { "type": "boolean", "default": "false" }
3107
+ },
3108
+ {
3109
+ "name": "flush",
3110
+ "description": "Remove the default 12px body padding.",
3111
+ "value": { "type": "boolean", "default": "false" }
3112
+ },
3113
+ {
3114
+ "name": "scrollbars",
3115
+ "description": "Put System 7 scroll rails on the window edge — the classic document\nwindow. The body slot renders inside a built-in `vf-scroll-area` pulled\none system pixel under the frame on every side, so the rails repaint the\nborder lines and a `resizable` window's grow box lands in the corner\ncell. Values mirror `vf-scroll-area`'s `axis`; the `heading` names the\nscroll region; the viewport part is re-exported. Implies `flush` (the\nviewport carries its own padding). The slotted composition (SPEC §5\nvf-scroll-area) still works for windows that want an inset well instead.",
3116
+ "value": {
3117
+ "type": "'vertical' | 'horizontal' | 'both' | undefined"
3118
+ }
3119
+ },
3120
+ {
3121
+ "name": "width",
3122
+ "description": "Width in whole system px. What the number means is the component's own\naffair — the measure a paragraph wraps to, a caption column, a window's\nbox — but the mechanics are shared: the length lands on the host as a\nlive `calc(var(--vf-scale, 1) * Npx)`, so the box scales with the\ndisplay and sits on the device-pixel grid by construction. Remove it\nand the width returns to layout.",
3123
+ "value": { "type": "number | null | undefined" }
3124
+ },
3125
+ {
3126
+ "name": "height",
3127
+ "description": "Height in whole system px; see width. Content that outgrows a\ndeclared height overflows the box rather than growing it — the number\nis the layout, and content that doesn't fit is a number to raise.",
3128
+ "value": { "type": "number | null | undefined" }
3129
+ },
3130
+ {
3131
+ "name": "top",
3132
+ "description": "Offset from the top of the positioning parent, in whole system px.\nSetting this (or `left`) absolutely positions the element within its\nparent; the coordinate left unset is 0. Remove both to return the\nelement to normal flow.",
3133
+ "value": { "type": "number | null | undefined" }
3134
+ },
3135
+ {
3136
+ "name": "left",
3137
+ "description": "Offset from the left of the positioning parent, in whole system px.\nSee top.",
3138
+ "value": { "type": "number | null | undefined" }
3139
+ }
3140
+ ],
3141
+ "slots": [
3142
+ { "name": "", "description": "Default slot: window body content." }
3143
+ ],
3144
+ "events": [
3145
+ {
3146
+ "name": "vf-close",
3147
+ "description": "Close box clicked. Detail `{ reason: 'close' }` (shape- compatible with vf-dialog's `vf-close`). The window does NOT remove itself; the consumer decides what closing means."
3148
+ },
3149
+ {
3150
+ "name": "vf-zoom",
3151
+ "description": "Zoom box clicked. Detail `{}`."
3152
+ }
3153
+ ],
3154
+ "js": {
3155
+ "properties": [
3156
+ {
3157
+ "name": "variant",
3158
+ "description": "Chrome variant. Omit for the standard 18px striped bar; `'utility'` for\nthe slim windoid bar (dot-grid dither, 7×7 widgets, no title patch — the\nheading still names the widgets). Inside a `vf-desktop`, utility windows\nfloat above the document tier and never take the single-active state.",
3159
+ "type": "'utility' | undefined"
3160
+ },
3161
+ {
3162
+ "name": "heading",
3163
+ "description": "Title text shown centered in the title bar.",
3164
+ "type": "string"
3165
+ },
3166
+ {
3167
+ "name": "active",
3168
+ "description": "Whether this is the frontmost (active) window: stripes and widgets show.\nManaged automatically by an enclosing `vf-desktop`.",
3169
+ "type": "boolean"
3170
+ },
3171
+ {
3172
+ "name": "closable",
3173
+ "description": "Show the close box (left side of the title bar).",
3174
+ "type": "boolean"
3175
+ },
3176
+ {
3177
+ "name": "zoomable",
3178
+ "description": "Show the zoom box (right side of the title bar).",
3179
+ "type": "boolean"
3180
+ },
3181
+ {
3182
+ "name": "movable",
3183
+ "description": "Allow dragging the window by its title bar.",
3184
+ "type": "boolean"
3185
+ },
3186
+ {
3187
+ "name": "resizable",
3188
+ "description": "Show a grow box at the bottom-right corner for resizing.",
3189
+ "type": "boolean"
3190
+ },
3191
+ {
3192
+ "name": "flush",
3193
+ "description": "Remove the default 12px body padding.",
3194
+ "type": "boolean"
3195
+ },
3196
+ {
3197
+ "name": "scrollbars",
3198
+ "description": "Put System 7 scroll rails on the window edge — the classic document\nwindow. The body slot renders inside a built-in `vf-scroll-area` pulled\none system pixel under the frame on every side, so the rails repaint the\nborder lines and a `resizable` window's grow box lands in the corner\ncell. Values mirror `vf-scroll-area`'s `axis`; the `heading` names the\nscroll region; the viewport part is re-exported. Implies `flush` (the\nviewport carries its own padding). The slotted composition (SPEC §5\nvf-scroll-area) still works for windows that want an inset well instead.",
3199
+ "type": "'vertical' | 'horizontal' | 'both' | undefined"
3200
+ },
3201
+ {
3202
+ "name": "width",
3203
+ "description": "Width in whole system px. What the number means is the component's own\naffair — the measure a paragraph wraps to, a caption column, a window's\nbox — but the mechanics are shared: the length lands on the host as a\nlive `calc(var(--vf-scale, 1) * Npx)`, so the box scales with the\ndisplay and sits on the device-pixel grid by construction. Remove it\nand the width returns to layout.",
3204
+ "type": "number | null | undefined"
3205
+ },
3206
+ {
3207
+ "name": "height",
3208
+ "description": "Height in whole system px; see width. Content that outgrows a\ndeclared height overflows the box rather than growing it — the number\nis the layout, and content that doesn't fit is a number to raise.",
3209
+ "type": "number | null | undefined"
3210
+ },
3211
+ {
3212
+ "name": "top",
3213
+ "description": "Offset from the top of the positioning parent, in whole system px.\nSetting this (or `left`) absolutely positions the element within its\nparent; the coordinate left unset is 0. Remove both to return the\nelement to normal flow.",
3214
+ "type": "number | null | undefined"
3215
+ },
3216
+ {
3217
+ "name": "left",
3218
+ "description": "Offset from the left of the positioning parent, in whole system px.\nSee top.",
3219
+ "type": "number | null | undefined"
3220
+ }
3221
+ ],
3222
+ "events": [
3223
+ {
3224
+ "name": "vf-close",
3225
+ "description": "Close box clicked. Detail `{ reason: 'close' }` (shape- compatible with vf-dialog's `vf-close`). The window does NOT remove itself; the consumer decides what closing means."
3226
+ },
3227
+ {
3228
+ "name": "vf-zoom",
3229
+ "description": "Zoom box clicked. Detail `{}`."
3230
+ }
3231
+ ]
3232
+ }
3233
+ }
3234
+ ]
3235
+ }
3236
+ }
3237
+ }