@juwel-development/design-system 1.1.0 → 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.
Files changed (61) hide show
  1. package/dist/design-system.js +541 -36
  2. package/dist/index.css +1 -1
  3. package/dist/types/Display/Brandmark/Brandmark.d.ts +50 -0
  4. package/dist/types/Display/Checklist/Checklist.d.ts +26 -0
  5. package/dist/types/Display/DefinitionList/DefinitionList.d.ts +41 -0
  6. package/dist/types/Display/Figure/Figure.d.ts +52 -0
  7. package/dist/types/Display/Rail/Rail.d.ts +54 -0
  8. package/dist/types/Display/Table/Table.d.ts +61 -0
  9. package/dist/types/Display/Typography/Eyebrow/Eyebrow.d.ts +26 -0
  10. package/dist/types/Display/Typography/H1/H1.d.ts +27 -0
  11. package/dist/types/Display/Typography/H2/H2.d.ts +24 -0
  12. package/dist/types/Display/Typography/H3/H3.d.ts +23 -0
  13. package/dist/types/Display/Typography/H4/H4.d.ts +22 -0
  14. package/dist/types/Display/Typography/H5/H5.d.ts +23 -0
  15. package/dist/types/Display/Typography/H6/H6.d.ts +23 -0
  16. package/dist/types/Display/Typography/P/P.d.ts +25 -0
  17. package/dist/types/Display/Typography/Prose/Prose.d.ts +42 -0
  18. package/dist/types/Interaction/Button/Button.d.ts +7 -3
  19. package/dist/types/Interaction/Input/Input.d.ts +29 -0
  20. package/dist/types/Interaction/Link/Link.d.ts +27 -0
  21. package/dist/types/Interaction/TextArea/TextArea.d.ts +25 -0
  22. package/dist/types/Layout/Footer/Footer.d.ts +34 -0
  23. package/dist/types/Layout/Form/Form.d.ts +31 -0
  24. package/dist/types/Layout/Header/Header.d.ts +41 -0
  25. package/dist/types/Layout/Hero/Hero.d.ts +46 -0
  26. package/dist/types/Layout/PageHead/PageHead.d.ts +36 -0
  27. package/dist/types/Layout/Section/Section.d.ts +38 -0
  28. package/dist/types/Theme/Palette.d.ts +25 -6
  29. package/dist/types/index.d.ts +25 -1
  30. package/package.json +1 -1
  31. package/src/Display/.gitkeep +0 -0
  32. package/src/Display/Brandmark/Brandmark.tsx +97 -0
  33. package/src/Display/Checklist/Checklist.tsx +75 -0
  34. package/src/Display/DefinitionList/DefinitionList.tsx +90 -0
  35. package/src/Display/Figure/Figure.tsx +116 -0
  36. package/src/Display/Rail/Rail.tsx +108 -0
  37. package/src/Display/Table/Table.tsx +191 -0
  38. package/src/Display/Typography/Eyebrow/Eyebrow.tsx +46 -0
  39. package/src/Display/Typography/H1/H1.tsx +46 -0
  40. package/src/Display/Typography/H2/H2.tsx +43 -0
  41. package/src/Display/Typography/H3/H3.tsx +41 -0
  42. package/src/Display/Typography/H4/H4.tsx +40 -0
  43. package/src/Display/Typography/H5/H5.tsx +41 -0
  44. package/src/Display/Typography/H6/H6.tsx +41 -0
  45. package/src/Display/Typography/P/P.tsx +38 -0
  46. package/src/Display/Typography/Prose/Prose.tsx +91 -0
  47. package/src/Interaction/Button/Button.tsx +15 -10
  48. package/src/Interaction/Input/Input.tsx +103 -0
  49. package/src/Interaction/Link/Link.tsx +70 -0
  50. package/src/Interaction/TextArea/TextArea.tsx +93 -0
  51. package/src/Layout/.gitkeep +0 -0
  52. package/src/Layout/Footer/Footer.tsx +60 -0
  53. package/src/Layout/Form/Form.tsx +102 -0
  54. package/src/Layout/Header/Header.tsx +84 -0
  55. package/src/Layout/Hero/Hero.tsx +73 -0
  56. package/src/Layout/PageHead/PageHead.tsx +79 -0
  57. package/src/Layout/Section/Section.tsx +79 -0
  58. package/src/Theme/Palette.ts +38 -12
  59. package/src/Theme/renderTokens.ts +179 -2
  60. package/src/index.ts +25 -1
  61. package/src/tokens.css +113 -9
@@ -23,6 +23,144 @@ const MOTION = `:root {
23
23
  }
24
24
  }`;
25
25
 
26
+ /* The ring's width and offset are not colours: like motion they live in :root only, never in
27
+ @theme inline, so a consumer can re-point them. Constraint: both > 0 - a zero width is no
28
+ indicator, a zero offset drops the ring onto the fill where the 3:1-against-surface rule fails.
29
+ See docs/adr/0002-focus-ring-token-contract.md. */
30
+ const FOCUS_RING = `:root {
31
+ --focus-ring-width: 3px;
32
+ --focus-ring-offset: 2px;
33
+ }`;
34
+
35
+ /* Radius is not a colour: like motion and the focus-ring dimensions it lives in :root only, never
36
+ in @theme inline. One token names the corner every control reads; 0.5rem is exactly what
37
+ rounded-lg resolved to, so nothing changes visually while it keeps tracking the root font size.
38
+ No structure radius, no value constraint - both deliberate; see docs/adr/0003-radius-token-contract.md. */
39
+ const RADIUS = `:root {
40
+ --radius-control: 0.5rem;
41
+ }`;
42
+
43
+ /* The library's underline is not a colour: like the focus-ring dimensions it lives in :root only,
44
+ never @theme inline. Not prose's alone - every Link treatment that draws a line draws it from these.
45
+ Constraint: --underline-thickness-hover > --underline-thickness (the thicken on hover is the only
46
+ cue telling a prose link apart, never hue) and both > 0. The pair names a rest state and a thickened
47
+ one, so only prose - whose line is always on screen - reads the -hover token; a line that merely
48
+ appears on hover takes --underline-thickness. Either way the change is instant, decoration being off
49
+ the transition allowlist. See docs/adr/0006-link-treatment-contract.md. */
50
+ const UNDERLINE = `:root {
51
+ --underline-offset: 0.18em;
52
+ --underline-thickness: 1px;
53
+ --underline-thickness-hover: 2px;
54
+ }`;
55
+
56
+ /* Typography is not a colour and, unlike the blocks above, not :root-only: Tailwind 4's --font-*,
57
+ --text-*, --leading-* and --tracking-* are theme namespaces, so its own @theme block generates the
58
+ font-primary/text-display/leading-body/tracking-label utilities a sealed recipe reaches. No
59
+ Tailwind built-in is re-pointed; only new role names. See docs/adr/0004-typography-token-contract.md. */
60
+ const TYPOGRAPHY = `@theme {
61
+ /* Both faces default to inherit, so the library ships no @font-face and no face and a one-face
62
+ consumer renders as today. --font-primary is carried by content, --font-secondary by labels; a
63
+ component sets font-variant-numeric: tabular-nums only on a --font-primary carrying tnum and
64
+ font-variant-caps: small-caps only on a --font-secondary carrying smcp - both fail silently otherwise. */
65
+ --font-primary: inherit;
66
+ --font-secondary: inherit;
67
+
68
+ /* Type roles, not a scale: one role per job, so a component asks for what its text is, never a rung.
69
+ display > title > subtitle are the three heading steps H1-H3 bind to; H4-H6 share body and split
70
+ by weight, so there is no fourth role. display is the hero and must out-scale every subpage head. */
71
+ --text-display: clamp(3rem, 7vw, 6rem);
72
+ --leading-display: 1.05;
73
+ --text-title: clamp(2.25rem, 5vw, 4.25rem);
74
+ --leading-title: 1.1;
75
+ --text-subtitle: clamp(1.5rem, 3vw, 2.25rem);
76
+ --leading-subtitle: 1.2;
77
+
78
+ /* The reading block's opening paragraph (Prose #21): sized above body and below subtitle, led
79
+ tighter than body's 1.6 but not as tight as a heading's 1.2 - it is running text, not a head, so
80
+ it keeps running-text leading. A role, not a rung: one lede per block, nothing to choose. Reusing
81
+ subtitle was rejected in ADR 0004 - it welds the lede's size to H3's and its leading to a head's. */
82
+ --text-lede: 1.25rem; /* 20px */
83
+ --leading-lede: 1.4;
84
+ --text-body: 1.0625rem; /* 17px */
85
+ --leading-body: 1.6;
86
+ --text-small: 0.9375rem; /* 15px floor: below it tabular figures stop comparing column to column */
87
+ --text-label: 0.8125rem; /* 13px floor: below it the tracking reads as damage, not a device */
88
+
89
+ /* Two quantities on one property that must not collapse: --tracking-label is a fixed letter-spaced
90
+ style, --tracking-optical a correction that varies with size. The names say which is which, so the
91
+ distinction survives without the ADR in hand. Scope: the title role and above - H1, H2 and the page
92
+ head's h1 - and never below it, where the correction would read as damage. See the ADR. */
93
+ --tracking-label: 0.14em;
94
+ --tracking-optical: -0.02em;
95
+ }`;
96
+
97
+ /* The reading measure is in ch, not rem, so the character count stays held when the body size moves
98
+ under it; --measure-display is narrower because bigger type wants fewer characters per line, and
99
+ --measure-wide slightly wider - the standfirst of a page head (PageHead #18) is an opening statement,
100
+ not a reading column, so it runs a little past the reading measure by design. All three in ch and,
101
+ having no Tailwind namespace, in :root beside radius, read as max-w-[var(--measure)]. */
102
+ const MEASURE = `:root {
103
+ --measure: 66ch;
104
+ --measure-display: 36ch;
105
+ --measure-wide: 72ch;
106
+ }`;
107
+
108
+ /* Three spacing roles, not a ladder: --space-stack is the sibling gap in a stack, --space-region the air
109
+ around a region of a page - a form's region groups and the shell's bars, above and below the header and
110
+ the footer and between the header's nav items (#14/#15) - and --space-band the vertical air inside a page
111
+ section (#9). All three in em so they track the type ramp, not a fixed length. Three names a reader could
112
+ line up stack < region < band is exactly where "roles, not rungs" is tested - and it holds: each is bound
113
+ to one job with nothing to choose between, not to a step on a ramp a component picks from. A numbered
114
+ scale (--space-1..4) was rejected for that reason - ADR 0003/0004. em has no Tailwind namespace, so like
115
+ --measure these sit in :root. The gutter is a spacing role too but not a --space-* one and not in em -
116
+ see GUTTER below. */
117
+ const SPACING = `:root {
118
+ --space-stack: 0.5em;
119
+ --space-region: 1.5em;
120
+ --space-band: 4em;
121
+ }`;
122
+
123
+ /* The gutter is the horizontal inset holding content off the viewport edge (#9), and the one spacing role
124
+ measured against the screen rather than the type: it answers to how much room there is, not how large
125
+ the words are, so it is in rem/vw and never em. A clamp() lets it grow with the viewport with no
126
+ breakpoint, the device --text-display already uses. Not a --space-* role and no Tailwind namespace, so
127
+ it sits in :root beside the space roles, read as px-[var(--gutter)]. */
128
+ const GUTTER = `:root {
129
+ --gutter: clamp(1.5rem, 5vw, 4rem);
130
+ }`;
131
+
132
+ /* The fold is the least height a first screen takes (CONTEXT.md): measured against the screen not the
133
+ type, so in vh/rem and never em, in :root beside the gutter with no Tailwind namespace, read as
134
+ min-h-[var(--fold-height)]. Constraint: the vh component is < 100vh - a fold that exactly fills the
135
+ screen guarantees top-heaviness, so the next section's hairline must sit just inside it, and the
136
+ floor is enforced against the library's own value (ADR 0004), never a consumer's. */
137
+ const FOLD = `:root {
138
+ --fold-height: min(70vh, 40rem);
139
+ }`;
140
+
141
+ /* Four aspect roles in a @theme block like typography: --aspect-* is a Tailwind 4 namespace, so it
142
+ generates the aspect-portrait utilities Figure reaches rather than a :root value. Named shapes, not
143
+ a ladder - ADR 0003/0004 rejected a radius and spacing scale (see SPACING) because a scale lets a
144
+ component pick a rung with no rule saying which; four shapes with distinct jobs and no ordering do not. */
145
+ const ASPECT = `@theme {
146
+ --aspect-portrait: 4 / 5;
147
+ --aspect-square: 1 / 1;
148
+ --aspect-landscape: 3 / 2;
149
+ --aspect-wide: 16 / 9;
150
+ }`;
151
+
152
+ /* The checklist tick's two dimensions are not colours: like the underline and focus-ring dimensions
153
+ they live in :root only, never @theme inline, so a brand can re-point them. They are named rather
154
+ than recipe literals because of ADR 0004's test - whether the library sets the property. The tick is
155
+ drawn as a `::before` pseudo-element, the strongest possible case of an element a consumer cannot
156
+ reach: no `className` gets near it, so a value overriding it would have to fight a utility class on
157
+ an element that is not in the tree at all, and the value therefore needs a name. With `rule` that is
158
+ three names a brand re-points to get a different marker, and no member of Checklist gains a prop. */
159
+ const TICK = `:root {
160
+ --tick-length: 0.9rem;
161
+ --tick-thickness: 1px;
162
+ }`;
163
+
26
164
  const toKebabCase = (name: string): string =>
27
165
  name.replace(/[A-Z]/g, (char) => `-${char.toLowerCase()}`);
28
166
 
@@ -40,8 +178,13 @@ const declarations = (
40
178
  * silently shipping stale colours.
41
179
  */
42
180
  export const renderTokens = (): string => {
43
- const raw = (tokens: PaletteTokens) => (name: string) =>
44
- tokens[name as keyof PaletteTokens];
181
+ // The library no longer declares --color-ring but still reads it as a fallback, so a consumer's
182
+ // existing value keeps working; the plain hex in the palette is the default and what the contrast
183
+ // test checks. See docs/adr/0002-focus-ring-token-contract.md.
184
+ const raw = (tokens: PaletteTokens) => (name: string) => {
185
+ const value = tokens[name as keyof PaletteTokens];
186
+ return name === 'focusRing' ? `var(--color-ring, ${value})` : value;
187
+ };
45
188
  const reference = () => (name: string) => `var(--color-${toKebabCase(name)})`;
46
189
 
47
190
  return `${GENERATED_HEADER}
@@ -64,5 +207,39 @@ ${declarations(light, reference())}
64
207
 
65
208
  /* Motion is not a colour: it is carried in :root only, never mapped into @theme inline. */
66
209
  ${MOTION}
210
+
211
+ /* The focus ring's dimensions are not colours either, and sit in :root beside the motion block. */
212
+ ${FOCUS_RING}
213
+
214
+ /* Radius is not a colour either: one token for the corner every control reads, beside the others. */
215
+ ${RADIUS}
216
+
217
+ /* The library's underline dimensions are not colours either, and sit in :root beside radius. They are
218
+ not prose's alone: every Link treatment that draws a line draws it from these (docs/adr/0006). */
219
+ ${UNDERLINE}
220
+
221
+ /* Typography is a set of Tailwind theme namespaces, not colours: its own @theme block generates the
222
+ utilities, so it is not carried in @theme inline with the palette. */
223
+ ${TYPOGRAPHY}
224
+
225
+ /* The aspect roles are Tailwind theme namespaces like typography, so they take their own @theme block
226
+ after it and are not carried in @theme inline with the palette. */
227
+ ${ASPECT}
228
+
229
+ /* The reading measure has no Tailwind namespace, so it sits in :root beside radius. */
230
+ ${MEASURE}
231
+
232
+ /* Spacing has no Tailwind namespace either - --spacing is a single base multiplier ADR 0004 forbids
233
+ re-pointing - so the three roles sit in :root beside the measure. */
234
+ ${SPACING}
235
+
236
+ /* The gutter has no Tailwind namespace either, so it sits in :root beside the space roles. */
237
+ ${GUTTER}
238
+
239
+ /* The fold height has no Tailwind namespace either, so it sits in :root beside the gutter. */
240
+ ${FOLD}
241
+
242
+ /* The checklist tick's dimensions are not colours either, and sit in :root beside the underline block. */
243
+ ${TICK}
67
244
  `;
68
245
  };
package/src/index.ts CHANGED
@@ -1,6 +1,30 @@
1
1
  import './styles.css';
2
2
 
3
- export type { IButtonProps } from 'Interaction/Button/Button';
3
+ export { Brandmark } from 'Display/Brandmark/Brandmark';
4
+ export { Checklist } from 'Display/Checklist/Checklist';
5
+ export { DefinitionList } from 'Display/DefinitionList/DefinitionList';
6
+ export { Figure } from 'Display/Figure/Figure';
7
+ export { Rail } from 'Display/Rail/Rail';
8
+ export { Table } from 'Display/Table/Table';
9
+ export { Eyebrow } from 'Display/Typography/Eyebrow/Eyebrow';
10
+ export { H1 } from 'Display/Typography/H1/H1';
11
+ export { H2 } from 'Display/Typography/H2/H2';
12
+ export { H3 } from 'Display/Typography/H3/H3';
13
+ export { H4 } from 'Display/Typography/H4/H4';
14
+ export { H5 } from 'Display/Typography/H5/H5';
15
+ export { H6 } from 'Display/Typography/H6/H6';
16
+ export { P } from 'Display/Typography/P/P';
17
+ export { Prose } from 'Display/Typography/Prose/Prose';
4
18
  export { Button } from 'Interaction/Button/Button';
19
+ export { Input } from 'Interaction/Input/Input';
20
+ export { Link } from 'Interaction/Link/Link';
21
+ export { TextArea } from 'Interaction/TextArea/TextArea';
22
+ export { Footer } from 'Layout/Footer/Footer';
23
+ export type { FormState } from 'Layout/Form/Form';
24
+ export { Form } from 'Layout/Form/Form';
25
+ export { Header } from 'Layout/Header/Header';
26
+ export { Hero } from 'Layout/Hero/Hero';
27
+ export { PageHead } from 'Layout/PageHead/PageHead';
28
+ export { Section } from 'Layout/Section/Section';
5
29
  export type { PaletteTokens } from 'Theme/Palette';
6
30
  export { dark, light } from 'Theme/Palette';
package/src/tokens.css CHANGED
@@ -14,17 +14,19 @@
14
14
  --color-foreground: #0f172a;
15
15
  --color-muted: #64748b;
16
16
  --color-border: #e2e8f0;
17
+ --color-control-border: #64748b;
18
+ --color-rule: #808fa3;
19
+ --color-backing: #f1f5f9;
17
20
  --color-primary: #8b5cf6;
18
21
  --color-primary-hover: #7c3aed;
19
22
  --color-primary-foreground: #f8fafc;
20
- --color-primary-ring: #a78bfa;
21
23
  --color-secondary: #0ea5e9;
22
24
  --color-secondary-hover: #0284c7;
23
25
  --color-secondary-foreground: #f8fafc;
24
- --color-secondary-ring: #38bdf8;
25
26
  --color-disabled: #94a3b8;
26
27
  --color-disabled-hover: #64748b;
27
- --color-ring: #94a3b8;
28
+ --color-focus-ring: var(--color-ring, #475569);
29
+ --color-link: #2563eb;
28
30
  --color-success: #10b981;
29
31
  --color-warning: #f59e0b;
30
32
  --color-error: #d63384;
@@ -36,17 +38,19 @@
36
38
  --color-foreground: #f8fafc;
37
39
  --color-muted: #94a3b8;
38
40
  --color-border: #334155;
41
+ --color-control-border: #94a3b8;
42
+ --color-rule: #5b6a80;
43
+ --color-backing: #1e293b;
39
44
  --color-primary: #7c3aed;
40
45
  --color-primary-hover: #8b5cf6;
41
46
  --color-primary-foreground: #f8fafc;
42
- --color-primary-ring: #a78bfa;
43
47
  --color-secondary: #0284c7;
44
48
  --color-secondary-hover: #0ea5e9;
45
49
  --color-secondary-foreground: #f8fafc;
46
- --color-secondary-ring: #38bdf8;
47
50
  --color-disabled: #475569;
48
51
  --color-disabled-hover: #334155;
49
- --color-ring: #64748b;
52
+ --color-focus-ring: var(--color-ring, #cbd5e1);
53
+ --color-link: #60a5fa;
50
54
  --color-success: #34d399;
51
55
  --color-warning: #fbbf24;
52
56
  --color-error: #f48fb1;
@@ -58,17 +62,19 @@
58
62
  --color-foreground: var(--color-foreground);
59
63
  --color-muted: var(--color-muted);
60
64
  --color-border: var(--color-border);
65
+ --color-control-border: var(--color-control-border);
66
+ --color-rule: var(--color-rule);
67
+ --color-backing: var(--color-backing);
61
68
  --color-primary: var(--color-primary);
62
69
  --color-primary-hover: var(--color-primary-hover);
63
70
  --color-primary-foreground: var(--color-primary-foreground);
64
- --color-primary-ring: var(--color-primary-ring);
65
71
  --color-secondary: var(--color-secondary);
66
72
  --color-secondary-hover: var(--color-secondary-hover);
67
73
  --color-secondary-foreground: var(--color-secondary-foreground);
68
- --color-secondary-ring: var(--color-secondary-ring);
69
74
  --color-disabled: var(--color-disabled);
70
75
  --color-disabled-hover: var(--color-disabled-hover);
71
- --color-ring: var(--color-ring);
76
+ --color-focus-ring: var(--color-focus-ring);
77
+ --color-link: var(--color-link);
72
78
  --color-success: var(--color-success);
73
79
  --color-warning: var(--color-warning);
74
80
  --color-error: var(--color-error);
@@ -87,3 +93,101 @@
87
93
  --motion-duration-color: 0ms;
88
94
  }
89
95
  }
96
+
97
+ /* The focus ring's dimensions are not colours either, and sit in :root beside the motion block. */
98
+ :root {
99
+ --focus-ring-width: 3px;
100
+ --focus-ring-offset: 2px;
101
+ }
102
+
103
+ /* Radius is not a colour either: one token for the corner every control reads, beside the others. */
104
+ :root {
105
+ --radius-control: 0.5rem;
106
+ }
107
+
108
+ /* The library's underline dimensions are not colours either, and sit in :root beside radius. They are
109
+ not prose's alone: every Link treatment that draws a line draws it from these (docs/adr/0006). */
110
+ :root {
111
+ --underline-offset: 0.18em;
112
+ --underline-thickness: 1px;
113
+ --underline-thickness-hover: 2px;
114
+ }
115
+
116
+ /* Typography is a set of Tailwind theme namespaces, not colours: its own @theme block generates the
117
+ utilities, so it is not carried in @theme inline with the palette. */
118
+ @theme {
119
+ /* Both faces default to inherit, so the library ships no @font-face and no face and a one-face
120
+ consumer renders as today. --font-primary is carried by content, --font-secondary by labels; a
121
+ component sets font-variant-numeric: tabular-nums only on a --font-primary carrying tnum and
122
+ font-variant-caps: small-caps only on a --font-secondary carrying smcp - both fail silently otherwise. */
123
+ --font-primary: inherit;
124
+ --font-secondary: inherit;
125
+
126
+ /* Type roles, not a scale: one role per job, so a component asks for what its text is, never a rung.
127
+ display > title > subtitle are the three heading steps H1-H3 bind to; H4-H6 share body and split
128
+ by weight, so there is no fourth role. display is the hero and must out-scale every subpage head. */
129
+ --text-display: clamp(3rem, 7vw, 6rem);
130
+ --leading-display: 1.05;
131
+ --text-title: clamp(2.25rem, 5vw, 4.25rem);
132
+ --leading-title: 1.1;
133
+ --text-subtitle: clamp(1.5rem, 3vw, 2.25rem);
134
+ --leading-subtitle: 1.2;
135
+
136
+ /* The reading block's opening paragraph (Prose #21): sized above body and below subtitle, led
137
+ tighter than body's 1.6 but not as tight as a heading's 1.2 - it is running text, not a head, so
138
+ it keeps running-text leading. A role, not a rung: one lede per block, nothing to choose. Reusing
139
+ subtitle was rejected in ADR 0004 - it welds the lede's size to H3's and its leading to a head's. */
140
+ --text-lede: 1.25rem; /* 20px */
141
+ --leading-lede: 1.4;
142
+ --text-body: 1.0625rem; /* 17px */
143
+ --leading-body: 1.6;
144
+ --text-small: 0.9375rem; /* 15px floor: below it tabular figures stop comparing column to column */
145
+ --text-label: 0.8125rem; /* 13px floor: below it the tracking reads as damage, not a device */
146
+
147
+ /* Two quantities on one property that must not collapse: --tracking-label is a fixed letter-spaced
148
+ style, --tracking-optical a correction that varies with size. The names say which is which, so the
149
+ distinction survives without the ADR in hand. Scope: the title role and above - H1, H2 and the page
150
+ head's h1 - and never below it, where the correction would read as damage. See the ADR. */
151
+ --tracking-label: 0.14em;
152
+ --tracking-optical: -0.02em;
153
+ }
154
+
155
+ /* The aspect roles are Tailwind theme namespaces like typography, so they take their own @theme block
156
+ after it and are not carried in @theme inline with the palette. */
157
+ @theme {
158
+ --aspect-portrait: 4 / 5;
159
+ --aspect-square: 1 / 1;
160
+ --aspect-landscape: 3 / 2;
161
+ --aspect-wide: 16 / 9;
162
+ }
163
+
164
+ /* The reading measure has no Tailwind namespace, so it sits in :root beside radius. */
165
+ :root {
166
+ --measure: 66ch;
167
+ --measure-display: 36ch;
168
+ --measure-wide: 72ch;
169
+ }
170
+
171
+ /* Spacing has no Tailwind namespace either - --spacing is a single base multiplier ADR 0004 forbids
172
+ re-pointing - so the three roles sit in :root beside the measure. */
173
+ :root {
174
+ --space-stack: 0.5em;
175
+ --space-region: 1.5em;
176
+ --space-band: 4em;
177
+ }
178
+
179
+ /* The gutter has no Tailwind namespace either, so it sits in :root beside the space roles. */
180
+ :root {
181
+ --gutter: clamp(1.5rem, 5vw, 4rem);
182
+ }
183
+
184
+ /* The fold height has no Tailwind namespace either, so it sits in :root beside the gutter. */
185
+ :root {
186
+ --fold-height: min(70vh, 40rem);
187
+ }
188
+
189
+ /* The checklist tick's dimensions are not colours either, and sit in :root beside the underline block. */
190
+ :root {
191
+ --tick-length: 0.9rem;
192
+ --tick-thickness: 1px;
193
+ }