@juwel-development/design-system 3.9.0 → 3.10.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 (81) hide show
  1. package/README.md +9 -298
  2. package/dist/design-system.js +1043 -589
  3. package/dist/index.css +1 -1
  4. package/dist/types/Arrangement/ColumnLayout/ColumnLayout.d.ts +92 -0
  5. package/dist/types/Arrangement/ColumnLayout/ColumnLayoutCompositionError.d.ts +3 -0
  6. package/dist/types/Arrangement/ColumnLayout/ColumnLayoutConfigurationError.d.ts +3 -0
  7. package/dist/types/Arrangement/FieldRow/FieldRow.d.ts +87 -0
  8. package/dist/types/Arrangement/FieldRow/FieldRowCompositionError.d.ts +3 -0
  9. package/dist/types/Arrangement/FieldRow/FieldRowConfigurationError.d.ts +3 -0
  10. package/dist/types/Arrangement/FieldRow/IControlEdge.d.ts +6 -0
  11. package/dist/types/Arrangement/FieldRow/alignControlEdges.d.ts +7 -0
  12. package/dist/types/Display/Box/Box.d.ts +41 -0
  13. package/dist/types/Display/Brandmark/Brandmark.d.ts +1 -1
  14. package/dist/types/Display/DefinitionList/DefinitionList.d.ts +59 -9
  15. package/dist/types/Display/DefinitionList/DefinitionListConfigurationError.d.ts +3 -0
  16. package/dist/types/Display/Figure/Figure.d.ts +2 -2
  17. package/dist/types/Display/Icon/Icon.d.ts +26 -0
  18. package/dist/types/Display/Table/Table.d.ts +118 -8
  19. package/dist/types/Display/Table/TableConfigurationError.d.ts +3 -0
  20. package/dist/types/Display/Typography/Eyebrow/Eyebrow.d.ts +1 -1
  21. package/dist/types/Display/Typography/H1/H1.d.ts +3 -2
  22. package/dist/types/Display/Typography/H2/H2.d.ts +3 -2
  23. package/dist/types/Display/Typography/H3/H3.d.ts +3 -2
  24. package/dist/types/Display/Typography/H4/H4.d.ts +3 -2
  25. package/dist/types/Display/Typography/H5/H5.d.ts +3 -2
  26. package/dist/types/Display/Typography/H6/H6.d.ts +3 -2
  27. package/dist/types/Display/Typography/Note/Note.d.ts +1 -1
  28. package/dist/types/Display/Typography/P/P.d.ts +3 -2
  29. package/dist/types/Display/Typography/Prose/Prose.d.ts +1 -1
  30. package/dist/types/Interaction/Button/Button.d.ts +23 -3
  31. package/dist/types/Interaction/Slider/Slider.d.ts +9 -4
  32. package/dist/types/Interaction/Tabs/Tabs.d.ts +31 -6
  33. package/dist/types/Layout/Header/Header.d.ts +67 -8
  34. package/dist/types/Layout/ScrollContainer/ScrollContainer.d.ts +37 -0
  35. package/dist/types/Layout/Section/Section.d.ts +1 -1
  36. package/dist/types/Layout/Sidebar/Sidebar.d.ts +4 -0
  37. package/dist/types/Theme/Palette.d.ts +31 -7
  38. package/dist/types/index.d.ts +6 -0
  39. package/package.json +1 -1
  40. package/src/Arrangement/ColumnLayout/ColumnLayout.tsx +249 -0
  41. package/src/Arrangement/ColumnLayout/ColumnLayoutCompositionError.ts +8 -0
  42. package/src/Arrangement/ColumnLayout/ColumnLayoutConfigurationError.ts +6 -0
  43. package/src/Arrangement/FieldRow/FieldRow.tsx +313 -0
  44. package/src/Arrangement/FieldRow/FieldRowCompositionError.ts +6 -0
  45. package/src/Arrangement/FieldRow/FieldRowConfigurationError.ts +6 -0
  46. package/src/Arrangement/FieldRow/IControlEdge.ts +6 -0
  47. package/src/Arrangement/FieldRow/alignControlEdges.ts +35 -0
  48. package/src/Display/Box/Box.tsx +77 -0
  49. package/src/Display/Checklist/Checklist.tsx +1 -1
  50. package/src/Display/DefinitionList/DefinitionList.tsx +164 -22
  51. package/src/Display/DefinitionList/DefinitionListConfigurationError.ts +6 -0
  52. package/src/Display/Icon/Icon.tsx +63 -0
  53. package/src/Display/Table/Table.tsx +434 -44
  54. package/src/Display/Table/TableConfigurationError.ts +6 -0
  55. package/src/Display/Typography/H1/H1.tsx +3 -2
  56. package/src/Display/Typography/H2/H2.tsx +3 -2
  57. package/src/Display/Typography/H3/H3.tsx +3 -2
  58. package/src/Display/Typography/H4/H4.tsx +3 -2
  59. package/src/Display/Typography/H5/H5.tsx +3 -2
  60. package/src/Display/Typography/H6/H6.tsx +3 -2
  61. package/src/Display/Typography/P/P.tsx +3 -2
  62. package/src/Display/Typography/Prose/Prose.tsx +3 -3
  63. package/src/Interaction/Button/Button.tsx +45 -17
  64. package/src/Interaction/Input/Input.tsx +1 -1
  65. package/src/Interaction/MultiSelect/MultiSelect.tsx +1 -1
  66. package/src/Interaction/NumberInput/NumberInput.tsx +1 -1
  67. package/src/Interaction/Select/Select.tsx +1 -1
  68. package/src/Interaction/Slider/Slider.tsx +34 -18
  69. package/src/Interaction/Tabs/Tabs.tsx +38 -11
  70. package/src/Interaction/TextArea/TextArea.tsx +1 -1
  71. package/src/Layout/Dialog/Dialog.tsx +4 -3
  72. package/src/Layout/Header/Header.tsx +139 -39
  73. package/src/Layout/PageHead/PageHead.tsx +6 -5
  74. package/src/Layout/ScrollContainer/ScrollContainer.tsx +149 -0
  75. package/src/Layout/Sidebar/Sidebar.tsx +8 -4
  76. package/src/Theme/Palette.ts +37 -9
  77. package/src/Theme/renderTokens.ts +101 -5
  78. package/src/index.ts +6 -0
  79. package/src/tokens.css +68 -4
  80. package/src/tokens.dark.css +66 -4
  81. package/src/tokens.light.css +64 -2
@@ -1,8 +1,8 @@
1
1
  import type { VariantProps } from 'class-variance-authority';
2
2
  import type { FunctionComponent, ReactNode } from 'react';
3
3
  declare const figure: (props?: ({
4
- ratio?: "portrait" | "square" | "landscape" | "wide" | null | undefined;
5
- focus?: "center" | "top" | "bottom" | "left" | "right" | null | undefined;
4
+ ratio?: "square" | "portrait" | "landscape" | "wide" | null | undefined;
5
+ focus?: "center" | "left" | "right" | "bottom" | "top" | null | undefined;
6
6
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
7
7
  export interface IFigureProps extends VariantProps<typeof figure> {
8
8
  /** The image source. Produce it through the consuming build's image pipeline - see @UXGuidelines. */
@@ -0,0 +1,26 @@
1
+ import type { FunctionComponent } from 'react';
2
+ declare const drawings: {
3
+ readonly sort: readonly ["M5.5 13V3", "M3 5.5l2.5-2.5L8 5.5", "M10.5 3v10", "M8 10.5l2.5 2.5 2.5-2.5"];
4
+ readonly 'sort-ascending': readonly ["M8 13V3", "M4 7l4-4 4 4"];
5
+ readonly 'sort-descending': readonly ["M8 3v10", "M4 9l4 4 4-4"];
6
+ };
7
+ export interface IIconProps {
8
+ /** Which drawing. A name selects a shape, never a state or a behaviour. */
9
+ name: keyof typeof drawings;
10
+ testId?: string;
11
+ }
12
+ /**
13
+ * A reusable visual glyph: the three sort indicators a consumer composes inside a header cell's
14
+ * plain Button, or beside any text that names what the icon reinforces.
15
+ *
16
+ * @Guarantees — enforced on every render
17
+ * - Hidden from assistive technology and out of the tab order: it adds no stop and no spoken name.
18
+ * - Scales with the surrounding font-size and takes the surrounding text colour.
19
+ * - The three drawings differ in shape, so the ordering is never carried by colour alone.
20
+ *
21
+ * @CallerMustEnsure
22
+ * - The control or text beside it carries the meaning: a `Button` label, a header cell's `ariaSort`.
23
+ * An icon standing alone says nothing to a screen reader.
24
+ */
25
+ export declare const Icon: FunctionComponent<IIconProps>;
26
+ export {};
@@ -1,33 +1,78 @@
1
1
  import type { VariantProps } from 'class-variance-authority';
2
- import type { FunctionComponent, ReactNode } from 'react';
2
+ import { type FunctionComponent, type ReactNode } from 'react';
3
+ import type { Observable, Subject } from 'rxjs';
4
+ declare const table: (props?: ({
5
+ density?: "compact" | "comfortable" | null | undefined;
6
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
3
7
  declare const tableCell: (props?: ({
4
- variant?: "note" | "value" | null | undefined;
8
+ variant?: "value" | "note" | null | undefined;
5
9
  align?: "center" | "left" | "right" | null | undefined;
6
10
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
7
11
  declare const tableHeaderCell: (props?: ({
8
12
  align?: "center" | "left" | "right" | null | undefined;
9
13
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
10
- interface ITableRootProps {
14
+ /** The width roles a column may take, as its fixed width or as a floor. Each names a column job the
15
+ * consumer specification attests - the subject's name, a short comparison fact, a tabular figure
16
+ * with its unit, one action control - and is read from the theme as `--table-column-<role>`. */
17
+ type TableColumnWidthRole = 'name' | 'fact' | 'figure' | 'action';
18
+ /**
19
+ * One column's allocation of a table's width, independent of the rows currently displayed: either
20
+ * fixed at a named width role, or a positive share of the width the fixed columns leave, with an
21
+ * optional named minimum as its floor. Declared once on `Table.Root`, in cell order.
22
+ */
23
+ export type TableColumnAllocation = {
24
+ /** The role whose width this column takes exactly, at any available width. */
25
+ readonly width: TableColumnWidthRole;
26
+ readonly weight?: never;
27
+ /** A floor: a `width` smaller than its minimum resolves to the minimum. */
28
+ readonly minWidth?: TableColumnWidthRole;
29
+ } | {
30
+ /** This column's share of the width the fixed columns leave, relative to its siblings' weights:
31
+ * a positive finite number. `3` beside `1` is three quarters and one quarter of it. */
32
+ readonly weight: number;
33
+ readonly width?: never;
34
+ /** The least this column takes. While a minimum holds a column, the other proportional columns
35
+ * share what is left; with none, the share may shrink to nothing. */
36
+ readonly minWidth?: TableColumnWidthRole;
37
+ };
38
+ export interface ITableRootProps extends VariantProps<typeof table> {
11
39
  /** The table's accessible name. Rendered as the first child; always present. */
12
40
  caption: string;
13
41
  /** What the note column is. Governs narrow-viewport behaviour; omit when there is none. */
14
42
  notes?: 'supplementary' | 'content';
43
+ /** The columns' allocations, in the order the cells of every row are written. Omit it and widths
44
+ * follow content as before; declare it and every row shares one allocation the rows' content
45
+ * cannot move, and the narrow-viewport `notes` behaviour is replaced by residual scrolling. */
46
+ columns?: readonly TableColumnAllocation[];
15
47
  children?: ReactNode;
16
48
  testId?: string;
17
49
  }
18
- interface ITableSectionProps {
50
+ export interface ITableSectionProps {
19
51
  children?: ReactNode;
20
52
  }
21
- interface ITableRowProps {
53
+ export interface ITableRowProps {
22
54
  children?: ReactNode;
23
55
  testId?: string;
56
+ /** Emits once per activation of the row body - a click, or Enter or Space while the row has
57
+ * focus. Its presence is what makes the row interactive: a tab stop, a visible focus ring and a
58
+ * pointer cursor. Nested links and controls keep their own operations and never emit here.
59
+ * Activation changes nothing about the row; the consumer decides what the request means. */
60
+ onClick$?: Subject<void>;
61
+ /** The consumer's selection for this row. Rendered as `aria-selected` and the marker bar; omitted
62
+ * or not yet emitted means unselected. Selection is independent of `onClick$`: a selected row
63
+ * may be noninteractive, and an interactive row may be unselected. */
64
+ isSelected$?: Observable<boolean>;
24
65
  }
25
- interface ITableCellProps extends VariantProps<typeof tableCell> {
66
+ export interface ITableCellProps extends VariantProps<typeof tableCell> {
26
67
  children?: ReactNode;
27
68
  }
28
- interface ITableHeaderCellProps extends VariantProps<typeof tableHeaderCell> {
69
+ export interface ITableHeaderCellProps extends VariantProps<typeof tableHeaderCell> {
29
70
  /** Explicit, never inferred from Head/Body position - inference would need render-time context. */
30
71
  scope: 'row' | 'col';
72
+ /** The order the column is *currently* displayed in, as accessibility metadata only. Omit it on a
73
+ * column that is not sortable; set it on the one ordered column. Changing it neither reorders rows
74
+ * nor triggers anything - the consumer owns the sort and composes the action in `children`. */
75
+ ariaSort?: 'none' | 'ascending' | 'descending' | 'other';
31
76
  children?: ReactNode;
32
77
  }
33
78
  /**
@@ -41,9 +86,74 @@ interface ITableHeaderCellProps extends VariantProps<typeof tableHeaderCell> {
41
86
  * - The block reads as a table from two rule weights alone: the heavier `rule` above the first row,
42
87
  * `border` hairlines between rows, and no bottom rule on the last. No cell borders, fill, zebra or hover.
43
88
  * - `Cell variant="value"` sets tabular figures; `variant="note"` does not. Both at the 15px small role.
44
- * - `HeaderCell` emits the `scope` it is given; none is inferred.
89
+ * - `HeaderCell` emits the `scope` it is given; none is inferred. It emits `ariaSort` the same way:
90
+ * the attribute states the displayed order and the component never orders, cycles or requests one.
91
+ * - Residual horizontal overflow scrolls in every `notes` mode, with no opt-in. The wrapper is
92
+ * keyboard-reachable while there is something to scroll - always, as a named region, with no note
93
+ * column; as a named group only once the table overflows, with one. No vertical bound is ever set:
94
+ * a long table sits inside a `ScrollContainer` with `axis="vertical"`, which owns that axis.
95
+ * - `Root columns` declares every column's allocation once, in cell order, and every row shares it:
96
+ * a fixed column takes its named width role at any available width; proportional columns share
97
+ * the width the fixed ones leave by their weights, each floored at its named minimum, and while a
98
+ * minimum holds one column the others share what is left. A fixed width below its minimum is the
99
+ * minimum. Nothing a row holds moves a boundary: filtering, sorting, paging, long or short content,
100
+ * an empty body and its repopulation all leave the allocation as it was. Only the definitions, the
101
+ * theme's `--table-column-*` values and the available width can. All-fixed columns do not stretch.
102
+ * - With `columns` declared, no narrow-viewport rule applies whatever `notes` says: the note column
103
+ * stays, rows stay tabular, and what does not fit scrolls in the wrapper as above. Text wraps inside
104
+ * its allocation, an unbroken run included; the table never truncates, hides or resizes content.
105
+ * Without `columns`, widths follow content and both `notes` behaviours are exactly as before.
106
+ * - `Root density` insets every header and body cell from one theme pair: `comfortable` (the
107
+ * default, the former spacing exactly) or `compact`, per table. It moves no type size and no
108
+ * control's own dimensions. A non-positive or non-finite `weight` throws
109
+ * {@link TableConfigurationError}, loud and early.
110
+ * - A `Row` given `onClick$` is interactive: a tab stop with the shared focus ring, activated by a
111
+ * click on its body or by Enter or Space while focused, emitting exactly once per activation. A
112
+ * nested link, button or other control - and anything inside one - performs its own operation and
113
+ * never activates the row. Without `onClick$` the row body is inert and adds no tab stop; nested
114
+ * controls stay operable. Activation never changes selection.
115
+ * - A `Row` given `isSelected$` carries `aria-selected` and, when true, the marker bar along its
116
+ * leading edge in `foreground` - a shape, not a colour, and not the focus ring. Omitted or not yet
117
+ * emitted reads unselected; a replaced source reads unselected until it emits; unmounting
118
+ * unsubscribes. The selection input is independent of `onClick$`, so a row may be selected and
119
+ * noninteractive, interactive and unselected, or both. Rendering and selection changes emit nothing.
120
+ * - The row stays a `tr` in a `table`: no grid role, no arrow-key navigation. `aria-selected` is a
121
+ * WAI-ARIA 1.2 state of `row` and valid here, but Chromium exposes a row's selected state only
122
+ * inside a `grid`, so Chrome and Edge screen readers do not announce it on these rows (accepted
123
+ * limitation, #113). The marker bar is the one guaranteed selection cue.
124
+ * - A table holding a selection input anywhere insets every row's first cell by the cell padding,
125
+ * head and foot included, so the marker has room and no column shifts as the selection moves. A
126
+ * table holding an interactive row makes ring room around itself, so a focused row's ring is never
127
+ * clipped by the scroll region. A table with neither keeps its static geometry exactly.
128
+ *
129
+ * @CallerMustEnsure — the component cannot see these and does not check them
130
+ * - Every row writes exactly as many cells as there are `columns`, in the same order. A row with
131
+ * fewer leaves tracks empty; one with more breaks onto a second line of its own row.
132
+ * - A cell whose content cannot wrap - a `Button`, an `Input`, an image - sits in a column whose
133
+ * fixed width or minimum holds it: the `action` role holds one standard control at comfortable
134
+ * density. The allocation never widens for content, so an under-allocated control overflows its
135
+ * cell rather than moving its neighbours.
136
+ * - A cell's `align` and `variant` are the consumer's as before; an allocation sets neither.
137
+ * - Each row's `onClick$` and `isSelected$` are tied to that row's stable identity, so a reordered or
138
+ * temporarily removed row keeps its association. Table holds no identity, registry or policy, and
139
+ * removing a row never asks the consumer to clear or replace its selection.
140
+ * - What an activation means - select, open, toggle - is the consumer's answer, given by rerendering
141
+ * from its own state. A request left unanswered leaves the rendered selection unchanged.
142
+ * - An interactive row announces no verb of its own; the caption, a row header or a nested link
143
+ * should make the row's purpose plain.
45
144
  *
46
145
  * @UXGuidelines
146
+ * - Allocate by job, not by measurement: the subject's `name` first, proportional with a minimum so it
147
+ * wraps rather than vanishes; comparison facts proportional at `fact`; figures fixed at `figure`,
148
+ * right-aligned; the action column fixed at `action`, last. A theme that re-points a role moves
149
+ * every table using it, which is the point of naming the role rather than the width.
150
+ * - `compact` is for a dense comparison the viewer scans, not for fitting more in: it changes air,
151
+ * not type, so a table that overflows at `comfortable` mostly still overflows at `compact`.
152
+ * - A sortable column is composed, not configured: a `Button variant="plain"` inside the `HeaderCell`
153
+ * carries the label and an `Icon` (`sort`, `sort-ascending`, `sort-descending`) that matches the
154
+ * order the consumer currently displays, and `ariaSort` on the same cell says so to assistive
155
+ * technology. Only the ordered column carries `ariaSort`; an action-only column carries no sort
156
+ * control. Until the consumer's data arrives, both icon and `ariaSort` keep stating the old order.
47
157
  * - `align` is a cell property but reads as a column one: set the same `align` on a `HeaderCell` and
48
158
  * every `Cell` beneath it, and keep them in sync - the component cannot align a column for you.
49
159
  * - Choose `notes` by what the note column *is*: `"supplementary"` drops it below 48rem for everyone
@@ -0,0 +1,3 @@
1
+ export declare class TableConfigurationError extends Error {
2
+ constructor(reason: string);
3
+ }
@@ -1,7 +1,7 @@
1
1
  import type { VariantProps } from 'class-variance-authority';
2
2
  import type { FunctionComponent, ReactNode } from 'react';
3
3
  declare const eyebrow: (props?: ({
4
- color?: "foreground" | "muted" | "success" | "warning" | "error" | "info" | null | undefined;
4
+ color?: "error" | "foreground" | "muted" | "success" | "warning" | "info" | null | undefined;
5
5
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
6
6
  interface IEyebrowProps extends VariantProps<typeof eyebrow> {
7
7
  children: ReactNode;
@@ -1,7 +1,7 @@
1
1
  import type { VariantProps } from 'class-variance-authority';
2
2
  import type { FunctionComponent, ReactNode } from 'react';
3
3
  declare const h1: (props?: ({
4
- color?: "foreground" | "muted" | "success" | "warning" | "error" | "info" | null | undefined;
4
+ color?: "error" | "foreground" | "muted" | "success" | "warning" | "info" | null | undefined;
5
5
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
6
6
  interface IH1Props extends VariantProps<typeof h1> {
7
7
  children: ReactNode;
@@ -12,7 +12,8 @@ interface IH1Props extends VariantProps<typeof h1> {
12
12
  *
13
13
  * @Guarantees — enforced on every render
14
14
  * - Renders an `h1`; its outline level and the display role are one choice, not two (docs/adr/0005).
15
- * - Reads `--font-primary`, sized by `--text-display`, led by `--leading-display` and optically
15
+ * - Reads `--font-heading`, the heading family that follows `--font-primary` until a theme
16
+ * re-points it (#120), sized by `--text-display`, led by `--leading-display` and optically
16
17
  * corrected by `--tracking-optical`, the large-type correction every role from title up carries.
17
18
  * - Bounded at `--measure-display`, the display role's own measure — narrower than the reading column
18
19
  * because bigger type wants fewer characters per line (docs/adr/0004). The bound is the recipe's,
@@ -1,7 +1,7 @@
1
1
  import type { VariantProps } from 'class-variance-authority';
2
2
  import type { FunctionComponent, ReactNode } from 'react';
3
3
  declare const h2: (props?: ({
4
- color?: "foreground" | "muted" | "success" | "warning" | "error" | "info" | null | undefined;
4
+ color?: "error" | "foreground" | "muted" | "success" | "warning" | "info" | null | undefined;
5
5
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
6
6
  interface IH2Props extends VariantProps<typeof h2> {
7
7
  children: ReactNode;
@@ -12,7 +12,8 @@ interface IH2Props extends VariantProps<typeof h2> {
12
12
  *
13
13
  * @Guarantees — enforced on every render
14
14
  * - Renders an `h2`; its outline level and the title role are one choice, not two (docs/adr/0005).
15
- * - Reads `--font-primary`, sized by `--text-title`, led by `--leading-title` and optically corrected
15
+ * - Reads `--font-heading`, the heading family that follows `--font-primary` until a theme
16
+ * re-points it (#120), sized by `--text-title`, led by `--leading-title` and optically corrected
16
17
  * by `--tracking-optical` — the title role is the smallest role that carries it, so `H3` and below
17
18
  * take none.
18
19
  * - `color` selects `foreground`, `muted`, `success`, `warning`, `error` or `info`; nothing else
@@ -1,7 +1,7 @@
1
1
  import type { VariantProps } from 'class-variance-authority';
2
2
  import type { FunctionComponent, ReactNode } from 'react';
3
3
  declare const h3: (props?: ({
4
- color?: "foreground" | "muted" | "success" | "warning" | "error" | "info" | null | undefined;
4
+ color?: "error" | "foreground" | "muted" | "success" | "warning" | "info" | null | undefined;
5
5
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
6
6
  interface IH3Props extends VariantProps<typeof h3> {
7
7
  children: ReactNode;
@@ -13,7 +13,8 @@ interface IH3Props extends VariantProps<typeof h3> {
13
13
  *
14
14
  * @Guarantees — enforced on every render
15
15
  * - Renders an `h3`; its outline level and the subtitle role are one choice, not two (docs/adr/0005).
16
- * - Reads `--font-primary`, sized by `--text-subtitle` and led by `--leading-subtitle`.
16
+ * - Reads `--font-heading`, the heading family that follows `--font-primary` until a theme
17
+ * re-points it (#120), sized by `--text-subtitle` and led by `--leading-subtitle`.
17
18
  * - `color` selects `foreground`, `muted`, `success`, `warning`, `error` or `info`; nothing else
18
19
  * paints text. A status tone changes colour only and adds no announcement semantics.
19
20
  *
@@ -1,7 +1,7 @@
1
1
  import type { VariantProps } from 'class-variance-authority';
2
2
  import type { FunctionComponent, ReactNode } from 'react';
3
3
  declare const h4: (props?: ({
4
- color?: "foreground" | "muted" | "success" | "warning" | "error" | "info" | null | undefined;
4
+ color?: "error" | "foreground" | "muted" | "success" | "warning" | "info" | null | undefined;
5
5
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
6
6
  interface IH4Props extends VariantProps<typeof h4> {
7
7
  children: ReactNode;
@@ -12,7 +12,8 @@ interface IH4Props extends VariantProps<typeof h4> {
12
12
  *
13
13
  * @Guarantees — enforced on every render
14
14
  * - Renders an `h4`; its outline level and the body role are one choice, not two (docs/adr/0005).
15
- * - Reads `--font-primary`, sized by `--text-body`, and is bold so it stands apart from a paragraph.
15
+ * - Reads `--font-heading`, the heading family that follows `--font-primary` until a theme
16
+ * re-points it (#120), sized by `--text-body`, and is bold so it stands apart from a paragraph.
16
17
  * - `color` selects `foreground`, `muted`, `success`, `warning`, `error` or `info`; nothing else
17
18
  * paints text. A status tone changes colour only and adds no announcement semantics.
18
19
  *
@@ -1,7 +1,7 @@
1
1
  import type { VariantProps } from 'class-variance-authority';
2
2
  import type { FunctionComponent, ReactNode } from 'react';
3
3
  declare const h5: (props?: ({
4
- color?: "foreground" | "muted" | "success" | "warning" | "error" | "info" | null | undefined;
4
+ color?: "error" | "foreground" | "muted" | "success" | "warning" | "info" | null | undefined;
5
5
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
6
6
  interface IH5Props extends VariantProps<typeof h5> {
7
7
  children: ReactNode;
@@ -13,7 +13,8 @@ interface IH5Props extends VariantProps<typeof h5> {
13
13
  *
14
14
  * @Guarantees — enforced on every render
15
15
  * - Renders an `h5`; its outline level and the body role are one choice, not two (docs/adr/0005).
16
- * - Reads `--font-primary`, sized by `--text-body`, semibold so it stands apart from a paragraph.
16
+ * - Reads `--font-heading`, the heading family that follows `--font-primary` until a theme
17
+ * re-points it (#120), sized by `--text-body`, semibold so it stands apart from a paragraph.
17
18
  * - `color` selects `foreground`, `muted`, `success`, `warning`, `error` or `info`; nothing else
18
19
  * paints text. A status tone changes colour only and adds no announcement semantics.
19
20
  *
@@ -1,7 +1,7 @@
1
1
  import type { VariantProps } from 'class-variance-authority';
2
2
  import type { FunctionComponent, ReactNode } from 'react';
3
3
  declare const h6: (props?: ({
4
- color?: "foreground" | "muted" | "success" | "warning" | "error" | "info" | null | undefined;
4
+ color?: "error" | "foreground" | "muted" | "success" | "warning" | "info" | null | undefined;
5
5
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
6
6
  interface IH6Props extends VariantProps<typeof h6> {
7
7
  children: ReactNode;
@@ -13,7 +13,8 @@ interface IH6Props extends VariantProps<typeof h6> {
13
13
  *
14
14
  * @Guarantees — enforced on every render
15
15
  * - Renders an `h6`; its outline level and the body role are one choice, not two (docs/adr/0005).
16
- * - Reads `--font-primary`, sized by `--text-body`, medium so it stands apart from a paragraph.
16
+ * - Reads `--font-heading`, the heading family that follows `--font-primary` until a theme
17
+ * re-points it (#120), sized by `--text-body`, medium so it stands apart from a paragraph.
17
18
  * - `color` selects `foreground`, `muted`, `success`, `warning`, `error` or `info`; nothing else
18
19
  * paints text. A status tone changes colour only and adds no announcement semantics.
19
20
  *
@@ -1,7 +1,7 @@
1
1
  import type { VariantProps } from 'class-variance-authority';
2
2
  import type { FunctionComponent, ReactNode } from 'react';
3
3
  declare const note: (props?: ({
4
- color?: "foreground" | "muted" | "success" | "warning" | "error" | "info" | null | undefined;
4
+ color?: "error" | "foreground" | "muted" | "success" | "warning" | "info" | null | undefined;
5
5
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
6
6
  export interface INoteProps extends VariantProps<typeof note> {
7
7
  /** The annotation. A `ReactNode` rather than a string, because these lines carry inline links. */
@@ -1,7 +1,7 @@
1
1
  import type { VariantProps } from 'class-variance-authority';
2
2
  import type { FunctionComponent, ReactNode } from 'react';
3
3
  declare const p: (props?: ({
4
- color?: "foreground" | "muted" | "success" | "warning" | "error" | "info" | null | undefined;
4
+ color?: "error" | "foreground" | "muted" | "success" | "warning" | "info" | null | undefined;
5
5
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
6
6
  interface IPProps extends VariantProps<typeof p> {
7
7
  children: ReactNode;
@@ -12,7 +12,8 @@ interface IPProps extends VariantProps<typeof p> {
12
12
  * table cell — because the reading measure belongs to whatever owns the reading column (Prose #21).
13
13
  *
14
14
  * @Guarantees — enforced on every render
15
- * - Renders a `p`, reading `--font-primary`, sized by `--text-body` and led by `--leading-body`.
15
+ * - Renders a `p`, reading `--font-body` (the body family, which follows `--font-primary` until a
16
+ * theme re-points it, #120), sized by `--text-body` and led by `--leading-body`.
16
17
  * - `color` selects `foreground`, `muted`, `success`, `warning`, `error` or `info`; nothing else
17
18
  * paints text. A status tone changes colour only and adds no announcement semantics.
18
19
  *
@@ -1,7 +1,7 @@
1
1
  import type { VariantProps } from 'class-variance-authority';
2
2
  import type { FunctionComponent, ReactNode } from 'react';
3
3
  declare const proseBody: (props?: ({
4
- color?: "foreground" | "muted" | "success" | "warning" | "error" | "info" | null | undefined;
4
+ color?: "error" | "foreground" | "muted" | "success" | "warning" | "info" | null | undefined;
5
5
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
6
6
  interface IProseRootProps {
7
7
  children?: ReactNode;
@@ -2,7 +2,7 @@ import type { VariantProps } from 'class-variance-authority';
2
2
  import type { FunctionComponent, ReactNode } from 'react';
3
3
  import type { Subject } from 'rxjs';
4
4
  declare const button: (props?: ({
5
- variant?: "primary" | "secondary" | "ghost" | null | undefined;
5
+ variant?: "primary" | "secondary" | "ghost" | "outlined" | "destructive" | "plain" | null | undefined;
6
6
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
7
7
  interface IButtonProps extends VariantProps<typeof button> {
8
8
  /** Optional: an icon-only button renders none, and names itself with `ariaLabel` instead. */
@@ -22,10 +22,27 @@ interface IButtonProps extends VariantProps<typeof button> {
22
22
  *
23
23
  * @component
24
24
  *
25
+ * @Variants
26
+ * - `primary` and `secondary`: the filled actions
27
+ * - `outlined`: the quiet secondary - `secondary` text and edge on an unfilled surface, for an
28
+ * action beside a primary one that must not compete with it
29
+ * - `destructive`: an action that removes or ends something, filled with the `error` status tone
30
+ * and inked with `errorForeground`. The tone reinforces words it never replaces: the label, or
31
+ * the `ariaLabel` of a symbol-only button, must say what the action does
32
+ * - `ghost`: the padded text action with a hover underline and no floor
33
+ *
34
+ * The four faced variants share the control minimum width and inset, so a row of them aligns, and
35
+ * every one of them shrinks and wraps its label where its holder is narrower than that.
36
+ *
25
37
  * @UXGuidelines
26
38
  * - Use clear, action-oriented text (e.g., "Save" instead of "OK")
27
- * - Keep button text concise (1-3 words)
39
+ * - Keep button text short; a translated label that runs longer wraps where the layout constrains
40
+ * it, so the words stay readable rather than overflowing
28
41
  * - Use primary buttons for main actions, secondary buttons for alternative actions
42
+ * - `plain` is a plain action (CONTEXT.md): an operable button in the typography and colour of the
43
+ * text around it, with no face, padding, corner or hover underline of its own. Reach for it where
44
+ * the action belongs to a line of content - a sortable column header composed with `Icon` - and
45
+ * for `ghost` where a quiet but still button-shaped action is wanted
29
46
  * - Maintain consistent button styling throughout the application
30
47
  * - Provide visual feedback on hover/active states
31
48
  * - Ensure sufficient touch target size (minimum 44x44px) for mobile users
@@ -35,8 +52,11 @@ interface IButtonProps extends VariantProps<typeof button> {
35
52
  *
36
53
  * @Accessibility
37
54
  * - Ensure adequate color contrast (4.5:1 minimum ratio)
38
- * - Provide focus styles for keyboard navigation
55
+ * - Provide focus styles for keyboard navigation: every variant, `plain` included, draws the shared
56
+ * focus ring and nothing else changes on focus
57
+ * - A disabled `plain` button is told apart by the `disabled` text tone and keeps native disabled semantics
39
58
  * - Use appropriate ARIA attributes when needed
59
+ * - Status colour never stands alone: a destructive action is named by its words or symbol
40
60
  */
41
61
  export declare const Button: FunctionComponent<IButtonProps>;
42
62
  export {};
@@ -1,4 +1,4 @@
1
- import type { FunctionComponent } from 'react';
1
+ import { type FunctionComponent } from 'react';
2
2
  import type { Subject } from 'rxjs';
3
3
  export interface ISliderProps {
4
4
  /** The operating range's lower bound - interaction geometry, not a content rule (ADR 0009). */
@@ -20,7 +20,10 @@ export interface ISliderProps {
20
20
  * could never read a value at all.
21
21
  */
22
22
  onInput$: Subject<number>;
23
- /** The control's accessible name. */
23
+ /**
24
+ * Always rendered as a visible label associated with the control, the way `Input`'s is (#112);
25
+ * it doubles as the accessible name.
26
+ */
24
27
  label: string;
25
28
  /**
26
29
  * How the value is announced, in the consumer's wording ("$60 a week"). Left out, assistive
@@ -35,7 +38,9 @@ export interface ISliderProps {
35
38
  * A control that sets one numeric value by moving one thumb along a fixed, visible operating
36
39
  * range. Controlled: the consumer holds the value and passes it back in, and a value it does not
37
40
  * pass back is never adopted. It renders the value nowhere - the consumer sets any figures beside
38
- * it with typography - and it fills its container's width the way `Input` does. `disabled` is the
39
- * explicit non-operable state.
41
+ * it with typography - and it fills its container's width the way `Input` does. Its label follows
42
+ * `Input` too: visible, associated, and the only text the component renders (#112). It is still
43
+ * a live control and not a form field, so none of Input's annotations - hint, error, optional
44
+ * marker - come with it. `disabled` is the explicit non-operable state.
40
45
  */
41
46
  export declare const Slider: FunctionComponent<ISliderProps>;
@@ -18,8 +18,10 @@ export interface ITabsTabProps {
18
18
  /** The stable identity connecting this tab to its panel and emitted by selection requests.
19
19
  * Not React's `key`, and never inferred from the label or the position. */
20
20
  value: string;
21
- /** The visible text label. Text only - no icons, no per-tab markup. */
22
- children: string;
21
+ /** The label: visible text naming the view - a plain string still works - optionally with an
22
+ * icon, a count or inline emphasis beside it. Decorative parts carry the consumer's own
23
+ * `aria-hidden`; nothing in it is interactive, as the tab is the control. Rendered whole. */
24
+ children: ReactNode;
23
25
  testId?: string;
24
26
  }
25
27
  export interface ITabsPanelProps {
@@ -47,15 +49,31 @@ export interface ITabsPanelProps {
47
49
  * immediately; focus stays on the operated tab. Up/Down are left to the browser.
48
50
  * - Roving tabindex: Tab enters the list at the active tab, then the active panel - a consistent
49
51
  * Tab stop whether or not its content is focusable. Inactive panels add no stop.
50
- * - The row scrolls horizontally on overflow - one line, no wrap, no shrink - and holds its own
51
- * ring room, so the focused tab's ring survives the scroll clip.
52
+ * - A label is rendered whole, text and markup alike, however long its translation runs: never
53
+ * clipped, shortened, elided or replaced by a hint. The tab's accessible name is computed from
54
+ * its content, so what the device reads is what the viewer sees, less what the consumer marked
55
+ * `aria-hidden`. A rich label and a plain string are one and the same control.
56
+ * - The row is one line of controls. As it narrows, a label's text wraps inside its tab, down to
57
+ * the tab's longest word; only when the controls still cannot fit does the row scroll
58
+ * horizontally - the row, never the page. The row holds its own ring room so the focused tab's
59
+ * ring survives the scroll clip, and focus reveals a scrolled-off tab, so every tab is reachable
60
+ * by keyboard without scrolling first.
61
+ * - The tabs on the row share one height, so every marker sits on one line whether or not a
62
+ * neighbour's label wrapped.
52
63
  * - Selection is marked by a persistent line under the active tab: `--tab-marker-thickness` in
53
64
  * `foreground`, constant thickness in both states, so switching shifts no widths and no weights.
54
65
  * Keyboard focus is the separate shared focus ring. Colour moves on the one motion token.
55
66
  *
56
67
  * @CallerMustEnsure — the component cannot see these and does not check them
57
68
  * - One List with Tab elements as direct DOM children (arrays and fragments are supported),
58
- * and sibling Panels under Root. Do not wrap tabs in host elements.
69
+ * and the Panels under the same Root - as siblings of the List, or with it inside one
70
+ * arrangement such as a `Stack` (see the separation guidance below). Do not wrap tabs in host
71
+ * elements.
72
+ * - A label is a meaningful visible text name, with non-interactive decoration at most: no link,
73
+ * button, input or other focusable descendant, since the tab is the one control and a nested
74
+ * control inside a `button` is invalid. Decorative parts - an icon that repeats the text - carry
75
+ * `aria-hidden` so they stay out of the name; the library does not guess which parts those are.
76
+ * A count or an emphasis is meaning and stays in the name.
59
77
  * - At least two tabs, each `value` unique and stable, each with exactly one matching `Panel`
60
78
  * under the same `Root`, and `active` naming a declared pair. Invalid input is a contract
61
79
  * violation, not a request for a fallback.
@@ -66,10 +84,17 @@ export interface ITabsPanelProps {
66
84
  *
67
85
  * @UXGuidelines
68
86
  * - Labels are short names for views, not actions; the consuming app words and translates them.
87
+ * A long translation wraps inside its tab and the row scrolls only when even the longest words
88
+ * do not fit, so a label is never the reason to shorten a name.
69
89
  * - This is not a router: no location, no history, no deep links. Wire `onSelect$` to whatever
70
90
  * owns the active key and pass that key back in.
91
+ * - Tabs sets no spacing between the row and the panel; the separation is a `Stack` between Root
92
+ * and its members - `<Tabs.Root><Stack gap="region"><Tabs.List/>…<Tabs.Panel/>…</Stack>
93
+ * </Tabs.Root>`. `gap="stack"` holds the row and its view together as one block; `gap="region"`
94
+ * sets the view apart as a region of its own. Inactive panels are hidden, so the gap is exactly
95
+ * one whichever panel is active, from the token the consumer's other groups already use.
71
96
  * - The panel is an opaque slot: compose the view's own rhythm inside it - a `Stack`, a `Prose` -
72
- * as the view owns it; Tabs sets no spacing between the row and the panel.
97
+ * as the view owns it.
73
98
  */
74
99
  export declare const Tabs: {
75
100
  readonly Root: FunctionComponent<ITabsRootProps>;