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,222 @@
1
+ import { tileImage as e, tileRects as t, vfTileMaskSize as n } from "./tile.js";
2
+ import { css as r, unsafeCSS as i } from "lit";
3
+ var a = [[
4
+ 0,
5
+ 0,
6
+ 1,
7
+ 1
8
+ ], [
9
+ 2,
10
+ 1,
11
+ 1,
12
+ 1
13
+ ]], o = e(4, 2, t(a)), s = r`
14
+ /* Hide the native bar, keep the native scrolling. Both lines ship:
15
+ scrollbar-width reaches Chromium 121+/Firefox/Safari 18.2+, the pseudo
16
+ covers older WebKit — the last scrollbar pseudo left in the kit. */
17
+ .vf-scroll {
18
+ scrollbar-width: none;
19
+ }
20
+ .vf-scroll::-webkit-scrollbar {
21
+ display: none;
22
+ }
23
+
24
+ /* The rail: a grid of [arrow | track | arrow] along its axis, sized to the
25
+ 15px inside the component frame (1px divider + 14px channel). The rail
26
+ always uses the default arrow pointer, never the host's cursor —
27
+ otherwise a textarea's text I-beam bleeds over the reserved rail. */
28
+ .vf-rail {
29
+ display: grid;
30
+ background: var(--vf-white, #fff);
31
+ cursor: var(--vf-cursor, default);
32
+ touch-action: none;
33
+ }
34
+ .vf-rail--vertical {
35
+ width: calc(var(--vf-scale, 1) * 15px);
36
+ grid-template-rows:
37
+ calc(var(--vf-scale, 1) * 15px)
38
+ 1fr
39
+ calc(var(--vf-scale, 1) * 15px);
40
+ border-left: calc(var(--vf-scale, 1) * 1px) solid var(--vf-black, #000);
41
+ }
42
+ .vf-rail--horizontal {
43
+ height: calc(var(--vf-scale, 1) * 15px);
44
+ grid-template-columns:
45
+ calc(var(--vf-scale, 1) * 15px)
46
+ 1fr
47
+ calc(var(--vf-scale, 1) * 15px);
48
+ border-top: calc(var(--vf-scale, 1) * 1px) solid var(--vf-black, #000);
49
+ }
50
+ /* Explicit placement, so hiding the arrows (inactive-window/degenerate
51
+ states) never reflows the track into an arrow cell — its box holds still
52
+ through every state flip. */
53
+ .vf-rail--vertical .vf-rail-button--decrement {
54
+ grid-row: 1;
55
+ }
56
+ .vf-rail--vertical .vf-rail-track {
57
+ grid-row: 2;
58
+ }
59
+ .vf-rail--vertical .vf-rail-button--increment {
60
+ grid-row: 3;
61
+ }
62
+ .vf-rail--horizontal .vf-rail-button--decrement {
63
+ grid-column: 1;
64
+ }
65
+ .vf-rail--horizontal .vf-rail-track {
66
+ grid-column: 2;
67
+ }
68
+ .vf-rail--horizontal .vf-rail-button--increment {
69
+ grid-column: 3;
70
+ }
71
+
72
+ /* Arrow cells: white boxes carrying only the 1px divider that faces the
73
+ track — the frame edges are the component's border, the content edge the
74
+ rail's own divider — so no line ever doubles to 2px. Each cell's padding
75
+ box is by construction the sprite's 14×14 interior, wherever the cell
76
+ sits (flush to the frame, or against the corner cell), so the glyph
77
+ lands at the padding origin with no per-variant offsets. */
78
+ .vf-rail-button {
79
+ display: grid;
80
+ place-items: start;
81
+ background: var(--vf-white, #fff);
82
+ overflow: hidden;
83
+ }
84
+ .vf-rail--vertical .vf-rail-button--decrement {
85
+ border-bottom: calc(var(--vf-scale, 1) * 1px) solid var(--vf-black, #000);
86
+ }
87
+ .vf-rail--vertical .vf-rail-button--increment {
88
+ border-top: calc(var(--vf-scale, 1) * 1px) solid var(--vf-black, #000);
89
+ }
90
+ .vf-rail--horizontal .vf-rail-button--decrement {
91
+ border-right: calc(var(--vf-scale, 1) * 1px) solid var(--vf-black, #000);
92
+ }
93
+ .vf-rail--horizontal .vf-rail-button--increment {
94
+ border-left: calc(var(--vf-scale, 1) * 1px) solid var(--vf-black, #000);
95
+ }
96
+ .vf-rail-arrow {
97
+ display: block;
98
+ grid-area: 1 / 1;
99
+ width: calc(var(--vf-scale, 1) * 14px);
100
+ height: calc(var(--vf-scale, 1) * 14px);
101
+ }
102
+ /* Hollow at rest, solid while pressed (ScrollRailController holds
103
+ data-pressed on the button for the press's whole extent, auto-repeat
104
+ included). */
105
+ .vf-rail-arrow--fill,
106
+ .vf-rail-button[data-pressed] .vf-rail-arrow--rest {
107
+ display: none;
108
+ }
109
+ .vf-rail-button[data-pressed] .vf-rail-arrow--fill {
110
+ display: block;
111
+ }
112
+
113
+ /* The track between the arrows: the trough (dither) fills it, the thumb
114
+ travels it. */
115
+ .vf-rail-track {
116
+ position: relative;
117
+ overflow: hidden;
118
+ min-width: 0;
119
+ min-height: 0;
120
+ }
121
+ /* The trough: the token base color under the dither, clipping the raster's
122
+ overdraw (the art is ceiled up to whole motifs of the measured track). */
123
+ .vf-rail-trough {
124
+ position: absolute;
125
+ inset: 0;
126
+ overflow: hidden;
127
+ background-color: var(--vf-scrollbar-track, var(--vf-white, #fff));
128
+ pointer-events: none;
129
+ }
130
+ /* The dither raster ScrollRailController sizes and writes — one image px
131
+ per system px, magnified nearest-neighbor (the vf-img idiom), so the
132
+ lattice is 1-bit at every scale. */
133
+ .vf-rail-trough-art {
134
+ position: absolute;
135
+ top: 0;
136
+ left: 0;
137
+ background-size: 100% 100%;
138
+ background-repeat: no-repeat;
139
+ image-rendering: pixelated;
140
+ }
141
+ /* The fixed System 7 thumb (never proportional): a white 16px box spanning
142
+ the channel. Its border reads as inset 1px from each channel rail — the
143
+ divider and the frame — over its extent; the controller writes its
144
+ travel as a whole-system-px translate. */
145
+ .vf-rail-thumb {
146
+ position: absolute;
147
+ top: 0;
148
+ left: 0;
149
+ background: var(--vf-scrollbar-thumb, #ffffff);
150
+ border: calc(var(--vf-scale, 1) * 1px) solid var(--vf-black, #000);
151
+ }
152
+ .vf-rail--vertical .vf-rail-thumb {
153
+ width: 100%;
154
+ height: calc(var(--vf-scale, 1) * 16px);
155
+ }
156
+ .vf-rail--horizontal .vf-rail-thumb {
157
+ height: 100%;
158
+ width: calc(var(--vf-scale, 1) * 16px);
159
+ }
160
+
161
+ /* The both-axes corner cell. It supplies the two interior dividers
162
+ (against the vertical increment arrow above and the horizontal one
163
+ beside it) that those buttons leave to it; a resizable window's grow box
164
+ lands exactly over it. */
165
+ .vf-rail-corner {
166
+ background: var(--vf-white, #fff);
167
+ border-top: calc(var(--vf-scale, 1) * 1px) solid var(--vf-black, #000);
168
+ border-left: calc(var(--vf-scale, 1) * 1px) solid var(--vf-black, #000);
169
+ }
170
+
171
+ /* ── Always-a-rail (driven by ScrollStateController) ─────────────────────
172
+ Idle axis: the reserved rail keeps its arrow buttons — System 7 drew
173
+ them on any bar in an active window — while the dither and thumb appear
174
+ only once that axis actually overflows, leaving arrows on a bare white
175
+ channel. The drawn arrows are inert (ScrollRailController's press guard):
176
+ there is nothing to scroll. */
177
+ .vf-scroll[data-overflow-y='false'] ~ .vf-rail--vertical .vf-rail-trough,
178
+ .vf-scroll[data-overflow-y='false'] ~ .vf-rail--vertical .vf-rail-thumb,
179
+ .vf-scroll[data-overflow-x='false'] ~ .vf-rail--horizontal .vf-rail-trough,
180
+ .vf-scroll[data-overflow-x='false'] ~ .vf-rail--horizontal .vf-rail-thumb {
181
+ display: none;
182
+ }
183
+ /* Inactive window: both axes blanked to the bare rail — arrows included,
184
+ unlike the idle state — regardless of overflow: a window that isn't
185
+ frontmost must not display interactive scroll UX (HIG; System 7 blanked
186
+ deactivated scrollbars to the empty rail). */
187
+ .vf-scroll[data-window-inactive] ~ .vf-rail .vf-rail-trough,
188
+ .vf-scroll[data-window-inactive] ~ .vf-rail .vf-rail-thumb,
189
+ .vf-scroll[data-window-inactive] ~ .vf-rail .vf-rail-button {
190
+ display: none;
191
+ }
192
+
193
+ /* ── Degenerate tracks (written by ScrollRailController) ─────────────────
194
+ A track shorter than the 16px thumb drops the thumb; a rail too short
195
+ for its arrow cells drops the trough and arrows too and reads as the
196
+ bare white rail — the classic Control Manager's decision table. */
197
+ .vf-rail[data-degenerate] .vf-rail-thumb {
198
+ display: none;
199
+ }
200
+ .vf-rail[data-degenerate='rail'] .vf-rail-trough,
201
+ .vf-rail[data-degenerate='rail'] .vf-rail-button {
202
+ display: none;
203
+ }
204
+
205
+ /* Forced colors: the raster keeps literal ink, so it hides and the trough
206
+ repaints as the remapped ink token masked by the same art (the vfDots
207
+ idiom — and like every masked tile, the span mask keeps the zoom
208
+ caveat). Everything else re-inks through the vfBase token remap; the
209
+ arrows are currentColor SVG and follow the palette by themselves. */
210
+ @media (forced-colors: active) {
211
+ .vf-rail-trough {
212
+ background-color: var(--vf-black, #000);
213
+ mask-image: ${i(o)};
214
+ ${n(4, 2)}
215
+ }
216
+ .vf-rail-trough-art {
217
+ display: none;
218
+ }
219
+ }
220
+ `;
221
+ //#endregion
222
+ export { a as TROUGH_RECTS, s as vfScrollRail };
@@ -0,0 +1,13 @@
1
+ /**
2
+ * The hard 1-bit drop shadow — a solid black copy of the box offset down-right
3
+ * with no blur and no spread, System 7's only depth cue. The offset is
4
+ * tokenized via `--vf-shadow-offset` and scales with `--vf-scale`.
5
+ *
6
+ * Compose it into any raised surface. Every raised surface in the kit does:
7
+ * {@link vfPanel} (menus, popups) and {@link vfChromeFrame} (window and dialog
8
+ * frames). One declaration, so the kit can't grow two shadows.
9
+ *
10
+ * Kept in its own module because it is the one recipe consumed on its own:
11
+ * vf-swatch composes the shadow without either surface class.
12
+ */
13
+ export declare const vfHardShadowDecls: import("lit").CSSResult;
@@ -0,0 +1,5 @@
1
+ import { unsafeCSS as e } from "lit";
2
+ //#region src/styles/recipes/shadow.ts
3
+ var t = e("\n box-shadow: calc(var(--vf-scale, 1) * var(--vf-shadow-offset, 2px))\n calc(var(--vf-scale, 1) * var(--vf-shadow-offset, 2px)) 0 0\n var(--vf-black, #000);\n");
4
+ //#endregion
5
+ export { t as vfHardShadowDecls };
@@ -0,0 +1,24 @@
1
+ /**
2
+ * The two host switches shared by the kit's static-text components
3
+ * (`vf-label`, `vf-paragraph`) — the only things those two do identically:
4
+ *
5
+ * - `face="display" | "body"` picks the face explicitly, so a caption can be
6
+ * set in body copy or a paragraph in chrome type. Each component keeps its
7
+ * own *default* face in its own stylesheet (a label is chrome, a paragraph is
8
+ * body); this is the override.
9
+ * - `dim` greys the text to `--vf-disabled` — the System 7 dimmed-text
10
+ * treatment (SPEC §1), for static text beside a disabled control.
11
+ *
12
+ * Each face renders at its native strike size — one design px = one system
13
+ * px. The body face doubles as the kit's fine print, exactly as System 7's
14
+ * did: a dialog's small captions are Geneva 9 at its own size (SPEC §3,
15
+ * "One size, honestly").
16
+ *
17
+ * Every rule is `:host([attr])`, one specificity step above the plain `:host`
18
+ * rule a component sets its own defaults in, so they win wherever they are
19
+ * composed.
20
+ *
21
+ * Offering both faces is the point of the recipe, so this is the one module
22
+ * that pulls both font files whatever the component does with it.
23
+ */
24
+ export declare const vfStaticText: import("lit").CSSResult;
@@ -0,0 +1,17 @@
1
+ import { vfBodyDecls as e } from "./body-face.js";
2
+ import { vfDisplayDecls as t } from "./display-face.js";
3
+ import { css as n } from "lit";
4
+ //#region src/styles/recipes/static-text.ts
5
+ var r = n`
6
+ :host([face='display']) {
7
+ ${t}
8
+ }
9
+ :host([face='body']) {
10
+ ${e}
11
+ }
12
+ :host([dim]) {
13
+ color: var(--vf-disabled, #c0c0c0);
14
+ }
15
+ `;
16
+ //#endregion
17
+ export { r as vfStaticText };
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Panel recipe for menus and popups: white face, 1px black border, hard
3
+ * offset shadow.
4
+ */
5
+ export declare const vfPanel: import("lit").CSSResult;
6
+ /**
7
+ * Chrome-frame recipe for the two framed containers (vf-window, vf-dialog):
8
+ * the same white face, 1px black border and hard offset shadow as
9
+ * {@link vfPanel}. Apply `.vf-frame` to the outer frame element.
10
+ *
11
+ * Skin only — each component adds its own layout, because they size very
12
+ * differently: vf-window's frame is a full-size flex column (the body flexes,
13
+ * the title bar and grow box don't), while vf-dialog's is a plain block that
14
+ * the native `<dialog>` shrink-wraps.
15
+ */
16
+ export declare const vfChromeFrame: import("lit").CSSResult;
17
+ /**
18
+ * The classic modal-dialog (dBoxProc) frame: 1px outer border, 2px white gap,
19
+ * 2px inner band — and NO drop shadow, per the `Windows/modal dialog.png`
20
+ * reference. System 7's alert box drew the
21
+ * *mirror* trace (2px outer, 2px gap, 1px inner rule, with the hard shadow) —
22
+ * a different chrome the kit does not ship: an alert is a composition over
23
+ * this frame, not a component.
24
+ *
25
+ * Apply `.vf-modal-frame` to the outer element and `.vf-modal-frame-inner` to
26
+ * a wrapper inside it; content goes in the wrapper.
27
+ */
28
+ export declare const vfModalFrame: import("lit").CSSResult;
@@ -0,0 +1,27 @@
1
+ import { vfHardShadowDecls as e } from "./shadow.js";
2
+ import { css as t, unsafeCSS as n } from "lit";
3
+ //#region src/styles/recipes/surface.ts
4
+ var r = n(`
5
+ background: var(--vf-white, #fff);
6
+ border: calc(var(--vf-scale, 1) * 1px) solid var(--vf-black, #000);
7
+ ${e}
8
+ `), i = t`
9
+ .vf-panel {
10
+ ${r}
11
+ }
12
+ `, a = t`
13
+ .vf-frame {
14
+ ${r}
15
+ }
16
+ `, o = t`
17
+ .vf-modal-frame {
18
+ background: var(--vf-white, #fff);
19
+ border: calc(var(--vf-scale, 1) * 1px) solid var(--vf-black, #000);
20
+ }
21
+ .vf-modal-frame-inner {
22
+ margin: calc(var(--vf-scale, 1) * 2px);
23
+ border: calc(var(--vf-scale, 1) * 2px) solid var(--vf-black, #000);
24
+ }
25
+ `;
26
+ //#endregion
27
+ export { a as vfChromeFrame, o as vfModalFrame, i as vfPanel };
@@ -0,0 +1,122 @@
1
+ import { type CSSResult } from 'lit';
2
+ /**
3
+ * The lattice a repeating tile's span has to land on, in system px.
4
+ *
5
+ * `--vf-scale` is `target / trueDpr` in lowest terms — 1, 3/2, 4/3, 8/5, 6/5,
6
+ * 5/4 — and a span of `N` system px is `N × scale` CSS px, which the engine can
7
+ * hold only when its layout grid can express it (Chromium's `LayoutUnit` is
8
+ * 1/64 CSS px, Gecko's app unit 1/60). For a scale `p/q` that needs `q` to
9
+ * divide `64 × N`, and the 64 already absorbs every power of two — so what is
10
+ * left is the ODD part of `q`, and every density the ladder derives has an odd
11
+ * part of 1, 3 or 5:
12
+ *
13
+ * dpr 1 → 1 dpr 1.25 → 8/5 dpr 1.5 → 4/3
14
+ * dpr 2 → 3/2 dpr 2.5 → 6/5 dpr 3 → 4/3 dpr 4 → 5/4
15
+ *
16
+ * 15 covers all of them at once, which is why a tile is measured in multiples
17
+ * of it rather than per-display: the span is a constant of the art, not of the
18
+ * screen, so the CSS stays a plain `calc()` with nothing to recompute when the
19
+ * density changes.
20
+ */
21
+ export declare const TILE_LATTICE = 15;
22
+ /**
23
+ * The span a motif of `motif` system px tiles at: the smallest multiple of
24
+ * {@link TILE_LATTICE} that is also a whole number of motifs, so the tile is
25
+ * both seamless and holdable.
26
+ *
27
+ * 1 → 15 2 → 30 3 → 15 4 → 60 12 → 60
28
+ */
29
+ export declare function tileSpan(motif: number): number;
30
+ /**
31
+ * A repeating fill's art, as a data URI: the `motif` markup laid into a tile of
32
+ * {@link tileSpan} system px by an SVG `<pattern>`, so the image carries the
33
+ * whole span while the source stays the two or three rects that are actually
34
+ * the artwork.
35
+ *
36
+ * The subdivision inside the tile costs nothing: the span is a whole number of
37
+ * device pixels (below), the SVG divides its box into that many whole units,
38
+ * and `shape-rendering: crispEdges` keeps every rect on them.
39
+ *
40
+ * @param motifWidth the motif's width in system px
41
+ * @param motifHeight the motif's height in system px
42
+ * @param motif the motif's markup, URL-encoded as the rest of the kit's
43
+ * inline SVG is (`%3Crect …/%3E`)
44
+ * @param spanWidth explicit tile width in system px (default
45
+ * `tileSpan(motifWidth)`) — a placed tile grid
46
+ * (src/tile-grid.ts) may size its tile independently of the
47
+ * lattice, since each placed box paint-snaps on its own
48
+ * @param spanHeight explicit tile height in system px, likewise
49
+ */
50
+ export declare function tileImage(motifWidth: number, motifHeight: number, motif: string, spanWidth?: number, spanHeight?: number): string;
51
+ /**
52
+ * One rect of a 1-bit motif, stated as data: `[x, y, w, h, fill]` in system px
53
+ * over the motif cell, painted in order. `fill` is a CSS hex color; omitted
54
+ * means black, the SVG default. The motif stated once as data is what lets a
55
+ * surface derive BOTH of its artifacts — the SVG tile ({@link tileImage} via
56
+ * {@link tileRects}) and the raster tile ({@link tileRaster}) — from the same
57
+ * two or three rects that are actually the artwork.
58
+ */
59
+ export type TileRect = readonly [x: number, y: number, w: number, h: number, fill?: string];
60
+ /** {@link TileRect} data as the URL-encoded rect markup {@link tileImage} takes. */
61
+ export declare function tileRects(rects: readonly TileRect[]): string;
62
+ /**
63
+ * The motif tiled over `spanWidth × spanHeight` system px as a RASTER image —
64
+ * a PNG data URI at one image px per system px — for the whole-surface fill a
65
+ * converted tiled surface paints (src/tile-grid.ts).
66
+ *
67
+ * A raster because of how engines rasterize a fill's art, not how they place
68
+ * its box. Chromium rasterizes an SVG image at the box's *stored* (layout)
69
+ * size — fractional at every scale the layout grid can't hold — and ignores
70
+ * `image-rendering` for SVG entirely, so the 1-bit rects antialias to gray no
71
+ * matter how exactly the box paint-snapped (measured; inline `crispEdges`
72
+ * patterns smear the same way). A raster source under
73
+ * `image-rendering: pixelated` is sampled nearest-neighbor, which can only
74
+ * ever produce source colors: the art is 1-bit by construction at every
75
+ * scale, holdable or not.
76
+ *
77
+ * Encoded through an offscreen canvas (a PNG encoder, not a render surface:
78
+ * the motif is drawn once into a cell, pattern-filled across the span, read
79
+ * out as a data URI and released — kit art only, so there is nothing
80
+ * cross-origin to taint). Where canvas is unavailable the SVG tile is
81
+ * returned instead, so the declaration still paints.
82
+ */
83
+ export declare function tileRaster(motifWidth: number, motifHeight: number, rects: readonly TileRect[], spanWidth?: number, spanHeight?: number): string;
84
+ /**
85
+ * `background-size` for a REPEATING tile — stated as the motif, sized as the
86
+ * span that motif tiles at ({@link tileSpan}).
87
+ *
88
+ * A tile is the one place where "one system pixel is a whole number of device
89
+ * pixels" is not enough on its own. That contract holds (`src/zoom.ts`), but
90
+ * the CSS *length* expressing it need not be one the engine can hold: at
91
+ * `--vf-scale` 4/3 a 2-system-px tile is 2.6667 CSS px and Chromium stores
92
+ * 2.65625. A single edge survives that — paint snaps each box to the device
93
+ * grid independently, which is why every border, stepped corner and magnified
94
+ * icon rasterizes exactly at 4/3 — but a tiled fill is ONE snapped box holding
95
+ * N unsnapped repeats, each placed at `k × tileSize`. The error compounds until
96
+ * the boundary has walked a whole device pixel, and 43% of the desktop dither
97
+ * rasterizes to mid-gray.
98
+ *
99
+ * Enlarging the tile removes the accumulation instead of trading it away: the
100
+ * span is holdable by construction, so every repeat lands exactly, and because
101
+ * the span is a whole number of motifs the pattern is unchanged — one system
102
+ * pixel of texture is still one system pixel, the same count of device pixels
103
+ * as every other edge on the screen. What is left over at an unholdable scale
104
+ * is a single seam per span rather than per motif, and the spans the kit uses
105
+ * (30 and 60 system px) are holdable at every density the ladder derives, so
106
+ * there is no seam at all.
107
+ *
108
+ * The cost is a bigger tile texture — 60 CSS px square at most, for art that
109
+ * repeats forever — and nothing else. There is no rounding, no display-derived
110
+ * custom property to keep in sync, and no `round()`, so it behaves the same in
111
+ * every engine.
112
+ *
113
+ * @param motifWidth the motif's width in system px
114
+ * @param motifHeight the motif's height in system px (defaults to the width)
115
+ */
116
+ export declare function vfTileSize(motifWidth: number, motifHeight?: number): CSSResult;
117
+ /**
118
+ * {@link vfTileSize} as `mask-size`, for the forced-colors branches that repaint
119
+ * a `url()` tile in the ink token by masking with the same art (the windoid
120
+ * dots, the barber stripes). Same span, so the mask lands on the fill.
121
+ */
122
+ export declare function vfTileMaskSize(motifWidth: number, motifHeight?: number): CSSResult;
@@ -0,0 +1,40 @@
1
+ import { css as e } from "lit";
2
+ //#region src/styles/recipes/tile.ts
3
+ var t = 15, n = (e, t) => t === 0 ? e : n(t, e % t);
4
+ function r(e) {
5
+ return e * 15 / n(e, 15);
6
+ }
7
+ function i(e, t, n, i = r(e), a = r(t)) {
8
+ let o = i, s = a;
9
+ return `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='${o}' height='${s}' shape-rendering='crispEdges'%3E%3Cdefs%3E%3Cpattern id='m' width='${e}' height='${t}' patternUnits='userSpaceOnUse'%3E${n}%3C/pattern%3E%3C/defs%3E%3Crect width='${o}' height='${s}' fill='url(%23m)'/%3E%3C/svg%3E")`;
10
+ }
11
+ function a(e) {
12
+ return e.map(([e, t, n, r, i]) => `%3Crect ${(e ? `x='${e}' ` : "") + (t ? `y='${t}' ` : "")}width='${n}' height='${r}'${i ? ` fill='${encodeURIComponent(i)}'` : ""}/%3E`).join("");
13
+ }
14
+ function o(e, t, n, o = r(e), s = r(t)) {
15
+ let c = () => i(e, t, a(n), o, s);
16
+ if (typeof document > "u") return c();
17
+ let l = document.createElement("canvas");
18
+ l.width = e, l.height = t;
19
+ let u = l.getContext("2d");
20
+ if (!u) return c();
21
+ for (let [e, t, r, i, a] of n) u.fillStyle = a ?? "#000000", u.fillRect(e, t, r, i);
22
+ let d = document.createElement("canvas");
23
+ d.width = o, d.height = s;
24
+ let f = d.getContext("2d"), p = f?.createPattern(l, "repeat");
25
+ return !f || !p ? c() : (f.fillStyle = p, f.fillRect(0, 0, o, s), `url("${d.toDataURL("image/png")}")`);
26
+ }
27
+ function s(t, n = t) {
28
+ return e`
29
+ background-size: calc(var(--vf-scale, 1) * ${r(t)}px)
30
+ calc(var(--vf-scale, 1) * ${r(n)}px);
31
+ `;
32
+ }
33
+ function c(t, n = t) {
34
+ return e`
35
+ mask-size: calc(var(--vf-scale, 1) * ${r(t)}px)
36
+ calc(var(--vf-scale, 1) * ${r(n)}px);
37
+ `;
38
+ }
39
+ //#endregion
40
+ export { t as TILE_LATTICE, i as tileImage, o as tileRaster, a as tileRects, r as tileSpan, c as vfTileMaskSize, s as vfTileSize };
@@ -0,0 +1,49 @@
1
+ /**
2
+ * The striped title bar shared by vf-window and vf-dialog: a fixed-height row
3
+ * with a 1px bottom rule, and a centered white title patch that interrupts the
4
+ * racing stripes running behind it.
5
+ *
6
+ * Apply `.vf-title-bar` to the bar and `.vf-title` to the title element, and
7
+ * place a `.vf-stripes` layer (see {@link vfStripes}) as the bar's first child.
8
+ *
9
+ * Title geometry is stated in whole system px, traced from the InfiniteMac
10
+ * reference: in the 17px interior (18px bar minus the rule) the cap band sits
11
+ * on rows 4..12 — 4px of white above and below the 9px caps — with 7px of
12
+ * white between the ink and the stripes on either side. The face puts the cap
13
+ * band at rows 3..11 of its 16px line box (ascent 12, caps 9 on the
14
+ * baseline), so the line box rides 1px below the interior top and lands the
15
+ * caps exactly; letters carry a 1px side bearing, so 6px of padding makes the
16
+ * traced 7. Flex-CENTERING either axis is what this recipe deliberately does
17
+ * not do: 16 into 17 halves to a fraction, and (bar − patch)/2 is fractional
18
+ * whenever the two widths' parities differ — half a system px off, which a
19
+ * 3:1 display renders as ink one device px off the grid.
20
+ *
21
+ * The horizontal remainder can't be quantized statically (the patch width
22
+ * follows the heading's text run), so the patch stays flex-centered and
23
+ * `TitleCenterController` (src/chrome.ts) cancels the fraction through
24
+ * `--vf-title-dx` — controller-owned, like the grid-snap offsets.
25
+ *
26
+ * The title's clearance for whatever else shares the bar is set per component
27
+ * with `--vf-title-inset`: vf-dialog takes the 16px default (nothing but the
28
+ * title is in there), vf-window sets 60px so an ellipsized title can't run
29
+ * under its close/zoom widgets.
30
+ *
31
+ * `touch-action` is deliberately NOT declared here. vf-dialog's bar is always a
32
+ * drag handle, vf-window's only when `[movable]` — suppressing touch scrolling
33
+ * on a bar that can't be dragged would be a real behavior change, so each
34
+ * component keeps that one declaration under its own selector.
35
+ */
36
+ export declare const vfTitleBar: import("lit").CSSResult;
37
+ /**
38
+ * The title-bar window widgets (close box left, zoom box right) shared by
39
+ * `vf-window` and a `closable` `vf-dialog` — one recipe so the two components'
40
+ * widgets are identical by construction, like the bar itself (the
41
+ * `Windows/moveable modal dialog.png` reference draws the dialog's close box
42
+ * exactly as the document window's: 11×11 at left:8px). Pair with the
43
+ * templates in src/chrome.ts, which carry the matching class/part contract.
44
+ *
45
+ * Geometry is the standard 18px bar's; vf-window's utility variant overrides
46
+ * the sizes under its own selector. Host-state rules (hiding widgets on an
47
+ * inactive window) stay with the component — a dialog has no inactive state.
48
+ */
49
+ export declare const vfWindowWidgets: import("lit").CSSResult;