@vanduo-oss/framework 1.4.6 → 1.5.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/CHANGELOG.md CHANGED
@@ -8,6 +8,17 @@ 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.0] - 2026-06-18
12
+
13
+ ### Added
14
+ - **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`.
15
+
16
+ ### Removed
17
+ - **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.
18
+
19
+ ### Changed
20
+ - Removed the unused empty `packages/` workspace and `pnpm-workspace.yaml` (the esbuild build-approval remains in `package.json` `pnpm.allowedBuilds`).
21
+
11
22
  ## [1.4.6] - 2026-06-12
12
23
 
13
24
  ### Added
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Vanduo Framework v1.4.6
1
+ # Vanduo Framework v1.5.0
2
2
 
3
3
  <p align="center">
4
4
  <img src="vanduo-banner.svg" alt="Vanduo Framework Banner" width="100%">
@@ -25,7 +25,12 @@ 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.0
29
+
30
+ - **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.
31
+ - **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.
32
+
33
+ ## Previous: 1.4.6
29
34
 
30
35
  - **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
36
  - **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 +52,8 @@ Vanduo is a zero-dependency UI framework built with HTML, CSS, and vanilla JavaS
47
52
  ### CDN
48
53
 
49
54
  ```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>
55
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/vanduo-oss/framework@v1.5.0/dist/vanduo.min.css">
56
+ <script src="https://cdn.jsdelivr.net/gh/vanduo-oss/framework@v1.5.0/dist/vanduo.min.js"></script>
52
57
  <script>
53
58
  Vanduo.init();
54
59
  </script>
@@ -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.0",
3
+ "builtAt": "2026-06-18T19:13:58.613Z",
4
+ "commit": "8f0e1b5",
5
5
  "mode": "development+production"
6
6
  }