clampography 2.0.0-beta.9 → 2.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.
package/src/base.js CHANGED
@@ -1,471 +1,637 @@
1
- /**
2
- * Base typography and layout styles (structure only, no colors).
3
- */
4
- export default {
5
- // Global CSS Variables (Spacing & Fonts)
6
- "@layer base": {
7
- ":root": {
8
- "--spacing-xs": "clamp(0.5rem, 0.375rem + 0.625vw, 0.75rem)",
9
- "--spacing-sm": "clamp(0.75rem, 0.5625rem + 0.9375vw, 1.25rem)",
10
- "--spacing-md": "clamp(1rem, 0.75rem + 1.25vw, 1.5rem)",
11
- "--spacing-lg": "clamp(1.5rem, 1.125rem + 1.875vw, 2.5rem)",
12
- "--spacing-xl": "clamp(2rem, 1.5rem + 2.5vw, 3rem)",
13
- "--scroll-offset": "5rem",
14
- "--font-family-base":
15
- "system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif",
16
- "--font-family-mono":
17
- "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace",
18
- },
19
- },
20
-
21
- // Body Structure
22
- "body": {
23
- "font-family": "var(--font-family-base)",
24
- "font-size": "clamp(1rem, 0.95rem + 0.25vw, 1.125rem)",
25
- "line-height": "1.75",
26
- "text-rendering": "optimizeLegibility",
27
- "-webkit-font-smoothing": "antialiased",
28
- "-moz-osx-font-smoothing": "grayscale",
29
- "text-wrap": "pretty",
30
- },
31
-
32
- // Shared Heading Structure
33
- ":where(h1, h2, h3, h4, h5, h6)": {
34
- "font-weight": "600",
35
- "scroll-margin-top": "var(--scroll-offset)",
36
- },
37
-
38
- // Specific Headings (Values from base.css)
39
- "h1": {
40
- "font-size": "clamp(2.25rem, 1.95rem + 1.5vw, 3rem)",
41
- "line-height": "1.1111",
42
- "font-weight": "800",
43
- "margin-top": "0",
44
- "margin-bottom": "var(--spacing-xl)",
45
- },
46
-
47
- "h2": {
48
- "font-size": "clamp(1.5rem, 1.35rem + 0.75vw, 1.875rem)",
49
- "line-height": "1.3333",
50
- "font-weight": "700",
51
- "margin-top": "var(--spacing-xl)",
52
- "margin-bottom": "var(--spacing-md)",
53
- },
54
-
55
- "h3": {
56
- "font-size": "clamp(1.25rem, 1.15rem + 0.5vw, 1.5rem)",
57
- "line-height": "1.6",
58
- "margin-top": "var(--spacing-lg)",
59
- "margin-bottom": "var(--spacing-sm)",
60
- },
61
-
62
- "h4": {
63
- "font-size": "clamp(1rem, 0.975rem + 0.125vw, 1.125rem)",
64
- "line-height": "1.5",
65
- "margin-top": "var(--spacing-lg)",
66
- "margin-bottom": "var(--spacing-sm)",
67
- },
68
-
69
- "h5": {
70
- "font-size": "1rem",
71
- "line-height": "1.5",
72
- "margin-top": "var(--spacing-md)",
73
- "margin-bottom": "var(--spacing-xs)",
74
- },
75
-
76
- "h6": {
77
- "font-size": "0.875rem",
78
- "line-height": "1.5",
79
- "margin-top": "var(--spacing-md)",
80
- "margin-bottom": "var(--spacing-xs)",
81
- },
82
-
83
- // Links
84
- "a": {
85
- "text-decoration-line": "underline",
86
- "text-decoration-thickness": "0.0625em",
87
- "text-underline-offset": "0.15em",
88
- "cursor": "pointer",
89
- },
90
-
91
- ":where(h1, h2, h3, h4, h5, h6) a": {
92
- "text-decoration": "none",
93
- },
94
-
95
- // Lists & Menus
96
- "menu": {
97
- "list-style": "none",
98
- "margin-bottom": "var(--spacing-md)",
99
- "padding-left": "0",
100
- },
101
-
102
- "menu > li::before": {
103
- "display": "none",
104
- },
105
-
106
- "hgroup": {
107
- "margin-bottom": "var(--spacing-lg)",
108
- },
109
-
110
- "hgroup :where(h1, h2, h3, h4, h5, h6)": {
111
- "margin-bottom": "var(--spacing-xs)",
112
- },
113
-
114
- "hgroup :where(p)": {
115
- "margin-top": "0",
116
- "margin-bottom": "0",
117
- "font-size": "0.875em",
118
- "font-weight": "400",
119
- "line-height": "1.5",
120
- },
121
-
122
- "p": {
123
- "line-height": "1.75",
124
- "margin-bottom": "var(--spacing-md)",
125
- },
126
-
127
- // Inline elements
128
- ":where(strong, b)": {
129
- "font-weight": "700",
130
- },
131
-
132
- ":where(em, i, cite, var)": {
133
- "font-style": "italic",
134
- },
135
-
136
- "dfn": {
137
- "font-style": "normal",
138
- "font-weight": "600",
139
- },
140
-
141
- "small": {
142
- "font-size": "0.875em",
143
- "line-height": "1.5",
144
- },
145
-
146
- ":where(code, kbd, samp)": {
147
- "font-family": "var(--font-family-mono)",
148
- "padding":
149
- "clamp(0.0625rem, 0.05rem + 0.0625vw, 0.125rem) clamp(0.1875rem, 0.15rem + 0.1875vw, 0.7125rem)",
150
- },
151
-
152
- ":where(code:not(pre code), kbd, samp)": {
153
- "padding":
154
- "clamp(0.0625rem, 0.05rem + 0.0625vw, 0.125rem) clamp(0.1875rem, 0.15rem + 0.1875vw, 0.3125rem)",
155
- },
156
-
157
- ":where(sub, sup)": {
158
- "font-size": "0.75em",
159
- "line-height": "0",
160
- "position": "relative",
161
- "vertical-align": "baseline",
162
- },
163
-
164
- "sup": {
165
- "top": "-0.5em",
166
- },
167
-
168
- "sub": {
169
- "bottom": "-0.25em",
170
- },
171
-
172
- "abbr[title]": {
173
- "text-decoration": "underline dotted",
174
- "cursor": "help",
175
- },
176
-
177
- "del": {
178
- "text-decoration": "line-through",
179
- },
180
-
181
- "ins": {
182
- "text-decoration": "underline",
183
- },
184
-
185
- "s": {
186
- "text-decoration": "line-through",
187
- },
188
-
189
- "u": {
190
- "text-decoration": "underline",
191
- },
192
-
193
- "mark": {
194
- "font-style": "normal",
195
- "font-weight": "inherit",
196
- },
197
-
198
- "address": {
199
- "font-style": "normal",
200
- "margin-top": "var(--spacing-md)",
201
- "margin-bottom": "var(--spacing-md)",
202
- },
203
-
204
- "time": {
205
- "font-style": "normal",
206
- "font-variant-numeric": "tabular-nums",
207
- },
208
-
209
- // Blockquotes
210
- "blockquote": {
211
- "margin-top": "var(--spacing-lg)",
212
- "margin-bottom": "var(--spacing-lg)",
213
- "padding-left": "var(--spacing-md)",
214
- },
215
-
216
- "blockquote blockquote": {
217
- "margin-top": "var(--spacing-sm)",
218
- "margin-bottom": "var(--spacing-sm)",
219
- "padding-left": "var(--spacing-sm)",
220
- },
221
-
222
- "q": {
223
- "font-style": "inherit",
224
- },
225
-
226
- // Lists
227
- ":where(ul, ol)": {
228
- "list-style": "none",
229
- "margin-bottom": "var(--spacing-md)",
230
- "padding-left": "clamp(1.5rem, 1.25rem + 1.25vw, 2rem)",
231
- },
232
-
233
- "li": {
234
- "position": "relative",
235
- "padding-left": "0.375em",
236
- },
237
-
238
- "li + li": {
239
- "margin-top": "var(--spacing-xs)",
240
- },
241
-
242
- "li > :where(ul, ol):first-child": {
243
- "margin-top": "var(--spacing-xs)",
244
- },
245
-
246
- "li > :where(ul, ol)": {
247
- "margin-top": "var(--spacing-xs)",
248
- },
249
-
250
- "ul > li::before": {
251
- "content": "''",
252
- "position": "absolute",
253
- "left": "-1.125em",
254
- "top": "calc(0.875em - 0.1875em)",
255
- "width": "0.375em",
256
- "height": "0.375em",
257
- "background-color": "currentColor",
258
- "border-radius": "50%",
259
- },
260
-
261
- "ol": {
262
- "counter-reset": "list-counter",
263
- },
264
-
265
- "ol > li": {
266
- "counter-increment": "list-counter",
267
- },
268
-
269
- "ol > li::before": {
270
- "content": "counter(list-counter) '.'",
271
- "position": "absolute",
272
- "left": "-2.5em",
273
- "width": "1.75em",
274
- "text-align": "right",
275
- "font-weight": "600",
276
- "color": "currentColor",
277
- },
278
-
279
- ":where(ul, ol) :where(ul, ol)": {
280
- "margin-bottom": "0",
281
- "padding-left": "var(--spacing-md)",
282
- },
283
-
284
- "dl": {
285
- "margin-top": "var(--spacing-md)",
286
- "margin-bottom": "var(--spacing-md)",
287
- },
288
-
289
- "dt": {
290
- "font-weight": "600",
291
- "margin-top": "var(--spacing-sm)",
292
- },
293
-
294
- "dt:first-child": {
295
- "margin-top": "0",
296
- },
297
-
298
- "dd": {
299
- "margin-left": "var(--spacing-md)",
300
- },
301
-
302
- "dt + dd": {
303
- "margin-top": "var(--spacing-xs)",
304
- },
305
-
306
- "dd + dd": {
307
- "margin-top": "var(--spacing-xs)",
308
- },
309
-
310
- "dd:last-child": {
311
- "margin-bottom": "0",
312
- },
313
-
314
- // Pre / Code
315
- "pre": {
316
- "margin-top": "var(--spacing-md)",
317
- "margin-bottom": "var(--spacing-md)",
318
- "font-family": "var(--font-family-mono)",
319
- "line-height": "1.6",
320
- "overflow-x": "auto",
321
- },
322
-
323
- "pre code": {
324
- "font-size": "inherit",
325
- "padding": "0",
326
- "background": "none",
327
- "border-radius": "0",
328
- },
329
-
330
- // Fieldset & Form
331
- "fieldset": {
332
- "margin-top": "var(--spacing-md)",
333
- "margin-bottom": "var(--spacing-md)",
334
- "padding": "var(--spacing-md)",
335
- "border": "0",
336
- },
337
-
338
- "legend": {
339
- "font-weight": "600",
340
- "padding": "0 var(--spacing-xs)",
341
- },
342
-
343
- "output": {
344
- "display": "inline-block",
345
- "font-variant-numeric": "tabular-nums",
346
- },
347
-
348
- ":where(meter, progress)": {
349
- "display": "inline-block",
350
- "vertical-align": "middle",
351
- },
352
-
353
- // Media
354
- ":where(img, video)": {
355
- "max-width": "100%",
356
- "height": "auto",
357
- "break-inside": "avoid",
358
- },
359
-
360
- "figure": {
361
- "margin-top": "var(--spacing-lg)",
362
- "margin-bottom": "var(--spacing-lg)",
363
- },
364
-
365
- "figcaption": {
366
- "margin-top": "0.375rem",
367
- "font-size": "0.875em",
368
- "line-height": "1.5",
369
- },
370
-
371
- // Tables
372
- "table": {
373
- "width": "100%",
374
- "margin-top": "var(--spacing-md)",
375
- "margin-bottom": "var(--spacing-md)",
376
- "border-collapse": "collapse",
377
- "font-size": "0.9375em",
378
- "line-height": "1.6",
379
- },
380
-
381
- "caption": {
382
- "margin-bottom": "var(--spacing-xs)",
383
- "font-size": "0.875em",
384
- "font-weight": "600",
385
- "text-align": "left",
386
- },
387
-
388
- "th, td": {
389
- "padding": "var(--spacing-xs) var(--spacing-sm)",
390
- "text-align": "left",
391
- },
392
-
393
- "th": {
394
- "font-weight": "600",
395
- },
396
-
397
- "thead th": {
398
- "vertical-align": "bottom",
399
- },
400
-
401
- "tbody th, tbody td": {
402
- "vertical-align": "top",
403
- },
404
-
405
- "tfoot th, tfoot td": {
406
- "vertical-align": "top",
407
- },
408
-
409
- "tbody + tbody": {
410
- "border-top-width": "2px",
411
- },
412
-
413
- // Horizontal Rule
414
- "hr": {
415
- "margin-top": "var(--spacing-xl)",
416
- "margin-bottom": "var(--spacing-xl)",
417
- "border": "0",
418
- },
419
-
420
- // Interactive
421
- ":where(:focus, :focus-visible)": {
422
- "outline-offset": "2px",
423
- },
424
-
425
- "details": {
426
- "margin-top": "var(--spacing-md)",
427
- "margin-bottom": "var(--spacing-md)",
428
- },
429
-
430
- "summary": {
431
- "cursor": "pointer",
432
- "font-weight": "600",
433
- },
434
-
435
- "details[open] > summary": {
436
- "margin-bottom": "var(--spacing-xs)",
437
- },
438
-
439
- "dialog": {
440
- "font-size": "inherit",
441
- "line-height": "inherit",
442
- },
443
-
444
- // Utilities - Margin Cleanup
445
- ":where(h1, h2, h3, h4, h5, h6, p, ul, ol, dl, blockquote, figure, table, pre):first-child":
446
- {
1
+ export default (options = {}) => {
2
+ const root = options.root || ":root";
3
+
4
+ // Helper to scope selectors safely (ignoring commas inside parentheses)
5
+ const scope = (selector) => {
6
+ const parts = [];
7
+ let current = "";
8
+ let depth = 0;
9
+
10
+ for (let i = 0; i < selector.length; i++) {
11
+ const char = selector[i];
12
+ if (char === "(") depth++;
13
+ if (char === ")") depth--;
14
+
15
+ if (char === "," && depth === 0) {
16
+ parts.push(current.trim());
17
+ current = "";
18
+ } else {
19
+ current += char;
20
+ }
21
+ }
22
+ parts.push(current.trim());
23
+
24
+ const typographyPrefix = options.typography && options.typography !== "global" ? ` ${options.typography}` : "";
25
+
26
+ return parts
27
+ .filter(Boolean) // Remove empty strings
28
+ .map((part) => {
29
+ if (part === ":root" || part === "body") return root;
30
+
31
+ // Apply typography scope isolation if configured
32
+ if (typographyPrefix) {
33
+ return `${root}${typographyPrefix} ${part}`;
34
+ }
35
+
36
+ // Avoid double spacing
37
+ return `${root} ${part}`;
38
+ })
39
+ .join(", ");
40
+ };
41
+
42
+ // Fluid math engine: Generates mathematically perfect clamp() strings
43
+ // dynamically based on the configured min and max screen sizes.
44
+ const minScreenRem = (options.fluidMin || 320) / 16;
45
+ const maxScreenRem = (options.fluidMax || 1280) / 16;
46
+
47
+ // scaleMode: 'viewport' (default) uses vw — scales relative to the browser window.
48
+ // scaleMode: 'container' uses cqi scales relative to the nearest @container ancestor.
49
+ // When no @container is defined, cqi falls back to the <html> element (same as vw).
50
+ const fluidUnit = (options.scaleMode || options["scale-mode"] || "viewport") === "container" ? "cqi" : "vw";
51
+
52
+ const makeFluid = (minRem, maxRem) => {
53
+ // If min and max are the same, or if we have invalid screens, just return the static value
54
+ if (minRem === maxRem || minScreenRem >= maxScreenRem) return `${minRem}rem`;
55
+
56
+ const slope = (maxRem - minRem) / (maxScreenRem - minScreenRem);
57
+ const intersection = minRem - slope * minScreenRem;
58
+
59
+ const format = (num) => parseFloat(num.toFixed(4));
60
+
61
+ return `clamp(${minRem}rem, ${format(intersection)}rem + ${format(slope * 100)}${fluidUnit}, ${maxRem}rem)`;
62
+ };
63
+
64
+ return {
65
+ // ROOT CONFIGURATION (CSS variables)
66
+ // Uses :where() for zero specificity so user overrides always win regardless of layer/source order
67
+ [`:where(${root})`]: {
68
+ // FLUID SPACING SYSTEM
69
+ "--clampography-spacing-xs": makeFluid(0.25, 0.75),
70
+ "--clampography-spacing-sm": makeFluid(0.375, 1.25),
71
+ "--clampography-spacing-md": makeFluid(0.5, 1.5),
72
+ "--clampography-spacing-lg": makeFluid(0.75, 2.5),
73
+ "--clampography-spacing-xl": makeFluid(1, 3),
74
+ "--clampography-list-indent": makeFluid(1.5, 2),
75
+ "--clampography-scroll-offset": "5rem",
76
+ "--clampography-font-base":
77
+ "Inter, system-ui, -apple-system, 'Segoe UI Variable Display', 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif",
78
+ "--clampography-font-mono":
79
+ "ui-monospace, 'Cascadia Code', 'Cascadia Mono', 'Segoe UI Mono', 'Ubuntu Mono', SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace",
80
+
81
+ // FLUID TYPOGRAPHY ENGINE — Viewport Bounds
82
+ // Unitless rem values derived from plugin options: fluid-min and fluid-max.
83
+ // e.g. 320px → 20rem, 1280px → 80rem
84
+ "--clampography-fluid-min": String(minScreenRem),
85
+ "--clampography-fluid-max": String(maxScreenRem),
86
+
87
+ // HEADINGS FLUID TYPOGRAPHY
88
+ // Each heading exposes --min and --max as unitless rem values.
89
+ // Override these in :root to change font-size bounds without rewriting the full clamp().
90
+ //
91
+ // Example reduce H1 max size on large screens:
92
+ // :root { --clampography-h1-max: 3; }
93
+ //
94
+ // Example — increase H2 minimum size on mobile:
95
+ // :root { --clampography-h2-min: 1.5; }
96
+ //
97
+ // --clampography-h*-slope and --clampography-h*-base are computed automatically via CSS calc().
98
+ // --clampography-h*-size is the final fluid clamp() value used by the heading element.
99
+
100
+ // H1
101
+ "--clampography-h1-min": "1.875",
102
+ "--clampography-h1-max": "4",
103
+ "--clampography-h1-slope": "calc((var(--clampography-h1-max) - var(--clampography-h1-min)) / (var(--clampography-fluid-max) - var(--clampography-fluid-min)))",
104
+ "--clampography-h1-base": "calc(var(--clampography-h1-min) - var(--clampography-h1-slope) * var(--clampography-fluid-min))",
105
+ [`--clampography-h1-size`]: `clamp(calc(var(--clampography-h1-min) * 1rem), calc(var(--clampography-h1-base) * 1rem + var(--clampography-h1-slope) * 100${fluidUnit}), calc(var(--clampography-h1-max) * 1rem))`,
106
+
107
+ // H2
108
+ "--clampography-h2-min": "1.25",
109
+ "--clampography-h2-max": "3",
110
+ "--clampography-h2-slope": "calc((var(--clampography-h2-max) - var(--clampography-h2-min)) / (var(--clampography-fluid-max) - var(--clampography-fluid-min)))",
111
+ "--clampography-h2-base": "calc(var(--clampography-h2-min) - var(--clampography-h2-slope) * var(--clampography-fluid-min))",
112
+ [`--clampography-h2-size`]: `clamp(calc(var(--clampography-h2-min) * 1rem), calc(var(--clampography-h2-base) * 1rem + var(--clampography-h2-slope) * 100${fluidUnit}), calc(var(--clampography-h2-max) * 1rem))`,
113
+
114
+ // H3
115
+ "--clampography-h3-min": "1.125",
116
+ "--clampography-h3-max": "2.25",
117
+ "--clampography-h3-slope": "calc((var(--clampography-h3-max) - var(--clampography-h3-min)) / (var(--clampography-fluid-max) - var(--clampography-fluid-min)))",
118
+ "--clampography-h3-base": "calc(var(--clampography-h3-min) - var(--clampography-h3-slope) * var(--clampography-fluid-min))",
119
+ [`--clampography-h3-size`]: `clamp(calc(var(--clampography-h3-min) * 1rem), calc(var(--clampography-h3-base) * 1rem + var(--clampography-h3-slope) * 100${fluidUnit}), calc(var(--clampography-h3-max) * 1rem))`,
120
+
121
+ // H4
122
+ "--clampography-h4-min": "1",
123
+ "--clampography-h4-max": "1.5",
124
+ "--clampography-h4-slope": "calc((var(--clampography-h4-max) - var(--clampography-h4-min)) / (var(--clampography-fluid-max) - var(--clampography-fluid-min)))",
125
+ "--clampography-h4-base": "calc(var(--clampography-h4-min) - var(--clampography-h4-slope) * var(--clampography-fluid-min))",
126
+ [`--clampography-h4-size`]: `clamp(calc(var(--clampography-h4-min) * 1rem), calc(var(--clampography-h4-base) * 1rem + var(--clampography-h4-slope) * 100${fluidUnit}), calc(var(--clampography-h4-max) * 1rem))`,
127
+
128
+ // H5 — static by default (min === max); set --clampography-h5-max to a different value to make it fluid
129
+ "--clampography-h5-min": "1",
130
+ "--clampography-h5-max": "1",
131
+ "--clampography-h5-slope": "calc((var(--clampography-h5-max) - var(--clampography-h5-min)) / (var(--clampography-fluid-max) - var(--clampography-fluid-min)))",
132
+ "--clampography-h5-base": "calc(var(--clampography-h5-min) - var(--clampography-h5-slope) * var(--clampography-fluid-min))",
133
+ [`--clampography-h5-size`]: `clamp(calc(var(--clampography-h5-min) * 1rem), calc(var(--clampography-h5-base) * 1rem + var(--clampography-h5-slope) * 100${fluidUnit}), calc(var(--clampography-h5-max) * 1rem))`,
134
+
135
+ // H6 — static by default (min === max)
136
+ "--clampography-h6-min": "0.875",
137
+ "--clampography-h6-max": "0.875",
138
+ "--clampography-h6-slope": "calc((var(--clampography-h6-max) - var(--clampography-h6-min)) / (var(--clampography-fluid-max) - var(--clampography-fluid-min)))",
139
+ "--clampography-h6-base": "calc(var(--clampography-h6-min) - var(--clampography-h6-slope) * var(--clampography-fluid-min))",
140
+ [`--clampography-h6-size`]: `clamp(calc(var(--clampography-h6-min) * 1rem), calc(var(--clampography-h6-base) * 1rem + var(--clampography-h6-slope) * 100${fluidUnit}), calc(var(--clampography-h6-max) * 1rem))`,
141
+
142
+ // Global heading scale multiplier (default: 1 = no scaling).
143
+ // Override in :root to proportionally scale all headings at once.
144
+ // Example: :root { --clampography-heading-scale: 0.85; }
145
+ "--clampography-heading-scale": "1",
146
+
147
+ // Individual heading scales (default to global scale)
148
+ "--clampography-h1-scale": "var(--clampography-heading-scale)",
149
+ "--clampography-h2-scale": "var(--clampography-heading-scale)",
150
+ "--clampography-h3-scale": "var(--clampography-heading-scale)",
151
+ "--clampography-h4-scale": "var(--clampography-heading-scale)",
152
+ "--clampography-h5-scale": "var(--clampography-heading-scale)",
153
+ "--clampography-h6-scale": "var(--clampography-heading-scale)",
154
+ },
155
+
156
+ // BODY STYLES (Typography baseline)
157
+ // Note: font-family is intentionally NOT set here.
158
+ // It is applied in extra.js with user-font priority via --font-sans.
159
+ [(() => {
160
+ const typographyPrefix = options.typography && options.typography !== "global" ? ` ${options.typography}` : "";
161
+ const bodyBase = root === ":root" ? "body" : root;
162
+ return typographyPrefix ? `${bodyBase}${typographyPrefix}` : bodyBase;
163
+ })()]: {
164
+ "font-size": makeFluid(0.875, 1.125),
165
+ "line-height": "1.75",
166
+ "text-rendering": "optimizeLegibility",
167
+ "-webkit-font-smoothing": "antialiased",
168
+ "-moz-osx-font-smoothing": "grayscale",
169
+ "text-wrap": "pretty",
170
+ },
171
+
172
+ // HEADINGS (H1-H6)
173
+ [scope(":where(h1, h2, h3, h4, h5, h6)")]: {
174
+ "font-weight": "600",
175
+ "scroll-margin-top": "var(--clampography-scroll-offset)",
176
+ },
177
+
178
+ [scope("h1")]: {
179
+ "font-size": "calc(var(--clampography-h1-size) * var(--clampography-h1-scale))",
180
+ "line-height": "1.1111",
181
+ "font-weight": "800",
182
+ "margin-top": "0",
183
+ "margin-bottom": "var(--clampography-spacing-xl)",
184
+ },
185
+
186
+ [scope("h2")]: {
187
+ "font-size": "calc(var(--clampography-h2-size) * var(--clampography-h2-scale))",
188
+ "line-height": "1.3333",
189
+ "font-weight": "700",
190
+ "margin-top": "var(--clampography-spacing-xl)",
191
+ "margin-bottom": "var(--clampography-spacing-md)",
192
+ },
193
+
194
+ [scope("h3")]: {
195
+ "font-size": "calc(var(--clampography-h3-size) * var(--clampography-h3-scale))",
196
+ "line-height": "1.5",
197
+ "margin-top": "var(--clampography-spacing-lg)",
198
+ "margin-bottom": "var(--clampography-spacing-sm)",
199
+ },
200
+
201
+ [scope("h4")]: {
202
+ "font-size": "calc(var(--clampography-h4-size) * var(--clampography-h4-scale))",
203
+ "line-height": "1.5",
204
+ "margin-top": "var(--clampography-spacing-lg)",
205
+ "margin-bottom": "var(--clampography-spacing-sm)",
206
+ },
207
+
208
+ [scope("h5")]: {
209
+ "font-size": "calc(var(--clampography-h5-size) * var(--clampography-h5-scale))",
210
+ "line-height": "1.5",
211
+ "margin-top": "var(--clampography-spacing-md)",
212
+ "margin-bottom": "var(--clampography-spacing-xs)",
213
+ },
214
+
215
+ [scope("h6")]: {
216
+ "font-size": "calc(var(--clampography-h6-size) * var(--clampography-h6-scale))",
217
+ "line-height": "1.5",
218
+ "margin-top": "var(--clampography-spacing-md)",
219
+ "margin-bottom": "var(--clampography-spacing-xs)",
220
+ },
221
+
222
+ [scope(":is(h1, h2, h3, h4, h5, h6):first-child")]: {
447
223
  "margin-top": "0",
448
224
  },
449
225
 
450
- ":where(p, ul, ol, dl, blockquote, figure, table, pre):last-child": {
451
- "margin-bottom": "0",
452
- },
226
+ // LINKS
227
+ [scope("a")]: {
228
+ "text-decoration-line": "underline",
229
+ cursor: "pointer",
230
+ },
231
+
232
+ [scope(":where(h1, h2, h3, h4, h5, h6) a")]: {
233
+ "text-decoration": "none",
234
+ },
235
+
236
+ // MENU
237
+ [scope("menu")]: {
238
+ "list-style": "none",
239
+ "margin-bottom": "var(--clampography-spacing-md)",
240
+ "padding-inline-start": "0",
241
+ },
242
+
243
+ [scope("menu > li::before")]: {
244
+ display: "none",
245
+ },
246
+
247
+ // HGROUP
248
+ [scope("hgroup")]: {
249
+ "margin-bottom": "var(--clampography-spacing-lg)",
250
+ },
251
+
252
+ [scope("hgroup :where(h1, h2, h3, h4, h5, h6)")]: {
253
+ "margin-bottom": "var(--clampography-spacing-xs)",
254
+ },
453
255
 
454
- // Print Styles
455
- "@media print": {
456
- "body": {
457
- "font-size": "12pt",
256
+ [scope("hgroup :where(p)")]: {
257
+ "margin-top": "0",
258
+ "margin-bottom": "0",
259
+ "font-size": "0.875em",
260
+ "font-weight": "400",
458
261
  "line-height": "1.5",
459
- "color": "#000",
460
262
  },
461
- ":where(h1, h2, h3, h4, h5, h6)": {
462
- "break-after": "avoid",
263
+
264
+ // TEXT CONTENT
265
+ [scope("p")]: {
266
+ "line-height": "1.75",
267
+ "margin-bottom": "var(--clampography-spacing-md)",
268
+ },
269
+
270
+ [scope(":where(strong, b)")]: {
271
+ "font-weight": "700",
463
272
  },
464
- ":where(img, figure, pre, table)": {
465
- "break-inside": "avoid",
273
+
274
+ [scope(":where(em, i, cite, var)")]: {
275
+ "font-style": "italic",
276
+ },
277
+
278
+ [scope("dfn")]: {
279
+ "font-style": "italic",
280
+ "font-weight": "600",
281
+ },
282
+
283
+ [scope("small")]: {
284
+ "font-size": "0.875em",
285
+ "line-height": "1.5",
466
286
  },
467
- "a": {
287
+
288
+ [scope(":where(code, kbd, samp)")]: {
289
+ "font-family": "var(--clampography-font-mono)",
290
+ "font-size": "0.875em",
291
+ "-webkit-font-smoothing": "auto",
292
+ "-moz-osx-font-smoothing": "auto",
293
+ },
294
+
295
+ [scope("kbd")]: {
296
+ "font-weight": "600",
297
+ },
298
+
299
+ [scope("data")]: {
300
+ "font-variant-numeric": "tabular-nums",
301
+ },
302
+
303
+ [scope(":where(sub, sup)")]: {
304
+ "font-size": "0.75em",
305
+ "line-height": "0",
306
+ position: "relative",
307
+ "vertical-align": "baseline",
308
+ },
309
+
310
+ [scope("sup")]: {
311
+ top: "-0.5em",
312
+ },
313
+
314
+ [scope("sub")]: {
315
+ bottom: "-0.25em",
316
+ },
317
+
318
+ [scope("abbr[title]")]: {
319
+ "text-decoration": "underline dotted",
320
+ "text-underline-offset": "4px",
321
+ cursor: "help",
322
+ },
323
+
324
+ [scope("del")]: {
325
+ "text-decoration": "line-through",
326
+ },
327
+
328
+ [scope("ins")]: {
329
+ "text-decoration": "underline",
330
+ },
331
+
332
+ [scope("s")]: {
333
+ "text-decoration": "line-through",
334
+ },
335
+
336
+ [scope("u")]: {
468
337
  "text-decoration": "underline",
469
338
  },
470
- },
339
+
340
+ [scope("mark")]: {
341
+ "font-style": "normal",
342
+ "font-weight": "inherit",
343
+ },
344
+
345
+ [scope("address")]: {
346
+ "font-style": "italic",
347
+ "margin-top": "var(--clampography-spacing-md)",
348
+ "margin-bottom": "var(--clampography-spacing-md)",
349
+ },
350
+
351
+ [scope("time")]: {
352
+ "font-style": "normal",
353
+ "font-variant-numeric": "tabular-nums",
354
+ },
355
+
356
+ // BLOCKQUOTES
357
+ [scope("blockquote")]: {
358
+ "margin-top": "var(--clampography-spacing-lg)",
359
+ "margin-bottom": "var(--clampography-spacing-lg)",
360
+ "padding-inline-start": "var(--clampography-spacing-md)",
361
+ },
362
+
363
+ [scope("blockquote blockquote")]: {
364
+ "margin-top": "var(--clampography-spacing-sm)",
365
+ "margin-bottom": "var(--clampography-spacing-sm)",
366
+ "padding-inline-start": "var(--clampography-spacing-sm)",
367
+ },
368
+
369
+ [scope("q")]: {
370
+ "font-style": "inherit",
371
+ },
372
+
373
+ // LISTS
374
+ [scope(":where(ul, ol)")]: {
375
+ "list-style": "none",
376
+ "margin-bottom": "var(--clampography-spacing-md)",
377
+ "padding-inline-start": "var(--clampography-list-indent)",
378
+ },
379
+
380
+ [scope("li")]: {
381
+ position: "relative",
382
+ },
383
+
384
+ [scope("li + li")]: {
385
+ "margin-top": "var(--clampography-spacing-xs)",
386
+ },
387
+
388
+ // Collapse margins for text-like block elements inside li
389
+ // to prevent them from creating extra gaps around nested lists.
390
+ [scope("li > :where(p, dl, figure, table, pre)")]: {
391
+ "margin-top": "0",
392
+ "margin-bottom": "0",
393
+ },
394
+
395
+ [scope("li > blockquote")]: {
396
+ "margin-top": "var(--clampography-spacing-sm)",
397
+ "margin-bottom": "var(--clampography-spacing-sm)",
398
+ },
399
+
400
+ // Nested lists: top gap matches sibling spacing (--clampography-spacing-xs).
401
+ // No bottom margin — the next li already gets margin-top from li+li.
402
+ [scope("li > :where(ul, ol)")]: {
403
+ "margin-top": "var(--clampography-spacing-xs)",
404
+ "margin-bottom": "0",
405
+ },
406
+
407
+ [scope("ul > li::before")]: {
408
+ content: "''",
409
+ position: "absolute",
410
+ "inset-inline-end": "100%",
411
+ "margin-inline-end": "0.75em",
412
+ top: "0.65em",
413
+ width: "0.375em",
414
+ height: "0.375em",
415
+ "background-color": "currentColor",
416
+ "border-radius": "50%",
417
+ },
418
+
419
+ [scope("ol")]: {
420
+ "counter-reset": "list-counter",
421
+ },
422
+
423
+ [scope("ol > li")]: {
424
+ "counter-increment": "list-counter",
425
+ },
426
+
427
+ [scope("ol > li::before")]: {
428
+ content: "counter(list-counter) '.'",
429
+ position: "absolute",
430
+ "inset-inline-end": "100%",
431
+ "margin-inline-end": "0.5em",
432
+ "font-weight": "600",
433
+ "font-variant-numeric": "tabular-nums",
434
+ "text-align": "end",
435
+ color: "currentColor",
436
+ },
437
+
438
+ // DEFINITION LISTS
439
+ [scope("dl")]: {
440
+ "margin-top": "var(--clampography-spacing-md)",
441
+ "margin-bottom": "var(--clampography-spacing-md)",
442
+ },
443
+
444
+ [scope("dt")]: {
445
+ "font-weight": "600",
446
+ "margin-top": "var(--clampography-spacing-sm)",
447
+ },
448
+
449
+ [scope("dt:first-child")]: {
450
+ "margin-top": "0",
451
+ },
452
+
453
+ [scope("dd")]: {
454
+ "margin-inline-start": "var(--clampography-spacing-md)",
455
+ },
456
+
457
+ [scope("dt + dd")]: {
458
+ "margin-top": "var(--clampography-spacing-xs)",
459
+ },
460
+
461
+ [scope("dd + dd")]: {
462
+ "margin-top": "var(--clampography-spacing-xs)",
463
+ },
464
+
465
+ [scope("dd:last-child")]: {
466
+ "margin-bottom": "0",
467
+ },
468
+
469
+ // CODE BLOCKS
470
+ [scope("pre")]: {
471
+ "margin-top": "var(--clampography-spacing-md)",
472
+ "margin-bottom": "var(--clampography-spacing-md)",
473
+ "font-family": "var(--clampography-font-mono)",
474
+ "line-height": "1.6",
475
+ "overflow-x": "auto",
476
+ "-webkit-font-smoothing": "auto",
477
+ "-moz-osx-font-smoothing": "auto",
478
+ },
479
+
480
+ [scope("pre code")]: {
481
+ "font-size": "inherit",
482
+ padding: "0",
483
+ background: "none",
484
+ "border-radius": "0",
485
+ },
486
+
487
+ // FORMS
488
+ // Structural resets — inherit typography from root, no visual styling here.
489
+ // Visual styling (colors, borders, padding) is handled by forms.js.
490
+ [scope("input, button, textarea, select, optgroup")]: {
491
+ "font-family": "inherit",
492
+ "font-size": "100%",
493
+ "line-height": "inherit",
494
+ },
495
+
496
+ [scope("textarea")]: {
497
+ "line-height": "1.5",
498
+ },
499
+
500
+ [scope("button, [type='button'], [type='reset'], [type='submit']")]: {
501
+ cursor: "pointer",
502
+ },
503
+
504
+ [scope("fieldset")]: {
505
+ "margin-top": "var(--clampography-spacing-md)",
506
+ "margin-bottom": "var(--clampography-spacing-md)",
507
+ padding: "var(--clampography-spacing-sm)",
508
+ },
509
+
510
+ [scope("legend")]: {
511
+ "font-weight": "600",
512
+ padding: "0 var(--clampography-spacing-xs)",
513
+ },
514
+
515
+ [scope("label")]: {
516
+ display: "inline-block",
517
+ "font-weight": "600",
518
+ "margin-bottom": "var(--clampography-spacing-xs)",
519
+ },
520
+
521
+ [scope("output")]: {
522
+ display: "inline-block",
523
+ "font-variant-numeric": "tabular-nums",
524
+ },
525
+
526
+ [scope(":where(meter, progress)")]: {
527
+ display: "inline-block",
528
+ "vertical-align": "middle",
529
+ },
530
+
531
+ // MEDIA
532
+ [scope(":where(img, video, canvas, audio, iframe, svg)")]: {
533
+ "max-width": "100%",
534
+ height: "auto",
535
+ "vertical-align": "middle",
536
+ },
537
+
538
+ [scope("figure")]: {
539
+ "margin-top": "var(--clampography-spacing-lg)",
540
+ "margin-bottom": "var(--clampography-spacing-lg)",
541
+ },
542
+
543
+ [scope("figcaption")]: {
544
+ "margin-top": "0.375rem",
545
+ "font-size": "0.875em",
546
+ "line-height": "1.5",
547
+ },
548
+
549
+ // TABLES
550
+ [scope("table")]: {
551
+ width: "100%",
552
+ "margin-top": "var(--clampography-spacing-md)",
553
+ "margin-bottom": "var(--clampography-spacing-md)",
554
+ "border-collapse": "collapse",
555
+ "font-size": "1em",
556
+ "line-height": "1.6",
557
+ },
558
+
559
+ [scope("caption")]: {
560
+ "margin-bottom": "var(--clampography-spacing-xs)",
561
+ "font-size": "0.875em",
562
+ "font-weight": "600",
563
+ "text-align": "start",
564
+ },
565
+
566
+ [scope("th, td")]: {
567
+ padding: "var(--clampography-spacing-xs) var(--clampography-spacing-sm)",
568
+ "text-align": "start",
569
+ },
570
+
571
+ [scope("th")]: {
572
+ "font-weight": "600",
573
+ },
574
+
575
+ [scope("thead th")]: {
576
+ "vertical-align": "bottom",
577
+ },
578
+
579
+ [scope("tbody th, tbody td")]: {
580
+ "vertical-align": "top",
581
+ },
582
+
583
+ [scope("tfoot th, tfoot td")]: {
584
+ "vertical-align": "top",
585
+ },
586
+
587
+ [scope("tbody + tbody")]: {
588
+ "border-top-width": "2px",
589
+ },
590
+
591
+ // SEPARATORS
592
+ [scope("hr")]: {
593
+ "margin-top": "var(--clampography-spacing-xl)",
594
+ "margin-bottom": "var(--clampography-spacing-xl)",
595
+ border: "0",
596
+ "border-top": "1px solid",
597
+ },
598
+
599
+ // INTERACTIVE ELEMENTS
600
+ [scope(":where(:focus, :focus-visible)")]: {
601
+ "outline-offset": "2px",
602
+ },
603
+
604
+ [scope("details")]: {
605
+ "margin-top": "var(--clampography-spacing-md)",
606
+ "margin-bottom": "var(--clampography-spacing-md)",
607
+ },
608
+
609
+ [scope("summary")]: {
610
+ cursor: "pointer",
611
+ "font-weight": "600",
612
+ },
613
+
614
+ [scope("details[open] > summary")]: {
615
+ "margin-bottom": "var(--clampography-spacing-xs)",
616
+ },
617
+
618
+ [scope("dialog")]: {
619
+ "font-size": "inherit",
620
+ "line-height": "inherit",
621
+ },
622
+
623
+ // UTILITIES
624
+ [
625
+ scope(
626
+ ":where(h1, h2, h3, h4, h5, h6, p, ul:not(li > ul, li > ol), ol:not(li > ul, li > ol), dl, blockquote, figure, table, pre):first-child",
627
+ )
628
+ ]: {
629
+ "margin-top": "0",
630
+ },
631
+
632
+ [scope(":where(p, ul, ol, dl, blockquote, figure, table, pre):last-child")]:
633
+ {
634
+ "margin-bottom": "0",
635
+ },
636
+ };
471
637
  };