@getvitops/generator 5.0.0 → 7.0.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.
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Counter — animates a number from a start value to the value already in its
3
+ * fallback text (`.counter__value`), e.g. the "0 → 94%" stat in a media tile.
4
+ * Consumes the generated token cascade (src/css/generated/tokens.css) only for
5
+ * `--custom-ease-out` (animation.css's engine layer).
6
+ *
7
+ * `<wc-counter>` owns the reading; this partial owns the interpolation. `--_n`
8
+ * is a registered custom property so the browser can smoothly animate it as a
9
+ * number — `counter()` cannot do decimals or locale grouping, so JS mirrors
10
+ * `--_n` into `.counter__presentation`'s text each frame via
11
+ * `Intl.NumberFormat`, formatted the same way the fallback text was. CSS still
12
+ * owns the interpolation, the easing curve and (as a second belt alongside the
13
+ * JS check) `prefers-reduced-motion`.
14
+ *
15
+ * `.counter__value` is toggled to the framework's `.sr-only` utility while
16
+ * running, not hidden — an element pulled out of the accessibility tree via
17
+ * `display: none`/`visibility: hidden` stops contributing to the accessible
18
+ * name, and that name must read the final value for the whole animation, not
19
+ * just at rest.
20
+ */
21
+ @property --_n {
22
+ syntax: '<number>';
23
+ initial-value: 0;
24
+ inherits: false;
25
+ }
26
+
27
+ .counter__presentation {
28
+ display: none;
29
+ }
30
+
31
+ wc-counter[running] .counter__presentation {
32
+ display: inline;
33
+ animation: counter-run var(--counter-duration, 1.2s) var(--counter-easing, var(--custom-ease-out))
34
+ forwards;
35
+ }
36
+
37
+ @keyframes counter-run {
38
+ from {
39
+ --_n: var(--counter-from, 0);
40
+ }
41
+ to {
42
+ --_n: var(--counter-to);
43
+ }
44
+ }
45
+
46
+ @media (prefers-reduced-motion: reduce) {
47
+ wc-counter[running] .counter__presentation {
48
+ animation: none;
49
+ }
50
+ }
@@ -287,7 +287,7 @@ input[type='color'] {
287
287
  position: relative;
288
288
  inline-size: var(--_switch-width);
289
289
  block-size: var(--_switch-height);
290
- border-radius: var(--br-switch-track, var(--br-pill, 999px));
290
+ border-radius: var(--br-switch-track, 999px);
291
291
  background: var(--color-bg-surface-muted);
292
292
  transition: background-color 0.2s ease;
293
293
  }
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Gallery — thumbnail grid whose triggers open a `.lightbox-dialog` (patterns/lightbox.css).
3
+ * Consumes the generated token cascade (src/css/generated/tokens.css).
4
+ *
5
+ * Split out of what used to be `.lightbox`'s grid half. The two are separate
6
+ * concerns — "a grid of images" and "view one full-screen" — and only the dialog
7
+ * half needs the `lightbox` name; the grid never was one.
8
+ *
9
+ * `<wc-gallery>` wraps this and augments the per-image `.lightbox-dialog`s it finds
10
+ * with prev/next, arrow keys, swipe and a counter. With no JS each `.gallery__trigger`
11
+ * is a real `command="show-modal" commandfor="…"` invoker for its own dialog, so the
12
+ * grid + dialogs already form a working modal lightbox before any script runs.
13
+ */
14
+
15
+ wc-gallery {
16
+ /* Unregistered custom elements default to `display: inline`, which forms an
17
+ inline box around this grid's block content until `elements.js` upgrades it. */
18
+ display: block;
19
+ }
20
+
21
+ .gallery {
22
+ --_br: var(--br-gallery, var(--br-gallery-group, var(--br-default)));
23
+
24
+ display: grid;
25
+ grid-template-columns: repeat(auto-fill, minmax(var(--gallery-thumb-size, 150px), 1fr));
26
+ gap: var(--gallery-gap, 0.5rem);
27
+ }
28
+
29
+ .gallery__item {
30
+ aspect-ratio: var(--gallery-aspect, 1);
31
+ overflow: hidden;
32
+ border-radius: var(--_br);
33
+ }
34
+
35
+ .gallery__trigger {
36
+ display: block;
37
+ inline-size: 100%;
38
+ block-size: 100%;
39
+ margin: 0;
40
+ padding: 0;
41
+ background: none;
42
+ border: none;
43
+ cursor: pointer;
44
+
45
+ &:focus-visible {
46
+ outline: 2px solid var(--gallery-focus-color, currentColor);
47
+ outline-offset: 2px;
48
+ }
49
+ }
50
+
51
+ .gallery__thumb {
52
+ inline-size: 100%;
53
+ block-size: 100%;
54
+ object-fit: cover;
55
+ transition: scale 0.3s ease;
56
+ }
57
+
58
+ .gallery__trigger:hover .gallery__thumb {
59
+ scale: 1.05;
60
+ }
@@ -1,42 +1,14 @@
1
1
  /**
2
- * Lightbox — pattern partial migrated from old-css-lib.
2
+ * Lightbox — the full-screen `<dialog>` a gallery trigger opens.
3
3
  * Consumes the generated token cascade (src/css/generated/tokens.css).
4
+ *
5
+ * The thumbnail grid that drives this moved to `patterns/gallery.css`
6
+ * (`.gallery` / `.gallery__item` / `.gallery__trigger` / `.gallery__thumb`) — this
7
+ * partial owns only the dialog a trigger opens. `<Gallery />` emits both; this
8
+ * file has no opinion on what triggers it.
4
9
  */
5
10
 
6
- /* ── Lightbox — thumbnail grid + full-screen dialog w/ view transitions ────── */
7
- .lightbox {
8
- --_br: var(--br-lightbox, var(--br-lightbox-group, var(--br-default)));
9
-
10
- display: grid;
11
- grid-template-columns: repeat(auto-fill, minmax(var(--lightbox-thumb-size, 150px), 1fr));
12
- gap: var(--lightbox-gap, 0.5rem);
13
-
14
- .lightbox__item {
15
- position: relative;
16
- aspect-ratio: var(--lightbox-aspect, 1);
17
- overflow: hidden;
18
- border-radius: var(--_br);
19
- cursor: pointer;
20
-
21
- &:focus-visible {
22
- outline: 2px solid var(--lightbox-focus-color, currentColor);
23
- outline-offset: 2px;
24
- }
25
- }
26
-
27
- .lightbox__thumb {
28
- inline-size: 100%;
29
- block-size: 100%;
30
- object-fit: cover;
31
- transition: scale 0.3s ease;
32
- view-transition-name: match-element;
33
- }
34
-
35
- .lightbox__item:hover .lightbox__thumb {
36
- scale: 1.05;
37
- }
38
- }
39
-
11
+ /* ── Lightbox dialog — full-screen, w/ view transitions ─────────────────────── */
40
12
  dialog.lightbox-dialog {
41
13
  position: fixed;
42
14
  inset: 0;
@@ -56,6 +28,14 @@ dialog.lightbox-dialog {
56
28
  ::backdrop rule, so it keeps the shared fade timing. */
57
29
  --scrim: transparent;
58
30
 
31
+ /* The entry, as effect vars rather than a keyframe — same approach as
32
+ dialog.css, and for the same reason: the top-layer driver in animation.css
33
+ then animates this OUT too, which an `animation:` on `[open]` never does
34
+ (it only ever plays once, on open). */
35
+ --opacity-from: 0;
36
+ --scale-x-from: 0.95;
37
+ --scale-y-from: 0.95;
38
+
59
39
  &[open] {
60
40
  display: grid;
61
41
  }
@@ -73,7 +53,13 @@ dialog.lightbox-dialog {
73
53
  max-inline-size: 100%;
74
54
  max-block-size: 90vh;
75
55
  object-fit: contain;
76
- view-transition-name: match-element;
56
+
57
+ /* `<wc-gallery>` assigns `view-transition-name: lightbox-active` to this and
58
+ the originating thumbnail at the moment it opens, then clears it on close —
59
+ that shared, runtime-assigned name is what morphs one into the other.
60
+ `match-element` (the auto-generated per-element name) does NOT do this: it
61
+ names each element uniquely, so it can never morph a thumbnail into a
62
+ different `<img>`. Do not put a `view-transition-name` here statically. */
77
63
  }
78
64
 
79
65
  .lightbox-dialog__close {
@@ -149,8 +135,3 @@ dialog.lightbox-dialog {
149
135
  text-align: center;
150
136
  }
151
137
  }
152
-
153
- ::view-transition-old(*),
154
- ::view-transition-new(*) {
155
- animation-duration: 0.3s;
156
- }
@@ -117,7 +117,7 @@
117
117
  flex-shrink: 0;
118
118
  font-size: 0.75em;
119
119
  padding: 0.125rem 0.375rem;
120
- border-radius: var(--br-count, var(--br-pill, 999px));
120
+ border-radius: var(--br-count, 999px);
121
121
  background: var(--color-bg-surface-muted);
122
122
  }
123
123
 
@@ -29,7 +29,6 @@
29
29
  /* ── Badge indicator — numeric notification bubble (distinct from .badge) ──── */
30
30
  .badge-indicator {
31
31
  --_size: var(--badge-size, 1.25rem);
32
- --_br: var(--br-badge, var(--br-badge-group, var(--br-default)));
33
32
 
34
33
  display: inline-flex;
35
34
  align-items: center;
@@ -40,7 +39,7 @@
40
39
  font-size: var(--fs-badge, var(--fs-badge-group, var(--fs-default)));
41
40
  font-weight: 600;
42
41
  line-height: 1;
43
- border-radius: var(--br-badge-indicator, var(--br-pill, 999px));
42
+ border-radius: var(--br-badge-indicator, 999px);
44
43
  background: var(--color-text-danger);
45
44
  color: #fff;
46
45
 
@@ -44,10 +44,16 @@ wc-tree {
44
44
  position: relative;
45
45
  }
46
46
 
47
- /* Nested list indent. Two shapes are supported: the item may BE the
48
- `<details>`, or be an `<li>` wrapping one — the latter is what `<Tree />`
49
- emits, because `<ul>`/`<li>` is what gives assistive tech list position and
50
- depth. Keep both; the flat shape is the pattern's original contract.
47
+ /* Nested list indent. Two shapes are supported IN THE NO-JS FALLBACK: the
48
+ item may BE the `<details>`, or be an `<li>` wrapping one — the latter is
49
+ what `<Tree />` emits, because `<ul>`/`<li>` is what gives assistive tech
50
+ list position and depth. `<wc-tree>` unwraps both shapes to one structure
51
+ on upgrade (`.tree__item` > `.tree__summary`/`.tree__content` + optional
52
+ nested `.tree`), which is why the first selector below already matches
53
+ the ENHANCED state for both former shapes with no changes — only the
54
+ second (`.tree__item > details > .tree`) is fallback-only now, kept for
55
+ the no-JS list shape. Same split applies to every other
56
+ `details`/`summary`-keyed selector in this file below.
51
57
 
52
58
  Indent is ONE `--tree-indent` per level, not two. This used to set both
53
59
  `margin-inline-start` and `padding-inline-start` to it, putting the guide
@@ -85,6 +91,7 @@ wc-tree {
85
91
  }
86
92
 
87
93
  .tree__content,
94
+ .tree__summary,
88
95
  details.tree__item > summary,
89
96
  .tree__item > details > summary {
90
97
  display: flex;
@@ -100,6 +107,26 @@ wc-tree {
100
107
  padding-inline-start: var(--_leaf-indent);
101
108
  }
102
109
 
110
+ /* `.tree__summary` — the enhanced-state row `<wc-tree>` builds on upgrade,
111
+ replacing `<summary>`. Not `.tree__content`: that class's
112
+ `padding-inline-start: var(--_leaf-indent)` exists specifically to make a
113
+ TOGGLE-LESS row line up with one that has a toggle — applying it to a row
114
+ that DOES have a toggle would double-indent every branch, the mirror
115
+ image of the 24px-misalignment bug the comment above this block
116
+ documents fixing. */
117
+ .tree__summary {
118
+ cursor: pointer;
119
+ /* Only source of this in the enhanced state — `<summary>`'s own
120
+ `user-select: none` came from `details.css`'s bare `details` selector,
121
+ which no longer matches once `<details>` is gone. Without it, a
122
+ drag-select across a label also fires a `click` and toggles the row. */
123
+ user-select: none;
124
+ }
125
+
126
+ /* Fallback-only from here: no-JS renders `<details>`/`<summary>`, which
127
+ `<wc-tree>` unwraps on upgrade (see the class docblock in `WCTree.ts`).
128
+ Kept for the two SSR'd shapes; nothing in the enhanced state matches
129
+ these. */
103
130
  details.tree__item > summary,
104
131
  .tree__item > details > summary {
105
132
  cursor: pointer;
@@ -127,6 +154,13 @@ wc-tree {
127
154
  transition: transform 0.2s ease;
128
155
  }
129
156
 
157
+ /* Enhanced state: `aria-expanded` is the mirror `#setExpanded` writes
158
+ alongside the group's `hidden`, kept in sync with it — see WCTree.ts. */
159
+ .tree__item[aria-expanded='true'] > .tree__summary .tree__toggle {
160
+ transform: rotate(90deg);
161
+ }
162
+
163
+ /* Fallback-only — see the note above `details.tree__item > summary`. */
130
164
  details.tree__item[open] > summary .tree__toggle,
131
165
  .tree__item > details[open] > summary .tree__toggle {
132
166
  transform: rotate(90deg);
@@ -165,8 +199,12 @@ wc-tree {
165
199
  transition: opacity 0.15s ease;
166
200
  }
167
201
 
168
- .tree__item:hover .tree__actions,
169
- .tree__item:focus-within .tree__actions {
202
+ /* `>`, not a descendant selector: `.tree__item` now carries the roving
203
+ tabindex directly, so arrow-keying to a deep item would otherwise light
204
+ up every ANCESTOR branch's own actions row too, not just the row hovered
205
+ or focused. */
206
+ .tree__item:hover > .tree__actions,
207
+ .tree__item:focus-within > .tree__actions {
170
208
  opacity: 1;
171
209
  }
172
210
 
@@ -174,6 +212,65 @@ wc-tree {
174
212
  opacity: 0.5;
175
213
  }
176
214
 
215
+ /* Focus now lands on `.tree__item` itself (the roving tabindex target), a
216
+ block box that — for an expanded branch — wraps its entire subtree. An
217
+ outline on it would ring every descendant, so it's suppressed there and
218
+ drawn on the ROW instead. */
219
+ .tree__item:focus {
220
+ outline: none;
221
+ }
222
+
223
+ /* `:focus-visible` alone under-covers a real case: script-moved focus only
224
+ matches `:focus-visible` if the PREVIOUSLY focused element did, so a
225
+ mouse click (which doesn't match) followed immediately by an arrow key
226
+ shows no ring at all in Chrome/Safari until the visitor tabs fully out
227
+ and back in. `[data-tree-keyboard]` is `<wc-tree>`'s own signal — set on
228
+ any handled navigation keydown, cleared on `pointerdown` — and covers
229
+ exactly that gap; `happy-dom` implements neither `:focus-visible` nor
230
+ layout, so this needs a manual browser check, not a unit test. */
231
+ .tree__item:focus-visible > .tree__summary,
232
+ .tree__item:focus-visible > .tree__content,
233
+ &[data-tree-keyboard] .tree__item:focus > .tree__summary,
234
+ &[data-tree-keyboard] .tree__item:focus > .tree__content {
235
+ outline: 2px solid var(--tree-focus-color, currentColor);
236
+ /* Inset, not the pattern's usual positive offset: the nested group below
237
+ clips its content (`overflow: clip`, next rule), so a positive offset
238
+ on the first or last row of an expanded subtree would be cut off by
239
+ its own ancestor. */
240
+ outline-offset: -2px;
241
+ }
242
+
243
+ /* Animated collapse — the enhanced-state replacement for `details.css`'s
244
+ `::details-content` transition, which no longer applies once `<details>`
245
+ is gone. Keyed on `[hidden]` itself, NOT on `[aria-expanded]`: the
246
+ browser's own find-in-page / fragment-navigation reveal removes `hidden`
247
+ directly or CSS anchored to a JS-set `aria-expanded` mirror would leave a
248
+ UA-revealed branch visually still collapsed until a `beforematch` handler
249
+ caught up (see WCTree.ts). Same idiom `details.css` verified working, and
250
+ the same warning applies: the tell of a regression is a branch that
251
+ doesn't open on the FIRST click, not a syntax error — check a real
252
+ interaction. */
253
+ .tree__item > .tree {
254
+ /* `clip` + a small margin, not `hidden`: an `overflow: hidden` group
255
+ would clip the focus ring (`outline-offset: -2px` above still needs a
256
+ few px of headroom) of its own first/last child row. */
257
+ overflow: clip;
258
+ overflow-clip-margin: 3px;
259
+
260
+ @media (prefers-reduced-motion: no-preference) {
261
+ block-size: auto;
262
+ transition:
263
+ block-size var(--animation-duration) var(--custom-ease-out),
264
+ content-visibility var(--animation-duration) allow-discrete;
265
+ }
266
+ }
267
+
268
+ @media (prefers-reduced-motion: no-preference) {
269
+ .tree__item > .tree[hidden] {
270
+ block-size: 0;
271
+ }
272
+ }
273
+
177
274
  &.tree--lines .tree__item {
178
275
  position: relative;
179
276
 
@@ -195,10 +292,16 @@ wc-tree {
195
292
 
196
293
  /* A node's own prose. `.tree__label` is `nowrap` + ellipsis for file-tree-style
197
294
  single-line names, so a description can't live in it — it sits below the
198
- summary row and wraps. Indented to the label, past the toggle. */
295
+ summary row and wraps. Indented to the label, past the toggle.
296
+
297
+ `margin-inline-end` is set here rather than left to come from an
298
+ ancestor: a BRANCH description used to get it incidentally from
299
+ `details.css`'s `<details>` margins (now gone — see the class docblock in
300
+ `WCTree.ts`), while a LEAF description, never inside a `<details>`, never
301
+ got one at all. Self-contained now, and consistent between the two. */
199
302
  .tree__desc {
200
303
  margin-block: 0.125rem 0.25rem;
201
- margin-inline-start: var(--_leaf-indent);
304
+ margin-inline: var(--_leaf-indent) 0.25rem;
202
305
  color: var(--color-text-surface-muted);
203
306
  text-wrap: pretty;
204
307
  }
@@ -284,3 +284,20 @@
284
284
  .relative {
285
285
  position: relative;
286
286
  }
287
+
288
+ /* ── Shape — the two geometric absolutes, Tailwind-named ──
289
+ Everything between "square" and "fully round" stays owned by the token
290
+ cascade (`patterns.radii`, group `br`, per-pattern `--br-<name>`) — these two
291
+ are the only radii with no design-token meaning of their own, which is why
292
+ they're utilities and not tokens. No responsive (`md-rounded-full`) variants:
293
+ a responsive radius is rare enough that Tailwind's own `@md:rounded-full`
294
+ covers it on that side.
295
+
296
+ Names + values are Tailwind-exact; Tailwind ships them natively, so they are
297
+ in TW_CLASH and the tailwind format drops these copies. */
298
+ .rounded-none {
299
+ border-radius: 0;
300
+ }
301
+ .rounded-full {
302
+ border-radius: 999px;
303
+ }