@juwel-development/design-system 3.9.1 → 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.
- package/README.md +9 -298
- package/dist/design-system.js +1026 -582
- package/dist/index.css +1 -1
- package/dist/types/Arrangement/ColumnLayout/ColumnLayout.d.ts +92 -0
- package/dist/types/Arrangement/ColumnLayout/ColumnLayoutCompositionError.d.ts +3 -0
- package/dist/types/Arrangement/ColumnLayout/ColumnLayoutConfigurationError.d.ts +3 -0
- package/dist/types/Arrangement/FieldRow/FieldRow.d.ts +87 -0
- package/dist/types/Arrangement/FieldRow/FieldRowCompositionError.d.ts +3 -0
- package/dist/types/Arrangement/FieldRow/FieldRowConfigurationError.d.ts +3 -0
- package/dist/types/Arrangement/FieldRow/IControlEdge.d.ts +6 -0
- package/dist/types/Arrangement/FieldRow/alignControlEdges.d.ts +7 -0
- package/dist/types/Display/Box/Box.d.ts +41 -0
- package/dist/types/Display/Brandmark/Brandmark.d.ts +1 -1
- package/dist/types/Display/DefinitionList/DefinitionList.d.ts +59 -9
- package/dist/types/Display/DefinitionList/DefinitionListConfigurationError.d.ts +3 -0
- package/dist/types/Display/Figure/Figure.d.ts +2 -2
- package/dist/types/Display/Icon/Icon.d.ts +26 -0
- package/dist/types/Display/Table/Table.d.ts +118 -8
- package/dist/types/Display/Table/TableConfigurationError.d.ts +3 -0
- package/dist/types/Display/Typography/Eyebrow/Eyebrow.d.ts +1 -1
- package/dist/types/Display/Typography/H1/H1.d.ts +3 -2
- package/dist/types/Display/Typography/H2/H2.d.ts +3 -2
- package/dist/types/Display/Typography/H3/H3.d.ts +3 -2
- package/dist/types/Display/Typography/H4/H4.d.ts +3 -2
- package/dist/types/Display/Typography/H5/H5.d.ts +3 -2
- package/dist/types/Display/Typography/H6/H6.d.ts +3 -2
- package/dist/types/Display/Typography/Note/Note.d.ts +1 -1
- package/dist/types/Display/Typography/P/P.d.ts +3 -2
- package/dist/types/Display/Typography/Prose/Prose.d.ts +1 -1
- package/dist/types/Interaction/Button/Button.d.ts +23 -3
- package/dist/types/Interaction/Tabs/Tabs.d.ts +31 -6
- package/dist/types/Layout/Header/Header.d.ts +67 -8
- package/dist/types/Layout/ScrollContainer/ScrollContainer.d.ts +37 -0
- package/dist/types/Layout/Section/Section.d.ts +1 -1
- package/dist/types/Layout/Sidebar/Sidebar.d.ts +4 -0
- package/dist/types/Theme/Palette.d.ts +31 -7
- package/dist/types/index.d.ts +6 -0
- package/package.json +1 -1
- package/src/Arrangement/ColumnLayout/ColumnLayout.tsx +249 -0
- package/src/Arrangement/ColumnLayout/ColumnLayoutCompositionError.ts +8 -0
- package/src/Arrangement/ColumnLayout/ColumnLayoutConfigurationError.ts +6 -0
- package/src/Arrangement/FieldRow/FieldRow.tsx +313 -0
- package/src/Arrangement/FieldRow/FieldRowCompositionError.ts +6 -0
- package/src/Arrangement/FieldRow/FieldRowConfigurationError.ts +6 -0
- package/src/Arrangement/FieldRow/IControlEdge.ts +6 -0
- package/src/Arrangement/FieldRow/alignControlEdges.ts +35 -0
- package/src/Display/Box/Box.tsx +77 -0
- package/src/Display/Checklist/Checklist.tsx +1 -1
- package/src/Display/DefinitionList/DefinitionList.tsx +164 -22
- package/src/Display/DefinitionList/DefinitionListConfigurationError.ts +6 -0
- package/src/Display/Icon/Icon.tsx +63 -0
- package/src/Display/Table/Table.tsx +434 -44
- package/src/Display/Table/TableConfigurationError.ts +6 -0
- package/src/Display/Typography/H1/H1.tsx +3 -2
- package/src/Display/Typography/H2/H2.tsx +3 -2
- package/src/Display/Typography/H3/H3.tsx +3 -2
- package/src/Display/Typography/H4/H4.tsx +3 -2
- package/src/Display/Typography/H5/H5.tsx +3 -2
- package/src/Display/Typography/H6/H6.tsx +3 -2
- package/src/Display/Typography/P/P.tsx +3 -2
- package/src/Display/Typography/Prose/Prose.tsx +3 -3
- package/src/Interaction/Button/Button.tsx +45 -17
- package/src/Interaction/Input/Input.tsx +1 -1
- package/src/Interaction/MultiSelect/MultiSelect.tsx +1 -1
- package/src/Interaction/NumberInput/NumberInput.tsx +1 -1
- package/src/Interaction/Select/Select.tsx +1 -1
- package/src/Interaction/Tabs/Tabs.tsx +38 -11
- package/src/Interaction/TextArea/TextArea.tsx +1 -1
- package/src/Layout/Dialog/Dialog.tsx +4 -3
- package/src/Layout/Header/Header.tsx +139 -39
- package/src/Layout/PageHead/PageHead.tsx +6 -5
- package/src/Layout/ScrollContainer/ScrollContainer.tsx +149 -0
- package/src/Layout/Sidebar/Sidebar.tsx +8 -4
- package/src/Theme/Palette.ts +37 -9
- package/src/Theme/renderTokens.ts +101 -5
- package/src/index.ts +6 -0
- package/src/tokens.css +68 -4
- package/src/tokens.dark.css +66 -4
- package/src/tokens.light.css +64 -2
package/README.md
CHANGED
|
@@ -11,291 +11,22 @@ npm install @juwel-development/design-system
|
|
|
11
11
|
|
|
12
12
|
`react`, `react-dom` and `rxjs` are peer dependencies - the consumer provides them.
|
|
13
13
|
|
|
14
|
-
##
|
|
14
|
+
## Integration
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
import { Button } from '@juwel-development/design-system';
|
|
18
|
-
import '@juwel-development/design-system/styles.css';
|
|
19
|
-
import { Subject } from 'rxjs';
|
|
20
|
-
|
|
21
|
-
const save$ = new Subject<void>();
|
|
16
|
+
Import the library stylesheet once in the consuming application:
|
|
22
17
|
|
|
23
|
-
|
|
18
|
+
```ts
|
|
19
|
+
import '@juwel-development/design-system/styles.css';
|
|
24
20
|
```
|
|
25
21
|
|
|
26
|
-
|
|
27
|
-
consumer composes with the rest of its reactive code.
|
|
28
|
-
|
|
29
|
-
If the host already imports Tailwind and only wants the palette, take the tokens alone:
|
|
22
|
+
If the host already imports Tailwind and only needs the tokens:
|
|
30
23
|
|
|
31
24
|
```css
|
|
32
25
|
@import "@juwel-development/design-system/tokens.css";
|
|
33
26
|
```
|
|
34
27
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
`Select` is a compound namespace: `Select.Root` renders a labelled, uncontrolled native
|
|
38
|
-
single-select field and `Select.Option` renders one text-only native option. Root starts on
|
|
39
|
-
an empty, selectable placeholder; `required` makes that empty value invalid without choosing an
|
|
40
|
-
option for the user.
|
|
41
|
-
|
|
42
|
-
```tsx
|
|
43
|
-
import { Select } from '@juwel-development/design-system';
|
|
44
|
-
import { Subject } from 'rxjs';
|
|
45
|
-
|
|
46
|
-
const marketChange$ = new Subject<string>();
|
|
47
|
-
|
|
48
|
-
<Select.Root
|
|
49
|
-
label={'Home market'}
|
|
50
|
-
name={'homeMarket'}
|
|
51
|
-
required={true}
|
|
52
|
-
placeholder={'Choose a market'}
|
|
53
|
-
onChange$={marketChange$}
|
|
54
|
-
>
|
|
55
|
-
<Select.Option value={'de'}>{'Germany'}</Select.Option>
|
|
56
|
-
<Select.Option value={'gb'}>{'United Kingdom'}</Select.Option>
|
|
57
|
-
</Select.Root>;
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
`Root` requires `label`, `name`, and `placeholder`; its `children` compose `Select.Option`
|
|
61
|
-
members, including arrays, fragments, conditional children and consumer components that
|
|
62
|
-
render options. An empty field can omit children. Each `Option` requires a `value` and a
|
|
63
|
-
text-only `children` label, and accepts an optional `testId`. Option values must be
|
|
64
|
-
unique, stable, nonempty strings; every label and message is worded by the consumer.
|
|
65
|
-
The empty string is reserved for the placeholder, which remains selectable so an optional
|
|
66
|
-
field can be cleared. The browser owns keyboard navigation and the native popup.
|
|
67
|
-
|
|
68
|
-
Optional props are `required`, `disabled`, `defaultValue`, `onChange$`, `optionalLabel`,
|
|
69
|
-
`hint`, `invalid`, `errorMessage`, and `testId`. Labels always name the control; hints and
|
|
70
|
-
visible errors describe it. `invalid` exposes the consumer's validation state through
|
|
71
|
-
`aria-invalid`, and `errorMessage` renders only while invalid. Styling follows Input's
|
|
72
|
-
control, typography, focus-ring, motion, and state tokens.
|
|
73
|
-
|
|
74
|
-
`defaultValue` initializes a matching option on mount; omitted or unmatched values start
|
|
75
|
-
empty. Later `defaultValue` changes do not overwrite the user's selection. Reordered or
|
|
76
|
-
relabeled options preserve a surviving selected value; removing that option returns the
|
|
77
|
-
control to empty. Options arriving later do not apply an earlier unmatched default.
|
|
78
|
-
|
|
79
|
-
`onChange$` emits the selected string once per user change, including `''` on clearing.
|
|
80
|
-
Rendering, option replacement, and native form reset do not emit. The consumer owns the
|
|
81
|
-
Subject and must reconcile its own domain state when replacing options or resetting a form.
|
|
82
|
-
Native form reset restores the original default while that option remains mounted; if it
|
|
83
|
-
is removed, reset returns to empty. A newly mounted option does not inherit an earlier
|
|
84
|
-
option's reset default, even when it reuses its value. Keep React keys stable (use the
|
|
85
|
-
option value) across translation and reordering to preserve native selection and reset
|
|
86
|
-
state. No `reset$` prop is needed. A new record can initialize through a remount. Forms can
|
|
87
|
-
also read the current value directly by `name`, without any event subscription.
|
|
88
|
-
|
|
89
|
-
The previous unpublished `<Select options={...} />` API has been removed. For Home Market
|
|
90
|
-
in `g-label-manager` #125, put the existing field props on `Select.Root` and map market
|
|
91
|
-
records to `<Select.Option key={market.id} value={market.id}>{translatedName}</Select.Option>`
|
|
92
|
-
children. Keep `required` and the localized `placeholder` on Root; the empty option is
|
|
93
|
-
provided by Root, so callers do not compose another empty Option. Derive types with
|
|
94
|
-
`ComponentProps<typeof Select.Root>` or `ComponentProps<typeof Select.Option>` from React.
|
|
95
|
-
The consumer still awaits a published library release before changing its dependency.
|
|
96
|
-
|
|
97
|
-
## MultiSelect
|
|
98
|
-
|
|
99
|
-
`MultiSelect` is a compound namespace for selecting zero, one or several options
|
|
100
|
-
independently from a finite set: `MultiSelect.Root` renders a labelled one-line dropdown
|
|
101
|
-
trigger with the selected options as individually removable chips, a count for the chips
|
|
102
|
-
that do not fit, and a clear-all control; `MultiSelect.Option` renders one checkable row in
|
|
103
|
-
the dropdown. The consumer owns the options, the selection, every word and what the selected
|
|
104
|
-
set means; the library owns the control, the selection semantics, focus and the presentation.
|
|
105
|
-
|
|
106
|
-
```tsx
|
|
107
|
-
import { MultiSelect } from '@juwel-development/design-system';
|
|
108
|
-
import { BehaviorSubject, Subject } from 'rxjs';
|
|
109
|
-
|
|
110
|
-
const topics$ = new BehaviorSubject<readonly string[]>([]);
|
|
111
|
-
const topicChange$ = new Subject<readonly string[]>();
|
|
112
|
-
topicChange$.subscribe((next) => topics$.next(next));
|
|
113
|
-
|
|
114
|
-
<MultiSelect.Root
|
|
115
|
-
label={'Main topics'}
|
|
116
|
-
selected$={topics$}
|
|
117
|
-
onChange$={topicChange$}
|
|
118
|
-
emptyLabel={'No topics selected'}
|
|
119
|
-
removeLabel={'Remove {label}'}
|
|
120
|
-
clearLabel={'Clear topics'}
|
|
121
|
-
overflowLabel={'+{count}'}
|
|
122
|
-
hint={'Songs match any selected topic.'}
|
|
123
|
-
>
|
|
124
|
-
<MultiSelect.Option value={'family'}>{'Family'}</MultiSelect.Option>
|
|
125
|
-
<MultiSelect.Option value={'love'}>{'Love'}</MultiSelect.Option>
|
|
126
|
-
</MultiSelect.Root>;
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
The same control in German changes only the caller's wording:
|
|
130
|
-
|
|
131
|
-
```tsx
|
|
132
|
-
<MultiSelect.Root
|
|
133
|
-
label={'Hauptthemen'}
|
|
134
|
-
selected$={topics$}
|
|
135
|
-
onChange$={topicChange$}
|
|
136
|
-
emptyLabel={'Keine Themen ausgewählt'}
|
|
137
|
-
removeLabel={'{label} entfernen'}
|
|
138
|
-
clearLabel={'Themen zurücksetzen'}
|
|
139
|
-
overflowLabel={'{count} weitere'}
|
|
140
|
-
>
|
|
141
|
-
<MultiSelect.Option value={'family'}>{'Familie'}</MultiSelect.Option>
|
|
142
|
-
<MultiSelect.Option value={'love'}>{'Liebe'}</MultiSelect.Option>
|
|
143
|
-
</MultiSelect.Root>;
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
`Root` requires `label`, `selected$`, `onChange$`, `emptyLabel`, `removeLabel`, `clearLabel`
|
|
147
|
-
and `overflowLabel`; `hint`, `disabled`, `testId` and `children` are optional. Each `Option`
|
|
148
|
-
requires a unique, stable string `value` and a text-only `children` label, and accepts a
|
|
149
|
-
`testId`. Options are direct children of `Root`; arrays and fragments are supported. The
|
|
150
|
-
wording contract is closed: `removeLabel` replaces `{label}` with the option's label to name a
|
|
151
|
-
chip's removal control, and `overflowLabel` replaces `{count}` with the number of selected
|
|
152
|
-
options hidden behind the count. There are no callback props and no built-in English; a
|
|
153
|
-
missing `hint` renders nothing.
|
|
154
|
-
|
|
155
|
-
**Streams.** `selected$` is a read-only `Observable<readonly string[]>` of the current
|
|
156
|
-
selection. Root renders the latest emission and nothing else: empty before the first
|
|
157
|
-
emission, empty again while a replaced source has not yet emitted, and updated silently on
|
|
158
|
-
every emission whether the dropdown is open, closed or disabled. Use a replaying source such
|
|
159
|
-
as a `BehaviorSubject` or `ReplaySubject(1)` so a remounted control shows the current state
|
|
160
|
-
at once. `onChange$` is a `Subject<readonly string[]>` that receives one fresh full proposed
|
|
161
|
-
selection per user edit - a toggle, a chip removal or clear-all - ordered by option order,
|
|
162
|
-
without duplicates and holding only supplied identities. Handed-in arrays are never mutated.
|
|
163
|
-
Rendering, opening, closing, option updates, source replacement and `selected$` emissions
|
|
164
|
-
never emit. Feed accepted proposals back into `selected$` immediately for ordinary
|
|
165
|
-
interaction; an unanswered proposal leaves the selection unchanged, and repeating the edit
|
|
166
|
-
repeats the proposal. Root subscribes only to `selected$`, unsubscribes on replacement and
|
|
167
|
-
unmount, and never completes either stream or assigns behaviour to their errors or completion.
|
|
168
|
-
|
|
169
|
-
**Caller obligations.** Keep option identities stable across reordering and translation, so
|
|
170
|
-
selection is preserved by identity; map options with the value as the React key. `selected$`
|
|
171
|
-
names supplied identities only: when an update removes options, remove their identities from
|
|
172
|
-
the selection in the same logical update. MultiSelect prunes nothing, invents nothing and
|
|
173
|
-
emits no synthetic change to reconcile invalid input.
|
|
174
|
-
|
|
175
|
-
**Behaviour.** The closed control stays on one line at every width: the leading chips that
|
|
176
|
-
fit are shown in option order, the rest are counted by `overflowLabel`, and at narrow widths
|
|
177
|
-
only the count remains. Overflow is recalculated on width, label and selection changes
|
|
178
|
-
without touching the selection; activating the count opens the dropdown, so every selection
|
|
179
|
-
stays reachable. Long chip labels truncate visually while the removal control keeps the full
|
|
180
|
-
name. The dropdown floats over the page on the shared `--elevation-floating` role, opens above
|
|
181
|
-
the control when the viewport below cannot hold it, is capped to the room it has and scrolls
|
|
182
|
-
its options. `disabled` keeps the selection visible, closes an open dropdown, disables every
|
|
183
|
-
control and emits nothing. With no options the dropdown opens empty.
|
|
184
|
-
|
|
185
|
-
**Keyboard and accessibility.** The visible label names the trigger and the option group; the
|
|
186
|
-
trigger exposes `aria-expanded` and, while open, `aria-controls`. Each option is a
|
|
187
|
-
`role="checkbox"` button with `aria-checked` and a tick that does not depend on colour. Enter,
|
|
188
|
-
Space or ArrowDown on the closed trigger opens the dropdown and focuses the first selected
|
|
189
|
-
option, or the first option; with no options focus stays on the trigger. Tab and Shift+Tab
|
|
190
|
-
traverse chips, clear-all and options normally with no focus trap; Space toggles a focused
|
|
191
|
-
option. Escape closes and returns focus to the trigger. Focus leaving the whole control, an
|
|
192
|
-
outside pointer interaction and the trigger itself close the dropdown without moving focus or
|
|
193
|
-
emitting. Chip removals and clear-all are named, non-submitting buttons outside the trigger:
|
|
194
|
-
a removal that takes its own focused control away moves focus to the next visible removal,
|
|
195
|
-
then the preceding one, then the trigger; clear-all returns focus to the trigger; a chip hidden
|
|
196
|
-
by overflow while focused hands focus to the trigger. This is a consumer-controlled selection
|
|
197
|
-
control, not a form field: it has no `name`, native submission, reset or validation.
|
|
198
|
-
|
|
199
|
-
## NumberInput
|
|
200
|
-
|
|
201
|
-
`NumberInput` is a labelled field for typed amounts and thresholds. It renders a text control
|
|
202
|
-
with a decimal keyboard hint (`inputmode="decimal"`) and keeps the entered text exactly as typed:
|
|
203
|
-
blank, `0`, `-`, `1.`, `1,5`, `1.5` and pasted content such as `12abc` stay distinct, untrimmed,
|
|
204
|
-
untruncated and unconverted. It is a separate roster entry with Input's field anatomy, not an
|
|
205
|
-
Input variant, and it does not extend Input's contract.
|
|
206
|
-
|
|
207
|
-
```tsx
|
|
208
|
-
import { NumberInput } from '@juwel-development/design-system';
|
|
209
|
-
import { Subject } from 'rxjs';
|
|
210
|
-
|
|
211
|
-
const maxPriceInput$ = new Subject<string>();
|
|
212
|
-
const maxPriceReset$ = new Subject<void>();
|
|
213
|
-
|
|
214
|
-
<NumberInput
|
|
215
|
-
label={'Maximum price'}
|
|
216
|
-
name={'maxPrice'}
|
|
217
|
-
placeholder={'No limit'}
|
|
218
|
-
hint={'Leave blank for no limit'}
|
|
219
|
-
defaultValue={savedMaxPrice}
|
|
220
|
-
invalid={maxPriceReading.kind === 'rejected'}
|
|
221
|
-
errorMessage={'Enter an amount such as 12.50'}
|
|
222
|
-
onInput$={maxPriceInput$}
|
|
223
|
-
reset$={maxPriceReset$}
|
|
224
|
-
/>;
|
|
225
|
-
```
|
|
226
|
-
|
|
227
|
-
`label` and `name` are required. Optional props are `required`, `optionalLabel`, `hint`,
|
|
228
|
-
`invalid`, `errorMessage`, `disabled`, `placeholder`, `defaultValue`, `onInput$`, `reset$` and
|
|
229
|
-
`testId`, with Input's meaning. Derive the props type with `ComponentProps<typeof NumberInput>`.
|
|
230
|
-
There are no `min`, `max`, `step`, `pattern` or length props and no steppers, formatting or key
|
|
231
|
-
filtering ([ADR 0009](docs/adr/0009-content-rules-stay-with-the-consumer.md)); the control has
|
|
232
|
-
textbox semantics, not spinbutton semantics.
|
|
233
|
-
|
|
234
|
-
**The consumer owns interpretation.** `onInput$` emits the current text as a string on every user
|
|
235
|
-
edit - typing, pasting and clearing by editing - and never a parsed number, `NaN` or `Infinity`.
|
|
236
|
-
The consumer decides whether the text is blank, unfinished, invalid or an accepted finite number,
|
|
237
|
-
which decimal and grouping conventions it accepts, and when to show an error; it drives `invalid`
|
|
238
|
-
and `errorMessage` in its own language. Validate the whole string before converting (a full-match
|
|
239
|
-
pattern, then `Number`), check the result with `Number.isFinite`, and never accept a numeric prefix
|
|
240
|
-
of invalid text. Blank is not zero: the field never converts one into the other. Integer-only and
|
|
241
|
-
whole-currency rules are consumer rules. The `EnglishParsing` and `GermanParsing` stories show one
|
|
242
|
-
such loop each; the library publishes no parser or locale service.
|
|
243
|
-
|
|
244
|
-
**Saved state and clearing.** `defaultValue` initialises the field on mount only; a later change
|
|
245
|
-
does not replace the current edit, so a mounted field keeps what the user typed across ordinary
|
|
246
|
-
rerenders. To restore saved text - returning to a tab, loading another record - remount the field
|
|
247
|
-
with the saved text as `defaultValue`. `reset$` empties the live node in place, so focus survives,
|
|
248
|
-
and emits nothing on `onInput$`; clear your own saved state alongside it if the field must stay
|
|
249
|
-
empty after a remount. Native form reset restores the form default (`defaultValue`), also silently.
|
|
250
|
-
Neither operation updates consumer-owned state. A disabled field accepts no edits and emits nothing,
|
|
251
|
-
but a `reset$` emission still clears it. Rendering, message changes and remount initialisation emit
|
|
252
|
-
no input events.
|
|
253
|
-
|
|
254
|
-
**Streams.** The component subscribes only to `reset$` and unsubscribes when the Subject is replaced
|
|
255
|
-
or the field unmounts. It only calls `.next()` on `onInput$` and never subscribes to, completes or
|
|
256
|
-
errors either stream; the consumer owns both lifetimes. There is no controlled `value` prop, no
|
|
257
|
-
live value stream and no React callback prop.
|
|
258
|
-
|
|
259
|
-
**Forms and accessibility.** The control works without JavaScript: the form submits the text by
|
|
260
|
-
`name`, and `required` checks presence only, so `1,5` and `twelve` both submit. Numeric validity is
|
|
261
|
-
not guaranteed by the form - a server must parse and validate on a no-JavaScript round-trip and
|
|
262
|
-
re-render the field `invalid` with its own error text, and a JavaScript consumer must itself prevent
|
|
263
|
-
an invalid submission. The label names the control, hint and error describe it, ids are unique per
|
|
264
|
-
instance, and the shared focus ring and field presentation apply in both themes. The device chooses
|
|
265
|
-
the actual keyboard: a decimal separator and digits are requested, but a particular layout, a minus
|
|
266
|
-
key or the exclusion of other characters is not promised.
|
|
267
|
-
|
|
268
|
-
## Collection
|
|
269
|
-
|
|
270
|
-
`Collection` is a vertical group of freely composed items with internal hairlines and
|
|
271
|
-
open outer edges. The library owns spacing and separation; the consumer owns content,
|
|
272
|
-
arrangement and interaction.
|
|
273
|
-
|
|
274
|
-
```tsx
|
|
275
|
-
import { Collection, Link, Note, Stack } from '@juwel-development/design-system';
|
|
276
|
-
|
|
277
|
-
<Collection.Root>
|
|
278
|
-
<Collection.Item>
|
|
279
|
-
<Stack>
|
|
280
|
-
<Link href={'/guide'}>Read the guide</Link>
|
|
281
|
-
<Note color={'muted'}>Supporting information</Note>
|
|
282
|
-
</Stack>
|
|
283
|
-
</Collection.Item>
|
|
284
|
-
<Collection.Item><Note>Freely composed content</Note></Collection.Item>
|
|
285
|
-
</Collection.Root>;
|
|
286
|
-
```
|
|
287
|
-
|
|
288
|
-
Both members accept only `children?: ReactNode` and `testId?: string`. Supply
|
|
289
|
-
`Collection.Item` children directly, through maps, or with conditional omissions.
|
|
290
|
-
The semantic list has no markers, horizontal indent, added focus stops or behavior.
|
|
291
|
-
An empty root renders no placeholder; a single item has no rule.
|
|
292
|
-
|
|
293
|
-
`--space-collection-item` names the vertical padding inside each item, defaulting to
|
|
294
|
-
`1em` above and below so it follows inherited type. This is a separate role from a
|
|
295
|
-
Stack's sibling gap or a Section's band: re-pointing it changes only Collection item
|
|
296
|
-
padding. It is declared in all three token stylesheets and accepts a nonnegative CSS
|
|
297
|
-
length. Hairlines use the existing `--color-border` role. There are no density,
|
|
298
|
-
padding or arrangement props; child components own their typography and wrapping.
|
|
28
|
+
Component APIs, usage guidance, and interactive examples live in Storybook.
|
|
29
|
+
Run `npm run storybook` to browse them.
|
|
299
30
|
|
|
300
31
|
## Theming
|
|
301
32
|
|
|
@@ -307,26 +38,6 @@ on an ancestor re-points every token underneath it.
|
|
|
307
38
|
A product re-themes the whole system by supplying its own values for the same role
|
|
308
39
|
names, which is what lets one design system serve several brands.
|
|
309
40
|
|
|
310
|
-
### Typography status tones
|
|
311
|
-
|
|
312
|
-
Typography whose colour is selectable accepts the general `success`, `warning`, `error`, and
|
|
313
|
-
`info` status tones alongside `foreground` and `muted`. This includes `H1`–`H6`, `Eyebrow`, `P`,
|
|
314
|
-
`Note`, and `Prose.Body`; fixed-colour members such as `Prose.Lede` and `Prose.Tail` remain fixed.
|
|
315
|
-
|
|
316
|
-
```tsx
|
|
317
|
-
<P color={'warning'}>Warning: patience is low and the offer gap is wide.</P>
|
|
318
|
-
```
|
|
319
|
-
|
|
320
|
-
A status tone reinforces status that the content already communicates: never use colour as the
|
|
321
|
-
only cue. Selecting one changes only the semantic text colour and does not add an ARIA role, live
|
|
322
|
-
region, icon, or wording. The caller remains responsible for announcement behavior when a changing
|
|
323
|
-
status needs it.
|
|
324
|
-
|
|
325
|
-
All four palette roles must remain at least 4.5:1 against `surface` in every theme because they can
|
|
326
|
-
paint normal-size and small text. They remain general roles rather than typography-only tokens, so
|
|
327
|
-
constraints from other carriers also apply; `error`, for example, remains Meter's depletion
|
|
328
|
-
endpoint and must keep that complete path at least 3:1 against `meterTrack`.
|
|
329
|
-
|
|
330
41
|
### Single-theme builds
|
|
331
42
|
|
|
332
43
|
`styles.css` and `tokens.css` carry both colour sets, so a product that ships only one
|
|
@@ -357,7 +68,7 @@ palette without regenerating fails the suite rather than shipping stale colours.
|
|
|
357
68
|
|
|
358
69
|
#### Migrating a custom palette to the `scrim` role
|
|
359
70
|
|
|
360
|
-
|
|
71
|
+
`PaletteTokens` includes the required `scrim` role. A consumer that constructs its own
|
|
361
72
|
palette object of this type must add a `scrim` colour - an `rgb(r g b / a)` value whose alpha is
|
|
362
73
|
part of the role, `rgb(15 23 42 / 0.5)` being the shipped default. Spreading `light`/`dark` and
|
|
363
74
|
overriding stays valid unchanged, and a theme that only overrides the generated
|
|
@@ -383,7 +94,7 @@ src/<Category>/<Component>/<Component>.tsx
|
|
|
383
94
|
<Component>.spec.tsx
|
|
384
95
|
```
|
|
385
96
|
|
|
386
|
-
`Category` is `Interaction`, `Display` or `Layout`. Imports inside `src` are written
|
|
97
|
+
`Category` is `Arrangement`, `Interaction`, `Display` or `Layout`. Imports inside `src` are written
|
|
387
98
|
from the source root (`Interaction/Button/Button`), not relatively.
|
|
388
99
|
|
|
389
100
|
## Releasing
|