kmcom-nuxt-layers 6.0.0 → 6.2.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.
Files changed (31) hide show
  1. package/docs/USAGE.md +8 -8
  2. package/docs/adr/0003-modular-step-system-replaces-fluid-clamps.md +58 -0
  3. package/layers/core/app/app.config.ts +6 -3
  4. package/layers/core/app/assets/css/tokens/spacing-fluid.css +24 -19
  5. package/layers/core/app/components/Element.test.ts +2 -2
  6. package/layers/core/app/components/LoadingScreen.vue +142 -76
  7. package/layers/core/app/composables/useLayoutAttrs.test.ts +35 -5
  8. package/layers/core/app/composables/useLayoutAttrs.ts +1 -1
  9. package/layers/core/app/composables/useLoading.ts +57 -36
  10. package/layers/core/app/plugins/loading.client.ts +21 -27
  11. package/layers/core/app/types/loading.ts +18 -8
  12. package/layers/core/app/utils/index.ts +1 -0
  13. package/layers/core/app/utils/loading.test.ts +48 -0
  14. package/layers/core/app/utils/loading.ts +53 -0
  15. package/layers/core/app/utils/resolveElementStyle.test.ts +2 -2
  16. package/layers/design-system/theming/app/types/designSystem.ts +21 -17
  17. package/layers/design-system/typography/app/app.config.ts +26 -0
  18. package/layers/design-system/typography/app/assets/css/typography.css +216 -187
  19. package/layers/design-system/typography/app/components/Typography/CodeBlock.vue +4 -4
  20. package/layers/design-system/typography/app/components/Typography/Headline.test.ts +36 -0
  21. package/layers/design-system/typography/app/components/Typography/Headline.vue +20 -21
  22. package/layers/design-system/typography/app/components/Typography/QuoteBlock.vue +4 -4
  23. package/layers/design-system/typography/app/components/Typography/RepeatText.vue +7 -6
  24. package/layers/design-system/typography/app/components/Typography/index.test.ts +31 -0
  25. package/layers/design-system/typography/app/components/Typography/index.vue +10 -8
  26. package/layers/design-system/typography/app/composables/typography.test.ts +53 -0
  27. package/layers/design-system/typography/app/composables/typography.ts +28 -43
  28. package/layers/design-system/typography/app/types/typography.ts +5 -32
  29. package/layers/motion/scroll/app/plugins/locomotive-scroll.client.ts +22 -0
  30. package/layers/structure/grid/app/assets/css/grid.css +11 -7
  31. package/package.json +1 -1
@@ -14,303 +14,332 @@
14
14
  --font-display: system-ui, sans-serif;
15
15
  }
16
16
 
17
+ /*
18
+ * Modular type scale — every size is a step on one ratio, not a hand-tuned clamp:
19
+ * font-size = round(--type-base × --type-scale ^ --step, --type-snap)
20
+ * 1.26 ≈ 2^(1/3), so three steps double a size. --step is set per element and the size
21
+ * is recomputed from the base each time, so nested steps never compound.
22
+ * Plain :root properties, not @theme: var(--step) must resolve on the element using it.
23
+ * Retune a subtree by overriding --type-base or --type-scale (both read where used);
24
+ * overriding --type-min/--type-max there does nothing — --type-base already substituted
25
+ * them at :root.
26
+ */
17
27
  :root {
18
- /* Fluid type scale — rounding interval. rem, never px (see §5 a11y). */
19
- --fluid-step: 0.25rem;
20
-
21
- /* Unrounded fluid ranges. Endpoints MUST be integer multiples of --fluid-step. */
22
- --fluid-text-xs-raw: clamp(0.5rem, 0.475rem + 0.1vi, 0.75rem);
23
- --fluid-text-sm-raw: clamp(0.75rem, 0.7rem + 0.15vi, 1rem);
24
- --fluid-text-md-raw: clamp(1rem, 0.9375rem + 0.35vi, 1.25rem);
25
- --fluid-text-lg-raw: clamp(1.25rem, 1.125rem + 0.75vi, 1.75rem);
26
- --fluid-text-xl-raw: clamp(1.5rem, 1.25rem + 1.5vi, 2.5rem);
27
- --fluid-text-2xl-raw: clamp(2rem, 1.5rem + 2.5vi, 3.5rem);
28
- --fluid-text-3xl-raw: clamp(2.5rem, 1.75rem + 4vi, 5rem);
29
- --fluid-text-4xl-raw: clamp(3rem, 2rem + 5vi, 6rem);
30
- --fluid-text-5xl-raw: clamp(3.75rem, 2.5rem + 6.5vi, 7.5rem);
31
- --fluid-text-6xl-raw: clamp(4.5rem, 3rem + 8vi, 9rem);
32
- --fluid-text-7xl-raw: clamp(5.5rem, 3.5rem + 10vi, 11rem);
33
- --fluid-text-8xl-raw: clamp(6.75rem, 4rem + 14vi, 13.5rem);
34
- --fluid-text-9xl-raw: clamp(8rem, 4.5rem + 20vi, 16rem);
35
-
36
- /* Damp heading growth under large OS text-scale prefs (>100%) so words
37
- don't break to one-per-line. Root font-size (rem) already scales by
38
- env(preferred-text-scale); this backs that out then re-applies it at
39
- half rate. No-op at 100% scale or without support (env() undefined → 1). */
40
- --heading-text-scale-damp: calc(1 + (env(preferred-text-scale, 1) - 1) * 0.5);
41
-
42
- /* Container-relative counterparts — same rem endpoints, cqi instead of vi.
43
- Require a `container-type` ancestor (e.g. `@container`); with none, cqi
44
- falls back to the small viewport per spec, so these are opt-in, not a
45
- replacement for the vi-based scale above. */
46
- /* Coefficients retuned vs the vi scale (not a straight port): small steps'
47
- min→max diff is only 0.25-0.5rem (1-2 --fluid-step increments), so vi's
48
- slope needs 250+rem of container width to cross even one rounding step —
49
- reads as frozen. Retuned so the full min→max transition happens between
50
- an 8rem and 24rem container (the actual draggable range in practice). */
51
- --fluid-text-xs-cq-raw: clamp(0.5rem, 0.375rem + 1.5625cqi, 0.75rem);
52
- --fluid-text-sm-cq-raw: clamp(0.75rem, 0.625rem + 1.5625cqi, 1rem);
53
- --fluid-text-md-cq-raw: clamp(1rem, 0.875rem + 1.5625cqi, 1.25rem);
54
- --fluid-text-lg-cq-raw: clamp(1.25rem, 1rem + 3.125cqi, 1.75rem);
55
- --fluid-text-xl-cq-raw: clamp(1.5rem, 1.25rem + 1.5cqi, 2.5rem);
56
- --fluid-text-2xl-cq-raw: clamp(2rem, 1.5rem + 2.5cqi, 3.5rem);
57
- --fluid-text-3xl-cq-raw: clamp(2.5rem, 1.75rem + 4cqi, 5rem);
58
- --fluid-text-4xl-cq-raw: clamp(3rem, 2rem + 5cqi, 6rem);
59
- --fluid-text-5xl-cq-raw: clamp(3.75rem, 2.5rem + 6.5cqi, 7.5rem);
60
- --fluid-text-6xl-cq-raw: clamp(4.5rem, 3rem + 8cqi, 9rem);
61
- --fluid-text-7xl-cq-raw: clamp(5.5rem, 3.5rem + 10cqi, 11rem);
62
- --fluid-text-8xl-cq-raw: clamp(6.75rem, 4rem + 14cqi, 13.5rem);
63
- --fluid-text-9xl-cq-raw: clamp(8rem, 4.5rem + 20cqi, 16rem);
28
+ --type-min: 1rem; /* base below 48rem */
29
+ --type-max: 1.2rem; /* base from 48rem — every step jumps ×1.2 there, by design */
30
+ --type-base: var(--type-min);
31
+ --type-scale: 1.26;
32
+ --type-snap: 0.0625rem; /* rounding interval: rem so it scales with the root; never px, never 0 */
33
+
34
+ /* Fluid display step, 5 → 7 across 360 → 1280px. tan(atan2(length, 1rem)) casts the
35
+ length to the unitless exponent pow() needs; normalised by 1rem (not 1px) so the
36
+ exponent stays 5 → 7 whatever the user's root font size; the vw term is scaled by root / 16px so a
37
+ larger root doesn't lower the exponent at a given width (h1 stays visibly larger). */
38
+ --step-display: tan(
39
+ atan2(clamp(5rem, 4.2176rem + 3.4784vw * tan(atan2(1rem, 16px)), 7rem), 1rem)
40
+ );
41
+ }
42
+
43
+ @media (width >= 48rem) {
44
+ :root {
45
+ --type-base: var(--type-max);
46
+ }
64
47
  }
65
48
 
66
49
  /*
67
- * Fluid type utilities, parallel to Tailwind's --text-* namespace.
50
+ * Step utilities, parallel to Tailwind's --text-* namespace.
68
51
  * Do not override --text-xs..--text-9xl — Nuxt UI components consume those.
69
- * round() must live in the utility declaration (not a custom property) so
70
- * var(--fluid-step) resolves per-element, not frozen at :root.
52
+ * round() lives in a real declaration behind @supports, never in a custom property:
53
+ * an unsupported browser then keeps the plain calc() above it instead of dropping
54
+ * font-size entirely.
55
+ * Line heights fall with size: anchored on the reference's measured body 1.7, h2/h3 1.2 and
56
+ * h1 0.95, eased in between (sm 1.5, 0 1.6 … 4 1.15, 5 1.1, 6 1.05, 7 1).
71
57
  */
72
- @utility text-fluid-xs {
73
- font-size: var(--fluid-text-xs-raw);
58
+ @utility text-step-sm {
59
+ --step: -1;
60
+
61
+ font-size: calc(var(--type-base) * pow(var(--type-scale), var(--step)));
74
62
  line-height: 1.5;
75
63
 
76
64
  @supports (font-size: round(1rem, 1rem)) {
77
- font-size: round(down, var(--fluid-text-xs-raw), var(--fluid-step));
65
+ font-size: round(
66
+ calc(var(--type-base) * pow(var(--type-scale), var(--step))),
67
+ var(--type-snap)
68
+ );
78
69
  }
79
70
  }
80
71
 
81
- @utility text-fluid-sm {
82
- font-size: var(--fluid-text-sm-raw);
83
- line-height: 1.45;
84
-
85
- @supports (font-size: round(1rem, 1rem)) {
86
- font-size: round(down, var(--fluid-text-sm-raw), var(--fluid-step));
87
- }
88
- }
72
+ @utility text-step-0 {
73
+ --step: 0;
89
74
 
90
- @utility text-fluid-md {
91
- font-size: var(--fluid-text-md-raw);
92
- line-height: 1.4;
75
+ font-size: calc(var(--type-base) * pow(var(--type-scale), var(--step)));
76
+ line-height: 1.6;
93
77
 
94
78
  @supports (font-size: round(1rem, 1rem)) {
95
- font-size: round(down, var(--fluid-text-md-raw), var(--fluid-step));
79
+ font-size: round(
80
+ calc(var(--type-base) * pow(var(--type-scale), var(--step))),
81
+ var(--type-snap)
82
+ );
96
83
  }
97
84
  }
98
85
 
99
- @utility text-fluid-lg {
100
- font-size: var(--fluid-text-lg-raw);
101
- line-height: 1.25;
102
-
103
- @supports (font-size: round(1rem, 1rem)) {
104
- font-size: round(down, var(--fluid-text-lg-raw), var(--fluid-step));
105
- }
106
- }
86
+ @utility text-step-1 {
87
+ --step: 1;
107
88
 
108
- @utility text-fluid-xl {
109
- font-size: var(--fluid-text-xl-raw);
110
- line-height: 1.15;
89
+ font-size: calc(var(--type-base) * pow(var(--type-scale), var(--step)));
90
+ line-height: 1.7;
111
91
 
112
92
  @supports (font-size: round(1rem, 1rem)) {
113
- font-size: round(down, var(--fluid-text-xl-raw), var(--fluid-step));
93
+ font-size: round(
94
+ calc(var(--type-base) * pow(var(--type-scale), var(--step))),
95
+ var(--type-snap)
96
+ );
114
97
  }
115
98
  }
116
99
 
117
- @utility text-fluid-2xl {
118
- font-size: var(--fluid-text-2xl-raw);
119
- line-height: 1.1;
120
-
121
- @supports (font-size: round(1rem, 1rem)) {
122
- font-size: round(down, var(--fluid-text-2xl-raw), var(--fluid-step));
123
- }
124
- }
100
+ @utility text-step-2 {
101
+ --step: 2;
125
102
 
126
- @utility text-fluid-3xl {
127
- font-size: var(--fluid-text-3xl-raw);
128
- line-height: 1.05;
103
+ font-size: calc(var(--type-base) * pow(var(--type-scale), var(--step)));
104
+ line-height: 1.2;
129
105
 
130
106
  @supports (font-size: round(1rem, 1rem)) {
131
- font-size: round(down, var(--fluid-text-3xl-raw), var(--fluid-step));
107
+ font-size: round(
108
+ calc(var(--type-base) * pow(var(--type-scale), var(--step))),
109
+ var(--type-snap)
110
+ );
132
111
  }
133
112
  }
134
113
 
135
- @utility text-fluid-4xl {
136
- font-size: calc(var(--fluid-text-4xl-raw) * var(--heading-text-scale-damp));
137
- line-height: 1;
114
+ @utility text-step-3 {
115
+ --step: 3;
116
+
117
+ font-size: calc(var(--type-base) * pow(var(--type-scale), var(--step)));
118
+ line-height: 1.2;
138
119
 
139
120
  @supports (font-size: round(1rem, 1rem)) {
140
- font-size: calc(
141
- round(down, var(--fluid-text-4xl-raw), var(--fluid-step)) * var(--heading-text-scale-damp)
121
+ font-size: round(
122
+ calc(var(--type-base) * pow(var(--type-scale), var(--step))),
123
+ var(--type-snap)
142
124
  );
143
125
  }
144
126
  }
145
127
 
146
- @utility text-fluid-5xl {
147
- font-size: calc(var(--fluid-text-5xl-raw) * var(--heading-text-scale-damp));
148
- line-height: 1;
128
+ @utility text-step-4 {
129
+ --step: 4;
130
+
131
+ font-size: calc(var(--type-base) * pow(var(--type-scale), var(--step)));
132
+ line-height: 1.15;
149
133
 
150
134
  @supports (font-size: round(1rem, 1rem)) {
151
- font-size: calc(
152
- round(down, var(--fluid-text-5xl-raw), var(--fluid-step)) * var(--heading-text-scale-damp)
135
+ font-size: round(
136
+ calc(var(--type-base) * pow(var(--type-scale), var(--step))),
137
+ var(--type-snap)
153
138
  );
154
139
  }
155
140
  }
156
141
 
157
- @utility text-fluid-6xl {
158
- font-size: calc(var(--fluid-text-6xl-raw) * var(--heading-text-scale-damp));
159
- line-height: 1;
142
+ @utility text-step-5 {
143
+ --step: 5;
144
+
145
+ font-size: calc(var(--type-base) * pow(var(--type-scale), var(--step)));
146
+ line-height: 1.1;
160
147
 
161
148
  @supports (font-size: round(1rem, 1rem)) {
162
- font-size: calc(
163
- round(down, var(--fluid-text-6xl-raw), var(--fluid-step)) * var(--heading-text-scale-damp)
149
+ font-size: round(
150
+ calc(var(--type-base) * pow(var(--type-scale), var(--step))),
151
+ var(--type-snap)
164
152
  );
165
153
  }
166
154
  }
167
155
 
168
- @utility text-fluid-7xl {
169
- font-size: calc(var(--fluid-text-7xl-raw) * var(--heading-text-scale-damp));
170
- line-height: 1;
156
+ @utility text-step-6 {
157
+ --step: 6;
158
+
159
+ font-size: calc(var(--type-base) * pow(var(--type-scale), var(--step)));
160
+ line-height: 1.05;
171
161
 
172
162
  @supports (font-size: round(1rem, 1rem)) {
173
- font-size: calc(
174
- round(down, var(--fluid-text-7xl-raw), var(--fluid-step)) * var(--heading-text-scale-damp)
163
+ font-size: round(
164
+ calc(var(--type-base) * pow(var(--type-scale), var(--step))),
165
+ var(--type-snap)
175
166
  );
176
167
  }
177
168
  }
178
169
 
179
- @utility text-fluid-8xl {
180
- font-size: calc(var(--fluid-text-8xl-raw) * var(--heading-text-scale-damp));
170
+ @utility text-step-7 {
171
+ --step: 7;
172
+
173
+ font-size: calc(var(--type-base) * pow(var(--type-scale), var(--step)));
181
174
  line-height: 1;
182
175
 
183
176
  @supports (font-size: round(1rem, 1rem)) {
184
- font-size: calc(
185
- round(down, var(--fluid-text-8xl-raw), var(--fluid-step)) * var(--heading-text-scale-damp)
177
+ font-size: round(
178
+ calc(var(--type-base) * pow(var(--type-scale), var(--step))),
179
+ var(--type-snap)
186
180
  );
187
181
  }
188
182
  }
189
183
 
190
- @utility text-fluid-9xl {
191
- font-size: calc(var(--fluid-text-9xl-raw) * var(--heading-text-scale-damp));
192
- line-height: 1;
184
+ @utility text-step-8 {
185
+ --step: 8;
186
+
187
+ font-size: calc(var(--type-base) * pow(var(--type-scale), var(--step)));
188
+ line-height: 0.95;
193
189
 
194
190
  @supports (font-size: round(1rem, 1rem)) {
195
- font-size: calc(
196
- round(down, var(--fluid-text-9xl-raw), var(--fluid-step)) * var(--heading-text-scale-damp)
191
+ font-size: round(
192
+ calc(var(--type-base) * pow(var(--type-scale), var(--step))),
193
+ var(--type-snap)
197
194
  );
198
195
  }
199
196
  }
200
197
 
201
- @utility text-fluid-xs-cq {
202
- font-size: var(--fluid-text-xs-cq-raw);
203
- line-height: 1.5;
198
+ @utility text-step-9 {
199
+ --step: 9;
204
200
 
205
- @supports (font-size: round(1rem, 1rem)) {
206
- font-size: round(down, var(--fluid-text-xs-cq-raw), var(--fluid-step));
207
- }
208
- }
209
-
210
- @utility text-fluid-sm-cq {
211
- font-size: var(--fluid-text-sm-cq-raw);
212
- line-height: 1.45;
201
+ font-size: calc(var(--type-base) * pow(var(--type-scale), var(--step)));
202
+ line-height: 0.95;
213
203
 
214
204
  @supports (font-size: round(1rem, 1rem)) {
215
- font-size: round(down, var(--fluid-text-sm-cq-raw), var(--fluid-step));
205
+ font-size: round(
206
+ calc(var(--type-base) * pow(var(--type-scale), var(--step))),
207
+ var(--type-snap)
208
+ );
216
209
  }
217
210
  }
218
211
 
219
- @utility text-fluid-md-cq {
220
- font-size: var(--fluid-text-md-cq-raw);
221
- line-height: 1.4;
212
+ @utility text-step-10 {
213
+ --step: 10;
222
214
 
223
- @supports (font-size: round(1rem, 1rem)) {
224
- font-size: round(down, var(--fluid-text-md-cq-raw), var(--fluid-step));
225
- }
226
- }
227
-
228
- @utility text-fluid-lg-cq {
229
- font-size: var(--fluid-text-lg-cq-raw);
230
- line-height: 1.25;
215
+ font-size: calc(var(--type-base) * pow(var(--type-scale), var(--step)));
216
+ line-height: 0.95;
231
217
 
232
218
  @supports (font-size: round(1rem, 1rem)) {
233
- font-size: round(down, var(--fluid-text-lg-cq-raw), var(--fluid-step));
219
+ font-size: round(
220
+ calc(var(--type-base) * pow(var(--type-scale), var(--step))),
221
+ var(--type-snap)
222
+ );
234
223
  }
235
224
  }
236
225
 
237
- @utility text-fluid-xl-cq {
238
- font-size: var(--fluid-text-xl-cq-raw);
239
- line-height: 1.15;
226
+ @utility text-step-11 {
227
+ --step: 11;
228
+
229
+ font-size: calc(var(--type-base) * pow(var(--type-scale), var(--step)));
230
+ line-height: 0.95;
240
231
 
241
232
  @supports (font-size: round(1rem, 1rem)) {
242
- font-size: round(down, var(--fluid-text-xl-cq-raw), var(--fluid-step));
233
+ font-size: round(
234
+ calc(var(--type-base) * pow(var(--type-scale), var(--step))),
235
+ var(--type-snap)
236
+ );
243
237
  }
244
238
  }
245
239
 
246
- @utility text-fluid-2xl-cq {
247
- font-size: var(--fluid-text-2xl-cq-raw);
248
- line-height: 1.1;
240
+ @utility text-step-12 {
241
+ --step: 12;
242
+
243
+ font-size: calc(var(--type-base) * pow(var(--type-scale), var(--step)));
244
+ line-height: 0.95;
249
245
 
250
246
  @supports (font-size: round(1rem, 1rem)) {
251
- font-size: round(down, var(--fluid-text-2xl-cq-raw), var(--fluid-step));
247
+ font-size: round(
248
+ calc(var(--type-base) * pow(var(--type-scale), var(--step))),
249
+ var(--type-snap)
250
+ );
252
251
  }
253
252
  }
254
253
 
255
- @utility text-fluid-3xl-cq {
256
- font-size: var(--fluid-text-3xl-cq-raw);
257
- line-height: 1.05;
254
+ @utility text-step-display {
255
+ --step: var(--step-display);
256
+
257
+ font-size: calc(var(--type-base) * pow(var(--type-scale), var(--step)));
258
+ line-height: 0.95;
258
259
 
259
260
  @supports (font-size: round(1rem, 1rem)) {
260
- font-size: round(down, var(--fluid-text-3xl-cq-raw), var(--fluid-step));
261
+ font-size: round(
262
+ calc(var(--type-base) * pow(var(--type-scale), var(--step))),
263
+ var(--type-snap)
264
+ );
261
265
  }
262
266
  }
263
267
 
264
- @utility text-fluid-4xl-cq {
265
- font-size: var(--fluid-text-4xl-cq-raw);
266
- line-height: 1;
268
+ /*
269
+ * Element defaults: body text at step 1, headings at their own step. Only body and
270
+ * h1–h6 set --step/font-size — descendants inherit the computed size normally; a
271
+ * universal `body *` rule would fight component CSS. @layer base, so any Tailwind or
272
+ * Nuxt UI text-* utility on an element still wins. Heading margins stay in core/base.css.
273
+ * Author headings only: Nuxt UI renders its own titles (UModal, USlideover, …) as
274
+ * h1–h6 carrying a data-slot attribute and often no size class, so they keep inheriting.
275
+ * :where() keeps the specificity of a bare type selector.
276
+ */
277
+ @layer base {
278
+ body {
279
+ --step: 1;
267
280
 
268
- @supports (font-size: round(1rem, 1rem)) {
269
- font-size: round(down, var(--fluid-text-4xl-cq-raw), var(--fluid-step));
281
+ line-height: 1.7;
270
282
  }
271
- }
272
283
 
273
- @utility text-fluid-5xl-cq {
274
- font-size: var(--fluid-text-5xl-cq-raw);
275
- line-height: 1;
284
+ /* Same expressions as the text-step-* utilities. h1 uses the display step. */
285
+ h1:where(:not([data-slot])) {
286
+ --step: var(--step-display);
276
287
 
277
- @supports (font-size: round(1rem, 1rem)) {
278
- font-size: round(down, var(--fluid-text-5xl-cq-raw), var(--fluid-step));
288
+ font-size: calc(var(--type-base) * pow(var(--type-scale), var(--step)));
289
+ line-height: 0.95;
290
+ text-wrap: balance;
279
291
  }
280
- }
281
292
 
282
- @utility text-fluid-6xl-cq {
283
- font-size: var(--fluid-text-6xl-cq-raw);
284
- line-height: 1;
293
+ h2:where(:not([data-slot])) {
294
+ --step: 3;
285
295
 
286
- @supports (font-size: round(1rem, 1rem)) {
287
- font-size: round(down, var(--fluid-text-6xl-cq-raw), var(--fluid-step));
296
+ line-height: 1.2;
297
+ text-wrap: balance;
288
298
  }
289
- }
290
299
 
291
- @utility text-fluid-7xl-cq {
292
- font-size: var(--fluid-text-7xl-cq-raw);
293
- line-height: 1;
300
+ h3:where(:not([data-slot])) {
301
+ --step: 2;
294
302
 
295
- @supports (font-size: round(1rem, 1rem)) {
296
- font-size: round(down, var(--fluid-text-7xl-cq-raw), var(--fluid-step));
303
+ line-height: 1.2;
304
+ text-wrap: balance;
297
305
  }
298
- }
299
306
 
300
- @utility text-fluid-8xl-cq {
301
- font-size: var(--fluid-text-8xl-cq-raw);
302
- line-height: 1;
307
+ h4:where(:not([data-slot])) {
308
+ --step: 1;
309
+ }
303
310
 
304
- @supports (font-size: round(1rem, 1rem)) {
305
- font-size: round(down, var(--fluid-text-8xl-cq-raw), var(--fluid-step));
311
+ h5:where(:not([data-slot])),
312
+ h6:where(:not([data-slot])) {
313
+ --step: 0;
306
314
  }
307
- }
308
315
 
309
- @utility text-fluid-9xl-cq {
310
- font-size: var(--fluid-text-9xl-cq-raw);
311
- line-height: 1;
316
+ body,
317
+ h2:where(:not([data-slot])),
318
+ h3:where(:not([data-slot])),
319
+ h4:where(:not([data-slot])),
320
+ h5:where(:not([data-slot])),
321
+ h6:where(:not([data-slot])) {
322
+ font-size: calc(var(--type-base) * pow(var(--type-scale), var(--step)));
323
+ }
312
324
 
313
325
  @supports (font-size: round(1rem, 1rem)) {
314
- font-size: round(down, var(--fluid-text-9xl-cq-raw), var(--fluid-step));
326
+ body,
327
+ h2:where(:not([data-slot])),
328
+ h3:where(:not([data-slot])),
329
+ h4:where(:not([data-slot])),
330
+ h5:where(:not([data-slot])),
331
+ h6:where(:not([data-slot])) {
332
+ font-size: round(
333
+ calc(var(--type-base) * pow(var(--type-scale), var(--step))),
334
+ var(--type-snap)
335
+ );
336
+ }
337
+
338
+ h1:where(:not([data-slot])) {
339
+ font-size: round(
340
+ calc(var(--type-base) * pow(var(--type-scale), var(--step))),
341
+ var(--type-snap)
342
+ );
343
+ }
315
344
  }
316
345
  }
@@ -3,7 +3,6 @@
3
3
  import { useTypography } from '../../composables/typography'
4
4
  import type { UiColors } from '../../types/colors'
5
5
  import type {
6
- FluidFontSize,
7
6
  FontFamily,
8
7
  FontLeading,
9
8
  FontSize,
@@ -13,6 +12,7 @@
13
12
  FontWidth,
14
13
  TextAlign,
15
14
  TextTransform,
15
+ TypeStep,
16
16
  } from '../../types/typography'
17
17
 
18
18
  const {
@@ -26,7 +26,7 @@
26
26
  transform = 'none',
27
27
  color = 'default',
28
28
  size = undefined,
29
- fluidSize = undefined,
29
+ step = undefined,
30
30
  font = 'mono',
31
31
  } = defineProps<{
32
32
  language?: string
@@ -39,7 +39,7 @@
39
39
  transform?: TextTransform
40
40
  color?: UiColors
41
41
  size?: FontSize
42
- fluidSize?: FluidFontSize
42
+ step?: TypeStep
43
43
  font?: FontFamily
44
44
  }>()
45
45
  const { classes } = useTypography(() => ({
@@ -52,7 +52,7 @@
52
52
  transform,
53
53
  font,
54
54
  ...(size !== undefined && { size }),
55
- ...(fluidSize !== undefined && { fluidSize }),
55
+ ...(step !== undefined && { step }),
56
56
  }))
57
57
  const colorClass = useColor(() => color, 'text')
58
58
  </script>
@@ -0,0 +1,36 @@
1
+ import { mount } from '@vue/test-utils'
2
+ import { describe, expect, it } from 'vite-plus/test'
3
+
4
+ import Headline from './Headline.vue'
5
+
6
+ const render = (props: Record<string, unknown>) =>
7
+ mount(Headline, { props, slots: { default: 'Title' } })
8
+ const classesOf = (props: Record<string, unknown>) => render(props).classes()
9
+ const styleOf = (props: Record<string, unknown>) => render(props).attributes('style') ?? ''
10
+
11
+ describe('Headline step', () => {
12
+ it('steps each level down from the display step by default', () => {
13
+ expect(styleOf({ level: 1 })).toContain('--step: var(--step-display)')
14
+ expect(styleOf({ level: 2 })).toContain('--step: calc(var(--step-display) - 1)')
15
+ expect(styleOf({ level: 6 })).toContain('--step: calc(var(--step-display) - 5)')
16
+ expect(classesOf({ level: 2 }).filter((c) => c.startsWith('text-step-'))).toEqual([])
17
+ })
18
+
19
+ it('drops the default step when an explicit size is given', () => {
20
+ const wrapper = render({ level: 2, size: 'sm' })
21
+ expect(wrapper.classes()).toContain('text-sm')
22
+ expect(wrapper.attributes('style') ?? '').not.toContain('--step')
23
+ })
24
+
25
+ it('leaves line height to the step unless leading is given', () => {
26
+ expect(classesOf({ level: 2 }).filter((c) => c.startsWith('leading-'))).toEqual([])
27
+ expect(classesOf({ level: 2, leading: 'leading-tight' })).toContain('leading-tight')
28
+ })
29
+
30
+ it('lets an explicit step win over size and the default', () => {
31
+ const wrapper = render({ level: 2, size: 'sm', step: 5 })
32
+ expect(wrapper.classes()).toContain('text-step-5')
33
+ expect(wrapper.classes()).not.toContain('text-sm')
34
+ expect(wrapper.attributes('style') ?? '').not.toContain('--step')
35
+ })
36
+ })