@carissa-io/theme-default 0.3.9 → 0.3.11

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/DESIGN.md CHANGED
@@ -10,7 +10,7 @@ never changes to the components.
10
10
 
11
11
  ## Principles
12
12
  - **Objective clarity:** information is prioritized over decoration.
13
- - **Mathematical grid:** precise alignment and spacing, square corners.
13
+ - **Mathematical grid:** precise alignment and spacing, square corners. "Square" means the token `$radius: 0`: every component reads that one token (the radio dot is the only round shape, and the date range band stays unrounded so it is unbroken), so the theme is square because the token is 0, and changing the token rounds all of it.
14
14
  - **High contrast:** absolute black (`#000`) for primary actions and focus; neutral scales for meta-data.
15
15
 
16
16
  ## Palette
@@ -18,9 +18,9 @@ never changes to the components.
18
18
  | :--- | :--- |
19
19
  | Primary | `#000000` |
20
20
  | Surface | `#fafafa` / `#f5f5f5` |
21
- | Border (fields) | `#737373` (hover and focus: `#000`, focus adds a 1px ring) |
21
+ | Border (fields) | `#666666` (hover and focus: `#000`, focus adds a 1px ring) |
22
22
  | Text (body) | `#171717` |
23
- | Text (meta, placeholder) | `#737373` (4.7:1; lighter greys only for disabled states) |
23
+ | Text (meta, placeholder) | `#666666` (5.7:1; lighter greys only for disabled states) |
24
24
 
25
25
  The same palette is available as Sass tokens (`$black`, `$muted`, `$surface`, `$error`...) in
26
26
  [scss/_tokens.scss](scss/_tokens.scss), the machine-readable palette: apps read them (for example to configure Bootstrap)
@@ -35,7 +35,7 @@ Both modes ship in the same package; dark is **opt-in per app** with `data-mode`
35
35
  | :--- | :--- | :--- |
36
36
  | `background` | `#ffffff` | `#0a0a0a` |
37
37
  | `text` | `#171717` | `#f5f5f5` |
38
- | `muted` | `#737373` | `#a3a3a3` |
38
+ | `muted` | `#666666` | `#a3a3a3` |
39
39
  | `surface` | `#f5f5f5` | `#171717` |
40
40
  | `surface-active` | `#e5e5e5` | `#262626` |
41
41
  | `primary` | `#000000` | `#ffffff` |
@@ -63,8 +63,9 @@ Contrast (WCAG 2.x): text 4.5:1, borders and the focus ring 3:1.
63
63
  | Pair | Light | Dark |
64
64
  | :--- | :--- | :--- |
65
65
  | `text` on `background` | 17.93 | 18.16 |
66
- | `muted` on `background` (labels, placeholders, field borders) | 4.74 | 7.85 |
67
- | `muted` on `surface` | 4.35 | 7.11 |
66
+ | `muted` on `background` (labels, placeholders, field borders) | 5.74 | 7.85 |
67
+ | `muted` on `surface` | 5.27 | 7.11 |
68
+ | `muted` on `surface-active` | 4.56 | 6.00 |
68
69
  | `on-primary` on `primary` | 21.00 | 21.00 |
69
70
  | `error` on `background` | 4.53 | 7.16 |
70
71
  | `focus` on `background` | 21.00 | 19.80 |
@@ -76,8 +77,8 @@ Contrast (WCAG 2.x): text 4.5:1, borders and the focus ring 3:1.
76
77
 
77
78
  The light `error` on `surface` (4.15) is below 4.5 for text, so the Alert never sets text in the status color (only the icon and the accent border, which need 3:1).
78
79
 
79
- `muted` on `surface` in light (4.35) is a known gap that already existed (muted text on the hover surface); the disabled states are exempt from
80
- WCAG but are listed in the backlog for a contrast review. Known gap in dark: the 0-width listbox option separator keeps the component default
80
+ Every `muted` text pairing passes AA in both modes (a test asserts it): the light muted was `#737373`, which gave 4.35 on `surface` and 3.76 on
81
+ `surface-active`, so it is now `#666666` (5.74 / 5.27 / 4.56). The disabled states are exempt from WCAG but are listed in the backlog for a contrast review. Known gap in dark: the 0-width listbox option separator keeps the component default
81
82
  (`#dee2e6`); it draws nothing today.
82
83
 
83
84
  ## Typography
@@ -114,7 +115,7 @@ Field text is 1rem (16px, which also avoids the zoom-on-focus of mobile browsers
114
115
  Three variants from [components-button](components/components-button), all square, 44px tall, with the same uppercase,
115
116
  bold, tracked 11px label as the field labels and 200ms color transitions.
116
117
  - **Primary:** black background, white text; hover `#262626`, active `#404040`.
117
- - **Secondary:** white background, 1px `#737373` border; on hover the border turns black and the background becomes `#e5e5e5` (`--c-surface-active`), so the state reads without relying on the border alone.
118
+ - **Secondary:** white background, 1px `#666666` border; on hover the border turns black and the background becomes `#e5e5e5` (`--c-surface-active`), so the state reads without relying on the border alone.
118
119
  - **Ghost:** transparent; a `#f5f5f5` surface appears on hover.
119
120
  - **Disabled:** `#a3a3a3` text on a `#f5f5f5` surface (primary), or a lighter border (secondary, ghost); no hover.
120
121
  - **Focus:** keyboard focus (`:focus-visible`) follows the general rule below: secondary and ghost thicken their border to
@@ -123,7 +124,7 @@ bold, tracked 11px label as the field labels and 200ms color transitions.
123
124
  ## Checkbox
124
125
  From [components-checkbox](components/components-checkbox): the whole row (box and label) is the click target and is at
125
126
  least 44px tall. The label is body text (1rem, `#171717`), not the uppercase field-label style.
126
- - **Box:** 20px square, square corners, 1px `#737373` border; the border turns black on hover.
127
+ - **Box:** 20px square, square corners, 1px `#666666` border; the border turns black on hover.
127
128
  - **Checked:** black background with a white Lucide `check` mark.
128
129
  - **Disabled:** `#a3a3a3` label and mark on a `#f5f5f5` box with a `#d4d4d4` border; no hover.
129
130
  - **Error:** `#dc3545`, 13px, under the row; the box border turns red too. The built-in error shows after the first
@@ -133,17 +134,17 @@ least 44px tall. The label is body text (1rem, `#171717`), not the uppercase fie
133
134
 
134
135
  ## Switch
135
136
  From [components-switch](components/components-switch), a checkbox with `role="switch"`, same row, label and error.
136
- - **Track:** 40 x 20px, square, white with a 1px `#737373` border (black on hover); a `#737373` square thumb.
137
+ - **Track:** 40 x 20px, square, white with a 1px `#666666` border (black on hover); a `#666666` square thumb.
137
138
  - **On:** black track, white thumb; the thumb slides in 200ms, like the other color transitions.
138
139
  - **Disabled:** `#f5f5f5` track with a `#d4d4d4` border and an `#a3a3a3` thumb.
139
140
  - **Focus:** an off track thickens its border to 2px; an on (black) track draws a 1px black ring offset by 2px.
140
141
 
141
142
  ## Radio group
142
143
  From [components-radio-group](components/components-radio-group): a fieldset whose legend uses the field-label style
143
- (11px, bold, uppercase, tracked, `#737373`). Each option row (dot and label) is the click target and is at least 44px
144
+ (11px, bold, uppercase, tracked, `#666666`). Each option row (dot and label) is the click target and is at least 44px
144
145
  tall; the label is body text (1rem, `#171717`).
145
146
  - **Dot:** 20px circle (the one round shape in the theme: a radio is a circle, so its radius is not the square `$radius`),
146
- 1px `#737373` border; the border turns black on hover.
147
+ 1px `#666666` border; the border turns black on hover.
147
148
  - **Checked:** black border with a 10px black inner dot on white.
148
149
  - **Disabled:** `#a3a3a3` label on a `#f5f5f5` dot with a `#d4d4d4` border; no hover. A single option can be disabled too.
149
150
  - **Error:** `#dc3545`, 13px, under the options; the dots get a red border. The built-in error shows after the first
@@ -153,7 +154,7 @@ tall; the label is body text (1rem, `#171717`).
153
154
 
154
155
  ## Textarea
155
156
  From [components-textarea](components/components-textarea), which is the input with `multiline`: it uses exactly the
156
- input's look (notched uppercase label, 1px `#737373` border that turns black on hover and focus, 1px black focus ring,
157
+ input's look (notched uppercase label, 1px `#666666` border that turns black on hover and focus, 1px black focus ring,
157
158
  `#dc3545` error). Only the control differs:
158
159
  - **Height:** at least 6rem (about three lines of 16px text) and resizable vertically only, so it never breaks the grid;
159
160
  `rows` sets the initial height.
@@ -161,9 +162,9 @@ input's look (notched uppercase label, 1px `#737373` border that turns black on
161
162
 
162
163
  ## Slider
163
164
  From [components-slider](components/components-slider), a native range input; the row is at least 44px tall. The label
164
- uses the field-label style (11px, bold, uppercase, tracked, `#737373`); the optional live value, on the same line at
165
+ uses the field-label style (11px, bold, uppercase, tracked, `#666666`); the optional live value, on the same line at
165
166
  the right, is body text (1rem, `#171717`).
166
- - **Track:** a 2px `#737373` line, square; the part before the thumb is black.
167
+ - **Track:** a 2px `#666666` line, square; the part before the thumb is black.
167
168
  - **Thumb:** 20px black square with a 1px white border (so it stays distinct where it crosses the track); `#262626` on
168
169
  hover, `#404040` while dragging.
169
170
  - **Disabled:** `#a3a3a3` label, thumb and fill on a `#d4d4d4` track; no hover.
@@ -171,7 +172,7 @@ the right, is body text (1rem, `#171717`).
171
172
 
172
173
  ## Popover
173
174
  From [components-popover](components/components-popover), the floating panel behind the select, combobox and date picker:
174
- white, a 1px `#737373` border, square corners and no shadow (flat, like the rest of the theme), 4px below its anchor.
175
+ white, a 1px `#666666` border, square corners and no shadow (flat, like the rest of the theme), 4px below its anchor.
175
176
  It fades in over 200ms and opens above the anchor when there is no room below. It has no padding of its own: the
176
177
  content (a list, a calendar) brings its spacing.
177
178
 
@@ -181,32 +182,32 @@ least 44px tall, body text (1rem, `#171717`) with 0.75rem of horizontal padding.
181
182
  - **Active** (hover or keyboard cursor): `#f5f5f5` surface.
182
183
  - **Selected:** bold text with a 2px black bar on the inline-start edge.
183
184
  - **Disabled:** `#a3a3a3`, no hover.
184
- - **Empty:** a `#737373` message with 0.75rem of padding.
185
+ - **Empty:** a `#666666` message with 0.75rem of padding.
185
186
  - **Height:** at most 15rem; longer lists scroll.
186
187
 
187
188
  ## Select
188
189
  From [components-select](components/components-select): a custom list, so it looks the same in every browser. The closed
189
- field is the Input (notched uppercase label, 1px `#737373` border, black on hover and focus, 44px tall); the value reads
190
- as body text, and the placeholder is `#737373` like the input placeholder. A 16px `#737373` chevron sits on the right
190
+ field is the Input (notched uppercase label, 1px `#666666` border, black on hover and focus, 44px tall); the value reads
191
+ as body text, and the placeholder is `#666666` like the input placeholder. A 16px `#666666` chevron sits on the right
191
192
  and turns black; when the list is open it rotates 180 degrees over 200ms and the border stays black. The open panel is
192
193
  the Popover and the options are the Listbox, as wide as the field. Errors, required marker and disabled look are the
193
194
  Input's.
194
195
 
195
196
  ## Combobox
196
197
  From [components-combobox](components/components-combobox): the Select field with a text input in place of the button, so
197
- the user can type to filter the list. Same Input look (notched uppercase label, 1px `#737373` border, black on hover and
198
- focus, 44px tall), same 16px `#737373` chevron that turns black and rotates 180 degrees when the list is open. The text
199
- is body text (`#171717`) and the placeholder `#737373`. A press anywhere in the box focuses the input (the chevron is
198
+ the user can type to filter the list. Same Input look (notched uppercase label, 1px `#666666` border, black on hover and
199
+ focus, 44px tall), same 16px `#666666` chevron that turns black and rotates 180 degrees when the list is open. The text
200
+ is body text (`#171717`) and the placeholder `#666666`. A press anywhere in the box focuses the input (the chevron is
200
201
  decorative, not a button); the list opens when typing or with the arrow keys. The panel is the Popover and the options
201
202
  the Listbox, as wide as the field; a message such as "No results" uses the Listbox empty style. Errors, required marker
202
203
  and disabled look are the Input's.
203
204
 
204
205
  ## Upload
205
206
  From [components-upload](components/components-upload): the Input field turned into a dropzone. The box keeps the
206
- notched uppercase label and the 1px `#737373` border but dashed, black on hover; while files are dragged over it the
207
- border is black and the background `#f5f5f5`. The zone is at least 5.5rem tall and centers its content as muted (`#737373`)
207
+ notched uppercase label and the 1px `#666666` border but dashed, black on hover; while files are dragged over it the
208
+ border is black and the background `#f5f5f5`. The zone is at least 5.5rem tall and centers its content as muted (`#666666`)
208
209
  body text, and the whole box is the click target. Chosen files are listed below the zone as 44px rows split by a 1px
209
- `#e5e5e5` line: the name in body text with an ellipsis, and a 44px square remove button (a multiplication sign, `#737373`,
210
+ `#e5e5e5` line: the name in body text with an ellipsis, and a 44px square remove button (a multiplication sign, `#666666`,
210
211
  black on hover, 1px black focus ring). The focus ring of the box is the Input's. Errors, required marker and disabled look
211
212
  (`#a3a3a3` on `#f5f5f5`) are the Input's.
212
213
 
@@ -214,7 +215,7 @@ black on hover, 1px black focus ring). The focus ring of the box is the Input's.
214
215
  From [components-calendar](components/components-calendar): a standalone month grid (the date picker shows it inside the
215
216
  Popover, which draws the only border). It has no outer border. The header is two 44px navigation buttons (muted
216
217
  chevrons that turn black, 1px black focus ring drawn inside) around the month and year in the label style (11px, bold,
217
- uppercase, `0.1em` tracking, `#737373`); the weekday row uses the same style. Days are 44px squares in body text
218
+ uppercase, `0.1em` tracking, `#666666`); the weekday row uses the same style. Days are 44px squares in body text
218
219
  (`#171717`) with square corners and a `#f5f5f5` hover. The selected day is a black square with white text; today is a
219
220
  1px black border, drawn inside the square so nothing shifts. Days outside `min..max` are `#a3a3a3` with no hover, and
220
221
  the days of the neighbor months are left empty. The keyboard focus ring is 1px black, inset. The grid always shows six
@@ -222,20 +223,20 @@ weeks so the height never changes between months.
222
223
 
223
224
  ## Date picker
224
225
  From [components-date-picker](components/components-date-picker): the Select field with a calendar in the panel. The closed
225
- field is the Input (notched uppercase label, 1px `#737373` border, black on hover and focus, 44px tall); the chosen date
226
- reads as body text (for example "Oct 14, 2026") and the placeholder is `#737373`. A 16px `#737373` calendar icon sits on the
226
+ field is the Input (notched uppercase label, 1px `#666666` border, black on hover and focus, 44px tall); the chosen date
227
+ reads as body text (for example "Oct 14, 2026") and the placeholder is `#666666`. A 16px `#666666` calendar icon sits on the
227
228
  right and turns black on hover and while open; it does not rotate, and the border stays black while open. The open panel
228
- is the Popover (1px `#737373` border, no shadow) with 0.5rem of padding around the Calendar, as wide as the calendar
229
+ is the Popover (1px `#666666` border, no shadow) with 0.5rem of padding around the Calendar, as wide as the calendar
229
230
  needs, not as the field. Errors, required marker and disabled look are the Input's.
230
231
 
231
232
  ## Search
232
233
  From [components-search](components/components-search): the Input of type search with two inline parts built on the input's
233
- `start` and `end` slots. The field is the Input's (notched uppercase label, 1px `#737373` border, black on hover and focus,
234
- 44px tall; the keyboard focus rule below applies). A 20px `#737373` magnifier icon sits on the left and turns black while the
235
- field has the focus. The clear button appears on the right only while there is text: a 16px `#737373` cross in the Input's
234
+ `start` and `end` slots. The field is the Input's (notched uppercase label, 1px `#666666` border, black on hover and focus,
235
+ 44px tall; the keyboard focus rule below applies). A 20px `#666666` magnifier icon sits on the left and turns black while the
236
+ field has the focus. The clear button appears on the right only while there is text: a 16px `#666666` cross in the Input's
236
237
  44px side-button box that turns black on hover; the browser's own clear button is hidden so the look is the same everywhere.
237
238
  Errors, required marker and disabled look are the Input's. The Input and Textarea accept `start` and `end` content (a prefix
238
- icon, a unit) in the same muted color (`#737373`) with `0.5rem` between the content and the text.
239
+ icon, a unit) in the same muted color (`#666666`) with `0.5rem` between the content and the text.
239
240
 
240
241
  ## Card
241
242
  From [components-card](components/components-card): `Card` with `CardHeader`, `CardBody` and `CardFooter`; the title and description are the Typography
@@ -275,7 +276,7 @@ placements. The look of each toast is the Alert's (see above), so its colors, ro
275
276
  only the fade stays. The region is in the page, not in the browser's top layer, so a toast shown while a modal is open sits behind the modal's backdrop.
276
277
 
277
278
  ## Divider
278
- From [components-divider](components/components-divider): `Divider`, a 1px line in `--c-muted` (the edge of the cards and fields: 4.74:1 on the background in light, 7.85:1 in dark),
279
+ From [components-divider](components/components-divider): `Divider`, a 1px line in `--c-muted` (the edge of the cards and fields: 5.74:1 on the background in light, 7.85:1 in dark),
279
280
  1rem of space around it. With a label the text is the small `--c-muted` text (0.875rem, same contrast) with 0.75rem between it and each line. Horizontal is an `hr`, vertical and labelled
280
281
  ones are `role="separator"`.
281
282
 
@@ -283,7 +284,7 @@ ones are `role="separator"`.
283
284
  From [components-badge](components/components-badge): `Badge` in six variants and two sizes (`md` 0.75rem, `sm` 0.6875rem, bold), square like the rest, plus a `dot`. The variants are **tinted
284
285
  like the Alert**: 12% of the role color (`--c-primary`, `--c-success`, `--c-warning`, `--c-error`, `--c-info`) over `--c-background` with `color-mix`, a 1px border at 35% of the same color.
285
286
  **The text is always `--c-text`**, never the status color: it is 13.6:1 or more on every tint in both modes (light 13.63 to 15.32, dark 13.63 to 15.87), where the raw light `error` on its own tint would
286
- fail. Neutral is `--c-surface` with a `--c-muted` border (text 16.44:1). The **dot** carries the role color (`--c-muted` for neutral) and is 3:1 or more as a graphic on its tint in both modes: in light 3.81 (error), 4.21 (warning), 4.27 (success), 5.56 (info), 15.97 (primary) and 4.35 (neutral); in dark 6.25 or more. A dot has no text, so the caller gives it `aria-label`.
287
+ fail. Neutral is `--c-surface` with a `--c-muted` border (text 16.44:1). The **dot** carries the role color (`--c-muted` for neutral) and is 3:1 or more as a graphic on its tint in both modes: in light 3.81 (error), 4.21 (warning), 4.27 (success), 5.56 (info), 15.97 (primary) and 5.27 (neutral); in dark 6.25 or more. A dot has no text, so the caller gives it `aria-label`.
287
288
 
288
289
  ## Tag
289
290
  From [components-tag](components/components-tag): `Tag`, a chip with the same six tints, the same text rule (`--c-text`, same ratios) and square corners as the Badge, bold, 0.875rem (`md`) or 0.75rem (`sm`). A removable tag is
@@ -291,7 +292,7 @@ one tab stop and takes the theme focus ring (1px `--c-focus`, offset 2px). The r
291
292
 
292
293
  ## Link
293
294
  From [components-link](components/components-link): `Link` in `default`, `subtle` and `inline`. The default is `--c-primary` (black in light, white in dark: 21:1 and 19.8:1 on the background), bold, **always underlined**: a black link
294
- beside near-black text is told apart by the underline alone, so there is no un-underlined default. Hover is `--c-primary-hover`. `subtle` is `--c-muted` (4.74:1 light, 7.85:1 dark), `--c-text` on hover. Focus is the theme ring
295
+ beside near-black text is told apart by the underline alone, so there is no un-underlined default. Hover is `--c-primary-hover`. `subtle` is `--c-muted` (5.74:1 light, 7.85:1 dark), `--c-text` on hover. Focus is the theme ring
295
296
  (1px `--c-focus`, offset 2px). Disabled is `--c-disabled-text` at full opacity (exempt from contrast, as the other disabled states).
296
297
 
297
298
  ## Avatar
@@ -315,42 +316,42 @@ or focus, stays open while the pointer is over it and closes on `Escape`; the bu
315
316
  ## Prose
316
317
  From [components-prose](components/components-prose): `Prose`, raw HTML content (markdown, a CMS, an article) with the same hierarchy as `Heading` and `Text`. Body text is `--c-text` at 1rem and a 1.6 line height, 65ch wide (small and
317
318
  large scale the text and the headings by 0.875 and 1.125). Headings use the type scale tokens: `h1` and `h2` black (900), `h3` to `h6` bold, with the line heights of the typography block. Links are `--c-primary` and always underlined
318
- (21:1 light, 19.8:1 dark), `--c-primary-hover` on hover (15.13:1 and 15.72:1). The quote edge, the `hr` and the table borders are `--c-muted` (4.74:1 light, 7.85:1 dark, above the 3:1 of a graphic); the quote text and the figure
319
+ (21:1 light, 19.8:1 dark), `--c-primary-hover` on hover (15.13:1 and 15.72:1). The quote edge, the `hr` and the table borders are `--c-muted` (5.74:1 light, 7.85:1 dark, above the 3:1 of a graphic); the quote text and the figure
319
320
  caption are `--c-muted` on the page background too. Code, `pre`, `kbd` and the table head sit on `--c-surface` with `--c-text` (16.44:1 in both modes); `kbd` has a `--c-muted` edge. Everything is square (`$radius`), code uses `$font-family-mono`.
320
- `--c-muted` is not used for text on `--c-surface` (4.35:1 in light).
321
+ `--c-muted` on `--c-surface` is 5.27:1 in light (AA), though the code and table text stay `--c-text`.
321
322
 
322
323
  ## Tabs
323
- From [components-tabs](components/components-tabs): `Tabs`, `TabList`, `Tab` and `TabPanel`. A 1px `--c-muted` line runs under the list (4.74:1 light, 7.85:1 dark) and the selected tab has a 2px `--c-primary` indicator on it (21:1 light, 19.8:1 dark, well above the 3:1 of a graphic) with
324
- bold `--c-text` (17.93:1 / 18.16:1). The other tabs are bold `--c-muted` (4.74:1 / 7.85:1) and turn `--c-text` on a `--c-surface` hover (16.44:1 in both modes); `--c-muted` is never used for text on `--c-surface` (4.35:1 in light), so the hover changes the text too.
324
+ From [components-tabs](components/components-tabs): `Tabs`, `TabList`, `Tab` and `TabPanel`. A 1px `--c-muted` line runs under the list (5.74:1 light, 7.85:1 dark) and the selected tab has a 2px `--c-primary` indicator on it (21:1 light, 19.8:1 dark, well above the 3:1 of a graphic) with
325
+ bold `--c-text` (17.93:1 / 18.16:1). The other tabs are bold `--c-muted` (5.74:1 / 7.85:1) and turn `--c-text` on a `--c-surface` hover (16.44:1 in both modes); `--c-muted` on `--c-surface` is 5.27:1 in light (AA), and the hover still changes the text color too.
325
326
  Square, 0.875rem, 1.25rem by 0.625rem of padding. The indicator is a bottom edge (horizontal) or an inline end edge (vertical). Keyboard focus is the theme's 1px `--c-focus` ring, drawn inside the tab so the scrolling list does not clip it.
326
327
 
327
328
  ## Accordion
328
- From [components-accordion](components/components-accordion): `Accordion` and `AccordionItem`. The box and the rules between the items are 1px `--c-muted` (4.74:1 / 7.85:1); each header is a full-width bold `--c-text` button (17.93:1 / 18.16:1) with a `--c-surface` hover (16.44:1) and a chevron
329
+ From [components-accordion](components/components-accordion): `Accordion` and `AccordionItem`. The box and the rules between the items are 1px `--c-muted` (5.74:1 / 7.85:1); each header is a full-width bold `--c-text` button (17.93:1 / 18.16:1) with a `--c-surface` hover (16.44:1) and a chevron
329
330
  that follows the text color and turns over when the item opens. 1.25rem of inline padding, 0.875rem of block padding on the header and 1rem on the content, square. Focus is the theme's 1px `--c-focus` ring, inside the header. The chevron is CSS, not an icon.
330
331
 
331
332
  ## Breadcrumb
332
- From [components-breadcrumb](components/components-breadcrumb): `Breadcrumb` and `BreadcrumbItem`. Links and separators are `--c-muted` (4.74:1 on the page in light, 7.85:1 in dark), links gain an underline and turn `--c-text` on hover (17.93:1 / 18.16:1), and the current page is bold `--c-text`
333
+ From [components-breadcrumb](components/components-breadcrumb): `Breadcrumb` and `BreadcrumbItem`. Links and separators are `--c-muted` (5.74:1 on the page in light, 7.85:1 in dark), links gain an underline and turn `--c-text` on hover (17.93:1 / 18.16:1), and the current page is bold `--c-text`
333
334
  and not a link. 0.875rem text. Focus is the theme's 1px `--c-focus` ring, offset 2px. The separator is generated content, so screen readers do not read it.
334
335
 
335
336
  ## Pagination
336
337
  From [components-pagination](components/components-pagination): `Pagination`. The controls follow the secondary button: `--c-background` with a 1px `--c-muted` border, bold `--c-text` (17.93:1 / 18.16:1), `--c-surface-active` on hover (14.23:1 / 13.88:1),
337
- square, 44px minimum (the theme's touch target). The current page is filled: `--c-on-primary` on `--c-primary`, 21:1 in both modes. The ellipsis is `--c-muted` (4.74:1 / 7.85:1) and previous and next, drawn as CSS chevrons, are disabled
338
+ square, 44px minimum (the theme's touch target). The current page is filled: `--c-on-primary` on `--c-primary`, 21:1 in both modes. The ellipsis is `--c-muted` (5.74:1 / 7.85:1) and previous and next, drawn as CSS chevrons, are disabled
338
339
  at the ends (half opacity). Focus is the theme's 1px `--c-focus` ring, offset 2px.
339
340
 
340
341
  ## Stepper
341
342
  From [components-stepper](components/components-stepper): `Stepper` and `Step`. The status is carried by the marker shape and the hidden status text, never by color alone: a done step is a filled `--c-primary` marker with a `--c-on-primary` check (21:1), the current one an outlined 2px
342
343
  `--c-primary` marker on `--c-background` with its number in `--c-text` (17.93:1 / 18.16:1), an upcoming one a `--c-surface-active` marker with its number in `--c-text` (14.23:1 / 13.88:1), and an error one a `--c-error` marker with a `--c-on-primary` mark
343
- (4.53:1 light, 7.59:1 dark) and a `--c-error` title (4.53:1 / 7.16:1). Titles are bold `--c-text`; upcoming titles and the descriptions are `--c-muted` (4.74:1 / 7.85:1). The connectors are 2px `--c-muted` lines (4.74:1 / 7.85:1, above 3:1)
344
+ (4.53:1 light, 7.59:1 dark) and a `--c-error` title (4.53:1 / 7.16:1). Titles are bold `--c-text`; upcoming titles and the descriptions are `--c-muted` (5.74:1 / 7.85:1). The connectors are 2px `--c-muted` lines (5.74:1 / 7.85:1, above 3:1)
344
345
  and `--c-primary` once the step is done. Clickable (completed) steps are buttons with the theme's 1px `--c-focus` ring, offset 2px.
345
346
 
346
347
  ## Sidebar
347
- From [components-sidebar](components/components-sidebar): `Sidebar` and its parts. A `--c-surface` column (16rem, 4.5rem collapsed) with a 1px `--c-muted` edge (4.74:1 light, 7.85:1 dark). Items are bold `--c-text` (16.44:1 on the surface in both modes), 0.875rem with 0.625rem of block padding, with a `--c-surface-active`
348
+ From [components-sidebar](components/components-sidebar): `Sidebar` and its parts. A `--c-surface` column (16rem, 4.5rem collapsed) with a 1px `--c-muted` edge (5.74:1 light, 7.85:1 dark). Items are bold `--c-text` (16.44:1 on the surface in both modes), 0.875rem with 0.625rem of block padding, with a `--c-surface-active`
348
349
  hover (14.23:1 light, 13.88:1 dark). The current page is filled: `--c-on-primary` on `--c-primary`, 21:1 in both modes, so it is told apart by shape and weight of fill, not by hue alone. Section titles use the label style (small, bold, uppercase, tracking) in `--c-text` and not
349
- `--c-muted`, which is only 4.35:1 on the surface in light. A disabled item is half opacity and not a link. The toggle is a 44px square, `--c-text` on a `--c-surface-active` hover. Square; focus is the theme's 1px `--c-focus` ring, inside the item.
350
+ `--c-muted`, which is 5.27:1 on the surface in light. A disabled item is half opacity and not a link. The toggle is a 44px square, `--c-text` on a `--c-surface-active` hover. Square; focus is the theme's 1px `--c-focus` ring, inside the item.
350
351
  When collapsed the labels stay in the accessibility tree.
351
352
 
352
353
  ## Navbar
353
- From [components-navbar](components/components-navbar): `Navbar` and its parts. A `--c-background` bar with a 1px `--c-muted` bottom edge. The brand is bold `--c-text` (17.93:1 light, 18.16:1 dark). Items read like the tabs: bold `--c-muted` (4.74:1 / 7.85:1), `--c-text` on a `--c-surface` hover (16.44:1),
354
+ From [components-navbar](components/components-navbar): `Navbar` and its parts. A `--c-background` bar with a 1px `--c-muted` bottom edge. The brand is bold `--c-text` (17.93:1 light, 18.16:1 dark). Items read like the tabs: bold `--c-muted` (5.74:1 / 7.85:1), `--c-text` on a `--c-surface` hover (16.44:1),
354
355
  and `--c-text` for the current page (`aria-current="page"`). The content collapses behind a 44px toggle below 48rem (the component's Sass breakpoint, a media query, no JS). Square; focus is the theme's 1px `--c-focus` ring, inside the item.
355
356
 
356
357
  ## Drawer
@@ -360,62 +361,71 @@ The panel slides in over `$transition-duration`; with reduced motion it just app
360
361
 
361
362
  ## Menu
362
363
  From [components-menu](components/components-menu): `Menu`, `MenuItem`, `MenuSeparator` and `MenuLabel`. The panel is the Popover's (`--c-background`, a 1px `--c-muted` border, flat). Items are `--c-text` with 0.625rem of block and 0.75rem of side padding, and hover and keyboard focus share a `--c-surface-active`
363
- background (14.23:1 light, 13.88:1 dark) plus the theme's 1px `--c-focus` ring. Labels are small `--c-muted` text (4.74:1 / 7.85:1 on the panel), the separator a 1px `--c-muted` line, a disabled item is half opacity. Square. The trigger is the app's own button, so its look is not part of the theme.
364
+ background (14.23:1 light, 13.88:1 dark) plus the theme's 1px `--c-focus` ring. Labels are small `--c-muted` text (5.74:1 / 7.85:1 on the panel), the separator a 1px `--c-muted` line, a disabled item is half opacity. Square. The trigger is the app's own button, so its look is not part of the theme.
364
365
 
365
366
  ## Table
366
367
  From [components-table](components/components-table): `Table` and its parts. The header is `--c-surface` with bold `--c-text` (16.4:1 light, 16.4:1 dark), the rows are divided by 1px `--c-muted` lines, cells have 1rem by 0.75rem of padding (0.75rem by 0.375rem when dense) at the small size. **Row hover** is `--c-surface-active` (14.2:1 / 13.9:1 for `--c-text`), not `--c-surface`, so it still shows on a striped row (stripes are `--c-surface`). The **selected row** is a 12% tint of `--c-primary` over `--c-background`
367
- (`color-mix`, light `#e0e0e0`, dark `#272727`), so `--c-text` is 13.6:1 / 13.7:1 on it and the tint follows the mode with no extra role. The **sticky header** background is `--c-surface`, a custom property and therefore opaque in both modes (rows never show through, and dark gets its own value; the component's neutral default is a literal white and must not be used in dark). The caption is `--c-muted` (4.74:1 / 7.85:1 on the background). The sort arrows are CSS glyphs, so the direction is a shape and not only an opacity. Square, the theme's 1px `--c-focus` ring inside the cell and the scroll region.
368
+ (`color-mix`, light `#e0e0e0`, dark `#272727`), so `--c-text` is 13.6:1 / 13.7:1 on it and the tint follows the mode with no extra role. The **sticky header** background is `--c-surface`, a custom property and therefore opaque in both modes (rows never show through, and dark gets its own value; the component's neutral default is a literal white and must not be used in dark). The caption is `--c-muted` (5.74:1 / 7.85:1 on the background). The sort arrows are CSS glyphs, so the direction is a shape and not only an opacity. Square, the theme's 1px `--c-focus` ring inside the cell and the scroll region.
368
369
 
369
370
  ## List
370
- From [components-list](components/components-list): `List` and `ListItem`. Rows sit on the page background, divided (or bordered) by 1px `--c-muted` lines, with 1rem by 0.75rem of padding. A link or button row highlights with `--c-surface-active` on hover (`--c-text` 14.2:1 / 13.9:1) and the current item (`aria-current`) takes the same 12% `--c-primary` tint as a selected table row (13.6:1 / 13.7:1). The secondary text is `--c-text` at the small size on purpose: `--c-muted` is 4.74:1 on the background but only 4.35:1 on `--c-surface` and 3.76:1 on `--c-surface-active`, both short of AA for text. Square, the theme's 1px `--c-focus` ring inside the row.
371
+ From [components-list](components/components-list): `List` and `ListItem`. Rows sit on the page background, divided (or bordered) by 1px `--c-muted` lines, with 1rem by 0.75rem of padding. A link or button row highlights with `--c-surface-active` on hover (`--c-text` 14.2:1 / 13.9:1) and the current item (`aria-current`) takes the same 12% `--c-primary` tint as a selected table row (13.6:1 / 13.7:1). The secondary text is `--c-text` at the small size on purpose: `--c-muted` is 5.74:1 on the background but only 5.27:1 on `--c-surface` and 3.76:1 on `--c-surface-active`, both short of AA for text. Square, the theme's 1px `--c-focus` ring inside the row.
371
372
 
372
373
  ## Description list
373
- From [components-description-list](components/components-description-list): `DescriptionList`, `DescriptionTerm`, `DescriptionDetail` and `DescriptionItem`. The term is a small bold `--c-muted` label (4.74:1 light, 7.85:1 dark on the background; do not put a description list on `--c-surface` in light, where it is 4.35:1) and the detail is `--c-text`. No fills or borders: the layout (vertical, horizontal, grid) is the only thing it sets, with 2rem between columns and 1rem between pairs.
374
+ From [components-description-list](components/components-description-list): `DescriptionList`, `DescriptionTerm`, `DescriptionDetail` and `DescriptionItem`. The term is a small bold `--c-muted` label (5.74:1 light, 7.85:1 dark on the background; do not put a description list on `--c-surface` in light, where it is 5.27:1) and the detail is `--c-text`. No fills or borders: the layout (vertical, horizontal, grid) is the only thing it sets, with 2rem between columns and 1rem between pairs.
374
375
 
375
376
  ## Stat
376
- From [components-stat](components/components-stat): `Stat`. A card-like block on `--c-background` with a 1px `--c-muted` border (the Card's outlined look), square, 1.25rem by 1.5rem of padding. It is not on `--c-surface` because the delta colors are checked against the background: **delta up is `--c-success`** (5.02:1 light, 11.36:1 dark), **delta down is `--c-error`** (4.53:1 light, 7.16:1 dark) and **flat is `--c-muted`** (4.74:1 / 7.85:1). On `--c-surface` the light `--c-error` would be 4.15:1, short of AA. The value is `--c-text` at the h1 size; the label and description are `--c-muted`. Color is never the only signal: the arrow glyph (up, down, flat) and the `delta_label` text for screen readers say the same. Whether "down" is good news (churn, cost) is the app's call: the color only follows the direction.
377
+ From [components-stat](components/components-stat): `Stat`. A card-like block on `--c-background` with a 1px `--c-muted` border (the Card's outlined look), square, 1.25rem by 1.5rem of padding. It is not on `--c-surface` because the delta colors are checked against the background: **delta up is `--c-success`** (5.02:1 light, 11.36:1 dark), **delta down is `--c-error`** (4.53:1 light, 7.16:1 dark) and **flat is `--c-muted`** (5.74:1 / 7.85:1). On `--c-surface` the light `--c-error` would be 4.15:1, short of AA. The value is `--c-text` at the h1 size; the label and description are `--c-muted`. Color is never the only signal: the arrow glyph (up, down, flat) and the `delta_label` text for screen readers say the same. Whether "down" is good news (churn, cost) is the app's call: the color only follows the direction.
377
378
 
378
379
  ## Empty state
379
- From [components-empty-state](components/components-empty-state): `EmptyState`. A centred block on the page background, with 3rem by 1.5rem of padding at the default size and 1rem between its parts. The icon is `--c-muted` (4.74:1 light, 7.85:1 dark, above the 3:1 non-text minimum), the title is `--c-text`, bold, at the h3 size (h4 small, h2 large) and the description is `--c-muted` at the body size (4.74:1 / 7.85:1 on the background; on `--c-surface` it would be 4.35:1 in light, short of AA, so do not put an empty state on a surface panel without moving the description to `--c-text`). The actions are the app's own buttons. No fills, borders or radius.
380
+ From [components-empty-state](components/components-empty-state): `EmptyState`. A centred block on the page background, with 3rem by 1.5rem of padding at the default size and 1rem between its parts. The icon is `--c-muted` (5.74:1 light, 7.85:1 dark, above the 3:1 non-text minimum), the title is `--c-text`, bold, at the h3 size (h4 small, h2 large) and the description is `--c-muted` at the body size (5.74:1 / 7.85:1 on the background; on `--c-surface` it would be 5.27:1 in light, short of AA, so do not put an empty state on a surface panel without moving the description to `--c-text`). The actions are the app's own buttons. No fills, borders or radius.
380
381
 
381
382
  ## Timeline
382
- From [components-timeline](components/components-timeline): `Timeline` and `TimelineItem`. The connector is a 1px `--c-muted` line. A **default** marker is a `--c-surface-active` dot with a 2px `--c-muted` ring; **success, warning, error and info** are solid dots in `--c-success`, `--c-warning`, `--c-error` and `--c-info` (light 5.02, 4.92, 4.53 and 6.70:1 on the background, dark 11.36, 11.86, 7.16 and 7.79:1: all above the 3:1 non-text minimum); the **current** step is a hollow `--c-primary` ring (21:1 / 19.8:1), a different shape from the dots, and the item carries `aria-current="step"`. An **icon marker** keeps a `--c-background` fill and takes the status as its 2px ring, so the `--c-text` icon is 17.93:1 / 18.16:1 in every status (a colored fill would not give one icon color AA in both modes). The status is also the visually hidden `status_label`, so colour is never the only signal. The title is bold `--c-text`, the time is `--c-muted` at the small size (4.74:1 / 7.85:1 on the background) and the description is `--c-text`. Keep a timeline on `--c-background`. Deviation from the first plan: the default dot is outlined with `--c-muted` instead of being a bare `--c-surface-active` dot, which is 1.2:1 on white.
383
+ From [components-timeline](components/components-timeline): `Timeline` and `TimelineItem`. The connector is a 1px `--c-muted` line. A **default** marker is a `--c-surface-active` dot with a 2px `--c-muted` ring; **success, warning, error and info** are solid dots in `--c-success`, `--c-warning`, `--c-error` and `--c-info` (light 5.02, 4.92, 4.53 and 6.70:1 on the background, dark 11.36, 11.86, 7.16 and 7.79:1: all above the 3:1 non-text minimum); the **current** step is a hollow `--c-primary` ring (21:1 / 19.8:1), a different shape from the dots, and the item carries `aria-current="step"`. An **icon marker** keeps a `--c-background` fill and takes the status as its 2px ring, so the `--c-text` icon is 17.93:1 / 18.16:1 in every status (a colored fill would not give one icon color AA in both modes). The status is also the visually hidden `status_label`, so colour is never the only signal. The title is bold `--c-text`, the time is `--c-muted` at the small size (5.74:1 / 7.85:1 on the background) and the description is `--c-text`. Keep a timeline on `--c-background`. Deviation from the first plan: the default dot is outlined with `--c-muted` instead of being a bare `--c-surface-active` dot, which is 1.2:1 on white.
383
384
 
384
385
  ## Tree
385
386
  From [components-tree](components/components-tree): `Tree` and `TreeItem`. Rows are 0.5rem by 0.75rem on the page background, 1.5rem of indent per level. **Hover** is `--c-surface-active` (`--c-text` 14.23:1 light, 13.88:1 dark); the **selected row** is the same 12% tint of `--c-primary` over `--c-background` as a selected table row (`color-mix`, light `#e0e0e0`, dark `#272727`) with `--c-text` (13.58:1 / 13.70:1), so a selected row still reads when hovered. The **chevron** is the component's CSS border chevron on `.c-tree__toggle` (parents only; right when closed, down when open), colored `--c-muted`: 3.76:1 on the hover and 3.59:1 on the selected tint in light, 6.00:1 and 5.92:1 in dark, above the 3:1 non-text minimum. A disabled item is half opacity (exempt from contrast). Square, the theme's 1px `--c-focus` ring inside the row.
386
387
 
387
388
  ## Code
388
- From [components-code](components/components-code): `Code`, `Kbd` and `CodeBlock`. The font is the tokens' monospace stack. **Inline code** is `--c-text` on `--c-surface` (16.44:1 in both modes), square, 0.875em. A **kbd** is a `--c-background` key with a 1px `--c-muted` border and a 1px `--c-muted` bottom edge, text `--c-text` (17.93:1 / 18.16:1). The **block** is `--c-surface` with a 1px `--c-muted` border (4.35:1 light, 7.11:1 dark against the surface, above 3:1) and `--c-text` at the small size; highlighted `children` are the app's to keep AA on `--c-surface`. The **copy button** reads like the secondary button: `--c-background` with a 1px `--c-muted` border, `--c-text` (17.93:1 / 18.16:1) and a `--c-surface-active` hover (`--c-text` 14.23:1 / 13.88:1), square, with the theme's 1px `--c-focus` ring inside it and on the focusable `pre`. The component has no uppercase label variables, so the button keeps the small body text instead of the secondary button's label style.
389
+ From [components-code](components/components-code): `Code`, `Kbd` and `CodeBlock`. The font is the tokens' monospace stack. **Inline code** is `--c-text` on `--c-surface` (16.44:1 in both modes), square, 0.875em. A **kbd** is a `--c-background` key with a 1px `--c-muted` border and a 1px `--c-muted` bottom edge, text `--c-text` (17.93:1 / 18.16:1). The **block** is `--c-surface` with a 1px `--c-muted` border (5.27:1 light, 7.11:1 dark against the surface, above 3:1) and `--c-text` at the small size; highlighted `children` are the app's to keep AA on `--c-surface`. The **copy button** reads like the secondary button: `--c-background` with a 1px `--c-muted` border, `--c-text` (17.93:1 / 18.16:1) and a `--c-surface-active` hover (`--c-text` 14.23:1 / 13.88:1), square, with the theme's 1px `--c-focus` ring inside it and on the focusable `pre`. The component has no uppercase label variables, so the button keeps the small body text instead of the secondary button's label style.
389
390
 
390
391
  ## Field
391
- From [components-field](components/components-field): `Field`. The label is `--c-text`, bold, at the small size (17.93:1 light, 18.16:1 dark on the background) and not `--c-muted`, which is only 4.35:1 on `--c-surface` in light. The hint is `--c-muted` at the small size (4.74:1 / 7.85:1 on the background) and stays visible, linked with `aria-describedby`. The error is `--c-error` (4.53:1 / 7.16:1), the same color as the Input's error line, and the required asterisk is `--c-error` too; color is never the only signal because the error is text with `role="alert"`. 0.5rem between label, control, hint and error; the horizontal layout uses a 10rem label column and 1.5rem of gap. Keep a field on `--c-background`.
392
+ From [components-field](components/components-field): `Field`. The label is `--c-text`, bold, at the small size (17.93:1 light, 18.16:1 dark on the background) and not `--c-muted`, which is 5.27:1 on `--c-surface` in light. The hint is `--c-muted` at the small size (5.74:1 / 7.85:1 on the background) and stays visible, linked with `aria-describedby`. The error is `--c-error` (4.53:1 / 7.16:1), the same color as the Input's error line, and the required asterisk is `--c-error` too; color is never the only signal because the error is text with `role="alert"`. 0.5rem between label, control, hint and error; the horizontal layout uses a 10rem label column and 1.5rem of gap. Keep a field on `--c-background`.
392
393
 
393
394
  ## Number input
394
- From [components-number-input](components/components-number-input): `NumberInput`. The field is the Input's, so it keeps its notched label, `--c-muted` border (4.74:1 / 7.85:1) and thickened `--c-focus` border. The stepper buttons read like the secondary button inside the box: square, `--c-muted` glyphs (4.74:1 / 7.85:1, above the 3:1 non-text minimum) turning `--c-text` on a `--c-surface-active` hover (14.23:1 / 13.88:1), half opacity at a bound (exempt from contrast). They are out of the tab order (the arrow keys do the same), so the focus ring stays on the field.
395
+ From [components-number-input](components/components-number-input): `NumberInput`. The field is the Input's, so it keeps its notched label, `--c-muted` border (5.74:1 / 7.85:1) and thickened `--c-focus` border. The stepper buttons read like the secondary button inside the box: square, `--c-muted` glyphs (5.74:1 / 7.85:1, above the 3:1 non-text minimum) turning `--c-text` on a `--c-surface-active` hover (14.23:1 / 13.88:1), half opacity at a bound (exempt from contrast). They are out of the tab order (the arrow keys do the same), so the focus ring stays on the field.
395
396
 
396
397
  ## OTP input
397
- From [components-otp-input](components/components-otp-input): `OtpInput`. Each box is built like the Input: `--c-background` with a 1px `--c-muted` border (4.74:1 / 7.85:1), `--c-primary` on hover, and the keyboard focus thickens the border to `--c-focus` plus a 1px `--c-focus` shadow (2px in total, 21:1 / 19.8:1), the same rule as every outlined control. An invalid box is `--c-error` (4.53:1 / 7.16:1) with the message under the boxes in `--c-error`; a disabled one is `--c-surface` with `--c-disabled-text` (exempt). The digit is `--c-text` at the h3 size (17.93:1 / 18.16:1). Square, 3rem by 3.5rem (above the 44px touch target), 0.5rem between boxes; the group label is the field label style in `--c-muted`.
398
+ From [components-otp-input](components/components-otp-input): `OtpInput`. Each box is built like the Input: `--c-background` with a 1px `--c-muted` border (5.74:1 / 7.85:1), `--c-primary` on hover, and the keyboard focus thickens the border to `--c-focus` plus a 1px `--c-focus` shadow (2px in total, 21:1 / 19.8:1), the same rule as every outlined control. An invalid box is `--c-error` (4.53:1 / 7.16:1) with the message under the boxes in `--c-error`; a disabled one is `--c-surface` with `--c-disabled-text` (exempt). The digit is `--c-text` at the h3 size (17.93:1 / 18.16:1). Square, 3rem by 3.5rem (above the 44px touch target), 0.5rem between boxes; the group label is the field label style in `--c-muted`.
398
399
 
399
400
  ## Phone
400
- From [components-phone](components/components-phone): `Phone`. The field is the Input's. The country select is a native `<select>` in the input's start slot, `--c-text` on a transparent background so the box color shows in both modes (17.93:1 / 18.16:1 on the background), with a 1px `--c-muted` divider before the number (4.74:1 / 7.85:1) and the theme's 1px `--c-focus` outline when it is focused by keyboard; disabled is `--c-disabled-text` (exempt). The open list is drawn by the platform, so it follows `color-scheme` in dark.
401
+ From [components-phone](components/components-phone): `Phone`. The field is the Input's. The country select is a native `<select>` in the input's start slot, `--c-text` on a transparent background so the box color shows in both modes (17.93:1 / 18.16:1 on the background), with a 1px `--c-muted` divider before the number (5.74:1 / 7.85:1) and the theme's 1px `--c-focus` outline when it is focused by keyboard; disabled is `--c-disabled-text` (exempt). The open list is drawn by the platform, so it follows `color-scheme` in dark.
401
402
 
402
403
  ## Tag input
403
404
  From [components-tag-input](components/components-tag-input): `TagInput`. The box is the Input's (square, `--c-muted` border, thickened `--c-focus` border on focus) and the chips are the Tag's, so the tag tints, borders and `--c-text` labels (14.8:1 or more on every tint) are the ones documented in the Tag section; the block only sets 0.375rem between chips and the text field and a 6rem minimum for the text field before it wraps to its own row. The placeholder is `--c-muted` as in the Input. Errors (`duplicate`, `max`, `invalid`) use the Input's `--c-error` line.
404
405
 
405
406
  ## Time picker
406
- From [components-time-picker](components/components-time-picker): `TimePicker`. The field is the Input's and the times are the Listbox's inside the Popover, so the `--c-muted` border (4.74:1 / 7.85:1), the thickened `--c-focus` border, the `--c-text` options (17.93:1 / 18.16:1), the `--c-surface` active option and the selected marker are the ones documented for the Select and the Combobox (reused, no new figure). The clock icon is the Select arrow's: 16px, `--c-muted` turning `--c-primary` on hover and when open, and `--c-disabled-text` when disabled (exempt). Open takes the `--c-focus` border; the placeholder is `--c-muted`. Errors (`invalid_time`, `out_of_range`) use the Input's `--c-error` line (4.53:1 / 7.16:1).
407
+ From [components-time-picker](components/components-time-picker): `TimePicker`. The field is the Input's and the times are the Listbox's inside the Popover, so the `--c-muted` border (5.74:1 / 7.85:1), the thickened `--c-focus` border, the `--c-text` options (17.93:1 / 18.16:1), the `--c-surface` active option and the selected marker are the ones documented for the Select and the Combobox (reused, no new figure). The clock icon is the Select arrow's: 16px, `--c-muted` turning `--c-primary` on hover and when open, and `--c-disabled-text` when disabled (exempt). Open takes the `--c-focus` border; the placeholder is `--c-muted`. Errors (`invalid_time`, `out_of_range`) use the Input's `--c-error` line (4.53:1 / 7.16:1).
407
408
 
408
409
  ## Date range
409
410
  From [components-date-range](components/components-date-range): `DateRange`. The trigger is the Date picker's and the days are the Calendar's (44px squares, `--c-text`, `--c-surface` hover), so those figures are reused. The two ends use the Calendar's selected look, `--c-on-primary` on `--c-primary` (21:1 / 19.8:1). The days between them are a `--c-surface-active` band with `--c-text` (14.23:1 / 13.88:1, the same pair as the Menu and Pagination hovers), square so the band is unbroken; it is painted on the cell and on the day, and it relies on the calendar grid keeping `border-spacing: 0` (a test asserts it). The band against the background is only 1.26:1 / 1.31:1, so it is not the only cue: the ends are solid black (or white in dark), and the days carry `aria-selected`. Two months sit side by side with 1rem between them, with 0.5rem of panel padding. Errors (`end_before_start`, `out_of_range`) use the Input's `--c-error` line.
410
411
 
411
412
  ## Rating
412
- From [components-rating](components/components-rating): `Rating`. The filled part is `--c-primary` (21:1 / 19.8:1 on the background) and the empty part is `--c-muted` (4.74:1 / 7.85:1), both above the 3:1 minimum for graphics. A filled star is a solid shape and an empty one an outline, so the two never rely on colour alone; their lightness differs by 4.43:1 in light and 2.52:1 in dark (below 3:1 there, which the solid-versus-outline shape makes up for). The preview while hovering is `--c-primary-hover` (15.13:1 in light, 15.72:1 in dark on the background). Stars are 1.75rem apart by 0.25rem, and the keyboard ring is the theme's 1px `--c-focus` outline (21:1 / 19.8:1). The group label is the field label style in `--c-muted`; a disabled rating is at half opacity (exempt).
413
+ From [components-rating](components/components-rating): `Rating`. The filled part is `--c-primary` (21:1 / 19.8:1 on the background) and the empty part is `--c-muted` (5.74:1 / 7.85:1), both above the 3:1 minimum for graphics. A filled star is a solid shape and an empty one an outline, so the two never rely on colour alone; their lightness differs by 4.43:1 in light and 2.52:1 in dark (below 3:1 there, which the solid-versus-outline shape makes up for). The preview while hovering is `--c-primary-hover` (15.13:1 in light, 15.72:1 in dark on the background). Stars are 1.75rem apart by 0.25rem, and the keyboard ring is the theme's 1px `--c-focus` outline (21:1 / 19.8:1). The group label is the field label style in `--c-muted`; a disabled rating is at half opacity (exempt).
413
414
 
414
415
  ## Color picker
415
- From [components-color-picker](components/components-color-picker): `ColorPicker`. The field is the Input's with a 1.25rem chip bordered in `--c-muted` (4.74:1 / 7.85:1) next to the hex text in `--c-text`; the panel is the Popover's. The thumbs of the area and of the hue and alpha tracks are a 2px `--c-background` ring over a 1px `--c-text` ring, so one of the two contrasts with any colour under it in both modes (the ring pair is 17.93:1 / 18.16:1 between itself); the keyboard focus draws the theme's 1px `--c-focus` outline offset by 2px around the area or the track (21:1 / 19.8:1). The hue and brightness gradients are the colour data and keep their literal colours; the transparent checkerboard is `--c-surface-active` over `--c-background`. The hex input is built like the Input box: `--c-background`, a 1px `--c-muted` border, `--c-error` when invalid (4.53:1 / 7.16:1), the monospace stack at the small size. Swatches are 1.75rem squares with a `--c-muted` border and a `--c-primary` ring when selected; areas, tracks and chips are square.
416
+ From [components-color-picker](components/components-color-picker): `ColorPicker`. The field is the Input's with a 1.25rem chip bordered in `--c-muted` (5.74:1 / 7.85:1) next to the hex text in `--c-text`; the panel is the Popover's. The thumbs of the area and of the hue and alpha tracks are a 2px `--c-background` ring over a 1px `--c-text` ring, so one of the two contrasts with any colour under it in both modes (the ring pair is 17.93:1 / 18.16:1 between itself); the keyboard focus draws the theme's 1px `--c-focus` outline offset by 2px around the area or the track (21:1 / 19.8:1). The hue and brightness gradients are the colour data and keep their literal colours; the transparent checkerboard is `--c-surface-active` over `--c-background`. The hex input is built like the Input box: `--c-background`, a 1px `--c-muted` border, `--c-error` when invalid (4.53:1 / 7.16:1), the monospace stack at the small size. Swatches are 1.75rem squares with a `--c-muted` border and a `--c-primary` ring when selected; areas, tracks and chips are square.
416
417
 
417
418
  ## Async autocomplete
418
- From [components-async-autocomplete](components/components-async-autocomplete): `AsyncAutocomplete`. The field, the panel and the options are the Combobox's (reused figures, no new contrast). While a request is pending the field shows a 16px spinner at the arrow's place: a 2px `--c-primary` arc (21:1 / 19.8:1) over a `--c-surface-active` track, slowed down under `prefers-reduced-motion`. The states are written in the panel: `loading` and `empty` in `--c-muted` (4.74:1 / 7.85:1, the Listbox's empty message color) and `error` in `--c-error` (4.53:1 / 7.16:1 on the background, which is the panel's); the state is also exposed as `aria-busy` and `data-state`, so colour is never the only signal. Open takes the `--c-focus` border.
419
+ From [components-async-autocomplete](components/components-async-autocomplete): `AsyncAutocomplete`. The field, the panel and the options are the Combobox's (reused figures, no new contrast). While a request is pending the field shows a 16px spinner at the arrow's place: a 2px `--c-primary` arc (21:1 / 19.8:1) over a `--c-surface-active` track, slowed down under `prefers-reduced-motion`. The states are written in the panel: `loading` and `empty` in `--c-muted` (5.74:1 / 7.85:1, the Listbox's empty message color) and `error` in `--c-error` (4.53:1 / 7.16:1 on the background, which is the panel's); the state is also exposed as `aria-busy` and `data-state`, so colour is never the only signal. Open takes the `--c-focus` border.
420
+
421
+ ## Section and container
422
+ From [components-section](components/components-section): `Container` and `Section`. Layout only: gutters of 1rem growing to 2rem at 40rem, container widths of 40, 48, 64 and 80rem, and vertical rhythms of 2, 4 and 6rem. The section title is `--c-text` in the h2 style (17.93:1 / 18.16:1 on the background) and the lead paragraph is `--c-muted` at the lead size (5.74:1 / 7.85:1 on the background, 5.27:1 / 7.11:1 on `--c-surface`), so both pass AA on either ground. A section with a title is a landmark named by it.
423
+
424
+ ## Faq
425
+ From [components-faq](components/components-faq): `Faq`. It is the Accordion (the box, the `--c-muted` rules, the `--c-surface` header hover and the 1px `--c-focus` ring documented in the Accordion section), so no new figure for the questions. The answers are `--c-text` (17.93:1 / 18.16:1) at a 1.6 line height and the list is capped at 48rem. The block must stay after the accordion block, because the FAQ stylesheet forwards the accordion's.
426
+
427
+ ## Carousel
428
+ From [components-carousel](components/components-carousel): `Carousel`. The arrows and the pause / play button are the secondary button's: square, 2.75rem, a 1px `--c-muted` border (5.74:1 / 7.85:1 on the background), a `--c-surface-active` hover and a `--c-text` glyph (17.93:1 / 18.16:1; 14.23:1 / 13.88:1 on the hover). The glyphs (corner, bars, triangle) are drawn with borders in `currentColor`, so they follow the mode and need no new colour. Disabled arrows are at half opacity (exempt). The dots are 0.625rem circles in `--c-muted` (5.74:1 / 7.85:1 on the background, 5.27:1 / 7.11:1 on `--c-surface`, above the 3:1 minimum for graphics) and the current one is `--c-primary` (21:1 / 19.8:1) and 1.5rem long. Colour is not the only cue: the current dot is a bar against circles, and it also carries `aria-current`; the lightness gap between the two colours is 3.66:1 in light and 2.52:1 in dark (below 3:1 there, which the shape makes up for). The keyboard ring is the theme's 1px `--c-focus` outline offset by 2px (21:1 / 19.8:1). Transitions run at 0.2s and stop under `prefers-reduced-motion`.
419
429
 
420
430
  ## Keyboard focus (all controls)
421
431
  One rule keeps the controls coherent: **an outlined control thickens its own border from 1px to 2px, black** (text fields,
package/README.md CHANGED
@@ -78,7 +78,7 @@ change.
78
78
  | `$black` | `#000` | primary, hover and focus |
79
79
  | `$white` | `#fff` | surfaces |
80
80
  | `$text` | `#171717` | body text |
81
- | `$muted` | `#737373` | labels, field borders, placeholders |
81
+ | `$muted` | `#666666` | labels, field borders, placeholders |
82
82
  | `$surface` | `#f5f5f5` | hover surface, disabled background |
83
83
  | `$surface-active` | `#e5e5e5` | pressed surface, dividers |
84
84
  | `$primary-hover` | `#262626` | primary action on hover |
@@ -142,7 +142,7 @@ it: check the names against the theme's `scss/_tokens.scss` before you switch.
142
142
 
143
143
  ## What it configures
144
144
  `components-input`, `-button`, `-checkbox`, `-switch`, `-radio-group`, `-slider`, `-popover`, `-listbox`, `-select`,
145
- `-combobox`, `-upload`, `-calendar`, `-date-picker`, `-search`, `-typography`, `-card`, `-alert`, `-spinner`, `-modal`, `-toast`, `-divider`, `-badge`, `-tag`, `-link`, `-avatar`, `-skeleton`, `-progress`, `-tooltip`, `-prose`, `-tabs`, `-accordion`, `-breadcrumb`, `-pagination`, `-stepper`, `-sidebar`, `-navbar`, `-drawer`, `-menu`, `-table`, `-list`, `-description-list`, `-stat`, `-empty-state`, `-timeline`, `-tree`, `-code`, `-field`, `-number-input`, `-otp-input`, `-phone`, `-tag-input`, `-time-picker`, `-date-range`, `-rating`, `-color-picker` and `-async-autocomplete`. `components-email`, `-password`, `-url` and `-textarea`
145
+ `-combobox`, `-upload`, `-calendar`, `-date-picker`, `-search`, `-typography`, `-card`, `-alert`, `-spinner`, `-modal`, `-toast`, `-divider`, `-badge`, `-tag`, `-link`, `-avatar`, `-skeleton`, `-progress`, `-tooltip`, `-prose`, `-tabs`, `-accordion`, `-breadcrumb`, `-pagination`, `-stepper`, `-sidebar`, `-navbar`, `-drawer`, `-menu`, `-table`, `-list`, `-description-list`, `-stat`, `-empty-state`, `-timeline`, `-tree`, `-code`, `-field`, `-number-input`, `-otp-input`, `-phone`, `-tag-input`, `-time-picker`, `-date-range`, `-rating`, `-color-picker`, `-async-autocomplete`, `-section`, `-faq` and `-carousel`. `components-email`, `-password`, `-url` and `-textarea`
146
146
  are styled through the input.
147
147
 
148
148
  ## Change a value
package/dist/index.css CHANGED
@@ -2,7 +2,7 @@
2
2
  :root {
3
3
  --c-background: #fff;
4
4
  --c-text: #171717;
5
- --c-muted: #737373;
5
+ --c-muted: #666666;
6
6
  --c-surface: #f5f5f5;
7
7
  --c-surface-active: #e5e5e5;
8
8
  --c-primary: #000;
@@ -22,7 +22,7 @@
22
22
  :root[data-mode=light] {
23
23
  --c-background: #fff;
24
24
  --c-text: #171717;
25
- --c-muted: #737373;
25
+ --c-muted: #666666;
26
26
  --c-surface: #f5f5f5;
27
27
  --c-surface-active: #e5e5e5;
28
28
  --c-primary: #000;
@@ -4407,6 +4407,10 @@ ol.c-list {
4407
4407
  outline-offset: -2px;
4408
4408
  }
4409
4409
 
4410
+ .c-code-block--has-copy .c-code-block__pre {
4411
+ padding-top: 2.5rem;
4412
+ }
4413
+
4410
4414
  .c-code-block__code {
4411
4415
  font-size: inherit;
4412
4416
  }
@@ -5042,3 +5046,222 @@ ol.c-list {
5042
5046
  animation-duration: 2s;
5043
5047
  }
5044
5048
  }
5049
+ .c-container {
5050
+ box-sizing: border-box;
5051
+ width: 100%;
5052
+ margin-inline: auto;
5053
+ padding-inline: 1rem;
5054
+ }
5055
+ @media (min-width: 40rem) {
5056
+ .c-container {
5057
+ padding-inline: 2rem;
5058
+ }
5059
+ }
5060
+
5061
+ .c-container--sm {
5062
+ max-width: 40rem;
5063
+ }
5064
+
5065
+ .c-container--md {
5066
+ max-width: 48rem;
5067
+ }
5068
+
5069
+ .c-container--lg {
5070
+ max-width: 64rem;
5071
+ }
5072
+
5073
+ .c-container--xl {
5074
+ max-width: 80rem;
5075
+ }
5076
+
5077
+ .c-container--full {
5078
+ max-width: none;
5079
+ }
5080
+
5081
+ .c-section {
5082
+ box-sizing: border-box;
5083
+ }
5084
+
5085
+ .c-section--sm {
5086
+ padding-block: 2rem;
5087
+ }
5088
+
5089
+ .c-section--md {
5090
+ padding-block: 4rem;
5091
+ }
5092
+
5093
+ .c-section--lg {
5094
+ padding-block: 6rem;
5095
+ }
5096
+
5097
+ .c-section__header {
5098
+ display: flex;
5099
+ flex-direction: column;
5100
+ gap: 0.75rem;
5101
+ max-width: 40rem;
5102
+ margin-bottom: 2rem;
5103
+ }
5104
+
5105
+ .c-section__title {
5106
+ margin: 0;
5107
+ font-size: 1.75rem;
5108
+ font-weight: 900;
5109
+ line-height: 1.2;
5110
+ color: var(--c-text);
5111
+ }
5112
+
5113
+ .c-section__description {
5114
+ margin: 0;
5115
+ font-size: 1.25rem;
5116
+ line-height: 1.6;
5117
+ color: var(--c-muted);
5118
+ }
5119
+
5120
+ .c-faq {
5121
+ max-width: 48rem;
5122
+ }
5123
+
5124
+ .c-faq .c-accordion__content {
5125
+ line-height: 1.6;
5126
+ color: var(--c-text);
5127
+ }
5128
+
5129
+ .c-carousel {
5130
+ box-sizing: border-box;
5131
+ }
5132
+
5133
+ .c-carousel__viewport {
5134
+ overflow: hidden;
5135
+ }
5136
+
5137
+ .c-carousel__track {
5138
+ position: relative;
5139
+ display: flex;
5140
+ gap: 1rem;
5141
+ overflow-x: auto;
5142
+ overscroll-behavior-x: contain;
5143
+ scroll-snap-type: x mandatory;
5144
+ scrollbar-width: none;
5145
+ touch-action: pan-x pan-y pinch-zoom;
5146
+ }
5147
+ .c-carousel__track::-webkit-scrollbar {
5148
+ display: none;
5149
+ }
5150
+
5151
+ .c-carousel__slide {
5152
+ box-sizing: border-box;
5153
+ flex: 0 0 calc((100% - 1rem * (var(--c-carousel-per-view, 1) - 1)) / var(--c-carousel-per-view, 1));
5154
+ min-width: 0;
5155
+ scroll-snap-align: start;
5156
+ }
5157
+
5158
+ .c-carousel__controls {
5159
+ display: flex;
5160
+ gap: 1rem;
5161
+ align-items: center;
5162
+ justify-content: center;
5163
+ margin-top: 1rem;
5164
+ }
5165
+
5166
+ .c-carousel__arrow,
5167
+ .c-carousel__toggle {
5168
+ position: relative;
5169
+ box-sizing: border-box;
5170
+ flex: none;
5171
+ width: 2.75rem;
5172
+ height: 2.75rem;
5173
+ padding: 0;
5174
+ color: var(--c-text);
5175
+ cursor: pointer;
5176
+ background: var(--c-background);
5177
+ border: 1px solid var(--c-muted);
5178
+ border-radius: 0;
5179
+ transition: background-color 0.2s ease-out;
5180
+ }
5181
+ .c-carousel__arrow:hover:not(:disabled),
5182
+ .c-carousel__toggle:hover:not(:disabled) {
5183
+ background: var(--c-surface-active);
5184
+ }
5185
+ .c-carousel__arrow:focus-visible,
5186
+ .c-carousel__toggle:focus-visible {
5187
+ outline: 1px solid var(--c-focus);
5188
+ outline-offset: 2px;
5189
+ }
5190
+ .c-carousel__arrow:disabled,
5191
+ .c-carousel__toggle:disabled {
5192
+ cursor: not-allowed;
5193
+ opacity: 0.5;
5194
+ }
5195
+ .c-carousel__arrow::before,
5196
+ .c-carousel__toggle::before {
5197
+ position: absolute;
5198
+ top: 50%;
5199
+ left: 50%;
5200
+ box-sizing: border-box;
5201
+ content: "";
5202
+ }
5203
+
5204
+ .c-carousel__arrow::before {
5205
+ width: 0.5rem;
5206
+ height: 0.5rem;
5207
+ border-top: 2px solid currentColor;
5208
+ border-right: 2px solid currentColor;
5209
+ }
5210
+
5211
+ .c-carousel__arrow--next::before {
5212
+ transform: translate(-65%, -50%) rotate(45deg);
5213
+ }
5214
+
5215
+ .c-carousel__arrow--prev::before {
5216
+ transform: translate(-35%, -50%) rotate(-135deg);
5217
+ }
5218
+
5219
+ .c-carousel__toggle--playing::before {
5220
+ width: 0.5rem;
5221
+ height: 0.75rem;
5222
+ border-right: 3px solid currentColor;
5223
+ border-left: 3px solid currentColor;
5224
+ transform: translate(-50%, -50%);
5225
+ }
5226
+
5227
+ .c-carousel__toggle--paused::before {
5228
+ border-top: 0.375rem solid transparent;
5229
+ border-bottom: 0.375rem solid transparent;
5230
+ border-left: 0.625rem solid currentColor;
5231
+ transform: translate(-35%, -50%);
5232
+ }
5233
+
5234
+ .c-carousel__dots {
5235
+ display: flex;
5236
+ gap: 0.5rem;
5237
+ align-items: center;
5238
+ }
5239
+
5240
+ .c-carousel__dot {
5241
+ box-sizing: border-box;
5242
+ width: 0.625rem;
5243
+ height: 0.625rem;
5244
+ padding: 0;
5245
+ cursor: pointer;
5246
+ background: var(--c-muted);
5247
+ border: 0;
5248
+ border-radius: 0;
5249
+ transition: width 0.2s ease-out, background-color 0.2s ease-out;
5250
+ }
5251
+ .c-carousel__dot:focus-visible {
5252
+ outline: 1px solid var(--c-focus);
5253
+ outline-offset: 2px;
5254
+ }
5255
+
5256
+ .c-carousel__dot--current {
5257
+ width: 1.5rem;
5258
+ background: var(--c-primary);
5259
+ }
5260
+
5261
+ @media (prefers-reduced-motion: reduce) {
5262
+ .c-carousel__arrow,
5263
+ .c-carousel__toggle,
5264
+ .c-carousel__dot {
5265
+ transition: none;
5266
+ }
5267
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@carissa-io/theme-default",
3
- "version": "0.3.9",
3
+ "version": "0.3.11",
4
4
  "description": "Carissa UI: the default theme (Swiss minimalism) as SCSS variable overrides of the Carissa components, with a compiled CSS build",
5
5
  "type": "module",
6
6
  "style": "dist/index.css",
@@ -93,7 +93,10 @@
93
93
  "@carissa-io/components-date-range": "^0.1.0",
94
94
  "@carissa-io/components-rating": "^0.1.0",
95
95
  "@carissa-io/components-color-picker": "^0.1.0",
96
- "@carissa-io/components-async-autocomplete": "^0.1.0"
96
+ "@carissa-io/components-async-autocomplete": "^0.1.0",
97
+ "@carissa-io/components-section": "^0.1.0",
98
+ "@carissa-io/components-faq": "^0.1.0",
99
+ "@carissa-io/components-carousel": "^0.1.0"
97
100
  },
98
101
  "peerDependenciesMeta": {
99
102
  "@carissa-io/components-input": {
@@ -275,6 +278,15 @@
275
278
  },
276
279
  "@carissa-io/components-async-autocomplete": {
277
280
  "optional": true
281
+ },
282
+ "@carissa-io/components-section": {
283
+ "optional": true
284
+ },
285
+ "@carissa-io/components-faq": {
286
+ "optional": true
287
+ },
288
+ "@carissa-io/components-carousel": {
289
+ "optional": true
278
290
  }
279
291
  },
280
292
  "devDependencies": {
@@ -287,6 +299,7 @@
287
299
  "@carissa-io/components-button": "^0.1.1",
288
300
  "@carissa-io/components-calendar": "^0.1.1",
289
301
  "@carissa-io/components-card": "^0.1.0",
302
+ "@carissa-io/components-carousel": "^0.1.0",
290
303
  "@carissa-io/components-checkbox": "^0.1.1",
291
304
  "@carissa-io/components-code": "^0.1.0",
292
305
  "@carissa-io/components-color-picker": "^0.1.0",
@@ -297,6 +310,7 @@
297
310
  "@carissa-io/components-divider": "^0.1.0",
298
311
  "@carissa-io/components-drawer": "^0.1.0",
299
312
  "@carissa-io/components-empty-state": "^0.1.0",
313
+ "@carissa-io/components-faq": "^0.1.0",
300
314
  "@carissa-io/components-field": "^0.1.0",
301
315
  "@carissa-io/components-input": "^0.2.1",
302
316
  "@carissa-io/components-link": "^0.1.0",
@@ -315,6 +329,7 @@
315
329
  "@carissa-io/components-radio-group": "^0.1.1",
316
330
  "@carissa-io/components-rating": "^0.1.0",
317
331
  "@carissa-io/components-search": "^0.1.1",
332
+ "@carissa-io/components-section": "^0.1.0",
318
333
  "@carissa-io/components-select": "^0.1.1",
319
334
  "@carissa-io/components-sidebar": "^0.1.0",
320
335
  "@carissa-io/components-skeleton": "^0.1.0",
package/scss/_tokens.scss CHANGED
@@ -9,7 +9,7 @@
9
9
  $black: #000; // primary, hover and focus
10
10
  $white: #fff;
11
11
  $text: #171717;
12
- $muted: #737373; // labels, field borders, placeholders (4.7:1 on white)
12
+ $muted: #666666; // labels, field borders, placeholders (5.7:1 on white)
13
13
  $surface: #f5f5f5; // hover surface, disabled background
14
14
  $surface-active: #e5e5e5;
15
15
  $primary-hover: #262626;
package/scss/index.scss CHANGED
@@ -78,7 +78,7 @@
78
78
  $c-button-primary-background-disabled: $var-surface,
79
79
  $c-button-primary-border-color-disabled: $var-surface,
80
80
 
81
- // Secondary: 1px #737373 border, black on hover
81
+ // Secondary: 1px #666666 border, black on hover
82
82
  $c-button-secondary-color: $var-text,
83
83
  $c-button-secondary-background: $var-background,
84
84
  $c-button-secondary-border-color: $var-muted,
@@ -125,7 +125,7 @@
125
125
  $c-checkbox-gap: 0.75rem,
126
126
  $c-checkbox-transition: (color $transition-duration ease, background-color $transition-duration ease, border-color $transition-duration ease, opacity $transition-duration ease),
127
127
 
128
- // Box: 20px square, 1px #737373 border, black on hover
128
+ // Box: 20px square, 1px #666666 border, black on hover
129
129
  $c-checkbox-box-size: 1.25rem,
130
130
  $c-checkbox-border-color: $var-muted,
131
131
  $c-checkbox-border-color-hover: $var-primary,
@@ -155,7 +155,7 @@
155
155
  );
156
156
 
157
157
  @use '@carissa-io/components-switch/scss' as switch with (
158
- // Track: square, 40 x 20px, white with a 1px #737373 border; black when on
158
+ // Track: square, 40 x 20px, white with a 1px #666666 border; black when on
159
159
  $c-switch-track-width: 2.5rem,
160
160
  $c-switch-track-height: 1.25rem,
161
161
  $c-switch-track-radius: $radius,
@@ -168,7 +168,7 @@
168
168
  $c-switch-track-background-disabled: $var-surface,
169
169
  $c-switch-track-border-color-disabled: $var-disabled-border,
170
170
 
171
- // Thumb: square, #737373 when off, white when on
171
+ // Thumb: square, #666666 when off, white when on
172
172
  $c-switch-thumb-size: 0.875rem,
173
173
  $c-switch-thumb-offset: 0.125rem,
174
174
  $c-switch-thumb-radius: $radius,
@@ -203,7 +203,7 @@
203
203
  $c-radio-group-dot-gap: 0.75rem,
204
204
  $c-radio-group-transition: (color $transition-duration ease, background-color $transition-duration ease, border-color $transition-duration ease, background-size $transition-duration ease),
205
205
 
206
- // Dot: 20px circle (a radio is round, so the radius stays 50%), 1px #737373 border, black on hover
206
+ // Dot: 20px circle (a radio is round, so the radius stays 50%), 1px #666666 border, black on hover
207
207
  $c-radio-group-dot-size: 1.25rem,
208
208
  $c-radio-group-border-color: $var-muted,
209
209
  $c-radio-group-border-color-hover: $var-primary,
@@ -246,7 +246,7 @@
246
246
  $c-slider-min-height: $min-height,
247
247
  $c-slider-transition: (background-color $transition-duration ease, border-color $transition-duration ease),
248
248
 
249
- // Track: 2px #737373 line with a black fill
249
+ // Track: 2px #666666 line with a black fill
250
250
  $c-slider-track-height: 2px,
251
251
  $c-slider-track-color: $var-muted,
252
252
  $c-slider-track-fill-color: $var-primary,
@@ -274,7 +274,7 @@
274
274
  );
275
275
 
276
276
  @use '@carissa-io/components-popover/scss' as popover with (
277
- // Panel: white, 1px #737373 border, square, flat (no shadow), 4px below the anchor
277
+ // Panel: white, 1px #666666 border, square, flat (no shadow), 4px below the anchor
278
278
  $c-popover-background: $var-background,
279
279
  $c-popover-color: $var-text,
280
280
  $c-popover-border-width: 1px,
@@ -356,7 +356,7 @@
356
356
  // Upload: the Input field with a dashed box as the dropzone. Its index forwards the input styles, already loaded and
357
357
  // configured above, so it must come after them.
358
358
  @use '@carissa-io/components-upload/scss' as upload with (
359
- // Box: dashed 1px #737373 (the input border), black on hover and while files are dragged over it
359
+ // Box: dashed 1px #666666 (the input border), black on hover and while files are dragged over it
360
360
  $c-upload-border-style: dashed,
361
361
  $c-upload-transition: (border-color $transition-duration ease, background-color $transition-duration ease, box-shadow $transition-duration ease, color $transition-duration ease),
362
362
  $c-upload-dragging-border-color: $var-primary,
@@ -788,7 +788,7 @@
788
788
  );
789
789
 
790
790
  // Link: --c-primary (black in light, white in dark) with an underline, because a black link on black text is told apart
791
- // by the underline alone; --c-primary-hover on hover. Subtle is --c-muted (4.7:1 on the background) and turns --c-text.
791
+ // by the underline alone; --c-primary-hover on hover. Subtle is --c-muted (5.7:1 on the background) and turns --c-text.
792
792
  // Disabled is --c-disabled-text at full opacity. The focus ring is the theme's 1px ring, offset 2px.
793
793
  @use '@carissa-io/components-link/scss' as link with (
794
794
  $c-link-color: $var-primary,
@@ -939,7 +939,7 @@
939
939
  );
940
940
 
941
941
  // Tabs: a --c-muted underline under the list and a 2px --c-primary indicator under the selected tab. Tabs read --c-muted
942
- // (4.7:1 on the background, 7.9:1 in dark), turn --c-text on a --c-surface hover and the selected one is --c-text and bold.
942
+ // (5.7:1 on the background, 7.9:1 in dark), turn --c-text on a --c-surface hover and the selected one is --c-text and bold.
943
943
  // Square, bold, the theme's 1px focus ring inside the tab. No other block forwards it.
944
944
  @use '@carissa-io/components-tabs/scss' as tabs with (
945
945
  $c-tabs-list-border-width: 1px,
@@ -985,7 +985,7 @@
985
985
  $c-accordion-transition-duration: $transition-duration
986
986
  );
987
987
 
988
- // Breadcrumb: links and separators in --c-muted (4.7:1 on the background), the current page in --c-text and bold. Links turn
988
+ // Breadcrumb: links and separators in --c-muted (5.7:1 on the background), the current page in --c-text and bold. Links turn
989
989
  // --c-text and underline on hover. The theme's 1px focus ring, offset 2px.
990
990
  @use '@carissa-io/components-breadcrumb/scss' as breadcrumb with (
991
991
  $c-breadcrumb-font-size: $font-size-small,
@@ -1062,7 +1062,7 @@
1062
1062
 
1063
1063
  // Sidebar: a --c-surface column with a 1px --c-muted edge. Items are --c-text (16.4:1 on the surface) with a --c-surface-active
1064
1064
  // hover (14.2:1); the current page is filled --c-primary with --c-on-primary (21:1), a shape and not only a hue. Section titles
1065
- // are the small uppercase label style in --c-text, because --c-muted on --c-surface is only 4.35:1 in light. Square, the
1065
+ // are the small uppercase label style in --c-text, because --c-muted on --c-surface is 5.27:1 in light (it passes, the label stays text for emphasis). Square, the
1066
1066
  // theme's 1px focus ring inside the item. No other block forwards it.
1067
1067
  @use '@carissa-io/components-sidebar/scss' as sidebar with (
1068
1068
  $c-sidebar-width: 16rem,
@@ -1098,7 +1098,7 @@
1098
1098
  $c-sidebar-transition-duration: $transition-duration
1099
1099
  );
1100
1100
 
1101
- // Navbar: a --c-background bar with a 1px --c-muted bottom edge. Items read like the tabs: bold --c-muted (4.7:1 on the
1101
+ // Navbar: a --c-background bar with a 1px --c-muted bottom edge. Items read like the tabs: bold --c-muted (5.7:1 on the
1102
1102
  // background), --c-text on a --c-surface hover, and --c-text for the current page. The breakpoint is the Sass default (48rem).
1103
1103
  // Square, 44px toggle, the theme's 1px focus ring inside the item. No other block forwards it.
1104
1104
  @use '@carissa-io/components-navbar/scss' as navbar with (
@@ -1155,7 +1155,7 @@
1155
1155
  );
1156
1156
 
1157
1157
  // Menu: the panel is the Popover's (--c-background, 1px --c-muted border, flat). Items are --c-text with a --c-surface-active
1158
- // hover and keyboard focus (14.2:1) and the theme's 1px focus ring; labels are small --c-muted text (4.7:1 on the panel) and
1158
+ // hover and keyboard focus (14.2:1) and the theme's 1px focus ring; labels are small --c-muted text (5.7:1 on the panel) and
1159
1159
  // the separator a 1px --c-muted line. Square. Its index forwards the popover styles, already loaded and configured above:
1160
1160
  // this block goes after the Popover one.
1161
1161
  @use '@carissa-io/components-menu/scss' as menu with (
@@ -1204,7 +1204,7 @@
1204
1204
  );
1205
1205
 
1206
1206
  // List: rows on the page background, divided by 1px --c-muted lines. The secondary text is --c-text at the small size, not
1207
- // --c-muted, because the row sits on --c-surface-active when hovered and --c-muted is only 3.8:1 there (4.35:1 on --c-surface).
1207
+ // --c-muted, because the row sits on --c-surface-active when hovered and --c-muted is 4.56:1 there (5.27:1 on --c-surface), so the text color keeps the hover clearly readable.
1208
1208
  // Hover is --c-surface-active; the current item is the same 12% --c-primary tint as a selected table row. Square, the theme's
1209
1209
  // 1px focus ring inside the row.
1210
1210
  @use '@carissa-io/components-list/scss' as list with (
@@ -1229,7 +1229,7 @@
1229
1229
  $c-list-transition-duration: $transition-duration
1230
1230
  );
1231
1231
 
1232
- // Description list: the term is a small bold --c-muted label (4.7:1 on the background, so use it on --c-background, where
1232
+ // Description list: the term is a small bold --c-muted label (5.7:1 on the background, so use it on --c-background, where
1233
1233
  // --c-muted is not enough on --c-surface) and the detail is --c-text. No fills or borders.
1234
1234
  @use '@carissa-io/components-description-list/scss' as description-list with (
1235
1235
  $c-description-list-gap-x: 2rem,
@@ -1245,7 +1245,7 @@
1245
1245
 
1246
1246
  // Stat: a card-like block on the page background with a 1px --c-muted border (the Card's outlined look), not on --c-surface:
1247
1247
  // the delta colors are checked against --c-background, where --c-success is 5:1 and --c-error 4.5:1 in light (4.15:1 for
1248
- // --c-error on --c-surface). The label and the description are --c-muted (4.7:1), the value is --c-text. The delta is
1248
+ // --c-error on --c-surface). The label and the description are --c-muted (5.7:1), the value is --c-text. The delta is
1249
1249
  // success when up, error when down and muted when flat; the arrow and the screen-reader text say the same, so color is never
1250
1250
  // the only signal. In dark the status colors are lighter (7.1:1 and above on the background).
1251
1251
  @use '@carissa-io/components-stat/scss' as stat with (
@@ -1271,9 +1271,9 @@
1271
1271
  $c-stat-delta-flat-color: $var-muted
1272
1272
  );
1273
1273
 
1274
- // Empty state: a centred block on the page background. The icon is --c-muted (4.7:1 light, 7.9:1 dark, above the 3:1 non-text
1275
- // minimum), the title --c-text at the h3 size and bold, the description --c-muted (4.7:1 on the background, so keep it off
1276
- // --c-surface, where light is 4.35:1). The actions are the app's buttons. Square, nothing else to set.
1274
+ // Empty state: a centred block on the page background. The icon is --c-muted (5.7:1 light, 7.9:1 dark, above the 3:1 non-text
1275
+ // minimum), the title --c-text at the h3 size and bold, the description --c-muted (5.7:1 on the background, so keep it off
1276
+ // --c-surface, where light is 5.27:1). The actions are the app's buttons. Square, nothing else to set.
1277
1277
  @use '@carissa-io/components-empty-state/scss' as empty-state with (
1278
1278
  $c-empty-state-padding: 3rem 1.5rem,
1279
1279
  $c-empty-state-padding-sm: 1.5rem 1rem,
@@ -1299,7 +1299,7 @@
1299
1299
  // success, warning, error and info are solid dots in their status color (4.5:1 or more against the background in light, 7:1 in
1300
1300
  // dark, above the 3:1 non-text minimum); the current step is a hollow --c-primary ring. An icon marker keeps a --c-background
1301
1301
  // fill and takes the status as its ring, so the --c-text icon is 17.9:1 / 18.2:1 in every status. The status text is the
1302
- // hidden status_label, the time is --c-muted (4.7:1 / 7.9:1 on the background) and the title --c-text, bold. Color is never the
1302
+ // hidden status_label, the time is --c-muted (5.7:1 / 7.9:1 on the background) and the title --c-text, bold. Color is never the
1303
1303
  // only signal.
1304
1304
  @use '@carissa-io/components-timeline/scss' as timeline with (
1305
1305
  $c-timeline-gap: 1.5rem,
@@ -1391,8 +1391,8 @@
1391
1391
  );
1392
1392
 
1393
1393
  // Field: the label, hint and error around a control with no label of its own (radio group, slider, custom control). The label is
1394
- // --c-text (17.93:1 / 18.16:1 on the background) in the label size and weight, not --c-muted, which is 4.35:1 on --c-surface in
1395
- // light. The hint is --c-muted (4.74:1 / 7.85:1 on the background) and the error is --c-error (4.53:1 / 7.16:1), the same
1394
+ // --c-text (17.93:1 / 18.16:1 on the background) in the label size and weight, not --c-muted, which is 5.27:1 on --c-surface in
1395
+ // light. The hint is --c-muted (5.74:1 / 7.85:1 on the background) and the error is --c-error (4.53:1 / 7.16:1), the same
1396
1396
  // color as the Input's error line, with the component's required asterisk. Keep a field on --c-background.
1397
1397
  @use '@carissa-io/components-field/scss' as field with (
1398
1398
  $c-field-font-family: $font-family,
@@ -1412,7 +1412,7 @@
1412
1412
  );
1413
1413
 
1414
1414
  // Number input: the field is the Input's (configured at the top; this module forwards it). The steppers read like the
1415
- // secondary button inside the box: --c-muted glyphs (4.74:1 / 7.85:1, above the 3:1 non-text minimum) turning --c-text on a
1415
+ // secondary button inside the box: --c-muted glyphs (5.74:1 / 7.85:1, above the 3:1 non-text minimum) turning --c-text on a
1416
1416
  // --c-surface-active hover (--c-text 14.23:1 / 13.88:1), square, half opacity at a bound.
1417
1417
  @use '@carissa-io/components-number-input/scss' as number-input with (
1418
1418
  $c-number-input-stepper-size: 1.5rem,
@@ -1427,7 +1427,7 @@
1427
1427
  $c-number-input-text-align: start
1428
1428
  );
1429
1429
 
1430
- // OTP input: one box per character, built like the Input box: --c-background with a 1px --c-muted border (4.74:1 / 7.85:1),
1430
+ // OTP input: one box per character, built like the Input box: --c-background with a 1px --c-muted border (5.74:1 / 7.85:1),
1431
1431
  // --c-primary on hover, and the keyboard focus thickens the border to the theme's black ring (--c-focus, 21:1 / 19.8:1). An
1432
1432
  // invalid box is --c-error (4.53:1 / 7.16:1) and the message under the boxes is --c-error too; a disabled one is --c-surface
1433
1433
  // with --c-disabled-text (exempt from contrast). The digit is --c-text (17.93:1 / 18.16:1). Square, 3rem by 3.5rem.
@@ -1520,7 +1520,7 @@
1520
1520
  $c-date-range-open-border-color: $var-focus
1521
1521
  );
1522
1522
 
1523
- // Rating: the stars are --c-primary (21:1 / 19.8:1 on the background) over --c-muted ones (4.74:1 / 7.85:1), so the filled
1523
+ // Rating: the stars are --c-primary (21:1 / 19.8:1 on the background) over --c-muted ones (5.74:1 / 7.85:1), so the filled
1524
1524
  // and the empty part differ by shape (a solid star against an outline one) and by lightness (4.43:1 / 2.52:1 between them),
1525
1525
  // never by hue alone; the preview while hovering is --c-primary-hover (15.13:1 in light). The keyboard ring is the theme's --c-focus. The label is the field label style in --c-muted.
1526
1526
  @use '@carissa-io/components-rating/scss' as rating with (
@@ -1582,7 +1582,7 @@
1582
1582
 
1583
1583
  // Async autocomplete: the Combobox's field (a text input over the Listbox in the Popover, configured above) with a spinner
1584
1584
  // while a request is pending (--c-primary arc over a --c-surface-active track) and one message in the panel per state:
1585
- // loading and empty in --c-muted (4.74:1 / 7.85:1, the Listbox's empty message color) and an error in --c-error (4.53:1 / 7.16:1).
1585
+ // loading and empty in --c-muted (5.74:1 / 7.85:1, the Listbox's empty message color) and an error in --c-error (4.53:1 / 7.16:1).
1586
1586
  @use '@carissa-io/components-async-autocomplete/scss' as async-autocomplete with (
1587
1587
  $c-async-autocomplete-color: $var-text,
1588
1588
  $c-async-autocomplete-min-height: 1.5em,
@@ -1597,3 +1597,58 @@
1597
1597
  $c-async-autocomplete-empty-color: $var-muted,
1598
1598
  $c-async-autocomplete-error-color: $var-error
1599
1599
  );
1600
+
1601
+ // Section and container: layout only. Gutters of 1rem growing to 2rem and the three vertical rhythms are plain lengths; the
1602
+ // section title is --c-text (17.93:1 / 18.16:1) in the theme's h2 style and the lead paragraph is --c-muted (5.74:1 / 7.85:1).
1603
+ @use '@carissa-io/components-section/scss' as section with (
1604
+ $c-container-gutter: 1rem,
1605
+ $c-container-gutter-wide: 2rem,
1606
+ $c-section-spacing-sm: 2rem,
1607
+ $c-section-spacing-md: 4rem,
1608
+ $c-section-spacing-lg: 6rem,
1609
+ $c-section-header-gap: 0.75rem,
1610
+ $c-section-header-margin-bottom: 2rem,
1611
+ $c-section-title-font-size: $font-size-h2,
1612
+ $c-section-title-font-weight: $font-weight-black,
1613
+ $c-section-title-line-height: 1.2,
1614
+ $c-section-title-color: $var-text,
1615
+ $c-section-description-font-size: $font-size-lead,
1616
+ $c-section-description-line-height: 1.6,
1617
+ $c-section-description-color: $var-muted
1618
+ );
1619
+
1620
+ // FAQ: the accordion block above does the look (its stylesheet is forwarded by the faq one, so keep this block after it); the
1621
+ // answers are --c-text (17.93:1 / 18.16:1) at a 1.6 line height and the list is capped at a readable measure.
1622
+ @use '@carissa-io/components-faq/scss' as faq with (
1623
+ $c-faq-max-width: 48rem,
1624
+ $c-faq-answer-color: $var-text,
1625
+ $c-faq-answer-line-height: 1.6
1626
+ );
1627
+
1628
+ // Carousel: the arrows and the pause / play button are the secondary button (--c-muted border, --c-text glyph at 17.93:1 / 18.16:1, a
1629
+ // --c-surface-active hover), square like every control of the theme. Inactive dots are --c-muted and the
1630
+ // current one is --c-primary and longer (1.5rem against 0.625rem), so position is not told by colour alone. The ring is the
1631
+ // theme's --c-focus.
1632
+ @use '@carissa-io/components-carousel/scss' as carousel with (
1633
+ $c-carousel-gap: 1rem,
1634
+ $c-carousel-transition-duration: $transition-duration,
1635
+ $c-carousel-controls-gap: 1rem,
1636
+ $c-carousel-controls-margin-top: 1rem,
1637
+ $c-carousel-button-size: $min-height,
1638
+ $c-carousel-button-radius: $radius,
1639
+ $c-carousel-button-border-width: 1px,
1640
+ $c-carousel-button-border-color: $var-muted,
1641
+ $c-carousel-button-color: $var-text,
1642
+ $c-carousel-button-background: $var-background,
1643
+ $c-carousel-button-background-hover: $var-surface-active,
1644
+ $c-carousel-button-disabled-opacity: 0.5,
1645
+ $c-carousel-icon-size: 0.5rem,
1646
+ $c-carousel-icon-thickness: 2px,
1647
+ $c-carousel-dot-size: 0.625rem,
1648
+ $c-carousel-dot-size-current: 1.5rem,
1649
+ $c-carousel-dot-gap: 0.5rem,
1650
+ $c-carousel-dot-color: $var-muted,
1651
+ $c-carousel-dot-color-current: $var-primary,
1652
+ $c-carousel-focus-outline: $focus-width solid $var-focus,
1653
+ $c-carousel-focus-outline-offset: 2px
1654
+ );