vintage-frames 0.5.4 → 0.5.5
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.
- package/custom-elements.json +45 -5
- package/dist/components/vf-scroll-area.d.ts +21 -2
- package/dist/components/vf-scroll-area.js +31 -6
- package/dist/components/vf-window.d.ts +12 -4
- package/dist/components/vf-window.js +5 -1
- package/dist/styles/recipes/scroll-rail.js +9 -3
- package/docs/SPEC.md +5 -5
- package/editor/vscode.html-custom-data.json +13 -3
- package/editor/web-types.json +26 -6
- package/package.json +1 -1
package/custom-elements.json
CHANGED
|
@@ -10756,7 +10756,7 @@
|
|
|
10756
10756
|
"declarations": [
|
|
10757
10757
|
{
|
|
10758
10758
|
"kind": "class",
|
|
10759
|
-
"description": "`<vf-scroll-area>` — a container whose scrollbars look like System 7.\n\nWhite box with a 1px black frame, an inner scrolling viewport, and scroll\nrails the kit draws itself as shadow DOM (the shared `vfScrollRail` recipe):\nboxed arrow buttons at each end, a loose 1-bit dot-dither trough, and the\nclassic fixed 16px thumb. The native scrollbar is hidden, never the native\nscrolling — wheel, trackpad momentum, keyboard, touch and assistive-tech\nscrolling stay the platform's, and ScrollRailController keeps the\nrail in sync while driving the classic interactions (thumb drag, trough\npaging, arrow stepping with auto-repeat). Every engine renders the same\nrail — there is no Firefox fallback skin anymore.\n\nEach reserved scroll rail is a permanent placeholder: arrow buttons on an\nempty white channel sit in the gutter even when the content fits, the\ndither and thumb filling in only once that axis overflows (System 7 drew\nan active window's no-overflow bar as arrows on a bare channel; driven\nby ScrollStateController). Which rails are reserved is set by\naxis; when both are reserved the bottom-right corner joins them.\n\nSize the host (width/height) from the outside; the viewport fills it.",
|
|
10759
|
+
"description": "`<vf-scroll-area>` — a container whose scrollbars look like System 7.\n\nWhite box with a 1px black frame, an inner scrolling viewport, and scroll\nrails the kit draws itself as shadow DOM (the shared `vfScrollRail` recipe):\nboxed arrow buttons at each end, a loose 1-bit dot-dither trough, and the\nclassic fixed 16px thumb. The native scrollbar is hidden, never the native\nscrolling — wheel, trackpad momentum, keyboard, touch and assistive-tech\nscrolling stay the platform's, and ScrollRailController keeps the\nrail in sync while driving the classic interactions (thumb drag, trough\npaging, arrow stepping with auto-repeat). Every engine renders the same\nrail — there is no Firefox fallback skin anymore.\n\nEach reserved scroll rail is a permanent placeholder: arrow buttons on an\nempty white channel sit in the gutter even when the content fits, the\ndither and thumb filling in only once that axis overflows (System 7 drew\nan active window's no-overflow bar as arrows on a bare channel; driven\nby ScrollStateController). Which rails are reserved is set by\naxis; when both are reserved the bottom-right corner joins them,\nand corner reserves that cell on a single-axis rail too — the\nrail stops 15px short of the frame, for a grow box to land in.\n\nSize the host (width/height) from the outside; the viewport fills it. The\nviewport insets its content 8px; flush drops that inset so content\nruns to the frame and the rails.",
|
|
10760
10760
|
"name": "VfScrollArea",
|
|
10761
10761
|
"cssProperties": [
|
|
10762
10762
|
{
|
|
@@ -10807,6 +10807,28 @@
|
|
|
10807
10807
|
"attribute": "axis",
|
|
10808
10808
|
"reflects": true
|
|
10809
10809
|
},
|
|
10810
|
+
{
|
|
10811
|
+
"kind": "field",
|
|
10812
|
+
"name": "flush",
|
|
10813
|
+
"type": {
|
|
10814
|
+
"text": "boolean"
|
|
10815
|
+
},
|
|
10816
|
+
"default": "false",
|
|
10817
|
+
"description": "Drop the viewport's 8px inset: slotted content and the (0,0) of placed\nchildren sit at the frame's inner edge, one system px from the frame\nbox (the border-floor compensation stays — see the viewport rule).\n`vf-window[scrollbars]` forwards its own `flush` here.",
|
|
10818
|
+
"attribute": "flush",
|
|
10819
|
+
"reflects": true
|
|
10820
|
+
},
|
|
10821
|
+
{
|
|
10822
|
+
"kind": "field",
|
|
10823
|
+
"name": "corner",
|
|
10824
|
+
"type": {
|
|
10825
|
+
"text": "boolean"
|
|
10826
|
+
},
|
|
10827
|
+
"default": "false",
|
|
10828
|
+
"description": "Reserve the bottom-right corner cell on a single-axis rail: the rail\nstops 15px short of the frame and the viewport spans the rest, leaving\nthe 15×15 cell — with the two dividers a `both` rail's corner carries —\nfor a grow box to land in. A `both` rail always has the cell, so the\nflag changes nothing there. `vf-window[scrollbars resizable]` sets it.",
|
|
10829
|
+
"attribute": "corner",
|
|
10830
|
+
"reflects": true
|
|
10831
|
+
},
|
|
10810
10832
|
{
|
|
10811
10833
|
"kind": "field",
|
|
10812
10834
|
"name": "label",
|
|
@@ -10915,6 +10937,24 @@
|
|
|
10915
10937
|
"description": "Which scroll rails to reserve as permanent placeholders: `vertical`\n(default), `horizontal`, or `both`. Each reserved rail shows an empty white\nchannel until its axis overflows; the unreserved axis still scrolls\nnatively (wheel, keyboard) but draws no rail.",
|
|
10916
10938
|
"fieldName": "axis"
|
|
10917
10939
|
},
|
|
10940
|
+
{
|
|
10941
|
+
"name": "flush",
|
|
10942
|
+
"type": {
|
|
10943
|
+
"text": "boolean"
|
|
10944
|
+
},
|
|
10945
|
+
"default": "false",
|
|
10946
|
+
"description": "Drop the viewport's 8px inset: slotted content and the (0,0) of placed\nchildren sit at the frame's inner edge, one system px from the frame\nbox (the border-floor compensation stays — see the viewport rule).\n`vf-window[scrollbars]` forwards its own `flush` here.",
|
|
10947
|
+
"fieldName": "flush"
|
|
10948
|
+
},
|
|
10949
|
+
{
|
|
10950
|
+
"name": "corner",
|
|
10951
|
+
"type": {
|
|
10952
|
+
"text": "boolean"
|
|
10953
|
+
},
|
|
10954
|
+
"default": "false",
|
|
10955
|
+
"description": "Reserve the bottom-right corner cell on a single-axis rail: the rail\nstops 15px short of the frame and the viewport spans the rest, leaving\nthe 15×15 cell — with the two dividers a `both` rail's corner carries —\nfor a grow box to land in. A `both` rail always has the cell, so the\nflag changes nothing there. `vf-window[scrollbars resizable]` sets it.",
|
|
10956
|
+
"fieldName": "corner"
|
|
10957
|
+
},
|
|
10918
10958
|
{
|
|
10919
10959
|
"name": "label",
|
|
10920
10960
|
"type": {
|
|
@@ -17256,7 +17296,7 @@
|
|
|
17256
17296
|
"text": "boolean"
|
|
17257
17297
|
},
|
|
17258
17298
|
"default": "false",
|
|
17259
|
-
"description": "Remove the default 12px body padding.",
|
|
17299
|
+
"description": "Remove the default 12px body padding. Under `scrollbars` the body has\nnone to remove, so the flag reaches the built-in scroll area instead\nand drops its viewport's 8px inset: content runs to the frame and the\nrails, and the (0,0) of placed children is the content region's corner.",
|
|
17260
17300
|
"attribute": "flush",
|
|
17261
17301
|
"reflects": true
|
|
17262
17302
|
},
|
|
@@ -17266,7 +17306,7 @@
|
|
|
17266
17306
|
"type": {
|
|
17267
17307
|
"text": "'vertical' | 'horizontal' | 'both' | undefined"
|
|
17268
17308
|
},
|
|
17269
|
-
"description": "Put System 7 scroll rails on the window edge — the classic document\nwindow. The body slot renders inside a built-in `vf-scroll-area` pulled\none system pixel under the frame on every side, so the rails repaint the\nborder lines and a `resizable` window's grow box lands in the corner\ncell. Values mirror `vf-scroll-area`'s `axis`; the
|
|
17309
|
+
"description": "Put System 7 scroll rails on the window edge — the classic document\nwindow. The body slot renders inside a built-in `vf-scroll-area` pulled\none system pixel under the frame on every side, so the rails repaint the\nborder lines and a `resizable` window's grow box lands in the corner\ncell — reserved on a single-axis rail too (the area's `corner`), unless\na populated status strip holds the grow box, when the rail runs edge to\nedge onto the strip's rule. Values mirror `vf-scroll-area`'s `axis`; the\n`heading` names the scroll region; the viewport part is re-exported.\nThe body's own padding goes; the viewport keeps its 8px inset unless\n`flush`, which passes through. The slotted composition (SPEC §5\nvf-scroll-area) still works for windows that want an inset well instead.",
|
|
17270
17310
|
"attribute": "scrollbars",
|
|
17271
17311
|
"reflects": true
|
|
17272
17312
|
},
|
|
@@ -17636,7 +17676,7 @@
|
|
|
17636
17676
|
"text": "boolean"
|
|
17637
17677
|
},
|
|
17638
17678
|
"default": "false",
|
|
17639
|
-
"description": "Remove the default 12px body padding.",
|
|
17679
|
+
"description": "Remove the default 12px body padding. Under `scrollbars` the body has\nnone to remove, so the flag reaches the built-in scroll area instead\nand drops its viewport's 8px inset: content runs to the frame and the\nrails, and the (0,0) of placed children is the content region's corner.",
|
|
17640
17680
|
"fieldName": "flush"
|
|
17641
17681
|
},
|
|
17642
17682
|
{
|
|
@@ -17644,7 +17684,7 @@
|
|
|
17644
17684
|
"type": {
|
|
17645
17685
|
"text": "'vertical' | 'horizontal' | 'both' | undefined"
|
|
17646
17686
|
},
|
|
17647
|
-
"description": "Put System 7 scroll rails on the window edge — the classic document\nwindow. The body slot renders inside a built-in `vf-scroll-area` pulled\none system pixel under the frame on every side, so the rails repaint the\nborder lines and a `resizable` window's grow box lands in the corner\ncell. Values mirror `vf-scroll-area`'s `axis`; the
|
|
17687
|
+
"description": "Put System 7 scroll rails on the window edge — the classic document\nwindow. The body slot renders inside a built-in `vf-scroll-area` pulled\none system pixel under the frame on every side, so the rails repaint the\nborder lines and a `resizable` window's grow box lands in the corner\ncell — reserved on a single-axis rail too (the area's `corner`), unless\na populated status strip holds the grow box, when the rail runs edge to\nedge onto the strip's rule. Values mirror `vf-scroll-area`'s `axis`; the\n`heading` names the scroll region; the viewport part is re-exported.\nThe body's own padding goes; the viewport keeps its 8px inset unless\n`flush`, which passes through. The slotted composition (SPEC §5\nvf-scroll-area) still works for windows that want an inset well instead.",
|
|
17648
17688
|
"fieldName": "scrollbars"
|
|
17649
17689
|
},
|
|
17650
17690
|
{
|
|
@@ -18,9 +18,13 @@ declare const VfScrollArea_base: (new (...args: any[]) => import("../position.js
|
|
|
18
18
|
* dither and thumb filling in only once that axis overflows (System 7 drew
|
|
19
19
|
* an active window's no-overflow bar as arrows on a bare channel; driven
|
|
20
20
|
* by {@link ScrollStateController}). Which rails are reserved is set by
|
|
21
|
-
* {@link axis}; when both are reserved the bottom-right corner joins them
|
|
21
|
+
* {@link axis}; when both are reserved the bottom-right corner joins them,
|
|
22
|
+
* and {@link corner} reserves that cell on a single-axis rail too — the
|
|
23
|
+
* rail stops 15px short of the frame, for a grow box to land in.
|
|
22
24
|
*
|
|
23
|
-
* Size the host (width/height) from the outside; the viewport fills it.
|
|
25
|
+
* Size the host (width/height) from the outside; the viewport fills it. The
|
|
26
|
+
* viewport insets its content 8px; {@link flush} drops that inset so content
|
|
27
|
+
* runs to the frame and the rails.
|
|
24
28
|
*
|
|
25
29
|
* @slot - Scrollable content.
|
|
26
30
|
* @csspart viewport - The inner scrolling container.
|
|
@@ -39,6 +43,21 @@ export declare class VfScrollArea extends VfScrollArea_base {
|
|
|
39
43
|
* natively (wheel, keyboard) but draws no rail.
|
|
40
44
|
*/
|
|
41
45
|
axis: 'vertical' | 'horizontal' | 'both';
|
|
46
|
+
/**
|
|
47
|
+
* Drop the viewport's 8px inset: slotted content and the (0,0) of placed
|
|
48
|
+
* children sit at the frame's inner edge, one system px from the frame
|
|
49
|
+
* box (the border-floor compensation stays — see the viewport rule).
|
|
50
|
+
* `vf-window[scrollbars]` forwards its own `flush` here.
|
|
51
|
+
*/
|
|
52
|
+
flush: boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Reserve the bottom-right corner cell on a single-axis rail: the rail
|
|
55
|
+
* stops 15px short of the frame and the viewport spans the rest, leaving
|
|
56
|
+
* the 15×15 cell — with the two dividers a `both` rail's corner carries —
|
|
57
|
+
* for a grow box to land in. A `both` rail always has the cell, so the
|
|
58
|
+
* flag changes nothing there. `vf-window[scrollbars resizable]` sets it.
|
|
59
|
+
*/
|
|
60
|
+
corner: boolean;
|
|
42
61
|
/**
|
|
43
62
|
* Accessible name for the scrolling viewport, applied as its `aria-label`
|
|
44
63
|
* (an `aria-label` on the host would not reach into the shadow DOM). The
|
|
@@ -13,7 +13,7 @@ import { property as h, query as g, state as _ } from "lit/decorators.js";
|
|
|
13
13
|
//#region src/components/vf-scroll-area.ts
|
|
14
14
|
var v = class extends r(d) {
|
|
15
15
|
constructor(...e) {
|
|
16
|
-
super(...e), this.scale = new t(this), this.gridSnap = new s(this), this.axis = "vertical", this.label = "", this.scrollState = new c(this, () => this.viewport, () => this.content, (e) => {
|
|
16
|
+
super(...e), this.scale = new t(this), this.gridSnap = new s(this), this.axis = "vertical", this.flush = !1, this.corner = !1, this.label = "", this.scrollState = new c(this, () => this.viewport, () => this.content, (e) => {
|
|
17
17
|
this._scrollable = e.x || e.y;
|
|
18
18
|
}), this.rail = new l(this, {
|
|
19
19
|
getScroll: () => this.viewport,
|
|
@@ -46,11 +46,13 @@ var v = class extends r(d) {
|
|
|
46
46
|
}
|
|
47
47
|
:host(:not([axis])) .box,
|
|
48
48
|
:host([axis='vertical']) .box,
|
|
49
|
-
:host([axis='both']) .box
|
|
49
|
+
:host([axis='both']) .box,
|
|
50
|
+
:host([corner]) .box {
|
|
50
51
|
grid-template-columns: 1fr auto;
|
|
51
52
|
}
|
|
52
53
|
:host([axis='horizontal']) .box,
|
|
53
|
-
:host([axis='both']) .box
|
|
54
|
+
:host([axis='both']) .box,
|
|
55
|
+
:host([corner]) .box {
|
|
54
56
|
grid-template-rows: 1fr auto;
|
|
55
57
|
}
|
|
56
58
|
.viewport {
|
|
@@ -73,6 +75,23 @@ var v = class extends r(d) {
|
|
|
73
75
|
var(--vf-scale, 1) * 8px + mod(var(--vf-scale, 1) * 1px, 1px)
|
|
74
76
|
);
|
|
75
77
|
}
|
|
78
|
+
/* flush: the inset goes, the border-floor term stays — content and the
|
|
79
|
+
(0,0) of placed children sit at the frame's inner edge, exactly one
|
|
80
|
+
system px from the frame box at every scale. */
|
|
81
|
+
:host([flush]) .viewport {
|
|
82
|
+
padding: mod(var(--vf-scale, 1) * 1px, 1px);
|
|
83
|
+
}
|
|
84
|
+
/* A single-axis rail with a reserved corner: the viewport spans the
|
|
85
|
+
track the missing rail would have taken, up to the corner cell, so
|
|
86
|
+
the rail stops 15px short of the frame. (A both-axes rail has no
|
|
87
|
+
missing track — the flag is inert there.) */
|
|
88
|
+
:host([corner][axis='horizontal']) .viewport {
|
|
89
|
+
grid-column: 1 / 3;
|
|
90
|
+
}
|
|
91
|
+
:host([corner]:not([axis])) .viewport,
|
|
92
|
+
:host([corner][axis='vertical']) .viewport {
|
|
93
|
+
grid-row: 1 / 3;
|
|
94
|
+
}
|
|
76
95
|
.vf-rail--vertical {
|
|
77
96
|
grid-area: 1 / 2;
|
|
78
97
|
}
|
|
@@ -100,7 +119,7 @@ var v = class extends r(d) {
|
|
|
100
119
|
];
|
|
101
120
|
}
|
|
102
121
|
render() {
|
|
103
|
-
let e = this.axis !== "horizontal", t = this.axis === "horizontal" || this.axis === "both";
|
|
122
|
+
let e = this.axis !== "horizontal", t = this.axis === "horizontal" || this.axis === "both", n = e && t || this.corner;
|
|
104
123
|
return p`
|
|
105
124
|
<div class="box vf-snap">
|
|
106
125
|
<div
|
|
@@ -114,11 +133,17 @@ var v = class extends r(d) {
|
|
|
114
133
|
</div>
|
|
115
134
|
${e ? u(this.rail, "vertical") : m}
|
|
116
135
|
${t ? u(this.rail, "horizontal") : m}
|
|
117
|
-
${
|
|
136
|
+
${n ? p`<div class="vf-rail-corner" aria-hidden="true"></div>` : m}
|
|
118
137
|
</div>
|
|
119
138
|
`;
|
|
120
139
|
}
|
|
121
140
|
};
|
|
122
|
-
n([h({ reflect: !0 })], v.prototype, "axis", void 0), n([h(
|
|
141
|
+
n([h({ reflect: !0 })], v.prototype, "axis", void 0), n([h({
|
|
142
|
+
type: Boolean,
|
|
143
|
+
reflect: !0
|
|
144
|
+
})], v.prototype, "flush", void 0), n([h({
|
|
145
|
+
type: Boolean,
|
|
146
|
+
reflect: !0
|
|
147
|
+
})], v.prototype, "corner", void 0), n([h()], v.prototype, "label", void 0), n([g(".viewport")], v.prototype, "viewport", void 0), n([g(".content")], v.prototype, "content", void 0), n([_()], v.prototype, "_scrollable", void 0), v = n([e("vf-scroll-area")], v);
|
|
123
148
|
//#endregion
|
|
124
149
|
export { v as VfScrollArea };
|
|
@@ -94,16 +94,24 @@ export declare class VfWindow extends VfWindow_base {
|
|
|
94
94
|
movable: boolean;
|
|
95
95
|
/** Show a grow box at the bottom-right corner for resizing. */
|
|
96
96
|
resizable: boolean;
|
|
97
|
-
/**
|
|
97
|
+
/**
|
|
98
|
+
* Remove the default 12px body padding. Under `scrollbars` the body has
|
|
99
|
+
* none to remove, so the flag reaches the built-in scroll area instead
|
|
100
|
+
* and drops its viewport's 8px inset: content runs to the frame and the
|
|
101
|
+
* rails, and the (0,0) of placed children is the content region's corner.
|
|
102
|
+
*/
|
|
98
103
|
flush: boolean;
|
|
99
104
|
/**
|
|
100
105
|
* Put System 7 scroll rails on the window edge — the classic document
|
|
101
106
|
* window. The body slot renders inside a built-in `vf-scroll-area` pulled
|
|
102
107
|
* one system pixel under the frame on every side, so the rails repaint the
|
|
103
108
|
* border lines and a `resizable` window's grow box lands in the corner
|
|
104
|
-
* cell
|
|
105
|
-
*
|
|
106
|
-
*
|
|
109
|
+
* cell — reserved on a single-axis rail too (the area's `corner`), unless
|
|
110
|
+
* a populated status strip holds the grow box, when the rail runs edge to
|
|
111
|
+
* edge onto the strip's rule. Values mirror `vf-scroll-area`'s `axis`; the
|
|
112
|
+
* `heading` names the scroll region; the viewport part is re-exported.
|
|
113
|
+
* The body's own padding goes; the viewport keeps its 8px inset unless
|
|
114
|
+
* `flush`, which passes through. The slotted composition (SPEC §5
|
|
107
115
|
* vf-scroll-area) still works for windows that want an inset well instead.
|
|
108
116
|
*/
|
|
109
117
|
scrollbars?: 'vertical' | 'horizontal' | 'both';
|
|
@@ -201,7 +201,9 @@ var z = 80, B = 54, V = 24, H = 8, U = class extends E(c(N)) {
|
|
|
201
201
|
frame on every side, so its own frame border repaints the window's
|
|
202
202
|
border lines exactly (no doubled frame), the rails run edge to edge,
|
|
203
203
|
and a resizable window's grow box (z-index 1) lands over the rail
|
|
204
|
-
corner cell
|
|
204
|
+
corner cell — which the render reserves on a single-axis rail too
|
|
205
|
+
(the area's corner flag), except when the status strip holds the
|
|
206
|
+
grow box instead. */
|
|
205
207
|
.edge-scroll {
|
|
206
208
|
width: calc(100% + var(--vf-scale, 1) * 2px);
|
|
207
209
|
height: calc(100% + var(--vf-scale, 1) * 2px);
|
|
@@ -380,6 +382,8 @@ var z = 80, B = 54, V = 24, H = 8, U = class extends E(c(N)) {
|
|
|
380
382
|
<vf-scroll-area
|
|
381
383
|
class="edge-scroll"
|
|
382
384
|
axis=${this.scrollbars}
|
|
385
|
+
?flush=${this.flush}
|
|
386
|
+
?corner=${this.resizable && !this._hasStatus}
|
|
383
387
|
label=${this.heading || I}
|
|
384
388
|
exportparts="viewport"
|
|
385
389
|
>
|
|
@@ -150,11 +150,17 @@ var s = i(r["gray-25"]), c = s.width, l = s.height, u = s.rects, d = e(c, l, t(u
|
|
|
150
150
|
width: calc(var(--vf-scale, 1) * 16px);
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
-
/* The
|
|
153
|
+
/* The corner cell: the rails' own 15px in both dimensions, so it sizes its
|
|
154
|
+
grid track even when only one rail is there to size it (vf-scroll-area's
|
|
155
|
+
corner flag on a single-axis rail). It supplies the two interior dividers
|
|
154
156
|
(against the vertical increment arrow above and the horizontal one
|
|
155
|
-
beside it) that those buttons leave to it
|
|
156
|
-
|
|
157
|
+
beside it) that those buttons leave to it — on a single-axis rail the
|
|
158
|
+
other divider continues the rail's line across the cell, the way
|
|
159
|
+
DrawGrowIcon drew it; a resizable window's grow box lands exactly over
|
|
160
|
+
it. */
|
|
157
161
|
.vf-rail-corner {
|
|
162
|
+
width: calc(var(--vf-scale, 1) * 15px);
|
|
163
|
+
height: calc(var(--vf-scale, 1) * 15px);
|
|
158
164
|
background: var(--vf-white, #fff);
|
|
159
165
|
border-top: calc(var(--vf-scale, 1) * 1px) solid var(--vf-black, #000);
|
|
160
166
|
border-left: calc(var(--vf-scale, 1) * 1px) solid var(--vf-black, #000);
|
package/docs/SPEC.md
CHANGED
|
@@ -208,7 +208,7 @@ Full-bleed classic desktop container.
|
|
|
208
208
|
|
|
209
209
|
#### `vf-window` (`VfWindow`, vf-window.ts)
|
|
210
210
|
The desktop-window shell: the classic document window (see DragThing screenshot), parameterized down to the windoid (see the Group A recipe table).
|
|
211
|
-
- **Attributes/props:** `heading: string` (title text), `width: number` / `height: number` (**declare them both** — whole system px, so the window keeps its proportion to the chrome inside it at every density. A window is a fixed box in both axes, the way a WIND resource was: one that grows with its body is one the user can neither predict nor control via the grow box. Each missing dimension falls back to something different — width to block layout, height to the content — and the window names whichever are missing, once, in the console), `active: boolean` (default **true**; reflect), `closable: boolean` (default true), `zoomable: boolean` (default false), `movable: boolean` (default false), `resizable: boolean` (default false), `flush: boolean` (default false — removes body padding), `variant?: 'utility'` (the slim windoid chrome), `scrollbars?: 'vertical' | 'horizontal' | 'both'` (edge scroll rails).
|
|
211
|
+
- **Attributes/props:** `heading: string` (title text), `width: number` / `height: number` (**declare them both** — whole system px, so the window keeps its proportion to the chrome inside it at every density. A window is a fixed box in both axes, the way a WIND resource was: one that grows with its body is one the user can neither predict nor control via the grow box. Each missing dimension falls back to something different — width to block layout, height to the content — and the window names whichever are missing, once, in the console), `active: boolean` (default **true**; reflect), `closable: boolean` (default true), `zoomable: boolean` (default false), `movable: boolean` (default false), `resizable: boolean` (default false), `flush: boolean` (default false — removes the body padding; under `scrollbars`, the built-in viewport's 8px inset instead), `variant?: 'utility'` (the slim windoid chrome), `scrollbars?: 'vertical' | 'horizontal' | 'both'` (edge scroll rails).
|
|
212
212
|
- **Visual:** `vfChromeFrame` + `vfTitleBar` (§4), plus a full-size flex-column layout on the frame. `display: block`. Sets `--vf-surface: var(--vf-white, #fff)` on itself.
|
|
213
213
|
- Title bar: from `vfTitleBar` — height `var(--vf-titlebar-height, 18px)`, white bg, bottom `1px solid black`, contains `.vf-stripes` layer (only when `active`). `touch-action: none` only when `[movable]`.
|
|
214
214
|
- Title: centered, bold, on a white patch (`padding: 0 6px`; cap band on interior rows 4..12, 7px of white between ink and stripes — see §4 `vfTitleBar` for the traced geometry and the lattice hold) above the stripes, with `--vf-title-inset: 60px` of clearance so it ellipsizes before reaching the widgets. Inactive: no stripes, widgets undrawn (transparent ink — they keep their tab stops; see A11y below), the grow box's nested squares hidden, and every managed scroll rail inside the window blanked (see "always-a-rail" §5 vf-scroll-area) — but the title text stays black (System 7 never grayed the window title).
|
|
@@ -217,7 +217,7 @@ The desktop-window shell: the classic document window (see DragThing screenshot)
|
|
|
217
217
|
- Body: `padding: 12px` (0 if `flush` or `scrollbars`), and `overflow: hidden` — the window being a fixed box, content taller than it is clipped at the frame the way the classic content region was, rather than painting out over the desktop; `scrollbars` is how the user reaches the rest. Two deliberate exemptions: `[scrollbars]` restores `overflow: visible`, because the edge-rail composition below pulls the scroll area one system px *outside* the body on every side and clipping would shave exactly that overhang off (the scroll area does its own clipping); and a control's drop-open panel is not clipped — `vf-select`'s list is `position: fixed` off the control's rect precisely to escape clipping ancestors (§5 vf-select), and it still escapes, because nothing between it and the viewport establishes a containing block for fixed descendants (the grid-snap correction is a `position: relative` left/top offset, never a transform — see §7). A `vf-menu` panel is anchored `position: absolute` and *would* clip, but a menu bar belongs to the desktop, not inside a window body.
|
|
218
218
|
- Grow box (if `resizable`): 15×15 at bottom-right corner, white bg, 1px black top/left borders, containing two overlapping small square outlines. Inactive: the cell and its borders stay, the nested squares go — System 7 drew a deactivated window's size box hollow, with its blanked scroll rails.
|
|
219
219
|
- Status bar (`status` slot): the classic bottom readout strip ("40px x 40px") — a 1px black rule (`vfRule`'s `.vf-rule-top`) over a white interior, `--vf-status-bar-height` (15px) in all, the grow box's own height, so a `resizable` window's grow box sits flush in the strip's right end (its top/left borders take over the rule there; the strip reserves 21px of right padding under `resizable` so text clears the cell). Slotted content rides the body face's native 12px line (`--vf-line-height`), whole-pixel centered in the 14px interior, `padding-inline: 6px`, nowrap + clipped. Takes **no space** until the slot is populated (the dialog-footer pattern), and collapses again when it empties. Composes with `scrollbars`: the edge rails' bottom overhang lands its frame line exactly on the strip's rule, so the two never double up — though the grow box then sits in the strip rather than the rail corner cell.
|
|
220
|
-
- Edge scroll rails (if `scrollbars`): the body slot renders inside a shadow `vf-scroll-area` (its `axis` = the attribute's value, `label` = the heading, `viewport` part re-exported) carrying the TeachText composition internally — `calc(100% + 2px·scale)` with `margin: -1px·scale`, one system px under the frame on every side, so the area's own frame border repaints the window's border lines and a `resizable` window's grow box (z-index 1) lands exactly over the rail-corner cell. Same caveats as the slotted composition (see vf-scroll-area §5), which remains supported for inset wells.
|
|
220
|
+
- Edge scroll rails (if `scrollbars`): the body slot renders inside a shadow `vf-scroll-area` (its `axis` = the attribute's value, `label` = the heading, `viewport` part re-exported) carrying the TeachText composition internally — `calc(100% + 2px·scale)` with `margin: -1px·scale`, one system px under the frame on every side, so the area's own frame border repaints the window's border lines and a `resizable` window's grow box (z-index 1) lands exactly over the rail-corner cell. `flush` passes through to the area (its viewport drops the 8px inset, keeping the border-floor term, so content and the (0,0) of placed children sit at the content region's corner); a `resizable` window sets the area's `corner`, so a single-axis rail stops at the corner cell too — unless the status strip is populated, when the grow box sits in the strip and the rail runs edge to edge onto the strip's rule. Same caveats as the slotted composition (see vf-scroll-area §5), which remains supported for inset wells.
|
|
221
221
|
- Utility variant (`variant="utility"`): the slim windoid bar traced from `Windows/utility-window.png` — `--vf-titlebar-height-utility` (12px = 11px interior + 1px rule), the `vfDots` dither instead of stripes (flush to the side borders — see §4 vfDots), 7×7 widgets (`top: 2px`; close `left: 7px`, zoom `right: 8px` — the art is asymmetric by that pixel) with a 2px patch ring where the striped bar's is 1px (`--vf-widget-ring`, internal geometry: the windoid sheet clears two px of dither beside its widgets), and the nested zoom square shrunk so its edges land at sprite col/row 3. No title patch: the display face's 16px line box can't sit in an 11px interior, so `.vf-title` is `display: none` under the variant (a retheming consumer can re-show it via `::part(title)`) and the heading names the widgets. A pressed windoid widget inverts whole — black interior under a white (invisible) borderline — rather than flashing the big bar's 9×9 sunburst, which can't land on a 5×5 interior.
|
|
222
222
|
- **A11y:** the close/zoom `aria-label`s are qualified by the title when there is one (`Close ${heading}` / `Zoom ${heading}`, falling back to bare `Close` / `Zoom`) — several windows are open at once by design, so a bare repeated "Close" gives an AT user no way to tell which window a widget belongs to. The frame is `role="group"`, named by the title patch via `aria-labelledby` when there is a heading (the utility variant's hidden patch still names it — AccName resolves hidden labelledby targets) — `group` rather than `region` deliberately, so a desktop of windows doesn't pollute landmark navigation; the title bar is a `<div>`, never a `<header>`, which would map to an unnamed `banner` landmark even inside the shadow root. An inactive window's widgets stay in the tree and the tab order but paint no ink (transparent border/background/patch ring — the bare System 7 bar): a background window whose body holds nothing focusable is still reachable, activated by `vf-desktop`'s `focusin` raise the moment Tab lands on a widget, and never drops focus to `<body>` when it deactivates. `npm run verify:window-a11y`.
|
|
223
223
|
- **Behavior:** close box click → `vf-close` (does NOT remove itself; consumer decides). Zoom box click → `vf-zoom`. If `movable`: dragging the title bar moves the window — the drag seeds its origin from the current offset position (once, converting to system px) and then states `left`/`top` via pointer capture, in whole system px like any authored placement. If `resizable`: dragging the grow box states `width`/`height` the same way, and fires `vf-resize` (detail `{ width, height, commit }`, sizes in whole system px) — one event per size the drag actually writes (`commit: false`), fired *after* the new box is applied so a handler that measures reads the resized layout, then a final `commit: true` as the gesture settles (release or cancel), only when it changed the size. Window content can follow the grow box from this event alone, no ResizeObserver. Fired by the gesture only: a programmatic `width`/`height` write fires nothing, the way a value set fires no `vf-change`.
|
|
@@ -389,11 +389,11 @@ Classic list box.
|
|
|
389
389
|
|
|
390
390
|
#### `vf-scroll-area` (`VfScrollArea`, vf-scroll-area.ts)
|
|
391
391
|
A container whose scrollbars look like System 7.
|
|
392
|
-
- **Attributes/props:** `axis: 'vertical' | 'horizontal' | 'both'` (default `'vertical'`, reflected) — which scroll rails to reserve as permanent placeholders (see "always-a-rail" below); `label: string` — accessible name for the viewport (`aria-label` on the viewport, since an `aria-label` on the host cannot reach into the shadow DOM). The viewport is a **Tab stop only while its content actually overflows** — the same state `ScrollStateController` measures for the rails; a fitting scroll area used to be a focusable stop with `role: generic` and no name, a dead Tab press. Whenever it is a stop it carries a role: `role="region"` when `label` names it (a named landmark), `role="group"` when not (an unnamed region is inert, so that role is reserved for the labelled case).
|
|
393
|
-
- **Visual:** `display: block`; the snapped wrapper carries a real 1px black frame and a grid reserving each rail as its own edge column/row — `[viewport | vertical rail]` over `[horizontal rail | corner]` — with the white inner viewport padded `8px` plus the border-floor compensation (a `mod()` term restoring what engines floor off the fractional border-width, so slotted content and the (0,0) of placed children sit exactly 9 system px from the frame box at every scale). Consumer sets width/height on host. The rails are the drawn `vfScrollRail` subtree (§4 — the classic 16px cell counting the frame line: divider, 14px channel, 15px arrow cells, the fixed 16px thumb, the `tileRaster` dot-dither trough), rendered as later siblings of the viewport and synced to its native scrolling by `ScrollRailController`; the viewport carries the `vf-scroll` class, which hides the native bar without touching the native scrolling. When both rails are reserved, the corner cell joins them, supplying the interior dividers the adjacent arrow cells leave to it. Every engine renders the same rail; the old `::-webkit-scrollbar` skin, its `.vf-scroll-frame` overlay contortion (WebKit quantized native scrollbar rects to whole CSS px) and the Firefox `scrollbar-color` fallback are all retired.
|
|
392
|
+
- **Attributes/props:** `axis: 'vertical' | 'horizontal' | 'both'` (default `'vertical'`, reflected) — which scroll rails to reserve as permanent placeholders (see "always-a-rail" below); `flush: boolean` (default false, reflected) — drops the viewport's 8px inset, keeping the border-floor term, so content and the (0,0) of placed children sit at the frame's inner edge; `corner: boolean` (default false, reflected) — reserves the bottom-right corner cell on a single-axis rail (the rail stops 15px short of the frame, the viewport spans the rest), for a grow box to land in; inert on `both`, which always has the cell; `label: string` — accessible name for the viewport (`aria-label` on the viewport, since an `aria-label` on the host cannot reach into the shadow DOM). The viewport is a **Tab stop only while its content actually overflows** — the same state `ScrollStateController` measures for the rails; a fitting scroll area used to be a focusable stop with `role: generic` and no name, a dead Tab press. Whenever it is a stop it carries a role: `role="region"` when `label` names it (a named landmark), `role="group"` when not (an unnamed region is inert, so that role is reserved for the labelled case).
|
|
393
|
+
- **Visual:** `display: block`; the snapped wrapper carries a real 1px black frame and a grid reserving each rail as its own edge column/row — `[viewport | vertical rail]` over `[horizontal rail | corner]` — with the white inner viewport padded `8px` plus the border-floor compensation (a `mod()` term restoring what engines floor off the fractional border-width, so slotted content and the (0,0) of placed children sit exactly 9 system px from the frame box at every scale; `flush` keeps only the compensation, and they sit exactly 1). Consumer sets width/height on host. The rails are the drawn `vfScrollRail` subtree (§4 — the classic 16px cell counting the frame line: divider, 14px channel, 15px arrow cells, the fixed 16px thumb, the `tileRaster` dot-dither trough), rendered as later siblings of the viewport and synced to its native scrolling by `ScrollRailController`; the viewport carries the `vf-scroll` class, which hides the native bar without touching the native scrolling. When both rails are reserved, the corner cell joins them, supplying the interior dividers the adjacent arrow cells leave to it; `corner` reserves the same 15×15 cell beside a single rail, its other divider continuing the rail's line across the cell — DrawGrowIcon's form, for the grow box. Every engine renders the same rail; the old `::-webkit-scrollbar` skin, its `.vf-scroll-frame` overlay contortion (WebKit quantized native scrollbar rects to whole CSS px) and the Firefox `scrollbar-color` fallback are all retired.
|
|
394
394
|
- **Always-a-rail behavior:** each *reserved* axis (per `axis`) renders its rail element as a permanent placeholder — arrow buttons on an empty white channel (dither off, no thumb; System 7 drew the arrows on any bar in an active window, and a scroller outside a window always counts as active) — until the content overflows that axis, when the dither and thumb fill in and the arrows go live (an idle axis's arrows are drawn but inert — the press guard in `ScrollRailController` skips them). `ScrollStateController` (`src/scroll-state.ts`) measures both axes and writes `data-overflow-x` / `data-overflow-y` (`"true"` / `"false"`) on the scroll element; the recipe keys the dither and thumb off those attributes. The unreserved axis still scrolls natively (wheel, keyboard) but draws no rail. Shared by vf-list and vf-text-area; a future `@container scroll-state(scrollable)` query could replace the JS for slotted-content components.
|
|
395
395
|
- **Inactive-window blanking:** the HIG's non-frontmost window must not display interactive scroll UX, so the controller also finds the nearest `vf-window` up the composed tree (light-DOM ancestor for a slotted scroller, shadow ancestor for `vf-window[scrollbars]`'s own edge rails), watches its reflected `active` attribute, and toggles a presence-only `data-window-inactive` on the scroll element. While present, the recipe empties dither/thumb/arrows on BOTH axes regardless of overflow — the bare channel, arrows included (unlike the idle rail, which keeps them), exactly as System 7 blanked a deactivated window's bars (its List Manager/TextEdit deactivated in-window scrollbars too). No `vf-window` ancestor → the attribute never appears: dialogs have no inactive state and a bare scroll component always draws live. Like the overflow half, this signal could one day go declarative — a custom property cascaded under `vf-window:not([active])` gating the recipe via an `@container style()` query; the rails being ordinary DOM, that migration is a plain selector swap (see the FUTURE note in scroll-state.ts).
|
|
396
|
-
- **Document-window (TeachText) composition:** to put the rails on a window's edge rather than inset in its body, slot the scroll area into a `vf-window[flush]` sized `calc(100% + 2px·scale)` with `margin: -1px·scale` — one system pixel under the window frame on every side. Its own frame border then repaints the window's border lines exactly (no doubled frame), and a resizable window's grow box lands exactly over the rail's corner cell, giving the classic System 7 document window. `vf-window[scrollbars]` renders exactly this composition from its own shadow tree, so the one-liner and the slotted form are geometrically identical; slot it yourself when the well should sit *inset* in the body instead (the installer's read-me well).
|
|
396
|
+
- **Document-window (TeachText) composition:** to put the rails on a window's edge rather than inset in its body, slot the scroll area into a `vf-window[flush]` sized `calc(100% + 2px·scale)` with `margin: -1px·scale` — one system pixel under the window frame on every side. Its own frame border then repaints the window's border lines exactly (no doubled frame), and a resizable window's grow box lands exactly over the rail's corner cell, giving the classic System 7 document window. `vf-window[scrollbars]` renders exactly this composition from its own shadow tree (forwarding its `flush`, and `corner` for a resizable window without a status strip), so the one-liner and the slotted form are geometrically identical; slot it yourself when the well should sit *inset* in the body instead (the installer's read-me well).
|
|
397
397
|
- **Slots:** default. **Parts:** `viewport`.
|
|
398
398
|
|
|
399
399
|
#### `vf-fieldset` (`VfFieldset`, vf-fieldset.ts)
|
|
@@ -932,7 +932,7 @@
|
|
|
932
932
|
},
|
|
933
933
|
{
|
|
934
934
|
"name": "vf-scroll-area",
|
|
935
|
-
"description": "`<vf-scroll-area>` — a container whose scrollbars look like System 7.\n\nWhite box with a 1px black frame, an inner scrolling viewport, and scroll\nrails the kit draws itself as shadow DOM (the shared `vfScrollRail` recipe):\nboxed arrow buttons at each end, a loose 1-bit dot-dither trough, and the\nclassic fixed 16px thumb. The native scrollbar is hidden, never the native\nscrolling — wheel, trackpad momentum, keyboard, touch and assistive-tech\nscrolling stay the platform's, and ScrollRailController keeps the\nrail in sync while driving the classic interactions (thumb drag, trough\npaging, arrow stepping with auto-repeat). Every engine renders the same\nrail — there is no Firefox fallback skin anymore.\n\nEach reserved scroll rail is a permanent placeholder: arrow buttons on an\nempty white channel sit in the gutter even when the content fits, the\ndither and thumb filling in only once that axis overflows (System 7 drew\nan active window's no-overflow bar as arrows on a bare channel; driven\nby ScrollStateController). Which rails are reserved is set by\naxis; when both are reserved the bottom-right corner joins them.\n\nSize the host (width/height) from the outside; the viewport fills it.\n---\n\n\n### **Slots:**\n - _default_ - Scrollable content.\n\n### **CSS Properties:**\n - **--vf-scrollbar-thumb** - scrollbar thumb/elevator (white) _(default: undefined)_\n- **--vf-scrollbar-track** - the scroll trough's base color under the dot-dither (white) _(default: undefined)_\n\n### **CSS Parts:**\n - **viewport** - The inner scrolling container.",
|
|
935
|
+
"description": "`<vf-scroll-area>` — a container whose scrollbars look like System 7.\n\nWhite box with a 1px black frame, an inner scrolling viewport, and scroll\nrails the kit draws itself as shadow DOM (the shared `vfScrollRail` recipe):\nboxed arrow buttons at each end, a loose 1-bit dot-dither trough, and the\nclassic fixed 16px thumb. The native scrollbar is hidden, never the native\nscrolling — wheel, trackpad momentum, keyboard, touch and assistive-tech\nscrolling stay the platform's, and ScrollRailController keeps the\nrail in sync while driving the classic interactions (thumb drag, trough\npaging, arrow stepping with auto-repeat). Every engine renders the same\nrail — there is no Firefox fallback skin anymore.\n\nEach reserved scroll rail is a permanent placeholder: arrow buttons on an\nempty white channel sit in the gutter even when the content fits, the\ndither and thumb filling in only once that axis overflows (System 7 drew\nan active window's no-overflow bar as arrows on a bare channel; driven\nby ScrollStateController). Which rails are reserved is set by\naxis; when both are reserved the bottom-right corner joins them,\nand corner reserves that cell on a single-axis rail too — the\nrail stops 15px short of the frame, for a grow box to land in.\n\nSize the host (width/height) from the outside; the viewport fills it. The\nviewport insets its content 8px; flush drops that inset so content\nruns to the frame and the rails.\n---\n\n\n### **Slots:**\n - _default_ - Scrollable content.\n\n### **CSS Properties:**\n - **--vf-scrollbar-thumb** - scrollbar thumb/elevator (white) _(default: undefined)_\n- **--vf-scrollbar-track** - the scroll trough's base color under the dot-dither (white) _(default: undefined)_\n\n### **CSS Parts:**\n - **viewport** - The inner scrolling container.",
|
|
936
936
|
"attributes": [
|
|
937
937
|
{
|
|
938
938
|
"name": "axis",
|
|
@@ -943,6 +943,16 @@
|
|
|
943
943
|
{ "name": "both" }
|
|
944
944
|
]
|
|
945
945
|
},
|
|
946
|
+
{
|
|
947
|
+
"name": "flush",
|
|
948
|
+
"description": "Drop the viewport's 8px inset: slotted content and the (0,0) of placed\nchildren sit at the frame's inner edge, one system px from the frame\nbox (the border-floor compensation stays — see the viewport rule).\n`vf-window[scrollbars]` forwards its own `flush` here.",
|
|
949
|
+
"values": []
|
|
950
|
+
},
|
|
951
|
+
{
|
|
952
|
+
"name": "corner",
|
|
953
|
+
"description": "Reserve the bottom-right corner cell on a single-axis rail: the rail\nstops 15px short of the frame and the viewport spans the rest, leaving\nthe 15×15 cell — with the two dividers a `both` rail's corner carries —\nfor a grow box to land in. A `both` rail always has the cell, so the\nflag changes nothing there. `vf-window[scrollbars resizable]` sets it.",
|
|
954
|
+
"values": []
|
|
955
|
+
},
|
|
946
956
|
{
|
|
947
957
|
"name": "label",
|
|
948
958
|
"description": "Accessible name for the scrolling viewport, applied as its `aria-label`\n(an `aria-label` on the host would not reach into the shadow DOM). The\nviewport is keyboard-focusable while its content overflows, so without a\nname it is announced only as an anonymous scrollable group; setting\n`label` also promotes it to a named `role=\"region\"` landmark. While\n`label` is empty the viewport is a plain `group` when scrollable (an\nunnamed region is inert) and role-less when not.",
|
|
@@ -1356,12 +1366,12 @@
|
|
|
1356
1366
|
},
|
|
1357
1367
|
{
|
|
1358
1368
|
"name": "flush",
|
|
1359
|
-
"description": "Remove the default 12px body padding.",
|
|
1369
|
+
"description": "Remove the default 12px body padding. Under `scrollbars` the body has\nnone to remove, so the flag reaches the built-in scroll area instead\nand drops its viewport's 8px inset: content runs to the frame and the\nrails, and the (0,0) of placed children is the content region's corner.",
|
|
1360
1370
|
"values": []
|
|
1361
1371
|
},
|
|
1362
1372
|
{
|
|
1363
1373
|
"name": "scrollbars",
|
|
1364
|
-
"description": "Put System 7 scroll rails on the window edge — the classic document\nwindow. The body slot renders inside a built-in `vf-scroll-area` pulled\none system pixel under the frame on every side, so the rails repaint the\nborder lines and a `resizable` window's grow box lands in the corner\ncell. Values mirror `vf-scroll-area`'s `axis`; the
|
|
1374
|
+
"description": "Put System 7 scroll rails on the window edge — the classic document\nwindow. The body slot renders inside a built-in `vf-scroll-area` pulled\none system pixel under the frame on every side, so the rails repaint the\nborder lines and a `resizable` window's grow box lands in the corner\ncell — reserved on a single-axis rail too (the area's `corner`), unless\na populated status strip holds the grow box, when the rail runs edge to\nedge onto the strip's rule. Values mirror `vf-scroll-area`'s `axis`; the\n`heading` names the scroll region; the viewport part is re-exported.\nThe body's own padding goes; the viewport keeps its 8px inset unless\n`flush`, which passes through. The slotted composition (SPEC §5\nvf-scroll-area) still works for windows that want an inset well instead.",
|
|
1365
1375
|
"values": [
|
|
1366
1376
|
{ "name": "vertical" },
|
|
1367
1377
|
{ "name": "horizontal" },
|
package/editor/web-types.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://raw.githubusercontent.com/JetBrains/web-types/master/schema/web-types.json",
|
|
3
3
|
"name": "vintage-frames",
|
|
4
|
-
"version": "0.5.
|
|
4
|
+
"version": "0.5.5",
|
|
5
5
|
"description-markup": "markdown",
|
|
6
6
|
"contributions": {
|
|
7
7
|
"html": {
|
|
@@ -2239,7 +2239,7 @@
|
|
|
2239
2239
|
},
|
|
2240
2240
|
{
|
|
2241
2241
|
"name": "vf-scroll-area",
|
|
2242
|
-
"description": "`<vf-scroll-area>` — a container whose scrollbars look like System 7.\n\nWhite box with a 1px black frame, an inner scrolling viewport, and scroll\nrails the kit draws itself as shadow DOM (the shared `vfScrollRail` recipe):\nboxed arrow buttons at each end, a loose 1-bit dot-dither trough, and the\nclassic fixed 16px thumb. The native scrollbar is hidden, never the native\nscrolling — wheel, trackpad momentum, keyboard, touch and assistive-tech\nscrolling stay the platform's, and ScrollRailController keeps the\nrail in sync while driving the classic interactions (thumb drag, trough\npaging, arrow stepping with auto-repeat). Every engine renders the same\nrail — there is no Firefox fallback skin anymore.\n\nEach reserved scroll rail is a permanent placeholder: arrow buttons on an\nempty white channel sit in the gutter even when the content fits, the\ndither and thumb filling in only once that axis overflows (System 7 drew\nan active window's no-overflow bar as arrows on a bare channel; driven\nby ScrollStateController). Which rails are reserved is set by\naxis; when both are reserved the bottom-right corner joins them.\n\nSize the host (width/height) from the outside; the viewport fills it.\n---\n\n\n### **Slots:**\n - _default_ - Scrollable content.\n\n### **CSS Properties:**\n - **--vf-scrollbar-thumb** - scrollbar thumb/elevator (white) _(default: undefined)_\n- **--vf-scrollbar-track** - the scroll trough's base color under the dot-dither (white) _(default: undefined)_\n\n### **CSS Parts:**\n - **viewport** - The inner scrolling container.",
|
|
2242
|
+
"description": "`<vf-scroll-area>` — a container whose scrollbars look like System 7.\n\nWhite box with a 1px black frame, an inner scrolling viewport, and scroll\nrails the kit draws itself as shadow DOM (the shared `vfScrollRail` recipe):\nboxed arrow buttons at each end, a loose 1-bit dot-dither trough, and the\nclassic fixed 16px thumb. The native scrollbar is hidden, never the native\nscrolling — wheel, trackpad momentum, keyboard, touch and assistive-tech\nscrolling stay the platform's, and ScrollRailController keeps the\nrail in sync while driving the classic interactions (thumb drag, trough\npaging, arrow stepping with auto-repeat). Every engine renders the same\nrail — there is no Firefox fallback skin anymore.\n\nEach reserved scroll rail is a permanent placeholder: arrow buttons on an\nempty white channel sit in the gutter even when the content fits, the\ndither and thumb filling in only once that axis overflows (System 7 drew\nan active window's no-overflow bar as arrows on a bare channel; driven\nby ScrollStateController). Which rails are reserved is set by\naxis; when both are reserved the bottom-right corner joins them,\nand corner reserves that cell on a single-axis rail too — the\nrail stops 15px short of the frame, for a grow box to land in.\n\nSize the host (width/height) from the outside; the viewport fills it. The\nviewport insets its content 8px; flush drops that inset so content\nruns to the frame and the rails.\n---\n\n\n### **Slots:**\n - _default_ - Scrollable content.\n\n### **CSS Properties:**\n - **--vf-scrollbar-thumb** - scrollbar thumb/elevator (white) _(default: undefined)_\n- **--vf-scrollbar-track** - the scroll trough's base color under the dot-dither (white) _(default: undefined)_\n\n### **CSS Parts:**\n - **viewport** - The inner scrolling container.",
|
|
2243
2243
|
"doc-url": "",
|
|
2244
2244
|
"attributes": [
|
|
2245
2245
|
{
|
|
@@ -2250,6 +2250,16 @@
|
|
|
2250
2250
|
"default": "'vertical'"
|
|
2251
2251
|
}
|
|
2252
2252
|
},
|
|
2253
|
+
{
|
|
2254
|
+
"name": "flush",
|
|
2255
|
+
"description": "Drop the viewport's 8px inset: slotted content and the (0,0) of placed\nchildren sit at the frame's inner edge, one system px from the frame\nbox (the border-floor compensation stays — see the viewport rule).\n`vf-window[scrollbars]` forwards its own `flush` here.",
|
|
2256
|
+
"value": { "type": "boolean", "default": "false" }
|
|
2257
|
+
},
|
|
2258
|
+
{
|
|
2259
|
+
"name": "corner",
|
|
2260
|
+
"description": "Reserve the bottom-right corner cell on a single-axis rail: the rail\nstops 15px short of the frame and the viewport spans the rest, leaving\nthe 15×15 cell — with the two dividers a `both` rail's corner carries —\nfor a grow box to land in. A `both` rail always has the cell, so the\nflag changes nothing there. `vf-window[scrollbars resizable]` sets it.",
|
|
2261
|
+
"value": { "type": "boolean", "default": "false" }
|
|
2262
|
+
},
|
|
2253
2263
|
{
|
|
2254
2264
|
"name": "label",
|
|
2255
2265
|
"description": "Accessible name for the scrolling viewport, applied as its `aria-label`\n(an `aria-label` on the host would not reach into the shadow DOM). The\nviewport is keyboard-focusable while its content overflows, so without a\nname it is announced only as an anonymous scrollable group; setting\n`label` also promotes it to a named `role=\"region\"` landmark. While\n`label` is empty the viewport is a plain `group` when scrollable (an\nunnamed region is inert) and role-less when not.",
|
|
@@ -2275,6 +2285,16 @@
|
|
|
2275
2285
|
"description": "Which scroll rails to reserve as permanent placeholders: `vertical`\n(default), `horizontal`, or `both`. Each reserved rail shows an empty white\nchannel until its axis overflows; the unreserved axis still scrolls\nnatively (wheel, keyboard) but draws no rail.",
|
|
2276
2286
|
"type": "'vertical' | 'horizontal' | 'both'"
|
|
2277
2287
|
},
|
|
2288
|
+
{
|
|
2289
|
+
"name": "flush",
|
|
2290
|
+
"description": "Drop the viewport's 8px inset: slotted content and the (0,0) of placed\nchildren sit at the frame's inner edge, one system px from the frame\nbox (the border-floor compensation stays — see the viewport rule).\n`vf-window[scrollbars]` forwards its own `flush` here.",
|
|
2291
|
+
"type": "boolean"
|
|
2292
|
+
},
|
|
2293
|
+
{
|
|
2294
|
+
"name": "corner",
|
|
2295
|
+
"description": "Reserve the bottom-right corner cell on a single-axis rail: the rail\nstops 15px short of the frame and the viewport spans the rest, leaving\nthe 15×15 cell — with the two dividers a `both` rail's corner carries —\nfor a grow box to land in. A `both` rail always has the cell, so the\nflag changes nothing there. `vf-window[scrollbars resizable]` sets it.",
|
|
2296
|
+
"type": "boolean"
|
|
2297
|
+
},
|
|
2278
2298
|
{
|
|
2279
2299
|
"name": "label",
|
|
2280
2300
|
"description": "Accessible name for the scrolling viewport, applied as its `aria-label`\n(an `aria-label` on the host would not reach into the shadow DOM). The\nviewport is keyboard-focusable while its content overflows, so without a\nname it is announced only as an anonymous scrollable group; setting\n`label` also promotes it to a named `role=\"region\"` landmark. While\n`label` is empty the viewport is a plain `group` when scrollable (an\nunnamed region is inert) and role-less when not.",
|
|
@@ -3275,12 +3295,12 @@
|
|
|
3275
3295
|
},
|
|
3276
3296
|
{
|
|
3277
3297
|
"name": "flush",
|
|
3278
|
-
"description": "Remove the default 12px body padding.",
|
|
3298
|
+
"description": "Remove the default 12px body padding. Under `scrollbars` the body has\nnone to remove, so the flag reaches the built-in scroll area instead\nand drops its viewport's 8px inset: content runs to the frame and the\nrails, and the (0,0) of placed children is the content region's corner.",
|
|
3279
3299
|
"value": { "type": "boolean", "default": "false" }
|
|
3280
3300
|
},
|
|
3281
3301
|
{
|
|
3282
3302
|
"name": "scrollbars",
|
|
3283
|
-
"description": "Put System 7 scroll rails on the window edge — the classic document\nwindow. The body slot renders inside a built-in `vf-scroll-area` pulled\none system pixel under the frame on every side, so the rails repaint the\nborder lines and a `resizable` window's grow box lands in the corner\ncell. Values mirror `vf-scroll-area`'s `axis`; the
|
|
3303
|
+
"description": "Put System 7 scroll rails on the window edge — the classic document\nwindow. The body slot renders inside a built-in `vf-scroll-area` pulled\none system pixel under the frame on every side, so the rails repaint the\nborder lines and a `resizable` window's grow box lands in the corner\ncell — reserved on a single-axis rail too (the area's `corner`), unless\na populated status strip holds the grow box, when the rail runs edge to\nedge onto the strip's rule. Values mirror `vf-scroll-area`'s `axis`; the\n`heading` names the scroll region; the viewport part is re-exported.\nThe body's own padding goes; the viewport keeps its 8px inset unless\n`flush`, which passes through. The slotted composition (SPEC §5\nvf-scroll-area) still works for windows that want an inset well instead.",
|
|
3284
3304
|
"value": {
|
|
3285
3305
|
"type": "'vertical' | 'horizontal' | 'both' | undefined"
|
|
3286
3306
|
}
|
|
@@ -3366,12 +3386,12 @@
|
|
|
3366
3386
|
},
|
|
3367
3387
|
{
|
|
3368
3388
|
"name": "flush",
|
|
3369
|
-
"description": "Remove the default 12px body padding.",
|
|
3389
|
+
"description": "Remove the default 12px body padding. Under `scrollbars` the body has\nnone to remove, so the flag reaches the built-in scroll area instead\nand drops its viewport's 8px inset: content runs to the frame and the\nrails, and the (0,0) of placed children is the content region's corner.",
|
|
3370
3390
|
"type": "boolean"
|
|
3371
3391
|
},
|
|
3372
3392
|
{
|
|
3373
3393
|
"name": "scrollbars",
|
|
3374
|
-
"description": "Put System 7 scroll rails on the window edge — the classic document\nwindow. The body slot renders inside a built-in `vf-scroll-area` pulled\none system pixel under the frame on every side, so the rails repaint the\nborder lines and a `resizable` window's grow box lands in the corner\ncell. Values mirror `vf-scroll-area`'s `axis`; the
|
|
3394
|
+
"description": "Put System 7 scroll rails on the window edge — the classic document\nwindow. The body slot renders inside a built-in `vf-scroll-area` pulled\none system pixel under the frame on every side, so the rails repaint the\nborder lines and a `resizable` window's grow box lands in the corner\ncell — reserved on a single-axis rail too (the area's `corner`), unless\na populated status strip holds the grow box, when the rail runs edge to\nedge onto the strip's rule. Values mirror `vf-scroll-area`'s `axis`; the\n`heading` names the scroll region; the viewport part is re-exported.\nThe body's own padding goes; the viewport keeps its 8px inset unless\n`flush`, which passes through. The slotted composition (SPEC §5\nvf-scroll-area) still works for windows that want an inset well instead.",
|
|
3375
3395
|
"type": "'vertical' | 'horizontal' | 'both' | undefined"
|
|
3376
3396
|
},
|
|
3377
3397
|
{
|