@juwel-development/design-system 3.11.0 → 4.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 (66) hide show
  1. package/README.md +16 -3
  2. package/dist/design-system.js +1029 -540
  3. package/dist/styles.css +2 -0
  4. package/dist/styles.dark.css +2 -0
  5. package/dist/styles.light.css +2 -0
  6. package/dist/types/Arrangement/ColumnLayout/ColumnLayout.d.ts +38 -18
  7. package/dist/types/Display/Callout/Callout.d.ts +31 -0
  8. package/dist/types/Display/Icon/Icon.d.ts +20 -24
  9. package/dist/types/Display/Meter/Meter.d.ts +5 -6
  10. package/dist/types/Display/Rule/Rule.d.ts +22 -0
  11. package/dist/types/Display/Table/Table.d.ts +57 -7
  12. package/dist/types/Display/Typography/Eyebrow/Eyebrow.d.ts +1 -1
  13. package/dist/types/Display/Typography/H1/H1.d.ts +1 -1
  14. package/dist/types/Display/Typography/H2/H2.d.ts +1 -1
  15. package/dist/types/Display/Typography/H3/H3.d.ts +1 -1
  16. package/dist/types/Display/Typography/H4/H4.d.ts +1 -1
  17. package/dist/types/Display/Typography/H5/H5.d.ts +1 -1
  18. package/dist/types/Display/Typography/H6/H6.d.ts +1 -1
  19. package/dist/types/Display/Typography/Note/Note.d.ts +16 -3
  20. package/dist/types/Display/Typography/P/P.d.ts +15 -2
  21. package/dist/types/Display/Typography/Prose/Prose.d.ts +14 -6
  22. package/dist/types/Interaction/Button/Button.d.ts +10 -4
  23. package/dist/types/Interaction/Slider/Slider.d.ts +3 -0
  24. package/dist/types/Interaction/Stepper/Stepper.d.ts +59 -0
  25. package/dist/types/Interaction/Stepper/StepperCompositionError.d.ts +3 -0
  26. package/dist/types/Interaction/Switch/Switch.d.ts +20 -0
  27. package/dist/types/Interaction/Tabs/Tabs.d.ts +24 -3
  28. package/dist/types/Layout/Dialog/Dialog.d.ts +11 -3
  29. package/dist/types/Layout/Form/FormConfigurationError.d.ts +3 -0
  30. package/dist/types/Layout/Pane/Pane.d.ts +55 -0
  31. package/dist/types/Layout/Shell/Shell.d.ts +39 -0
  32. package/dist/types/Layout/Sidebar/Sidebar.d.ts +16 -2
  33. package/dist/types/Theme/Palette.d.ts +11 -4
  34. package/dist/types/index.d.ts +21 -1
  35. package/package.json +18 -8
  36. package/src/Arrangement/ColumnLayout/ColumnLayout.tsx +86 -28
  37. package/src/Display/Callout/Callout.tsx +64 -0
  38. package/src/Display/Icon/Icon.tsx +53 -13
  39. package/src/Display/Meter/Meter.tsx +12 -13
  40. package/src/Display/Rule/Rule.tsx +31 -0
  41. package/src/Display/Table/Table.tsx +189 -24
  42. package/src/Display/Typography/Note/Note.tsx +27 -11
  43. package/src/Display/Typography/P/P.tsx +29 -4
  44. package/src/Display/Typography/Prose/Prose.tsx +44 -19
  45. package/src/Interaction/Button/Button.tsx +11 -4
  46. package/src/Interaction/Slider/Slider.tsx +3 -5
  47. package/src/Interaction/Stepper/Stepper.tsx +307 -0
  48. package/src/Interaction/Stepper/StepperCompositionError.ts +6 -0
  49. package/src/Interaction/Switch/Switch.tsx +64 -0
  50. package/src/Interaction/Tabs/Tabs.tsx +44 -10
  51. package/src/Layout/Dialog/Dialog.tsx +19 -6
  52. package/src/Layout/Form/Form.tsx +2 -1
  53. package/src/Layout/Form/FormConfigurationError.ts +6 -0
  54. package/src/Layout/Pane/Pane.tsx +159 -0
  55. package/src/Layout/Shell/Shell.tsx +124 -0
  56. package/src/Layout/Sidebar/Sidebar.tsx +31 -7
  57. package/src/Theme/Palette.ts +17 -6
  58. package/src/Theme/renderTokens.ts +61 -12
  59. package/src/index.ts +21 -2
  60. package/src/styles.css +5 -6
  61. package/src/styles.dark.css +5 -6
  62. package/src/styles.light.css +5 -6
  63. package/src/tokens.css +54 -9
  64. package/src/tokens.dark.css +51 -8
  65. package/src/tokens.light.css +49 -6
  66. package/dist/index.css +0 -2
@@ -10,7 +10,7 @@
10
10
  * Light and dark are two complete sets of the same roles rather than a set of `dark:` overrides
11
11
  * scattered through the components. A component therefore carries no dark-mode classes at all:
12
12
  * swapping the `.dark` class re-points the variables underneath it. Complete means `Required`: the
13
- * two roles the type marks optional are optional for a consumer's palette object, never here.
13
+ * roles the type marks optional are optional for a consumer's palette object, never here.
14
14
  *
15
15
  * The values below are still depot-tracker's brand (primary is its violet). They are carried over
16
16
  * so nothing changed visually during the extraction - a starting point to replace, not a decision.
@@ -18,6 +18,13 @@
18
18
  export type PaletteTokens = {
19
19
  /** Page and panel background. */
20
20
  surface: string;
21
+ /** Selected Table row surface, distinct from resting and hover surfaces. Keep foreground, muted
22
+ * link and status inks at 4.5:1; marker, rule and focusRing at 3:1 in each scheme.
23
+ * Optional for existing palette objects; CSS overrides inherit the shipped default. Consumers
24
+ * composing other inks or controls on this surface must ensure their contrast too. */
25
+ selectedSurface?: string;
26
+ /** Error ink/fill on a selected surface: 4.5:1 against selectedSurface and errorForeground. */
27
+ selectedError?: string;
21
28
  /** Default text colour on `surface`. */
22
29
  foreground: string;
23
30
  /** De-emphasised text - captions, help text, placeholders. */
@@ -42,11 +49,11 @@ export type PaletteTokens = {
42
49
 
43
50
  /** The themed veil behind a Dialog, marking the page beneath as present but unavailable. The one
44
51
  * role whose alpha is part of the colour - the page must show through, which a solid hex cannot
45
- * say - so it is an `rgb(r g b / a)` value, not a hex. Both shipped themes carry the same value;
46
- * the Dialog's modality never depends on the treatment, so no contrast constraint applies.
52
+ * say - so it is an `rgb(r g b / a)` value, not a hex. Constraint: compositing over surface
53
+ * must reduce its relative luminance by at least 50%; modality never depends on the treatment.
47
54
  *
48
55
  * Migration (added with Dialog): a consumer constructing its own `PaletteTokens` object must
49
- * add this role - `rgb(15 23 42 / 0.5)` is the shipped value. Spreading `light`/`dark` and
56
+ * add this role - `rgb(15 23 42 / 0.5)` is the light default. Spreading `light`/`dark` and
50
57
  * overriding is unaffected, and a theme that only overrides the generated custom properties
51
58
  * in CSS inherits the default. */
52
59
  scrim: string;
@@ -170,6 +177,8 @@ export type PaletteTokens = {
170
177
  */
171
178
  export const light: Required<PaletteTokens> = {
172
179
  surface: '#ffffff',
180
+ selectedSurface: '#f8fafc',
181
+ selectedError: '#cd2d7d',
173
182
  foreground: '#0f172a',
174
183
  muted: '#64748b',
175
184
  border: '#e2e8f0',
@@ -225,6 +234,8 @@ export const light: Required<PaletteTokens> = {
225
234
  */
226
235
  export const dark: Required<PaletteTokens> = {
227
236
  surface: '#0f172a',
237
+ selectedSurface: '#151e30',
238
+ selectedError: '#f48fb1',
228
239
  foreground: '#f8fafc',
229
240
  muted: '#94a3b8',
230
241
  border: '#334155',
@@ -232,7 +243,7 @@ export const dark: Required<PaletteTokens> = {
232
243
  rule: '#5b6a80',
233
244
  backing: '#1e293b',
234
245
 
235
- scrim: 'rgb(15 23 42 / 0.5)',
246
+ scrim: 'rgb(0 0 0 / 0.6)',
236
247
 
237
248
  meterFill: '#f8fafc',
238
249
  meterTrack: '#334155',
@@ -246,7 +257,7 @@ export const dark: Required<PaletteTokens> = {
246
257
  secondaryForeground: '#020617',
247
258
 
248
259
  disabled: '#475569',
249
- disabledHover: '#334155',
260
+ disabledHover: '#64748b',
250
261
 
251
262
  focusRing: '#cbd5e1',
252
263
 
@@ -96,14 +96,20 @@ const TYPOGRAPHY = `@theme {
96
96
  --font-primary: inherit;
97
97
  --font-secondary: inherit;
98
98
 
99
+ /* Visual emphasis for body copy and annotations (#135), independent of family and size. */
100
+ --font-weight-strong: 700;
101
+
99
102
  /* Type roles, not a scale: one role per job, so a component asks for what its text is, never a rung.
100
103
  display > title > subtitle are the three heading steps H1-H3 bind to; H4-H6 share body and split
101
104
  by weight, so there is no fourth role. display is the hero and must out-scale every subpage head. */
102
- --text-display: clamp(3rem, 7vw, 6rem);
105
+ /* At 1280, 1440 and 1920 CSS px (16px root), size(w / 2) / size(w) must be >= 0.75.
106
+ At 200% zoom the viewport halves, so physical growth is 2 * that ratio: at least 1.5x.
107
+ The rem terms preserve growth; at 1280px these sizes stay within 2px of the old vw clamps. */
108
+ --text-display: clamp(3rem, 3rem + 3.125vw, 6rem);
103
109
  --leading-display: 1.05;
104
- --text-title: clamp(2.25rem, 5vw, 4.25rem);
110
+ --text-title: clamp(2.25rem, 2.25rem + 2.25vw, 4.25rem);
105
111
  --leading-title: 1.1;
106
- --text-subtitle: clamp(1.5rem, 3vw, 2.25rem);
112
+ --text-subtitle: clamp(1.5rem, 1.5rem + 1vw, 2.25rem);
107
113
  --leading-subtitle: 1.2;
108
114
 
109
115
  /* The reading block's opening paragraph (Prose #21): sized above body and below subtitle, led
@@ -112,6 +118,10 @@ const TYPOGRAPHY = `@theme {
112
118
  subtitle was rejected in ADR 0004 - it welds the lede's size to H3's and its leading to a head's. */
113
119
  --text-lede: 1.25rem; /* 20px */
114
120
  --leading-lede: 1.4;
121
+
122
+ /* Whole correspondence blocks, independently of opening ledes (Prose #128). */
123
+ --text-letter: 1.25rem;
124
+ --leading-letter: 1.8;
115
125
  --text-body: 1.0625rem; /* 17px, floored at 16px: below it iOS Safari zooms the viewport when a control set at this role takes focus (#92) */
116
126
  --leading-body: 1.6;
117
127
  --text-small: 0.9375rem; /* 15px floor: below it tabular figures stop comparing column to column */
@@ -126,7 +136,7 @@ const TYPOGRAPHY = `@theme {
126
136
  /* The Dialog title role: the top heading of an independent transitory task, deliberately outside
127
137
  the page heading ladder (docs/adr/0005). Ships subtitle's values while staying an independent
128
138
  role, so a consumer re-points Dialog titles without moving every H3 (docs/adr/0004, Amendments). */
129
- --text-dialog-title: clamp(1.5rem, 3vw, 2.25rem);
139
+ --text-dialog-title: clamp(1.5rem, 1.5rem + 1vw, 2.25rem);
130
140
  --leading-dialog-title: 1.2;
131
141
 
132
142
  /* Two quantities on one property that must not collapse: --tracking-label is a fixed letter-spaced
@@ -147,6 +157,9 @@ const FAMILY_ROLES = `@utility font-heading {
147
157
  font-family: var(--font-heading, var(--font-primary));
148
158
  }
149
159
 
160
+ @utility font-letter {
161
+ font-family: var(--font-letter, var(--font-primary));
162
+ }
150
163
  @utility font-body {
151
164
  font-family: var(--font-body, var(--font-primary));
152
165
  }
@@ -161,6 +174,7 @@ const FAMILY_ROLES = `@utility font-heading {
161
174
  the action column, in rem (docs/adr/0008, Amendments, #97); 20rem clears --control-min-width. */
162
175
  const MEASURE = `:root {
163
176
  --measure: 66ch;
177
+ --measure-letter: 60ch;
164
178
  --measure-display: 36ch;
165
179
  --measure-wide: 72ch;
166
180
  --measure-action: 20rem;
@@ -218,6 +232,20 @@ const DEFINITION_SPACING = `:root {
218
232
  initial-value: 1px;
219
233
  }`;
220
234
 
235
+ // Resolve the fill row's width shortfall before its columns query the horizontal state.
236
+ const COLUMN_LAYOUT_FIT = `@property --column-layout-shortfall {
237
+ syntax: "<length>";
238
+ inherits: false;
239
+ initial-value: 1px;
240
+ }`;
241
+
242
+ /* Callout's leading mark and separation from its content (#131). The mark stays visible at a
243
+ positive CSS length; its inset accepts a nonnegative CSS length and follows inherited type. */
244
+ const CALLOUT_MARK = `:root {
245
+ --callout-mark-width: 0.25rem;
246
+ --space-callout-inset: 1em;
247
+ }`;
248
+
221
249
  /* Box's inner padding (#117): one role on all four sides, distinct from a sibling gap, a region's
222
250
  air or a page band, so a brand tunes every Box at once and no Box gains a padding prop (ADR 0008,
223
251
  the token-role test). In em like --space-collection-item, so it tracks the inherited type.
@@ -286,6 +314,16 @@ const TICK = `:root {
286
314
  --tick-thickness: 1px;
287
315
  }`;
288
316
 
317
+ /* Step geometry is independently themeable: positive marker size and line thicknesses,
318
+ nonnegative insets. The marker size must fit the consumer's font and ordinal count. */
319
+ const STEPPER = `:root {
320
+ --step-marker-size: 2em;
321
+ --step-inset-inline: 1em;
322
+ --step-inset-block: 0.5em;
323
+ --step-connector-thickness: 1px;
324
+ --step-selection-thickness: 2px;
325
+ }`;
326
+
289
327
  /* Tab label insets separate adjacent controls and give the label vertical breathing room.
290
328
  These are two axis-specific jobs, not a selectable scale; stack/region/band describe other jobs.
291
329
  Like those spacing roles, em tracks the label type. Non-negative values preserve the hit area. */
@@ -320,6 +358,13 @@ const SLIDER = `:root {
320
358
  --slider-thumb-size: 1.5rem;
321
359
  }`;
322
360
 
361
+ /* Switch geometry is independent of choice markers: its track holds two thumb positions.
362
+ Both lengths must be positive; track width derives from the thumb so positions stay distinct. */
363
+ const SWITCH = `:root {
364
+ --switch-thumb-size: 1rem;
365
+ --switch-track-inset: 0.125rem;
366
+ }`;
367
+
323
368
  /* The choice marker's two dimensions are not colours: like the tick they live in :root only,
324
369
  never @theme inline, so a brand re-points the marker without Choices gaining a prop. Sized in
325
370
  rem like the tick and the slider thumb. Constraints: both > 0 (a zero dot erases the one cue
@@ -363,13 +408,8 @@ const declarations = (
363
408
  .map((name) => ` --color-${toKebabCase(name)}: ${value(name)};`)
364
409
  .join('\n');
365
410
 
366
- // The library no longer declares --color-ring but still reads it as a fallback, so a consumer's
367
- // existing value keeps working; the plain hex in the palette is the default and what the contrast
368
- // test checks. See docs/adr/0002-focus-ring-token-contract.md.
369
- const raw = (tokens: Required<PaletteTokens>) => (name: string) => {
370
- const value = tokens[name as keyof PaletteTokens];
371
- return name === 'focusRing' ? `var(--color-ring, ${value})` : value;
372
- };
411
+ const raw = (tokens: Required<PaletteTokens>) => (name: string) =>
412
+ tokens[name as keyof PaletteTokens];
373
413
  const reference = () => (name: string) => `var(--color-${toKebabCase(name)})`;
374
414
 
375
415
  // Only the combined tokens.css carries the dark set; this gates it behind .dark and defines dark:.
@@ -406,7 +446,7 @@ ${UNDERLINE}
406
446
  utilities, so it is not carried in @theme inline with the palette. */
407
447
  ${TYPOGRAPHY}
408
448
 
409
- /* The heading, body and control family roles are utilities with a fallback to the primary face, not
449
+ /* The heading, body, letter and control family roles are utilities with a fallback to the primary face, not
410
450
  @theme variables, so a theme re-pointing either name at any scope reaches the text beneath it. */
411
451
  ${FAMILY_ROLES}
412
452
 
@@ -433,9 +473,14 @@ ${COLLECTION_SPACING}
433
473
  /* DefinitionList's item air and column minimums sit in :root beside the collection role (#123). */
434
474
  ${DEFINITION_SPACING}
435
475
 
476
+ ${COLUMN_LAYOUT_FIT}
477
+
436
478
  /* Box's inner padding, measured against inherited type, beside Collection's. */
437
479
  ${BOX_INSET}
438
480
 
481
+ /* Callout marks an edge without enclosing or arranging its content. */
482
+ ${CALLOUT_MARK}
483
+
439
484
  /* Table's cell insets, one pair per density, beside Box's. */
440
485
  ${TABLE_CELL_INSET}
441
486
 
@@ -459,11 +504,15 @@ ${TABLE_SELECTION_MARKER}
459
504
 
460
505
  ${TAB_INSETS}
461
506
 
507
+ ${STEPPER}
508
+
462
509
  ${SLIDER}
463
510
 
464
511
  /* The choice marker's dimensions are not colours either, and sit in :root beside the slider block. */
465
512
  ${CHOICE_MARKER}
466
513
 
514
+ ${SWITCH}
515
+
467
516
  /* The meter track's thickness is not a colour either, and sits in :root beside the tab blocks. */
468
517
  ${METER_TRACK}
469
518
 
package/src/index.ts CHANGED
@@ -1,20 +1,27 @@
1
- import './styles.css';
2
-
3
1
  export { Cluster } from 'Arrangement/Cluster/Cluster';
4
2
  export { ColumnLayout } from 'Arrangement/ColumnLayout/ColumnLayout';
3
+ export { ColumnLayoutCompositionError } from 'Arrangement/ColumnLayout/ColumnLayoutCompositionError';
4
+ export { ColumnLayoutConfigurationError } from 'Arrangement/ColumnLayout/ColumnLayoutConfigurationError';
5
5
  export { FieldRow } from 'Arrangement/FieldRow/FieldRow';
6
+ export { FieldRowCompositionError } from 'Arrangement/FieldRow/FieldRowCompositionError';
7
+ export { FieldRowConfigurationError } from 'Arrangement/FieldRow/FieldRowConfigurationError';
6
8
  export { Stack } from 'Arrangement/Stack/Stack';
7
9
  export { Box } from 'Display/Box/Box';
8
10
  export { Brandmark } from 'Display/Brandmark/Brandmark';
11
+ export { Callout } from 'Display/Callout/Callout';
9
12
  export { Checklist } from 'Display/Checklist/Checklist';
10
13
  export { Collection } from 'Display/Collection/Collection';
11
14
  export { DefinitionList } from 'Display/DefinitionList/DefinitionList';
15
+ export { DefinitionListConfigurationError } from 'Display/DefinitionList/DefinitionListConfigurationError';
12
16
  export { Figure } from 'Display/Figure/Figure';
13
17
  export { Icon } from 'Display/Icon/Icon';
14
18
  export { Meter } from 'Display/Meter/Meter';
19
+ export { MeterConfigurationError } from 'Display/Meter/MeterConfigurationError';
15
20
  export { Rail } from 'Display/Rail/Rail';
21
+ export { Rule } from 'Display/Rule/Rule';
16
22
  export type { TableColumnAllocation } from 'Display/Table/Table';
17
23
  export { Table } from 'Display/Table/Table';
24
+ export { TableConfigurationError } from 'Display/Table/TableConfigurationError';
18
25
  export { Eyebrow } from 'Display/Typography/Eyebrow/Eyebrow';
19
26
  export { H1 } from 'Display/Typography/H1/H1';
20
27
  export { H2 } from 'Display/Typography/H2/H2';
@@ -27,24 +34,36 @@ export { P } from 'Display/Typography/P/P';
27
34
  export { Prose } from 'Display/Typography/Prose/Prose';
28
35
  export { Button } from 'Interaction/Button/Button';
29
36
  export { Choices } from 'Interaction/Choices/Choices';
37
+ export { ChoicesCompositionError } from 'Interaction/Choices/ChoicesCompositionError';
30
38
  export { Input } from 'Interaction/Input/Input';
31
39
  export { Link } from 'Interaction/Link/Link';
32
40
  export { MultiSelect } from 'Interaction/MultiSelect/MultiSelect';
41
+ export { MultiSelectCompositionError } from 'Interaction/MultiSelect/MultiSelectCompositionError';
33
42
  export { NumberInput } from 'Interaction/NumberInput/NumberInput';
34
43
  export { Select } from 'Interaction/Select/Select';
35
44
  export { Slider } from 'Interaction/Slider/Slider';
45
+ export { SliderConfigurationError } from 'Interaction/Slider/SliderConfigurationError';
46
+ export { Stepper } from 'Interaction/Stepper/Stepper';
47
+ export { StepperCompositionError } from 'Interaction/Stepper/StepperCompositionError';
48
+ export { Switch } from 'Interaction/Switch/Switch';
36
49
  export { Tabs } from 'Interaction/Tabs/Tabs';
50
+ export { TabsCompositionError } from 'Interaction/Tabs/TabsCompositionError';
37
51
  export { TextArea } from 'Interaction/TextArea/TextArea';
38
52
  export { Cover } from 'Layout/Cover/Cover';
39
53
  export { Dialog } from 'Layout/Dialog/Dialog';
54
+ export { DialogCompositionError } from 'Layout/Dialog/DialogCompositionError';
55
+ export { DialogNamingError } from 'Layout/Dialog/DialogNamingError';
40
56
  export { Footer } from 'Layout/Footer/Footer';
41
57
  export type { FormState } from 'Layout/Form/Form';
42
58
  export { Form } from 'Layout/Form/Form';
59
+ export { FormConfigurationError } from 'Layout/Form/FormConfigurationError';
43
60
  export { Header } from 'Layout/Header/Header';
44
61
  export { Hero } from 'Layout/Hero/Hero';
45
62
  export { PageHead } from 'Layout/PageHead/PageHead';
63
+ export { Pane } from 'Layout/Pane/Pane';
46
64
  export { ScrollContainer } from 'Layout/ScrollContainer/ScrollContainer';
47
65
  export { Section } from 'Layout/Section/Section';
66
+ export { Shell } from 'Layout/Shell/Shell';
48
67
  export { Sidebar } from 'Layout/Sidebar/Sidebar';
49
68
  export type { PaletteTokens } from 'Theme/Palette';
50
69
  export { dark, light } from 'Theme/Palette';
package/src/styles.css CHANGED
@@ -1,9 +1,8 @@
1
1
  /* Component-level styles for the design system. */
2
- @import "tailwindcss";
2
+ @import "tailwindcss" source(none);
3
3
  @import "./tokens.css";
4
4
 
5
- /* Scan the component sources explicitly so their utilities are generated no matter
6
- which host imports this stylesheet. Tailwind's automatic content detection is
7
- anchored to the consumer and skips node_modules, so a host pulling the components
8
- in through a package would otherwise render them unstyled. */
9
- @source ".";
5
+ /* Only component code contributes utilities to the published stylesheets. */
6
+ @source "./**/*.tsx";
7
+ @source not "./**/*.spec.tsx";
8
+ @source not "./**/*.stories.tsx";
@@ -1,9 +1,8 @@
1
1
  /* Component-level styles for the design system, dark theme only (issue #62). */
2
- @import "tailwindcss";
2
+ @import "tailwindcss" source(none);
3
3
  @import "./tokens.dark.css";
4
4
 
5
- /* Scan the component sources explicitly so their utilities are generated no matter
6
- which host imports this stylesheet. Tailwind's automatic content detection is
7
- anchored to the consumer and skips node_modules, so a host pulling the components
8
- in through a package would otherwise render them unstyled. */
9
- @source ".";
5
+ /* Only component code contributes utilities to the published stylesheets. */
6
+ @source "./**/*.tsx";
7
+ @source not "./**/*.spec.tsx";
8
+ @source not "./**/*.stories.tsx";
@@ -1,9 +1,8 @@
1
1
  /* Component-level styles for the design system, light theme only (issue #62). */
2
- @import "tailwindcss";
2
+ @import "tailwindcss" source(none);
3
3
  @import "./tokens.light.css";
4
4
 
5
- /* Scan the component sources explicitly so their utilities are generated no matter
6
- which host imports this stylesheet. Tailwind's automatic content detection is
7
- anchored to the consumer and skips node_modules, so a host pulling the components
8
- in through a package would otherwise render them unstyled. */
9
- @source ".";
5
+ /* Only component code contributes utilities to the published stylesheets. */
6
+ @source "./**/*.tsx";
7
+ @source not "./**/*.spec.tsx";
8
+ @source not "./**/*.stories.tsx";
package/src/tokens.css CHANGED
@@ -11,6 +11,8 @@
11
11
 
12
12
  :root {
13
13
  --color-surface: #ffffff;
14
+ --color-selected-surface: #f8fafc;
15
+ --color-selected-error: #cd2d7d;
14
16
  --color-foreground: #0f172a;
15
17
  --color-muted: #64748b;
16
18
  --color-border: #e2e8f0;
@@ -28,7 +30,7 @@
28
30
  --color-secondary-foreground: #f8fafc;
29
31
  --color-disabled: #94a3b8;
30
32
  --color-disabled-hover: #64748b;
31
- --color-focus-ring: var(--color-ring, #475569);
33
+ --color-focus-ring: #475569;
32
34
  --color-link: #2563eb;
33
35
  --color-success: #047857;
34
36
  --color-warning: #b45309;
@@ -40,13 +42,15 @@
40
42
 
41
43
  .dark {
42
44
  --color-surface: #0f172a;
45
+ --color-selected-surface: #151e30;
46
+ --color-selected-error: #f48fb1;
43
47
  --color-foreground: #f8fafc;
44
48
  --color-muted: #94a3b8;
45
49
  --color-border: #334155;
46
50
  --color-control-border: #94a3b8;
47
51
  --color-rule: #5b6a80;
48
52
  --color-backing: #1e293b;
49
- --color-scrim: rgb(15 23 42 / 0.5);
53
+ --color-scrim: rgb(0 0 0 / 0.6);
50
54
  --color-meter-fill: #f8fafc;
51
55
  --color-meter-track: #334155;
52
56
  --color-primary: #8b5cf6;
@@ -56,8 +60,8 @@
56
60
  --color-secondary-hover: #38bdf8;
57
61
  --color-secondary-foreground: #020617;
58
62
  --color-disabled: #475569;
59
- --color-disabled-hover: #334155;
60
- --color-focus-ring: var(--color-ring, #cbd5e1);
63
+ --color-disabled-hover: #64748b;
64
+ --color-focus-ring: #cbd5e1;
61
65
  --color-link: #60a5fa;
62
66
  --color-success: #34d399;
63
67
  --color-warning: #fbbf24;
@@ -69,6 +73,8 @@
69
73
 
70
74
  @theme inline {
71
75
  --color-surface: var(--color-surface);
76
+ --color-selected-surface: var(--color-selected-surface);
77
+ --color-selected-error: var(--color-selected-error);
72
78
  --color-foreground: var(--color-foreground);
73
79
  --color-muted: var(--color-muted);
74
80
  --color-border: var(--color-border);
@@ -150,14 +156,20 @@
150
156
  --font-primary: inherit;
151
157
  --font-secondary: inherit;
152
158
 
159
+ /* Visual emphasis for body copy and annotations (#135), independent of family and size. */
160
+ --font-weight-strong: 700;
161
+
153
162
  /* Type roles, not a scale: one role per job, so a component asks for what its text is, never a rung.
154
163
  display > title > subtitle are the three heading steps H1-H3 bind to; H4-H6 share body and split
155
164
  by weight, so there is no fourth role. display is the hero and must out-scale every subpage head. */
156
- --text-display: clamp(3rem, 7vw, 6rem);
165
+ /* At 1280, 1440 and 1920 CSS px (16px root), size(w / 2) / size(w) must be >= 0.75.
166
+ At 200% zoom the viewport halves, so physical growth is 2 * that ratio: at least 1.5x.
167
+ The rem terms preserve growth; at 1280px these sizes stay within 2px of the old vw clamps. */
168
+ --text-display: clamp(3rem, 3rem + 3.125vw, 6rem);
157
169
  --leading-display: 1.05;
158
- --text-title: clamp(2.25rem, 5vw, 4.25rem);
170
+ --text-title: clamp(2.25rem, 2.25rem + 2.25vw, 4.25rem);
159
171
  --leading-title: 1.1;
160
- --text-subtitle: clamp(1.5rem, 3vw, 2.25rem);
172
+ --text-subtitle: clamp(1.5rem, 1.5rem + 1vw, 2.25rem);
161
173
  --leading-subtitle: 1.2;
162
174
 
163
175
  /* The reading block's opening paragraph (Prose #21): sized above body and below subtitle, led
@@ -166,6 +178,10 @@
166
178
  subtitle was rejected in ADR 0004 - it welds the lede's size to H3's and its leading to a head's. */
167
179
  --text-lede: 1.25rem; /* 20px */
168
180
  --leading-lede: 1.4;
181
+
182
+ /* Whole correspondence blocks, independently of opening ledes (Prose #128). */
183
+ --text-letter: 1.25rem;
184
+ --leading-letter: 1.8;
169
185
  --text-body: 1.0625rem; /* 17px, floored at 16px: below it iOS Safari zooms the viewport when a control set at this role takes focus (#92) */
170
186
  --leading-body: 1.6;
171
187
  --text-small: 0.9375rem; /* 15px floor: below it tabular figures stop comparing column to column */
@@ -180,7 +196,7 @@
180
196
  /* The Dialog title role: the top heading of an independent transitory task, deliberately outside
181
197
  the page heading ladder (docs/adr/0005). Ships subtitle's values while staying an independent
182
198
  role, so a consumer re-points Dialog titles without moving every H3 (docs/adr/0004, Amendments). */
183
- --text-dialog-title: clamp(1.5rem, 3vw, 2.25rem);
199
+ --text-dialog-title: clamp(1.5rem, 1.5rem + 1vw, 2.25rem);
184
200
  --leading-dialog-title: 1.2;
185
201
 
186
202
  /* Two quantities on one property that must not collapse: --tracking-label is a fixed letter-spaced
@@ -191,12 +207,15 @@
191
207
  --tracking-optical: -0.02em;
192
208
  }
193
209
 
194
- /* The heading, body and control family roles are utilities with a fallback to the primary face, not
210
+ /* The heading, body, letter and control family roles are utilities with a fallback to the primary face, not
195
211
  @theme variables, so a theme re-pointing either name at any scope reaches the text beneath it. */
196
212
  @utility font-heading {
197
213
  font-family: var(--font-heading, var(--font-primary));
198
214
  }
199
215
 
216
+ @utility font-letter {
217
+ font-family: var(--font-letter, var(--font-primary));
218
+ }
200
219
  @utility font-body {
201
220
  font-family: var(--font-body, var(--font-primary));
202
221
  }
@@ -219,6 +238,7 @@
219
238
  of controls rather than a reading line (docs/adr/0008, Amendments). */
220
239
  :root {
221
240
  --measure: 66ch;
241
+ --measure-letter: 60ch;
222
242
  --measure-display: 36ch;
223
243
  --measure-wide: 72ch;
224
244
  --measure-action: 20rem;
@@ -262,11 +282,23 @@
262
282
  initial-value: 1px;
263
283
  }
264
284
 
285
+ @property --column-layout-shortfall {
286
+ syntax: "<length>";
287
+ inherits: false;
288
+ initial-value: 1px;
289
+ }
290
+
265
291
  /* Box's inner padding, measured against inherited type, beside Collection's. */
266
292
  :root {
267
293
  --space-box-inset: 1em;
268
294
  }
269
295
 
296
+ /* Callout marks an edge without enclosing or arranging its content. */
297
+ :root {
298
+ --callout-mark-width: 0.25rem;
299
+ --space-callout-inset: 1em;
300
+ }
301
+
270
302
  /* Table's cell insets, one pair per density, beside Box's. */
271
303
  :root {
272
304
  --table-cell-inset-inline: 1rem;
@@ -311,6 +343,14 @@
311
343
  --tab-inset-block: 0.5em;
312
344
  }
313
345
 
346
+ :root {
347
+ --step-marker-size: 2em;
348
+ --step-inset-inline: 1em;
349
+ --step-inset-block: 0.5em;
350
+ --step-connector-thickness: 1px;
351
+ --step-selection-thickness: 2px;
352
+ }
353
+
314
354
  :root {
315
355
  --slider-track-thickness: 0.25rem;
316
356
  --slider-thumb-size: 1.5rem;
@@ -322,6 +362,11 @@
322
362
  --choice-marker-dot-size: 0.5rem;
323
363
  }
324
364
 
365
+ :root {
366
+ --switch-thumb-size: 1rem;
367
+ --switch-track-inset: 0.125rem;
368
+ }
369
+
325
370
  /* The meter track's thickness is not a colour either, and sits in :root beside the tab blocks. */
326
371
  :root {
327
372
  --meter-track-thickness: 0.5rem;