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,68 @@
1
+ import { vfDisplayDecls as e } from "./display-face.js";
2
+ import { vfFocusUnderline as t } from "./focus.js";
3
+ import { css as n } from "lit";
4
+ //#region src/styles/recipes/field.ts
5
+ var r = n`
6
+ .vf-field {
7
+ background: var(--vf-white, #fff);
8
+ border: calc(var(--vf-scale, 1) * 1px) solid var(--vf-black, #000);
9
+ border-radius: 0;
10
+ /* Editable text is set in the Chicago-style display face. */
11
+ ${e}
12
+ font-weight: var(--vf-font-weight, 700);
13
+ line-height: inherit;
14
+ color: var(--vf-black, #000);
15
+ user-select: text;
16
+ -webkit-user-select: text;
17
+ outline: none;
18
+ /* Stated, not inherited: Firefox's UA sheet puts \`cursor: text\` on
19
+ \`<input>\`, so the kit-wide cursor token (SPEC §3) has to be read here
20
+ to reach the well at all. The fallback is the I-beam it always was. */
21
+ cursor: var(--vf-cursor, text);
22
+ }
23
+ /* The wrapper boxing the well: the positioned anchor the focus rule hangs
24
+ from, and the element grid snapping moves (see the note above). */
25
+ .vf-field-well {
26
+ position: relative;
27
+ }
28
+ /* Keyboard focus is the kit's dashed rule one blank row under the well — not
29
+ a ring around it, and not a thickened border (see vfFocusUnderline). The
30
+ offset counts that blank row and the rule itself down from the well's
31
+ bottom edge: −(1 + 1).
32
+
33
+ Gated on a class rather than :focus-visible, which cannot express this for
34
+ a field: the selector matches any focus of an element that takes keyboard
35
+ input, so it is already true for a mouse click (see focus-modality.ts).
36
+ VfTextControlBase adds .vf-focus-rule for a keyboard focus only. */
37
+ .vf-field-well.vf-focus-rule::after {
38
+ --vf-focus-underline-offset: -2px;
39
+ ${t}
40
+ }
41
+ /* Its own token, deliberately not --vf-disabled: the disabled rule below is
42
+ an inactive control and exempt from contrast requirements, but a
43
+ placeholder sits in an ENABLED, operable field and isn't — and the dim
44
+ gray is 1.82:1 on the white well. #767676 is 4.54:1 (AA). System 7 had no
45
+ placeholder text at all, so like the focus rule this is a modern
46
+ affordance the kit adds, and it gets a color the modern requirement can
47
+ accept rather than a traced gray it must match. */
48
+ .vf-field::placeholder {
49
+ color: var(--vf-field-placeholder, #767676);
50
+ font-weight: inherit;
51
+ opacity: 1;
52
+ }
53
+ /* Disabled: the text dims to gray; the solid black box border stays. The
54
+ cursor returns to the arrow — a well that can't take input isn't text. */
55
+ .vf-field:disabled {
56
+ color: var(--vf-disabled, #c0c0c0);
57
+ cursor: var(--vf-cursor, default);
58
+ }
59
+ /* Selected text inverts to solid black-on-white — the 1-bit System 7
60
+ selection, not the browser's translucent blue. Reuses the list's highlight
61
+ tokens so every selection in the kit shares one color. */
62
+ .vf-field::selection {
63
+ background-color: var(--vf-highlight, #000);
64
+ color: var(--vf-highlight-text, #fff);
65
+ }
66
+ `;
67
+ //#endregion
68
+ export { r as vfField };
@@ -0,0 +1,76 @@
1
+ /**
2
+ * The dotted focus outline's two declarations, for composing inside any focus
3
+ * selector whose shape (`:host(:focus-visible) .box`, `.viewport:focus-visible`,
4
+ * a JS-toggled `.focus-ring`, …) is too varied to share a whole rule. Interpolate
5
+ * it into the component's own selector: `.box { ${vfFocusRing} }`.
6
+ *
7
+ * The offset defaults to +2px (ring sits just outside the box); set
8
+ * `--vf-focus-offset` in the same rule to inset it (negative, to stay in-box) or
9
+ * widen it. Keeps the outline default (`--vf-focus-outline`) authoritative in one
10
+ * place so a ring-style change is a single edit.
11
+ *
12
+ * Drawn in `currentColor` — the same mechanism as {@link vfFocusUnderline} —
13
+ * so the ring inverts with the ink it sits on. This is load-bearing for
14
+ * `vf-list-item` in a `multiple` list: Ctrl+Arrow deliberately walks the
15
+ * cursor across already-selected rows, and a hard-coded black ring on the
16
+ * inverted black selection bar left that cursor genuinely invisible while
17
+ * Space still acted on it. On the highlight bar `currentColor` is
18
+ * `--vf-highlight-text`, so the ring reads white there and black everywhere
19
+ * else — no unselected artwork changes.
20
+ */
21
+ export declare const vfFocusRing: import("lit").CSSResult;
22
+ /**
23
+ * Focus ring for non-text controls where focus and ring share one element.
24
+ * Apply to the focusable element:
25
+ * .control:focus-visible { ... }
26
+ * or compose this class name onto it.
27
+ */
28
+ export declare const vfFocus: import("lit").CSSResult;
29
+ /**
30
+ * The kit's focus indicator for a control that can carry the mark on its own
31
+ * face: a 1px dashed rule beneath it — 1 system px on, 1 off — instead of a
32
+ * ring around it. It has two placements, both out of this one recipe:
33
+ *
34
+ * - *Inside* the control, under the ink it marks: the button underlines its
35
+ * label, the two toggles their box and circle.
36
+ * - *Below* the control, under its whole box and clear of its hard shadow, for
37
+ * a control with no interior to give: vf-select's one line is shared by the
38
+ * label and the ▼, and a vf-swatch is nothing but fill.
39
+ *
40
+ * It is why none of those compose {@link vfFocus}.
41
+ *
42
+ * Keyboard focus is one of the modern requirements the kit adds rather than
43
+ * emulates (SPEC §1): System 7 predates full keyboard access and drew no such
44
+ * indicator at all. So the job is to render an affordance it never had in a
45
+ * 1-bit vocabulary it would recognize — a hairline dashed rule on the pixel
46
+ * grid, which reads on a white face and inside a control's tight silhouette
47
+ * where an offset ring has nowhere to go.
48
+ *
49
+ * Interpolate into an `::after` rule on the element being underlined (which
50
+ * needs `position: relative`), gated on whatever focus selector the component
51
+ * uses — and suppress the UA outline in the same rule set:
52
+ *
53
+ * button:focus-visible { outline: none; }
54
+ * button:focus-visible .label::after { ${vfFocusUnderline} }
55
+ *
56
+ * The rule spans that element's own box and leaves ONE blank system px row
57
+ * between itself and the ink above, which is what `--vf-focus-underline-offset`
58
+ * places — measured from the element's bottom edge up to the rule's, so
59
+ * positive insets it and negative drops it below. A negative one counts every
60
+ * row of ink between the two, since an absolutely positioned pseudo-element
61
+ * sizes to the PADDING box and the ink can carry on past it:
62
+ *
63
+ * - `4px` (the default) is a text box, where the ink stops at the baseline and
64
+ * the box continues 6px past it (2px half-leading from vfBase's 1.25 line box
65
+ * over the 16px em, plus the face's 4px descent): 6 − 2 = 4.
66
+ * - `-2px` is a well whose ink runs to its own bottom edge — the radio's circle,
67
+ * a field's wrapper — putting the rule in the second row below it.
68
+ * - `-3px` adds a 1px border the pseudo-element sits inside of (vf-checkbox).
69
+ * - past that, the rule also clears a hard shadow, which is ink no box the
70
+ * pseudo-element can size to contains: `-4px` for vf-select's 1px one, and
71
+ * for vf-swatch an offset that composes `--vf-shadow-offset` rather than
72
+ * hard-coding a depth its consumer can retheme.
73
+ *
74
+ * Drawn in `currentColor`, so it inverts with the label on a pressed face.
75
+ */
76
+ export declare const vfFocusUnderline: import("lit").CSSResult;
@@ -0,0 +1,39 @@
1
+ import { css as e } from "lit";
2
+ //#region src/styles/recipes/focus.ts
3
+ var t = e`
4
+ outline: var(--vf-focus-outline, 1px dotted currentColor);
5
+ outline-offset: calc(var(--vf-scale, 1) * var(--vf-focus-offset, 2px));
6
+ `, n = e`
7
+ .vf-focus:focus-visible {
8
+ ${t}
9
+ }
10
+ `, r = e`
11
+ content: '';
12
+ position: absolute;
13
+ left: 0;
14
+ right: 0;
15
+ bottom: calc(var(--vf-scale, 1) * var(--vf-focus-underline-offset, 4px));
16
+ height: calc(var(--vf-scale, 1) * 1px);
17
+ background: repeating-linear-gradient(
18
+ to right,
19
+ currentColor 0 calc(var(--vf-scale, 1) * 1px),
20
+ transparent calc(var(--vf-scale, 1) * 1px) calc(var(--vf-scale, 1) * 2px)
21
+ );
22
+ pointer-events: none;
23
+ /* Forced colors deletes every non-url() background-image, this gradient
24
+ included — and each carrier suppresses the UA outline in the same rule
25
+ set, so without this branch a keyboard user in high-contrast mode has no
26
+ focus mark anywhere the underline serves (ACCESSIBILITY-REVIEW §2.1).
27
+ forced-color-adjust: none turns the same 1-on/1-off gradient back on. It
28
+ stays currentColor, and the ::after's color inherits from an ancestor the
29
+ override DID reach — so the mark lands in the user's own palette
30
+ (CanvasText on a face, HighlightText on a highlight bar) and still
31
+ inverts with the ink it marks. Bare declarations in a nested @media apply
32
+ to the composing rule's own selector (CSSNestedDeclarations), which every
33
+ forced-colors engine supports. */
34
+ @media (forced-colors: active) {
35
+ forced-color-adjust: none;
36
+ }
37
+ `;
38
+ //#endregion
39
+ export { n as vfFocus, t as vfFocusRing, r as vfFocusUnderline };
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Shared base styles for every Vintage Frames component.
3
+ * See SPEC.md §3 (tokens) and §4 (recipes).
4
+ */
5
+ export declare const vfBase: import("lit").CSSResult;
@@ -0,0 +1,62 @@
1
+ import { vfBodyDecls as e } from "./body-face.js";
2
+ import { css as t } from "lit";
3
+ //#region src/styles/recipes/host.ts
4
+ var n = t`
5
+ :host {
6
+ box-sizing: border-box;
7
+ ${e}
8
+ font-weight: var(--vf-font-weight, 700);
9
+ line-height: 1.25;
10
+ color: var(--vf-black, #000);
11
+ user-select: none;
12
+ -webkit-user-select: none;
13
+ }
14
+ :host *,
15
+ :host *::before,
16
+ :host *::after {
17
+ box-sizing: inherit;
18
+ }
19
+ :host([hidden]) {
20
+ display: none !important;
21
+ }
22
+ /* Forced-colors mode (Windows High Contrast) — a modern adaptation the kit
23
+ ADDS, in what is arguably the most 1-bit-native form the platform offers:
24
+ the user chose their own two colors, and a two-color design honors them by
25
+ re-declaring its palette in the system pair rather than fighting the
26
+ override. The mechanism: forced colors rewrites color-valued properties at
27
+ used-value time, but a property whose computed value is a <system-color>
28
+ keyword is preserved — and var() substitution happens before the override
29
+ judges it. So remapping the tokens here repairs, in one place, every paint
30
+ site that routes through them: borders, faces, glyphs, the selection
31
+ inversion, the button's silhouette pseudos, vf-grid's masked rules.
32
+ What it cannot reach is filed per site: non-url()
33
+ background-images are deleted regardless of color (the focus underline and
34
+ title-bar stripe gradients — see vfFocusUnderline/vfStripes), url() tiles
35
+ are preserved with their literal ink (the dot/sprite tiles — masks over
36
+ these tokens instead), and box-shadows are never painted. */
37
+ @media (forced-colors: active) {
38
+ :host {
39
+ --vf-black: CanvasText;
40
+ --vf-white: Canvas;
41
+ --vf-surface: Canvas;
42
+ --vf-disabled: GrayText;
43
+ --vf-field-placeholder: GrayText;
44
+ --vf-highlight: Highlight;
45
+ --vf-highlight-text: HighlightText;
46
+ }
47
+ }
48
+ /* Device-pixel grid snapping (src/grid-snap.ts). The controller writes the
49
+ sub-half-pixel correction as two reserved custom properties on the host
50
+ (--vf-snap-dx/-dy); this class applies it to a component's top-level
51
+ painted element(s). Absolutely positioned satellites that anchor to the
52
+ host (vf-menu's panel, the default button's ring) compose the same
53
+ variables into their own insets instead. Controller-owned — never set the
54
+ variables by hand. */
55
+ .vf-snap {
56
+ position: relative;
57
+ left: var(--vf-snap-dx, 0px);
58
+ top: var(--vf-snap-dy, 0px);
59
+ }
60
+ `;
61
+ //#endregion
62
+ export { n as vfBase };
@@ -0,0 +1,80 @@
1
+ import { type TileRect } from './tile.js';
2
+ /**
3
+ * The windoid bar's dither: a 2-system-px motif with a single black pixel at
4
+ * its origin, stated once as rect data. Transparent ground, because the layer
5
+ * floats over the bar and the same art doubles as the forced-colors mask,
6
+ * where the ground would be opacity rather than paint. The SVG tile below is
7
+ * the CSS-repeated form (the no-declared-width fallback and the forced-colors
8
+ * mask); `vf-window` renders the exact fill — the whole-surface raster or a
9
+ * consumer token's placed tile grid — from the same data (src/tile-grid.ts).
10
+ */
11
+ export declare const DOT_MOTIF = 2;
12
+ export declare const DOT_RECTS: readonly TileRect[];
13
+ /** The dots' tile size in system px (30) — the consumer token's documented box. */
14
+ export declare const DOT_SPAN: number;
15
+ /**
16
+ * Racing stripes for title bars. Apply the class to an absolutely-positioned
17
+ * layer inset 3px (top/bottom) and 1px (left/right) within the title bar. At
18
+ * the 18px bar height this yields exactly six 1px stripes spanning the close
19
+ * box's top and bottom edges (title-bar interior 17px, band 11px tall); the
20
+ * side inset leaves the stripes one system px clear of the frame border,
21
+ * matching the widgets' 1px patch ring in the reference art.
22
+ *
23
+ * TWO renderings by engine, each the measured best (2026-08-09/10: all
24
+ * three engines at eight densities, then a four-geometry Blink
25
+ * registration sweep; the numbers live in scripts/verify-tile.mjs):
26
+ *
27
+ * - **Gecko and WebKit show the six placed solid rows** (`chromeTitleBar`
28
+ * renders the spans). Both engines device-snap solid boxes — the rows
29
+ * measured pixel-perfect at all eight densities in each — and each
30
+ * misrendered the gradient this layer once used: Gecko's GPU WebRender
31
+ * pipeline renders a gradient through a cached texture whose sampling
32
+ * softens a hard stop at default zoom (the originally reported bug), and
33
+ * WebKit landed the sixth stripe one device row thin at dpr 3 and the
34
+ * zoom-minted scales.
35
+ *
36
+ * - **Blink — and any engine failing both gate properties — shows the
37
+ * 12-unit SVG**: the band's 11 rows plus one empty pad row, viewBox
38
+ * stretched onto a 12px-tall box, crispEdges. Blink pixel-snaps a
39
+ * painted BOX to whole CSS px, which killed every mechanism whose
40
+ * geometry rides an 11-system-px box — no legal CSS height exists at
41
+ * scale 3/2 (16.5px), so placed rows fuse, rasters and an 11-tall SVG
42
+ * drop or wobble a row, and a clip-path comb (origin-anchored, so the
43
+ * even-height trick can't reach it) AAs at every fractional density.
44
+ * Twelve divides by 2 and 3, so 12·scale is a whole CSS length at every
45
+ * scale an integer display derives (18px at 3/2, 16px at 4/3): the box
46
+ * never rounds. Measured: registered Δ0 with the close box, whole-rhythm
47
+ * and zero-gray at dpr 1/1.5/2/3 across all four sweep geometries, and
48
+ * never worse than the retired gradient at the unholdable scales
49
+ * (1.25/1.7/2.3/2.5), where its residual is a hard one-row-thin first
50
+ * stripe with no gray — the gradient produced the same thin stripe PLUS
51
+ * a smeared row, so it is strictly dominated and gone.
52
+ *
53
+ * The gate is two engine-exclusive properties: `-moz-appearance` parses
54
+ * only in Gecko, `-webkit-backdrop-filter` only in WebKit (Blink's
55
+ * backdrop-filter is unprefixed, never aliased).
56
+ *
57
+ * Forced colors: every paint is token-routed — the rows' background-color
58
+ * and the rects' fill both resolve var(--vf-black), remapped to CanvasText
59
+ * — and the layer keeps forced-color-adjust: none (inherited, so it covers
60
+ * the SVG) so no forced palette repaints the active window's whole signal
61
+ * out from under it.
62
+ */
63
+ export declare const vfStripes: import("lit").CSSResult;
64
+ /**
65
+ * Dot-grid dither for the utility ("windoid") title bar — the slim bar's
66
+ * counterpart to {@link vfStripes}. Apply the class to an absolutely-positioned
67
+ * layer inset 2px top/bottom and FLUSH left/right: the close-up reference art
68
+ * runs the dots all the way into the side borders (the Windows/ sheet hand-
69
+ * insets them 2px, which the close-up shows is not the bar's own geometry).
70
+ *
71
+ * The layer's own CSS-repeated tile (a crisp 1-bit SVG on the 30-system-px
72
+ * span, overridable via `--vf-dots-pattern`) is the fallback for a window
73
+ * with no declared width. A `vf-window` that knows its width renders the
74
+ * exact fill INTO the layer instead — the whole-surface raster, or a consumer
75
+ * token's placed tile grid (src/tile-grid.ts) — and marks the layer
76
+ * `vf-tile-grid`, which switches the repeat off: the dot art is transparent-
77
+ * grounded, so a drifting CSS-repeated copy underneath would show through
78
+ * between the exactly-placed dots.
79
+ */
80
+ export declare const vfDots: import("lit").CSSResult;
@@ -0,0 +1,82 @@
1
+ import { tileImage as e, tileRects as t, tileSpan as n, vfTileMaskSize as r, vfTileSize as i } from "./tile.js";
2
+ import { css as a, unsafeCSS as o } from "lit";
3
+ var s = [[
4
+ 0,
5
+ 0,
6
+ 1,
7
+ 1,
8
+ "#000000"
9
+ ]], c = e(2, 2, t(s)), l = n(2), u = a`
10
+ .vf-stripes {
11
+ position: absolute;
12
+ inset: calc(var(--vf-scale, 1) * 3px) calc(var(--vf-scale, 1) * 1px);
13
+ pointer-events: none;
14
+ @media (forced-colors: active) {
15
+ forced-color-adjust: none;
16
+ }
17
+ }
18
+ .vf-stripes svg {
19
+ display: block;
20
+ position: absolute;
21
+ top: 0;
22
+ left: 0;
23
+ width: 100%;
24
+ height: calc(var(--vf-scale, 1) * 12px);
25
+ }
26
+ .vf-stripes rect {
27
+ fill: var(--vf-black, #000000);
28
+ }
29
+ .vf-stripes span {
30
+ display: none;
31
+ }
32
+ /* Gecko (-moz-appearance) and WebKit (-webkit-backdrop-filter): each
33
+ parses nowhere else — Blink's backdrop-filter is unprefixed. */
34
+ @supports (-moz-appearance: none) or (-webkit-backdrop-filter: blur(1px)) {
35
+ .vf-stripes svg {
36
+ display: none;
37
+ }
38
+ .vf-stripes span {
39
+ display: block;
40
+ position: absolute;
41
+ left: 0;
42
+ right: 0;
43
+ height: calc(var(--vf-scale, 1) * 1px);
44
+ background: var(--vf-black, #000000);
45
+ }
46
+ }
47
+ `, d = a`
48
+ .vf-dots {
49
+ position: absolute;
50
+ inset: calc(var(--vf-scale, 1) * 2px) 0;
51
+ background-image: var(--vf-dots-pattern, ${o(c)});
52
+ ${i(2)}
53
+ pointer-events: none;
54
+ /* The consumer-token art channel for the placed tile grid. */
55
+ --_vf-tile-image: var(--vf-dots-pattern, ${o(c)});
56
+ }
57
+ .vf-dots.vf-tile-grid {
58
+ background-image: none;
59
+ }
60
+ /* Forced colors preserves url() tiles verbatim, so the dots would stay
61
+ literal black — invisible on a dark high-contrast theme. Repainted as the
62
+ ink token through the same tile as a mask (the vf-grid rules idiom), so
63
+ the windoid bar's signature follows the user's palette. The exact-fill
64
+ children hide here and the span mask takes over — no mask pipeline
65
+ rasterizes exactly at a zoom-minted scale anyway (image-rendering does
66
+ not reach masks), so forced-colors keeps the span approach and its zoom
67
+ caveat, unchanged. */
68
+ @media (forced-colors: active) {
69
+ .vf-dots {
70
+ background-image: none;
71
+ background-color: var(--vf-black, #000);
72
+ mask-image: var(--vf-dots-pattern, ${o(c)});
73
+ ${r(2)}
74
+ }
75
+ .vf-dots .vf-tile,
76
+ .vf-dots .vf-tile-raster {
77
+ display: none;
78
+ }
79
+ }
80
+ `;
81
+ //#endregion
82
+ export { s as DOT_RECTS, l as DOT_SPAN, d as vfDots, u as vfStripes };
@@ -0,0 +1,69 @@
1
+ import { type TileRect } from './tile.js';
2
+ /**
3
+ * The trough's 25% lattice — a 4×2-system-px motif with a dot at (0,0) and
4
+ * (2,1), traced from the UI kit's "Scroll bg" sprite — stated once as rect
5
+ * data. The DOM trough renders it as a whole-surface raster
6
+ * (`tileRaster`, written by `ScrollRailController` — 1-bit at every scale,
7
+ * zoom-minted ones included); the SVG span tile below is the forced-colors
8
+ * mask, which repaints the same art in the remapped ink token.
9
+ */
10
+ export declare const TROUGH_MOTIF_X = 4;
11
+ export declare const TROUGH_MOTIF_Y = 2;
12
+ export declare const TROUGH_RECTS: readonly TileRect[];
13
+ /**
14
+ * System 7 scroll rails, drawn by the kit as ordinary shadow DOM — the skin
15
+ * for the subtree `renderScrollRail()` (src/scroll-rail.ts) produces. The
16
+ * native scrollbar is hidden, never the native scrolling: the scroller keeps
17
+ * real `overflow` scrolling (wheel, trackpad momentum, keyboard, touch and
18
+ * AT scrolling all stay the platform's), and the rail is synced to it by
19
+ * `ScrollRailController`.
20
+ *
21
+ * Why DOM and not the `::-webkit-scrollbar` skin this replaces: the engine
22
+ * owns themed-scrollbar geometry and quantizes it to whole CSS px — WebKit
23
+ * painted the rail one device pixel adrift (or a CSS px short) whenever the
24
+ * scroller's box edges or width left the whole-CSS-px lattice, which the
25
+ * kit's own odd-system-px insets produce constantly at scale 1.5. A DOM rail
26
+ * is kit art: subpixel layout device-snaps at paint like every other element,
27
+ * and the whole defect class is unrepresentable. It also renders identically
28
+ * in every engine (Firefox included), is assertable headless, and lets the
29
+ * trough adopt the exact tile raster the other dithers use.
30
+ *
31
+ * Geometry (system px) — the classic 16px cell, of which the outermost line
32
+ * is the component's own 1px frame: the rail element is the 15 inside it —
33
+ * a 1px divider on the content side (the rail's own border) plus the 14px
34
+ * channel. Arrow cells are 15 long (14 interior + the 1px divider facing the
35
+ * track); their glyphs are the 16-unit sprites windowed to the 14×14
36
+ * interior (`viewBox="1 1 14 14"`), since the sprite's outer ring is exactly
37
+ * the frame/divider lines the borders draw. The thumb is a fixed 16px white
38
+ * box spanning the channel, its border reading as inset 1px from each
39
+ * channel rail over its extent. The component supplies the outer frame as a
40
+ * real border and lays the rail out as a sibling of the scroller — AFTER it,
41
+ * so the state selectors below can reach the rail from the scroller's
42
+ * attributes.
43
+ *
44
+ * States, keyed off the attributes `ScrollStateController` writes on the
45
+ * scroller (now styling real elements, so there is no scrollbar-pseudo
46
+ * re-resolution caveat in any engine):
47
+ * - `data-overflow-{x,y}="false"` — idle rail: arrows stay drawn on a white
48
+ * channel, divider stays, no dither, no thumb — System 7 drew an active
49
+ * window's no-overflow bar as arrows on an empty channel, and a scroller
50
+ * outside a window always counts as active;
51
+ * - `data-window-inactive` — both axes blanked to the bare rail, arrows
52
+ * included, regardless of overflow (the HIG's inactive-window treatment);
53
+ * - `data-degenerate` (written by `ScrollRailController` when the track is
54
+ * too short) — `"thumb"` drops the thumb, `"rail"` drops the trough and
55
+ * arrows too, as the classic Control Manager did.
56
+ * A rail used without the controllers carries no such attributes, so none of
57
+ * these rules match and the full skin renders — the managed behavior stays
58
+ * strictly opt-in.
59
+ *
60
+ * FORCED COLORS: the rails, thumb, channel and frame land on the user's pair
61
+ * through the vfBase token remap like every other element. The arrows are
62
+ * inline `currentColor` SVG now, so they re-ink with the palette (the old
63
+ * `::-webkit-scrollbar` skin's one genuine residual — mask-image is ignored
64
+ * on scrollbar pseudos — dies here). The trough raster keeps literal ink, so
65
+ * it is hidden and the trough repaints as the ink token masked by the same
66
+ * art (the vfDots idiom; the span mask keeps the zoom caveat every masked
67
+ * surface has, an accepted residual).
68
+ */
69
+ export declare const vfScrollRail: import("lit").CSSResult;