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