vintage-frames 0.12.8 → 0.14.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 (62) hide show
  1. package/custom-elements.json +26 -26
  2. package/dist/chrome.d.ts +1 -1
  3. package/dist/components/vf-checkbox.d.ts +6 -5
  4. package/dist/components/vf-checkbox.js +41 -37
  5. package/dist/components/vf-container.d.ts +16 -20
  6. package/dist/components/vf-container.js +25 -17
  7. package/dist/components/vf-desktop.d.ts +4 -5
  8. package/dist/components/vf-desktop.js +13 -18
  9. package/dist/components/vf-fieldset.js +30 -22
  10. package/dist/components/vf-icon.js +55 -55
  11. package/dist/components/vf-list.js +30 -29
  12. package/dist/components/vf-menu.js +24 -22
  13. package/dist/components/vf-number-field.js +32 -30
  14. package/dist/components/vf-progress-bar.d.ts +2 -2
  15. package/dist/components/vf-progress-bar.js +65 -52
  16. package/dist/components/vf-radio.js +3 -4
  17. package/dist/components/vf-scroll-area.js +27 -26
  18. package/dist/components/vf-select.js +108 -90
  19. package/dist/components/vf-separator.d.ts +5 -5
  20. package/dist/components/vf-separator.js +36 -10
  21. package/dist/components/vf-slider.js +11 -11
  22. package/dist/components/vf-swatch.d.ts +2 -2
  23. package/dist/components/vf-swatch.js +73 -72
  24. package/dist/components/vf-text-area.js +37 -35
  25. package/dist/components/vf-text-field.js +20 -18
  26. package/dist/components/vf-window.d.ts +6 -9
  27. package/dist/components/vf-window.js +106 -87
  28. package/dist/index.d.ts +20 -10
  29. package/dist/index.js +63 -62
  30. package/dist/pattern-fill.d.ts +21 -50
  31. package/dist/pattern-fill.js +19 -54
  32. package/dist/scroll-rail.d.ts +6 -6
  33. package/dist/scroll-rail.js +24 -29
  34. package/dist/styles/base.d.ts +3 -1
  35. package/dist/styles/recipes/field.d.ts +10 -6
  36. package/dist/styles/recipes/field.js +8 -7
  37. package/dist/styles/recipes/focus.d.ts +7 -3
  38. package/dist/styles/recipes/pattern.d.ts +19 -10
  39. package/dist/styles/recipes/pattern.js +27 -18
  40. package/dist/styles/recipes/rule.d.ts +11 -8
  41. package/dist/styles/recipes/rule.js +31 -13
  42. package/dist/styles/recipes/scroll-rail.d.ts +21 -13
  43. package/dist/styles/recipes/scroll-rail.js +27 -37
  44. package/dist/styles/recipes/shadow.d.ts +10 -6
  45. package/dist/styles/recipes/shadow.js +4 -2
  46. package/dist/styles/recipes/stroke.d.ts +85 -0
  47. package/dist/styles/recipes/stroke.js +53 -0
  48. package/dist/styles/recipes/surface.d.ts +8 -4
  49. package/dist/styles/recipes/surface.js +23 -17
  50. package/dist/styles/recipes/tile.d.ts +11 -3
  51. package/dist/styles/recipes/tile.js +12 -12
  52. package/dist/styles/recipes/title-bar.d.ts +7 -0
  53. package/dist/styles/recipes/title-bar.js +25 -22
  54. package/dist/styles/recipes/toggle.js +3 -3
  55. package/dist/tile-grid.d.ts +98 -64
  56. package/dist/tile-grid.js +63 -30
  57. package/dist/track-width.d.ts +1 -2
  58. package/dist/zoom.d.ts +9 -9
  59. package/docs/SPEC.md +33 -32
  60. package/editor/vscode.html-custom-data.json +5 -5
  61. package/editor/web-types.json +8 -8
  62. package/package.json +1 -1
@@ -1,4 +1,5 @@
1
- import { type CSSResult, type ReactiveController, type ReactiveControllerHost } from 'lit';
1
+ import { type CSSResult, type ReactiveControllerHost } from 'lit';
2
+ import { RepeatTileController } from './tile-grid.js';
2
3
  import { type Pattern } from './patterns.js';
3
4
  /** What a component hands the controller. */
4
5
  export interface PatternFillOptions {
@@ -8,31 +9,14 @@ export interface PatternFillOptions {
8
9
  /** The resolved pattern, or null to paint nothing ({@link parsePattern}
9
10
  * turns an attribute value into one). */
10
11
  getPattern: () => Pattern | null | undefined;
11
- /**
12
- * The box's declared size in system px, per axis. A declared axis is used
13
- * as it is — exact, and free of any measurement; an axis left `null` or
14
- * `undefined` is measured off the box instead (a `ResizeObserver`,
15
- * `toSys`-rounded). Omit the whole option to measure both.
16
- */
17
- getSize?: () => {
18
- width?: number | null;
19
- height?: number | null;
20
- } | null | undefined;
21
12
  }
22
13
  /**
23
14
  * Paints a {@link Pattern} as a box's own background — the fill behind
24
- * `vf-container pattern="…"` and `vf-desktop pattern="…"`.
25
- *
26
- * It is the exact-fill idiom (src/tile-grid.ts) in its background form: the
27
- * motif is encoded as one raster at one image px per system px
28
- * (`tileRaster`, cached by pattern and size), sized in system px to the box
29
- * ceiled up to whole pattern cells plus one cell of overdraw, and stretched
30
- * onto that stated size under `image-rendering: pixelated`. Nearest-neighbor
31
- * sampling can only emit source colors and one image has no interior seams,
32
- * so the fill is 1-bit at every density and zoom — the same argument the
33
- * desktop dither rests on. A `background-size` stated in system px is one
34
- * stored length quantized once over the whole image (≤ 1/128 CSS px), not a
35
- * repeat, and the box's background painting area crops the overdraw.
15
+ * `vf-container pattern="…"` and `vf-desktop pattern="…"`: the pattern's
16
+ * motif as a repeating tile drawn at the display's resolution
17
+ * ({@link RepeatTileController}, src/tile-grid.ts), 1-bit at every density
18
+ * and zoom, and the same cost for a box of any size — a resize encodes
19
+ * nothing.
36
20
  *
37
21
  * A background rather than a child element, on purpose: it paints below all
38
22
  * content by definition — no `isolation: isolate` + `z-index: -1`, so a
@@ -44,46 +28,33 @@ export interface PatternFillOptions {
44
28
  * on the box only while patterned and hands `auto` back to the slot, so
45
29
  * slotted content renders as it did.
46
30
  *
47
- * What it writes, all on the box's inline style and removed together the
48
- * moment there is nothing to paint: `--_vf-pattern-image` (the raster) and
49
- * `--_vf-pattern-size` (two live `sysLength`s). Custom-property channels,
50
- * not `background-image` itself, so the recipe's forced-colors rule can
51
- * out-cascade them without `!important` — an inline `background-image`
52
- * could not be overridden from a stylesheet. The paper is the recipe's, a
53
- * token: `var(--vf-white)` behind ink that is literal black, like every kit
54
- * raster. Density and zoom re-encode nothing: the raster is keyed on system
55
- * px, and the stated lengths are live against `--vf-scale`.
31
+ * What it writes, on the box's inline style and removed the moment there is
32
+ * nothing to paint: `--_vf-pattern-image`, the tile. The paper is the
33
+ * recipe's, a token: `var(--vf-white)` behind ink that is literal black,
34
+ * like every kit raster.
56
35
  *
57
36
  * Phase is anchored at the box's top-left — where the desktop, trough and
58
37
  * swatch anchor theirs. Two same-pattern boxes meeting at an offset that is
59
38
  * not a multiple of 8 show a phase seam; a port-anchored phase would be a
60
39
  * contained follow-up, not a change here.
61
40
  */
62
- export declare class PatternFillController implements ReactiveController {
63
- #private;
41
+ export declare class PatternFillController extends RepeatTileController {
64
42
  constructor(host: ReactiveControllerHost & HTMLElement, opts: PatternFillOptions);
65
- /** The raster's current box in system px (overdraw included), or null
66
- * while nothing is painted. */
67
- get size(): {
68
- width: number;
69
- height: number;
70
- } | null;
71
- hostUpdated(): void;
72
43
  }
73
44
  /**
74
45
  * The rules behind {@link PatternFillController}, for the box it paints:
75
46
  * give the box the `vf-pattern-fill` class always, and `vf-patterned` while
76
47
  * a pattern is resolved (the component's template knows).
77
48
  *
78
- * Painting: the raster the controller wrote, at the size it stated, once
79
- * (`no-repeat` — the overdraw is the repeat), nearest-neighbor. The paper
80
- * and the pixelation ride the `vf-patterned` state so an unpatterned box
81
- * paints exactly nothing and inherits nothing new. `image-rendering`
82
- * inherits, so the slot hands `auto` back to slotted content: a consumer's
83
- * own `<img>` renders as it would anywhere else, and a kit element that
84
- * wants nearest-neighbor (`vf-img`, a `vf-icon`) says so itself. A page
85
- * that set `pixelated` on an ancestor gets `auto` inside a patterned box —
86
- * its own declaration on the child still wins, as everywhere.
49
+ * Painting: the tile the controller wrote, repeated from the padding box's
50
+ * corner at {@link vfRepeatTileSize}, nearest-neighbor. The paper and the
51
+ * pixelation ride the `vf-patterned` state so an unpatterned box paints
52
+ * exactly nothing and inherits nothing new. `image-rendering` inherits, so
53
+ * the slot hands `auto` back to slotted content: a consumer's own `<img>`
54
+ * renders as it would anywhere else, and a kit element that wants
55
+ * nearest-neighbor (`vf-img`, a `vf-icon`) says so itself. A page that set
56
+ * `pixelated` on an ancestor gets `auto` inside a patterned box — its own
57
+ * declaration on the child still wins, as everywhere.
87
58
  *
88
59
  * Forced colors: the image goes, the paper is the remapped Canvas — the
89
60
  * desktop's posture, a backdrop being decoration.
@@ -1,61 +1,26 @@
1
- import { sysLength as e, toSys as t } from "./scale.js";
2
- import { patternHex as n, patternMotif as r } from "./patterns.js";
3
- import { TileRasterCache as i } from "./tile-grid.js";
4
- import { TrackWidthController as a } from "./track-width.js";
5
- import { css as o } from "lit";
1
+ import { RepeatTileController as e, vfRepeatTileSize as t } from "./tile-grid.js";
2
+ import { patternMotif as n } from "./patterns.js";
3
+ import { css as r } from "lit";
6
4
  //#region src/pattern-fill.ts
7
- var s = class {
8
- #e;
9
- #t;
10
- #n = new i();
11
- #r;
12
- #i = null;
13
- #a = "";
14
- #o = "";
15
- #s = null;
5
+ var i = /* @__PURE__ */ new WeakMap(), a = (e) => {
6
+ let t = i.get(e);
7
+ return t || (t = n(e), i.set(e, t)), t;
8
+ }, o = class extends e {
16
9
  constructor(e, t) {
17
- this.#e = e, this.#t = t, this.#r = new a(e, () => {
18
- if (!t.getPattern()) return null;
19
- let e = t.getSize?.();
20
- return e == null || e.width == null || e.height == null ? t.getBox() : null;
21
- }), e.addController(this);
10
+ super(e, {
11
+ getBox: t.getBox,
12
+ getArt: () => {
13
+ let e = t.getPattern();
14
+ return e ? a(e) : null;
15
+ },
16
+ property: "--_vf-pattern-image"
17
+ });
22
18
  }
23
- get size() {
24
- return this.#s;
25
- }
26
- hostUpdated() {
27
- this.#c();
28
- }
29
- #c() {
30
- let t = this.#t.getBox() ?? null;
31
- t !== this.#i && (this.#u(), this.#i = t);
32
- let i = this.#t.getPattern() ?? null;
33
- if (!t || !i) {
34
- this.#u();
35
- return;
36
- }
37
- let a = this.#t.getSize?.() ?? null, o = c(a?.width) ?? this.#l(this.#r.width), s = c(a?.height) ?? this.#l(this.#r.height);
38
- if (!o || !s) {
39
- this.#u();
40
- return;
41
- }
42
- let l = (e) => Math.ceil((e + 8) / 8) * 8, u = l(o), d = l(s), f = r(i), p = this.#n.for(f.width, f.height, f.rects, u, d, n(i)), m = `${e(u)} ${e(d)}`;
43
- this.#s = {
44
- width: u,
45
- height: d
46
- }, (p !== this.#a || m !== this.#o) && (this.#a = p, this.#o = m, t.style.setProperty("--_vf-pattern-image", p), t.style.setProperty("--_vf-pattern-size", m));
47
- }
48
- #l(e) {
49
- return e > 0 ? t(e, this.#e) : 0;
50
- }
51
- #u() {
52
- this.#s = null, !(!this.#a && !this.#o) && (this.#a = "", this.#o = "", this.#i?.style.removeProperty("--_vf-pattern-image"), this.#i?.style.removeProperty("--_vf-pattern-size"));
53
- }
54
- }, c = (e) => typeof e == "number" && Number.isFinite(e) && e > 0 ? Math.trunc(e) : null, l = o`
19
+ }, s = r`
55
20
  .vf-pattern-fill {
56
21
  background-image: var(--_vf-pattern-image, none);
57
- background-size: var(--_vf-pattern-size, auto);
58
- background-repeat: no-repeat;
22
+ ${t}
23
+ background-repeat: repeat;
59
24
  }
60
25
  .vf-pattern-fill.vf-patterned {
61
26
  background-color: var(--vf-white, #fff);
@@ -71,4 +36,4 @@ var s = class {
71
36
  }
72
37
  `;
73
38
  //#endregion
74
- export { s as PatternFillController, l as vfPatternFill };
39
+ export { o as PatternFillController, s as vfPatternFill };
@@ -30,9 +30,10 @@ export interface ScrollRailOptions {
30
30
  * whole-system-px translate, so the art is crisp mid-scroll; accepts the
31
31
  * ≤1-frame lag behind compositor wheel scrolling every scripted scrollbar
32
32
  * has (at 1-bit there is no smooth motion to betray it — no rAF loop, no
33
- * polling). The same pass sizes the trough's whole-surface dither raster
34
- * (the exact-tile idiom, `tileRaster`) and writes the degenerate-track
35
- * state the recipe's decision table styles.
33
+ * polling). The same pass writes the trough's repeating dither tile for
34
+ * the density at the rail (`repeatTile`, src/tile-grid.ts — again on a
35
+ * zoom or display change) and the degenerate-track state the recipe's
36
+ * decision table styles.
36
37
  * - **Thumb drag** — pointer-captured and axis-locked, live scrolling (the
37
38
  * modern expectation; System 7's dotted-outline drag is a possible later
38
39
  * opt-in). The thumb's paint position always snaps to whole system px.
@@ -59,8 +60,7 @@ export declare class ScrollRailController implements ReactiveController {
59
60
  private press;
60
61
  private holdTimer;
61
62
  private repeatTimer;
62
- /** One raster per axis, re-encoded only when the track's size changes. */
63
- private readonly troughCache;
63
+ private stopScale?;
64
64
  constructor(host: ReactiveControllerHost & HTMLElement & {
65
65
  readonly renderRoot: HTMLElement | DocumentFragment;
66
66
  }, opts: ScrollRailOptions);
@@ -81,7 +81,7 @@ export declare class ScrollRailController implements ReactiveController {
81
81
  private unwire;
82
82
  private readonly onScroll;
83
83
  /**
84
- * Re-derive every measured output: thumb position, trough raster,
84
+ * Re-derive every measured output: thumb position, trough tile,
85
85
  * degenerate state. Safe to call any time; components hit it imperatively
86
86
  * when content changes without a box resize (a `<textarea>` on input —
87
87
  * the same moments they call `ScrollStateController.measure()`).
@@ -1,20 +1,17 @@
1
- import { effectiveScale as e, sysLength as t } from "./scale.js";
2
- import { TROUGH_MOTIF_X as n, TROUGH_MOTIF_Y as r, TROUGH_RECTS as i } from "./styles/recipes/scroll-rail.js";
3
- import { TileRasterCache as a } from "./tile-grid.js";
1
+ import { effectiveScale as e, onScaleChange as t, sysLength as n } from "./scale.js";
2
+ import { devicePxAt as r, repeatTile as i } from "./tile-grid.js";
3
+ import { TROUGH as a } from "./styles/recipes/scroll-rail.js";
4
4
  import "./motion.js";
5
5
  import { SCROLL_ARROW_DOWN as o, SCROLL_ARROW_DOWN_FILL as s, SCROLL_ARROW_LEFT as c, SCROLL_ARROW_LEFT_FILL as l, SCROLL_ARROW_RIGHT as u, SCROLL_ARROW_RIGHT_FILL as d, SCROLL_ARROW_UP as f, SCROLL_ARROW_UP_FILL as p } from "./glyphs.js";
6
6
  import { html as m } from "lit";
7
7
  //#region src/scroll-rail.ts
8
- var h = 16, g = 16, _ = 14, v = 66, y = class {
8
+ var h = 16, g = 16, _ = 66, v = class {
9
9
  constructor(t, n) {
10
- this.host = t, this.opts = n, this.scroller = null, this.press = null, this.troughCache = {
11
- vertical: new a(),
12
- horizontal: new a()
13
- }, this.onScroll = () => this.sync(), this.onPointerDown = (t) => {
10
+ this.host = t, this.opts = n, this.scroller = null, this.press = null, this.onScroll = () => this.sync(), this.onPointerDown = (t) => {
14
11
  if (t.button !== 0 || this.press !== null) return;
15
12
  let n = t.currentTarget, r = this.opts.getScroll();
16
13
  if (!r) return;
17
- let i = b(n), a = i === "vertical" ? "data-overflow-y" : "data-overflow-x";
14
+ let i = y(n), a = i === "vertical" ? "data-overflow-y" : "data-overflow-x";
18
15
  if (r.getAttribute(a) === "false" || r.hasAttribute("data-window-inactive") || n.getAttribute("data-degenerate") === "rail") return;
19
16
  let o = t.target, s = i === "vertical" ? t.clientY : t.clientX, c = e(this.host) || 1, l = o.closest(".vf-rail-thumb"), u = o.closest(".vf-rail-button--decrement"), d = o.closest(".vf-rail-button--increment");
20
17
  if (l) {
@@ -78,13 +75,13 @@ var h = 16, g = 16, _ = 14, v = 66, y = class {
78
75
  }, t.addController(this);
79
76
  }
80
77
  hostConnected() {
81
- this.wire(), this.sync();
78
+ this.wire(), this.sync(), this.stopScale = t(() => this.sync());
82
79
  }
83
80
  hostUpdated() {
84
81
  this.wire(), this.sync();
85
82
  }
86
83
  hostDisconnected() {
87
- this.clearRepeat(), this.press = null, this.unwire();
84
+ this.clearRepeat(), this.press = null, this.unwire(), this.stopScale?.(), this.stopScale = void 0;
88
85
  }
89
86
  wire() {
90
87
  let e = this.opts.getScroll() ?? null;
@@ -101,17 +98,17 @@ var h = 16, g = 16, _ = 14, v = 66, y = class {
101
98
  let e = this.scroller ?? this.opts.getScroll(), t = this.host.renderRoot;
102
99
  if (!(!e || !t)) for (let n of t.querySelectorAll(".vf-rail")) this.syncRail(n, e);
103
100
  }
104
- syncRail(a, o) {
105
- let s = b(a), c = a.querySelector(".vf-rail-track"), l = a.querySelector(".vf-rail-thumb");
101
+ syncRail(t, o) {
102
+ let s = y(t), c = t.querySelector(".vf-rail-track"), l = t.querySelector(".vf-rail-thumb");
106
103
  if (!c || !l) return;
107
104
  let u = e(this.host) || 1, d = c.getBoundingClientRect(), f = (s === "vertical" ? d.height : d.width) / u, p = f < 1 ? "rail" : f < g ? "thumb" : null;
108
- if (p ? a.setAttribute("data-degenerate", p) : a.removeAttribute("data-degenerate"), p === "rail") return;
109
- let m = this.scrollRange(o, s), h = s === "vertical" ? o.scrollTop : o.scrollLeft, v = f - g, y = m > 0 && v > 0 ? Math.max(0, Math.min(Math.floor(v), Math.round(h / m * v))) : 0;
110
- l.style.translate = s === "vertical" ? `0 ${t(y)}` : `${t(y)} 0`;
111
- let x = a.querySelector(".vf-rail-trough-art");
112
- if (x) {
113
- let e = Math.ceil((f + (s === "vertical" ? r : n)) / (s === "vertical" ? r : n)) * (s === "vertical" ? r : n), a = s === "vertical" ? _ : e, o = s === "vertical" ? e : _;
114
- x.style.width = t(a), x.style.height = t(o), x.style.backgroundImage = this.troughCache[s].for(n, r, i, a, o);
105
+ if (p ? t.setAttribute("data-degenerate", p) : t.removeAttribute("data-degenerate"), p === "rail") return;
106
+ let m = this.scrollRange(o, s), h = s === "vertical" ? o.scrollTop : o.scrollLeft, _ = f - g, v = m > 0 && _ > 0 ? Math.max(0, Math.min(Math.floor(_), Math.round(h / m * _))) : 0;
107
+ l.style.translate = s === "vertical" ? `0 ${n(v)}` : `${n(v)} 0`;
108
+ let b = t.querySelector(".vf-rail-trough");
109
+ if (b) {
110
+ let e = i(a, r(b));
111
+ b.style.getPropertyValue("--_vf-trough-tile") !== e && b.style.setProperty("--_vf-trough-tile", e);
115
112
  }
116
113
  }
117
114
  scrollRange(e, t) {
@@ -141,13 +138,13 @@ var h = 16, g = 16, _ = 14, v = 66, y = class {
141
138
  }
142
139
  armRepeat(e) {
143
140
  this.clearRepeat(), this.holdTimer = window.setTimeout(() => {
144
- this.repeatTimer = window.setInterval(e, v);
141
+ this.repeatTimer = window.setInterval(e, _);
145
142
  }, 200);
146
143
  }
147
144
  clearRepeat() {
148
145
  this.holdTimer !== void 0 && window.clearTimeout(this.holdTimer), this.repeatTimer !== void 0 && window.clearInterval(this.repeatTimer), this.holdTimer = void 0, this.repeatTimer = void 0;
149
146
  }
150
- }, b = (e) => e.classList.contains("vf-rail--horizontal") ? "horizontal" : "vertical", x = (e, t) => m`<svg
147
+ }, y = (e) => e.classList.contains("vf-rail--horizontal") ? "horizontal" : "vertical", b = (e, t) => m`<svg
151
148
  class="vf-rail-arrow vf-rail-arrow--${t}"
152
149
  viewBox="1 1 14 14"
153
150
  fill="currentColor"
@@ -156,7 +153,7 @@ var h = 16, g = 16, _ = 14, v = 66, y = class {
156
153
  >
157
154
  <path d=${e.d}></path>
158
155
  </svg>`;
159
- function S(e, t) {
156
+ function x(e, t) {
160
157
  let [n, r, i, a] = t === "vertical" ? [
161
158
  f,
162
159
  p,
@@ -179,19 +176,17 @@ function S(e, t) {
179
176
  @lostpointercapture=${e.onPointerUp}
180
177
  >
181
178
  <div class="vf-rail-button vf-rail-button--decrement">
182
- ${x(n, "rest")}${x(r, "fill")}
179
+ ${b(n, "rest")}${b(r, "fill")}
183
180
  </div>
184
181
  <div class="vf-rail-track">
185
- <div class="vf-rail-trough">
186
- <div class="vf-rail-trough-art"></div>
187
- </div>
182
+ <div class="vf-rail-trough"></div>
188
183
  <div class="vf-rail-thumb"></div>
189
184
  </div>
190
185
  <div class="vf-rail-button vf-rail-button--increment">
191
- ${x(i, "rest")}${x(a, "fill")}
186
+ ${b(i, "rest")}${b(a, "fill")}
192
187
  </div>
193
188
  </div>
194
189
  `;
195
190
  }
196
191
  //#endregion
197
- export { y as ScrollRailController, S as renderScrollRail };
192
+ export { v as ScrollRailController, x as renderScrollRail };
@@ -27,7 +27,9 @@ export { vfDisplay, vfDisplayDecls } from './recipes/display-face.js';
27
27
  export { vfBase } from './recipes/host.js';
28
28
  export { vfStaticText } from './recipes/static-text.js';
29
29
  export { vfDots, vfStripes } from './recipes/pattern.js';
30
- export { vfHardShadowDecls } from './recipes/shadow.js';
30
+ export { vfHardShadow, vfHardShadowDecls } from './recipes/shadow.js';
31
+ export { vfStrokeDecls, vfStrokeInset, vfStrokeShadow } from './recipes/stroke.js';
32
+ export type { VfStrokeOptions } from './recipes/stroke.js';
31
33
  export { vfChromeFrame, vfModalFrame, vfPanel } from './recipes/surface.js';
32
34
  export { RULE_EDGES, parseRule, ruleClasses, vfRule } from './recipes/rule.js';
33
35
  export type { RuleEdge } from './recipes/rule.js';
@@ -1,13 +1,17 @@
1
1
  /**
2
2
  * The System 7 editable-field skin (SPEC §1/§5): a white well with a 1px black
3
- * border, no corner radius, Chicago-style display type, and the kit's dashed
3
+ * frame, no corner radius, Chicago-style display type, and the kit's dashed
4
4
  * keyboard-focus rule under the well (no dotted ring). Add the `vf-field`
5
5
  * class to the inner native `<input>`/`<textarea>` and wrap it in a
6
- * `.vf-field-well` element; the host supplies layout (width, height, padding)
7
- * around it, and `VfTextControlBase` supplies the `.vf-focus-rule` class that
8
- * turns the rule on. Shared by vf-text-field, vf-text-area and
9
- * vf-number-field so the well, focus and disabled treatment stay identical
10
- * across all three.
6
+ * `.vf-field-well` element; the host supplies layout (width, height) around
7
+ * it, and `VfTextControlBase` supplies the `.vf-focus-rule` class that turns
8
+ * the rule on. Shared by vf-text-field, vf-text-area and vf-number-field so
9
+ * the well, focus and disabled treatment stay identical across all three.
10
+ *
11
+ * The frame is a stroke ({@link vfStrokeDecls}), so the recipe owns the
12
+ * input's padding and box-shadow: a host that insets the text restates the
13
+ * padding with `vfStrokeInset` (`padding: ${vfStrokeInset(0)}
14
+ * ${vfStrokeInset(6)}`) in a rule that outranks `.vf-field`.
11
15
  *
12
16
  * Why the wrapper: the rule is a pseudo-element, and a replaced element
13
17
  * generates none — neither `<input>` nor `<textarea>` can draw its own
@@ -1,11 +1,12 @@
1
1
  import { vfDisplayDecls as e } from "./display-face.js";
2
- import { vfFocusUnderline as t } from "./focus.js";
3
- import { css as n } from "lit";
2
+ import { vfStrokeDecls as t } from "./stroke.js";
3
+ import { vfFocusUnderline as n } from "./focus.js";
4
+ import { css as r } from "lit";
4
5
  //#region src/styles/recipes/field.ts
5
- var r = n`
6
+ var i = r`
6
7
  .vf-field {
7
8
  background: var(--vf-white, #fff);
8
- border: calc(var(--vf-scale, 1) * 1px) solid var(--vf-black, #000);
9
+ ${t()}
9
10
  border-radius: 0;
10
11
  /* Editable text is set in the Chicago-style display face. */
11
12
  ${e}
@@ -36,7 +37,7 @@ var r = n`
36
37
  VfTextControlBase adds .vf-focus-rule for a keyboard focus only. */
37
38
  .vf-field-well.vf-focus-rule::after {
38
39
  --vf-focus-underline-offset: -2px;
39
- ${t}
40
+ ${n}
40
41
  }
41
42
  /* Its own token, deliberately not --vf-disabled: the disabled rule below is
42
43
  an inactive control and exempt from contrast requirements, but a
@@ -50,7 +51,7 @@ var r = n`
50
51
  font-weight: inherit;
51
52
  opacity: 1;
52
53
  }
53
- /* Disabled: the text dims to gray; the solid black box border stays. The
54
+ /* Disabled: the text dims to gray; the solid black frame stays. The
54
55
  cursor returns to the arrow — a well that can't take input isn't text. */
55
56
  .vf-field:disabled {
56
57
  color: var(--vf-disabled, #c0c0c0);
@@ -65,4 +66,4 @@ var r = n`
65
66
  }
66
67
  `;
67
68
  //#endregion
68
- export { r as vfField };
69
+ export { i as vfField };
@@ -64,13 +64,17 @@ export declare const vfFocus: import("lit").CSSResult;
64
64
  * the box continues 6px past it (2px half-leading from vfBase's 1.25 line box
65
65
  * over the 16px em, plus the face's 4px descent): 6 − 2 = 4.
66
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).
67
+ * a field's wrapper, a box framed by a stroke (vf-checkbox), whose line is
68
+ * inside the padding box — putting the rule in the second row below it.
69
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
70
+ * pseudo-element can size to contains: `-3px` for vf-select's 1px one, and
71
71
  * for vf-swatch an offset that composes `--vf-shadow-offset` rather than
72
72
  * hard-coding a depth its consumer can retheme.
73
73
  *
74
+ * Under forced colors a stroke is a border again and the padding box sits
75
+ * inside it, so a stroked carrier states its offset and sides with
76
+ * `vfStrokeInset`, which adds the stroke back there.
77
+ *
74
78
  * Drawn in `currentColor`, so it inverts with the label on a pressed face.
75
79
  */
76
80
  export declare const vfFocusUnderline: import("lit").CSSResult;
@@ -1,5 +1,13 @@
1
- export declare const DOT_MOTIF: number;
2
- export declare const DOT_RECTS: readonly import("./tile.js").TileRect[];
1
+ /**
2
+ * The windoid bar's dither is the library's `dots` on its minimal cell: a
3
+ * 2-system-px motif with a single black pixel at its origin. Transparent
4
+ * ground, because the layer floats over the bar and the same art doubles as
5
+ * the forced-colors mask, where the ground would be opacity rather than
6
+ * paint. `vf-window` keeps the layer's repeating tile drawn from this motif
7
+ * (src/tile-grid.ts); the SVG tile below is the forced-colors mask and the
8
+ * consumer token's documented geometry.
9
+ */
10
+ export declare const DOTS: import("../../patterns.js").PatternMotif;
3
11
  /** The dots' tile size in system px (30) — the consumer token's documented box. */
4
12
  export declare const DOT_SPAN: number;
5
13
  /**
@@ -58,13 +66,14 @@ export declare const vfStripes: import("lit").CSSResult;
58
66
  * runs the dots all the way into the side borders (the Windows/ sheet hand-
59
67
  * insets them 2px, which the close-up shows is not the bar's own geometry).
60
68
  *
61
- * The layer's own CSS-repeated tile (a crisp 1-bit SVG on the 30-system-px
62
- * span, overridable via `--vf-dots-pattern`) is the fallback for a window
63
- * with no declared width. A `vf-window` that knows its width renders the
64
- * exact fill INTO the layer instead — the whole-surface raster, or a consumer
65
- * token's placed tile grid (src/tile-grid.ts) — and marks the layer
66
- * `vf-tile-grid`, which switches the repeat off: the dot art is transparent-
67
- * grounded, so a drifting CSS-repeated copy underneath would show through
68
- * between the exactly-placed dots.
69
+ * The kit dots are the layer's own background: the repeating tile
70
+ * `vf-window` writes as `--_vf-dots-tile` (src/tile-grid.ts), at any window
71
+ * width. A consumer `--vf-dots-pattern` takes over in one of two ways. A
72
+ * window that knows its width renders the token's placed tile grid INTO the
73
+ * layer and marks it `vf-tile-grid`, which switches the layer's background
74
+ * off: the dot art is transparent-grounded, so a drifting CSS-repeated copy
75
+ * underneath would show through between the exactly placed dots. A window
76
+ * with no declared width marks the layer `vf-dots-token`, and the token
77
+ * repeats in CSS on its documented 30-system-px span.
69
78
  */
70
79
  export declare const vfDots: import("lit").CSSResult;
@@ -1,8 +1,9 @@
1
1
  import { tileImage as e, tileRects as t, tileSpan as n, vfTileMaskSize as r, vfTileSize as i } from "./tile.js";
2
- import { PATTERNS as a, patternMotif as o } from "../../patterns.js";
3
- import { css as s, unsafeCSS as c } from "lit";
2
+ import { vfRepeatTileSize as a } from "../../tile-grid.js";
3
+ import { PATTERNS as o, patternMotif as s } from "../../patterns.js";
4
+ import { css as c, unsafeCSS as l } from "lit";
4
5
  //#region src/styles/recipes/pattern.ts
5
- var l = o(a.dots), u = l.width, d = l.rects, f = e(u, u, t(d)), p = n(u), m = s`
6
+ var u = s(o.dots), d = u.width, f = e(d, d, t(u.rects)), p = n(d), m = c`
6
7
  .vf-stripes {
7
8
  position: absolute;
8
9
  inset: calc(var(--vf-scale, 1) * 3px) calc(var(--vf-scale, 1) * 1px);
@@ -40,15 +41,21 @@ var l = o(a.dots), u = l.width, d = l.rects, f = e(u, u, t(d)), p = n(u), m = s`
40
41
  background: var(--vf-black, #000000);
41
42
  }
42
43
  }
43
- `, h = s`
44
+ `, h = c`
44
45
  .vf-dots {
45
46
  position: absolute;
46
47
  inset: calc(var(--vf-scale, 1) * 2px) 0;
47
- background-image: var(--vf-dots-pattern, ${c(f)});
48
- ${i(u)}
48
+ background-image: var(--_vf-dots-tile, none);
49
+ ${a}
50
+ image-rendering: pixelated;
49
51
  pointer-events: none;
50
52
  /* The consumer-token art channel for the placed tile grid. */
51
- --_vf-tile-image: var(--vf-dots-pattern, ${c(f)});
53
+ --_vf-tile-image: var(--vf-dots-pattern, ${l(f)});
54
+ }
55
+ .vf-dots.vf-dots-token {
56
+ background-image: var(--vf-dots-pattern);
57
+ ${i(d)}
58
+ image-rendering: auto;
52
59
  }
53
60
  .vf-dots.vf-tile-grid {
54
61
  background-image: none;
@@ -56,23 +63,25 @@ var l = o(a.dots), u = l.width, d = l.rects, f = e(u, u, t(d)), p = n(u), m = s`
56
63
  /* Forced colors preserves url() tiles verbatim, so the dots would stay
57
64
  literal black — invisible on a dark high-contrast theme. Repainted as the
58
65
  ink token through the same tile as a mask (the vf-grid rules idiom), so
59
- the windoid bar's signature follows the user's palette. The exact-fill
60
- children hide here and the span mask takes over — no mask pipeline
61
- rasterizes exactly at a zoom-minted scale anyway (image-rendering does
62
- not reach masks), so forced-colors keeps the span approach and its zoom
63
- caveat, unchanged. */
66
+ the windoid bar's signature follows the user's palette. The tile and the
67
+ token's placed tiles hide here and the span mask takes over — no mask
68
+ pipeline rasterizes exactly at a zoom-minted scale anyway
69
+ (image-rendering does not reach masks), so forced colors keeps the span
70
+ approach and its zoom caveat. */
64
71
  @media (forced-colors: active) {
65
- .vf-dots {
72
+ .vf-dots,
73
+ .vf-dots.vf-dots-token {
66
74
  background-image: none;
75
+ }
76
+ .vf-dots {
67
77
  background-color: var(--vf-black, #000);
68
- mask-image: var(--vf-dots-pattern, ${c(f)});
69
- ${r(u)}
78
+ mask-image: var(--vf-dots-pattern, ${l(f)});
79
+ ${r(d)}
70
80
  }
71
- .vf-dots .vf-tile,
72
- .vf-dots .vf-tile-raster {
81
+ .vf-dots .vf-tile {
73
82
  display: none;
74
83
  }
75
84
  }
76
85
  `;
77
86
  //#endregion
78
- export { u as DOT_MOTIF, d as DOT_RECTS, p as DOT_SPAN, h as vfDots, m as vfStripes };
87
+ export { u as DOTS, p as DOT_SPAN, h as vfDots, m as vfStripes };
@@ -9,15 +9,18 @@ export type RuleEdge = (typeof RULE_EDGES)[number];
9
9
  * floor is `.vf-rule-bottom`, a window's status strip's ceiling is
10
10
  * `.vf-rule-top`, and `vf-container rule="…"` takes the edges by name.
11
11
  *
12
- * A border, not a pseudo-element or a painted line, so it lives inside the
13
- * box's own `border-box` size (vfBase) and insets the padding box the way
14
- * FrameRect insets a rectangle's interior: content, percentage fills and
15
- * children placed against the box all begin inside the rule. Scales with
16
- * `--vf-scale`, so it is one system px at every density, and paints in
17
- * `--vf-black`, so it remaps under forced colors with the rest of the ink.
12
+ * A stroke (see {@link vfStrokeDecls}), inside the box's own `border-box`
13
+ * size (vfBase): the rule's row is padding and an inset shadow paints it
14
+ * black, so it is one system px at every density and zoom, and in-flow
15
+ * content begins inside it. Children placed against the box, and percentage
16
+ * fills, measure from the box's outer edge; a component whose contract puts
17
+ * them inside the rule gives them an inner box to lay out in (vf-container
18
+ * uses its slot). Paints in `--vf-black`, and under forced colors turns back
19
+ * into a border, so it remaps with the rest of the ink.
18
20
  *
19
- * One declaration per edge, so the kit cannot grow two rules — the
20
- * {@link vfHardShadowDecls} principle, applied to the line.
21
+ * The classes compose on one element: each edge's shadow goes through its
22
+ * own private property and one rule paints all four, so the classes own the
23
+ * element's `box-shadow`, and its padding on their edges.
21
24
  *
22
25
  * `npm run verify:rule` asserts a container's rule against the menu bar's.
23
26
  */