@vanduo-oss/framework 1.4.6 → 1.5.1

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/CHANGELOG.md CHANGED
@@ -8,6 +8,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8
8
  Full release notes — covering the framework, the documentation site, and
9
9
  ecosystem packages side by side — live at <https://vanduo.dev/#changelog>.
10
10
 
11
+ ## [1.5.1] - 2026-06-20
12
+
13
+ ### Changed
14
+ - Opened `dev-v151` development branch for the v1.5.1 patch cycle.
15
+
16
+ ### Enhanced
17
+ - **Expanding Cards — mobile** — Below `768px`, panels stack vertically and expand in place using the same flex-grow redistribution as the desktop strip (replacing progressive hide and non-animatable `order` reorder). Inactive rows render as compact stripes with icon + title; labels and subtitles transition smoothly. `ArrowUp`/`ArrowDown` keyboard navigation added alongside left/right.
18
+
19
+ ### Fixed
20
+ - **Expanding Cards — photo backgrounds** — Active panels use `background-size: cover` and `background-repeat: no-repeat` so wide expanded cards no longer show tiled repeats on the sides.
21
+
22
+ ## [1.5.0] - 2026-06-18
23
+
24
+ ### Added
25
+ - **Layout primitives** — CSS-only `.vd-box`, `.vd-stack`, `.vd-inline`, `.vd-center`, `.vd-frame` (golden-ratio aspect box), `.vd-cover`, and `.vd-switcher` (responsive row→column without a media query) containers (`css/primitives/primitives.css`) with a `data-*` API consuming the existing Fibonacci spacing/radius and semantic tokens. The composition layer between utilities and components; zero JS, no new public tokens. Unit specs in `tests/unit/primitives.spec.ts`.
26
+
27
+ ### Removed
28
+ - **Hex grid source** (`js/components/vd-hex.js`, `js/utils/hex-math.js`) and its tests. `VdHexGrid` was never bundled; it now ships solely as the standalone `@vanduo-oss/hex-grid` package.
29
+
30
+ ### Changed
31
+ - Removed the unused empty `packages/` workspace and `pnpm-workspace.yaml` (the esbuild build-approval remains in `package.json` `pnpm.allowedBuilds`).
32
+
11
33
  ## [1.4.6] - 2026-06-12
12
34
 
13
35
  ### Added
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Vanduo Framework v1.4.6
1
+ # Vanduo Framework v1.5.1
2
2
 
3
3
  <p align="center">
4
4
  <img src="vanduo-banner.svg" alt="Vanduo Framework Banner" width="100%">
@@ -25,7 +25,16 @@ Vanduo is a zero-dependency UI framework built with HTML, CSS, and vanilla JavaS
25
25
  - Theme Switcher menu variant for icon-only light/dark/system selection in navbars
26
26
  - Playwright-based browser coverage across Chromium, Firefox, and WebKit
27
27
 
28
- ## What's New in 1.4.6
28
+ ## What's New in 1.5.1
29
+
30
+ - Development branch opened for the v1.5.1 patch cycle (`dev-v151`).
31
+
32
+ ## Previous: 1.5.0
33
+
34
+ - **Layout primitives** — new CSS-only layout containers — `.vd-box`, `.vd-stack`, `.vd-inline`, `.vd-center`, `.vd-frame` (golden-ratio aspect box), `.vd-cover`, and `.vd-switcher` (container-query-free responsive row→column) — with a `data-*` API (`data-pad`, `data-gap`, `data-align`, `data-justify`, `data-round`, `data-ratio`…) that consumes the existing Fibonacci spacing/radius and semantic tokens. They are the composition layer between utilities and components — *utilities style an element; primitives arrange elements*. Zero JS, zero new public tokens.
35
+ - **Hex grid is now a standalone package** — `VdHexGrid` was never part of the bundle; its source has moved out of the framework tree. Install [`@vanduo-oss/hex-grid`](https://www.npmjs.com/package/@vanduo-oss/hex-grid) directly.
36
+
37
+ ## Previous: 1.4.6
29
38
 
30
39
  - **Leaner default CSS** — `css/vanduo.css` now bundles the `regular` + `fill` icon weights only (~45% smaller minified CSS). Need all six weights? Import `css/icons/icons-all.css`.
31
40
  - **No-icons core build** — new `dist/vanduo-core.min.css` (`@vanduo-oss/framework/css/core`) drops the bundled icons entirely (~123 KB lighter) for consumers who ship their own icon set.
@@ -47,8 +56,8 @@ Vanduo is a zero-dependency UI framework built with HTML, CSS, and vanilla JavaS
47
56
  ### CDN
48
57
 
49
58
  ```html
50
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/vanduo-oss/framework@v1.4.6/dist/vanduo.min.css">
51
- <script src="https://cdn.jsdelivr.net/gh/vanduo-oss/framework@v1.4.6/dist/vanduo.min.js"></script>
59
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/vanduo-oss/framework@v1.5.1/dist/vanduo.min.css">
60
+ <script src="https://cdn.jsdelivr.net/gh/vanduo-oss/framework@v1.5.1/dist/vanduo.min.js"></script>
52
61
  <script>
53
62
  Vanduo.init();
54
63
  </script>
@@ -33,7 +33,8 @@
33
33
  border-radius: 30px;
34
34
  cursor: pointer;
35
35
  background: var(--vd-expanding-card-bg, var(--vd-expanding-card-bg-default, #e6e9ed));
36
- background-size: auto 120%;
36
+ background-repeat: no-repeat;
37
+ background-size: cover;
37
38
  background-position: center;
38
39
  transition: 0.5s cubic-bezier(0.05, 0.61, 0.41, 0.95);
39
40
  }
@@ -68,7 +69,6 @@
68
69
  max-width: 600px;
69
70
  margin: 0;
70
71
  border-radius: 40px;
71
- background-size: auto 100%;
72
72
  }
73
73
 
74
74
  .vd-expanding-card.is-active .vd-expanding-card-shadow {
@@ -164,44 +164,113 @@
164
164
  transition-delay: 0.1s;
165
165
  }
166
166
 
167
- /* Progressive hide: narrow viewports hide rightmost cards (matches original) */
168
- @media screen and (max-width: 718px) {
167
+ /* Mobile: vertical stack, expand in place (mirrors desktop flex-grow model) */
168
+ @media screen and (max-width: 767.98px) {
169
169
  .vd-expanding-cards {
170
- min-width: 520px;
170
+ --vd-expanding-card-mobile-stripe: 56px;
171
+ --vd-expanding-card-mobile-active: clamp(200px, 55vw, 280px);
172
+ --vd-expanding-card-mobile-gap: 8px;
173
+
174
+ flex-direction: column;
175
+ align-items: stretch;
176
+ overflow: hidden;
177
+ min-width: 0;
178
+ max-width: 100%;
179
+ width: 100%;
180
+ height: min(
181
+ 72vh,
182
+ calc(
183
+ var(--vd-expanding-card-mobile-active) +
184
+ 5 * (var(--vd-expanding-card-mobile-stripe) + var(--vd-expanding-card-mobile-gap))
185
+ )
186
+ );
187
+ gap: var(--vd-expanding-card-mobile-gap);
171
188
  }
172
189
 
173
- .vd-expanding-card:nth-child(5) {
174
- display: none;
190
+ .vd-expanding-card {
191
+ min-width: 0;
192
+ width: 100%;
193
+ margin: 0;
194
+ flex: 1 1 var(--vd-expanding-card-mobile-stripe);
195
+ min-height: var(--vd-expanding-card-mobile-stripe);
196
+ transition:
197
+ flex 0.5s cubic-bezier(0.05, 0.61, 0.41, 0.95),
198
+ min-height 0.5s cubic-bezier(0.05, 0.61, 0.41, 0.95),
199
+ border-radius 0.5s cubic-bezier(0.05, 0.61, 0.41, 0.95),
200
+ margin 0.5s cubic-bezier(0.05, 0.61, 0.41, 0.95);
175
201
  }
176
- }
177
202
 
178
- @media screen and (max-width: 638px) {
179
- .vd-expanding-cards {
180
- min-width: 440px;
203
+ .vd-expanding-card.is-active {
204
+ flex: 10000 1 var(--vd-expanding-card-mobile-active);
205
+ min-height: var(--vd-expanding-card-mobile-active);
206
+ max-width: none;
207
+ border-radius: 32px;
181
208
  }
182
209
 
183
- .vd-expanding-card:nth-child(4) {
184
- display: none;
210
+ .vd-expanding-card:not(.is-active) {
211
+ flex: 1 1 var(--vd-expanding-card-mobile-stripe);
212
+ min-height: var(--vd-expanding-card-mobile-stripe);
213
+ border-radius: 28px;
185
214
  }
186
- }
187
215
 
188
- @media screen and (max-width: 558px) {
189
- .vd-expanding-cards {
190
- min-width: 360px;
216
+ .vd-expanding-card.is-active .vd-expanding-card-shadow {
217
+ box-shadow:
218
+ inset 0 -120px 120px -120px black,
219
+ inset 0 -120px 120px -100px black;
191
220
  }
192
221
 
193
- .vd-expanding-card:nth-child(3) {
194
- display: none;
222
+ .vd-expanding-card:not(.is-active) .vd-expanding-card-shadow {
223
+ bottom: 0;
224
+ height: 100%;
225
+ box-shadow: inset 0 0 80px 0 rgb(0 0 0 / 35%);
195
226
  }
196
- }
197
227
 
198
- @media screen and (max-width: 478px) {
199
- .vd-expanding-cards {
200
- min-width: 280px;
228
+ .vd-expanding-card.is-active .vd-expanding-card-label {
229
+ bottom: 20px;
230
+ left: 20px;
231
+ top: auto;
232
+ transform: none;
233
+ align-items: flex-end;
234
+ }
235
+
236
+ .vd-expanding-card:not(.is-active) .vd-expanding-card-label {
237
+ bottom: 8px;
238
+ left: 12px;
239
+ top: auto;
240
+ right: auto;
241
+ height: 40px;
242
+ transform: none;
243
+ align-items: center;
244
+ }
245
+
246
+ .vd-expanding-card:not(.is-active) .vd-expanding-card-info {
247
+ min-width: 0;
248
+ flex: 1;
249
+ white-space: nowrap;
250
+ overflow: hidden;
251
+ text-overflow: ellipsis;
252
+ }
253
+
254
+ .vd-expanding-card:not(.is-active) .vd-expanding-card-info > div {
255
+ left: 0;
256
+ opacity: 1;
257
+ }
258
+
259
+ .vd-expanding-card:not(.is-active) .vd-expanding-card-subtitle {
260
+ opacity: 0;
261
+ max-height: 0;
262
+ overflow: hidden;
263
+ }
264
+
265
+ .vd-expanding-card.is-active .vd-expanding-card-subtitle {
266
+ opacity: 1;
267
+ max-height: 2em;
201
268
  }
202
269
 
203
- .vd-expanding-card:nth-child(2) {
204
- display: none;
270
+ .vd-expanding-card:not(.is-active) .vd-expanding-card-title {
271
+ font-size: 1rem;
272
+ overflow: hidden;
273
+ text-overflow: ellipsis;
205
274
  }
206
275
  }
207
276
 
@@ -0,0 +1,257 @@
1
+ /**
2
+ * Vanduo Framework - Layout Primitives
3
+ *
4
+ * Unstyled, single-responsibility layout containers that consume design tokens.
5
+ * They express a layout INTENT (vertical rhythm, wrapping row, centered column)
6
+ * rather than a single property — the composition layer between utilities and
7
+ * components: tokens -> (utilities | primitives) -> components.
8
+ *
9
+ * Override semantics: this file is imported BEFORE core/helpers.css and the
10
+ * utilities layer, so a spacing/gap/padding/radius utility on the same element
11
+ * (e.g. .vd-gap-5) always wins over a primitive's default on equal specificity.
12
+ * Each primitive sets a vd-prefixed internal custom property defaulted from a
13
+ * token; data-* attributes remap that property.
14
+ *
15
+ * .vd-box data-pad data-bg data-round data-border
16
+ * .vd-stack data-gap data-align
17
+ * .vd-inline data-gap data-align data-justify data-wrap
18
+ * .vd-center data-axis data-max
19
+ * .vd-frame data-ratio
20
+ * .vd-cover data-min data-gap
21
+ * .vd-switcher data-threshold data-gap
22
+ *
23
+ * @since 1.5.0
24
+ * @license MIT
25
+ */
26
+
27
+ /* ═══════════════════════════════════════════════════════════════════════════
28
+ * BOX — a single padded / optionally skinned surface
29
+ * ═══════════════════════════════════════════════════════════════════════════ */
30
+
31
+ .vd-box {
32
+ --vd-box-pad: 0;
33
+ --vd-box-radius: 0;
34
+
35
+ display: block;
36
+ padding: var(--vd-box-pad);
37
+ border-radius: var(--vd-box-radius);
38
+ }
39
+
40
+ .vd-box[data-pad="0"] { --vd-box-pad: 0; }
41
+ .vd-box[data-pad="fib-1"] { --vd-box-pad: var(--vd-space-fib-1); }
42
+ .vd-box[data-pad="fib-2"] { --vd-box-pad: var(--vd-space-fib-2); }
43
+ .vd-box[data-pad="fib-3"] { --vd-box-pad: var(--vd-space-fib-3); }
44
+ .vd-box[data-pad="fib-5"] { --vd-box-pad: var(--vd-space-fib-5); }
45
+ .vd-box[data-pad="fib-8"] { --vd-box-pad: var(--vd-space-fib-8); }
46
+ .vd-box[data-pad="fib-13"] { --vd-box-pad: var(--vd-space-fib-13); }
47
+ .vd-box[data-pad="fib-21"] { --vd-box-pad: var(--vd-space-fib-21); }
48
+ .vd-box[data-pad="fib-34"] { --vd-box-pad: var(--vd-space-fib-34); }
49
+ .vd-box[data-pad="fib-55"] { --vd-box-pad: var(--vd-space-fib-55); }
50
+
51
+ /* Background — canonical semantic surfaces */
52
+ .vd-box[data-bg="primary"] { background-color: var(--vd-bg-primary); }
53
+ .vd-box[data-bg="secondary"] { background-color: var(--vd-bg-secondary); }
54
+ .vd-box[data-bg="tertiary"] { background-color: var(--vd-bg-tertiary); }
55
+ .vd-box[data-bg="dark"] { background-color: var(--vd-bg-dark); }
56
+ .vd-box[data-bg="darker"] { background-color: var(--vd-bg-darker); }
57
+
58
+ /* Radius — mirrors the .vd-rounded-* scale (uses data-round, NOT data-radius,
59
+ which is the global theme-customizer radius hook). */
60
+ .vd-box[data-round="none"] { --vd-box-radius: 0; }
61
+ .vd-box[data-round="sm"] { --vd-box-radius: var(--vd-radius-fib-2); }
62
+ .vd-box[data-round="md"] { --vd-box-radius: var(--vd-radius-fib-5); }
63
+ .vd-box[data-round="lg"] { --vd-box-radius: var(--vd-radius-fib-8); }
64
+ .vd-box[data-round="xl"] { --vd-box-radius: var(--vd-radius-fib-13); }
65
+ .vd-box[data-round="2xl"] { --vd-box-radius: var(--vd-radius-fib-21); }
66
+ .vd-box[data-round="full"] { --vd-box-radius: 9999px; }
67
+
68
+ .vd-box[data-border] { border: 1px solid var(--vd-border-color); }
69
+
70
+ /* ═══════════════════════════════════════════════════════════════════════════
71
+ * STACK — vertical rhythm (flex column with token-driven gap)
72
+ * ═══════════════════════════════════════════════════════════════════════════ */
73
+
74
+ .vd-stack {
75
+ --vd-stack-gap: 0;
76
+
77
+ display: flex;
78
+ flex-direction: column;
79
+ gap: var(--vd-stack-gap);
80
+ }
81
+
82
+ .vd-stack[data-gap="0"] { --vd-stack-gap: 0; }
83
+ .vd-stack[data-gap="fib-1"] { --vd-stack-gap: var(--vd-space-fib-1); }
84
+ .vd-stack[data-gap="fib-2"] { --vd-stack-gap: var(--vd-space-fib-2); }
85
+ .vd-stack[data-gap="fib-3"] { --vd-stack-gap: var(--vd-space-fib-3); }
86
+ .vd-stack[data-gap="fib-5"] { --vd-stack-gap: var(--vd-space-fib-5); }
87
+ .vd-stack[data-gap="fib-8"] { --vd-stack-gap: var(--vd-space-fib-8); }
88
+ .vd-stack[data-gap="fib-13"] { --vd-stack-gap: var(--vd-space-fib-13); }
89
+ .vd-stack[data-gap="fib-21"] { --vd-stack-gap: var(--vd-space-fib-21); }
90
+ .vd-stack[data-gap="fib-34"] { --vd-stack-gap: var(--vd-space-fib-34); }
91
+ .vd-stack[data-gap="fib-55"] { --vd-stack-gap: var(--vd-space-fib-55); }
92
+
93
+ .vd-stack[data-align="start"] { align-items: flex-start; }
94
+ .vd-stack[data-align="center"] { align-items: center; }
95
+ .vd-stack[data-align="end"] { align-items: flex-end; }
96
+ .vd-stack[data-align="stretch"] { align-items: stretch; }
97
+
98
+ /* ═══════════════════════════════════════════════════════════════════════════
99
+ * INLINE — horizontal cluster (wrapping flex row with token-driven gap)
100
+ * ═══════════════════════════════════════════════════════════════════════════ */
101
+
102
+ .vd-inline {
103
+ --vd-inline-gap: 0;
104
+
105
+ display: flex;
106
+ flex-direction: row;
107
+ flex-wrap: wrap;
108
+ align-items: center;
109
+ gap: var(--vd-inline-gap);
110
+ }
111
+
112
+ .vd-inline[data-gap="0"] { --vd-inline-gap: 0; }
113
+ .vd-inline[data-gap="fib-1"] { --vd-inline-gap: var(--vd-space-fib-1); }
114
+ .vd-inline[data-gap="fib-2"] { --vd-inline-gap: var(--vd-space-fib-2); }
115
+ .vd-inline[data-gap="fib-3"] { --vd-inline-gap: var(--vd-space-fib-3); }
116
+ .vd-inline[data-gap="fib-5"] { --vd-inline-gap: var(--vd-space-fib-5); }
117
+ .vd-inline[data-gap="fib-8"] { --vd-inline-gap: var(--vd-space-fib-8); }
118
+ .vd-inline[data-gap="fib-13"] { --vd-inline-gap: var(--vd-space-fib-13); }
119
+ .vd-inline[data-gap="fib-21"] { --vd-inline-gap: var(--vd-space-fib-21); }
120
+ .vd-inline[data-gap="fib-34"] { --vd-inline-gap: var(--vd-space-fib-34); }
121
+ .vd-inline[data-gap="fib-55"] { --vd-inline-gap: var(--vd-space-fib-55); }
122
+
123
+ .vd-inline[data-align="start"] { align-items: flex-start; }
124
+ .vd-inline[data-align="center"] { align-items: center; }
125
+ .vd-inline[data-align="end"] { align-items: flex-end; }
126
+ .vd-inline[data-align="baseline"] { align-items: baseline; }
127
+ .vd-inline[data-align="stretch"] { align-items: stretch; }
128
+
129
+ .vd-inline[data-justify="start"] { justify-content: flex-start; }
130
+ .vd-inline[data-justify="center"] { justify-content: center; }
131
+ .vd-inline[data-justify="end"] { justify-content: flex-end; }
132
+ .vd-inline[data-justify="between"] { justify-content: space-between; }
133
+ .vd-inline[data-justify="around"] { justify-content: space-around; }
134
+ .vd-inline[data-justify="evenly"] { justify-content: space-evenly; }
135
+
136
+ .vd-inline[data-wrap="nowrap"] { flex-wrap: nowrap; }
137
+
138
+ /* ═══════════════════════════════════════════════════════════════════════════
139
+ * CENTER — center a column horizontally (data-max) and/or its content (data-axis)
140
+ * ═══════════════════════════════════════════════════════════════════════════ */
141
+
142
+ .vd-center {
143
+ --vd-center-max: none;
144
+
145
+ display: block;
146
+ max-width: var(--vd-center-max);
147
+ margin-inline: auto;
148
+ }
149
+
150
+ .vd-center[data-max="fib-377"] { --vd-center-max: 377px; }
151
+ .vd-center[data-max="fib-610"] { --vd-center-max: 610px; }
152
+ .vd-center[data-max="fib-987"] { --vd-center-max: 987px; }
153
+
154
+ .vd-center[data-axis="both"] {
155
+ display: flex;
156
+ align-items: center;
157
+ justify-content: center;
158
+ }
159
+
160
+ .vd-center[data-axis="horizontal"] {
161
+ display: flex;
162
+ justify-content: center;
163
+ }
164
+
165
+ .vd-center[data-axis="vertical"] {
166
+ display: flex;
167
+ align-items: center;
168
+ }
169
+
170
+ /* ═══════════════════════════════════════════════════════════════════════════
171
+ * FRAME — a fixed aspect-ratio box (golden by default); media children cover it
172
+ * ═══════════════════════════════════════════════════════════════════════════ */
173
+
174
+ .vd-frame {
175
+ --vd-frame-ratio: 1.618;
176
+
177
+ display: block;
178
+ overflow: hidden;
179
+ aspect-ratio: var(--vd-frame-ratio);
180
+ }
181
+
182
+ .vd-frame > img,
183
+ .vd-frame > video,
184
+ .vd-frame > canvas,
185
+ .vd-frame > svg,
186
+ .vd-frame > iframe {
187
+ display: block;
188
+ width: 100%;
189
+ height: 100%;
190
+ object-fit: cover;
191
+ }
192
+
193
+ .vd-frame[data-ratio="golden"] { --vd-frame-ratio: 1.618; }
194
+ .vd-frame[data-ratio="golden-portrait"] { --vd-frame-ratio: 0.618; }
195
+ .vd-frame[data-ratio="square"] { --vd-frame-ratio: 1; }
196
+ .vd-frame[data-ratio="16-9"] { --vd-frame-ratio: 1.7778; }
197
+ .vd-frame[data-ratio="4-3"] { --vd-frame-ratio: 1.3333; }
198
+ .vd-frame[data-ratio="3-2"] { --vd-frame-ratio: 1.5; }
199
+ .vd-frame[data-ratio="21-9"] { --vd-frame-ratio: 2.3333; }
200
+
201
+ /* ═══════════════════════════════════════════════════════════════════════════
202
+ * COVER — a min-height region that vertically centers its content
203
+ * ═══════════════════════════════════════════════════════════════════════════ */
204
+
205
+ .vd-cover {
206
+ --vd-cover-min: 100vh;
207
+ --vd-cover-gap: 0;
208
+
209
+ display: flex;
210
+ flex-direction: column;
211
+ justify-content: center;
212
+ gap: var(--vd-cover-gap);
213
+ min-height: var(--vd-cover-min);
214
+ }
215
+
216
+ .vd-cover[data-min="screen"] { --vd-cover-min: 100vh; }
217
+ .vd-cover[data-min="half"] { --vd-cover-min: 50vh; }
218
+ .vd-cover[data-min="fib-610"] { --vd-cover-min: 610px; }
219
+ .vd-cover[data-min="fib-987"] { --vd-cover-min: 987px; }
220
+
221
+ .vd-cover[data-gap="fib-3"] { --vd-cover-gap: var(--vd-space-fib-3); }
222
+ .vd-cover[data-gap="fib-5"] { --vd-cover-gap: var(--vd-space-fib-5); }
223
+ .vd-cover[data-gap="fib-8"] { --vd-cover-gap: var(--vd-space-fib-8); }
224
+ .vd-cover[data-gap="fib-13"] { --vd-cover-gap: var(--vd-space-fib-13); }
225
+ .vd-cover[data-gap="fib-21"] { --vd-cover-gap: var(--vd-space-fib-21); }
226
+ .vd-cover[data-gap="fib-34"] { --vd-cover-gap: var(--vd-space-fib-34); }
227
+
228
+ /* ═══════════════════════════════════════════════════════════════════════════
229
+ * SWITCHER — auto row→column at a container-width threshold (no media query).
230
+ * When the container is wider than the threshold the children share a row;
231
+ * narrower and they each take a full row. Thresholds are Fibonacci by design.
232
+ * ═══════════════════════════════════════════════════════════════════════════ */
233
+
234
+ .vd-switcher {
235
+ --vd-switcher-threshold: 610px;
236
+ --vd-switcher-gap: 0;
237
+
238
+ display: flex;
239
+ flex-wrap: wrap;
240
+ gap: var(--vd-switcher-gap);
241
+ }
242
+
243
+ .vd-switcher > * {
244
+ flex-grow: 1;
245
+ flex-basis: calc((var(--vd-switcher-threshold) - 100%) * 999);
246
+ }
247
+
248
+ .vd-switcher[data-threshold="fib-377"] { --vd-switcher-threshold: 377px; }
249
+ .vd-switcher[data-threshold="fib-610"] { --vd-switcher-threshold: 610px; }
250
+ .vd-switcher[data-threshold="fib-987"] { --vd-switcher-threshold: 987px; }
251
+
252
+ .vd-switcher[data-gap="fib-3"] { --vd-switcher-gap: var(--vd-space-fib-3); }
253
+ .vd-switcher[data-gap="fib-5"] { --vd-switcher-gap: var(--vd-space-fib-5); }
254
+ .vd-switcher[data-gap="fib-8"] { --vd-switcher-gap: var(--vd-space-fib-8); }
255
+ .vd-switcher[data-gap="fib-13"] { --vd-switcher-gap: var(--vd-space-fib-13); }
256
+ .vd-switcher[data-gap="fib-21"] { --vd-switcher-gap: var(--vd-space-fib-21); }
257
+ .vd-switcher[data-gap="fib-34"] { --vd-switcher-gap: var(--vd-space-fib-34); }
package/css/vanduo.css CHANGED
@@ -12,6 +12,13 @@
12
12
  @import url('core/typography.css');
13
13
  @import url('core/tokens.css');
14
14
  @import url('core/grid.css');
15
+
16
+ /* Layer 1.5: Layout primitives (Box, Stack, Inline, Center).
17
+ Imported BEFORE helpers so the spacing/gap/padding/radius utilities that follow
18
+ override a primitive default on equal specificity (utilities stay the escape
19
+ hatch). Primitive token references resolve order-independently from :root. */
20
+ @import url('primitives/primitives.css');
21
+
15
22
  @import url('core/helpers.css');
16
23
 
17
24
  /* Layer 2: Icons (regular + fill; import css/icons/icons-all.css for all 6 weights) */
@@ -1,6 +1,6 @@
1
1
  {
2
- "version": "1.4.6",
3
- "builtAt": "2026-06-13T10:32:26.875Z",
4
- "commit": "82019ff",
2
+ "version": "1.5.1",
3
+ "builtAt": "2026-06-20T15:00:18.257Z",
4
+ "commit": "baf8178",
5
5
  "mode": "development+production"
6
6
  }