@reliability-design/web 1.4.8 → 1.4.9
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/CHANGELOG.md +61 -0
- package/dist/components/rel-alert/README.md +1 -1
- package/dist/components/rel-autocomplete/README.md +2 -1
- package/dist/components/rel-carousel/README.md +1 -0
- package/dist/components/rel-checkbox/README.md +2 -1
- package/dist/components/rel-date-picker/README.md +7 -1
- package/dist/components/rel-radio/README.md +1 -1
- package/dist/components/rel-range-picker/README.md +7 -1
- package/dist/components/rel-segmented-button/README.md +5 -4
- package/dist/components/rel-select/README.md +6 -3
- package/dist/components/rel-stepper/README.md +12 -3
- package/dist/components/rel-tab/README.md +1 -0
- package/dist/components/rel-textfield/README.md +2 -2
- package/dist/components/rel-time-picker/README.md +6 -0
- package/dist/index.d.mts +131 -18
- package/dist/index.d.ts +131 -18
- package/dist/index.js +1086 -239
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1071 -224
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,66 @@
|
|
|
1
1
|
# Changelog — @reliability-design/web
|
|
2
2
|
|
|
3
|
+
## 1.4.9 (2026-09-26)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
Bug-fix release from a twenty-one item report against 1.4.8. Mostly geometry: things the token and
|
|
7
|
+
variant gates cannot see, because those gates read source text and bound token names while every one
|
|
8
|
+
of these defects is about what the browser actually *paints*. Two reported items turned out not to
|
|
9
|
+
be defects at all and are noted under [Not changed](#notchanged-149) so they are not re-filed.
|
|
10
|
+
|
|
11
|
+
Paired releases: `@reliability-design/angular` 1.4.9, which now requires web `^1.4.9`.
|
|
12
|
+
`@reliability-design/icons` is unchanged and stays on 1.4.4.
|
|
13
|
+
|
|
14
|
+
Three changes are visible without a rename to warn you; they are listed under
|
|
15
|
+
[Changed](#changed-149).
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- **`rel-textfield` `variant="legend"` was broken at every size.** It painted a 23 / 30 / 37px outline against the 32 / 40 / 48 ladder, sat its value 4–7.5px low and overflowed the bottom border by up to 6px at `small` — while `getBoundingClientRect()` reported the correct height, which is why nothing caught it. Two Blink fieldset behaviours caused it: a fieldset's border box *includes* the legend's protruding top half, and the legend's full height is subtracted from the content area (the intended remedy, `flex: 1` on the row, is inert — a fieldset's anonymous content box does not take a fixed height). The fieldset is now a decorative, absolutely-positioned outline layer and the content rides the ordinary container row, so the variant measures identically to `variant="default"`. Also fixed in the same pass: the legend's text sat 4px right of the value, its inline inset was frozen at 12px and could not see the size ramp, and the default size rendered a heavier legend than either neighbour.
|
|
20
|
+
- **The floating label was struck through by the field's own border.** The label masks the border with an opaque strip rather than cutting a notch, and the strip was positioned against the wrong reference, leaving part of the painted band crossing the label text. Affects `rel-textfield`, `rel-select` and — through composition — `rel-autocomplete`. The two components need *different* corrections because their containing blocks differ; both are now derived from the border and ring widths rather than restated.
|
|
21
|
+
- **`rel-select`'s floating label painted an opaque block over the field.** It filled the label's whole ~20px line box to hide a 1px border, erasing roughly 10px of the trigger's interior and 10px above it. Figma specifies a strip whose height *equals the border it masks* — 1px at rest, 2px focused — which `rel-textfield` already used. Two further defects fixed with it: there was no disabled fill swap, so a disabled select painted a lighter block over its own trigger; and a hard-coded `top: -2px` nudge applied to `.focused` but not `.has-value`, so the label **jumped 2px** when focus arrived on a filled select.
|
|
22
|
+
- **`rel-autocomplete` lit three suggestion rows at once.** The committed selection, the keyboard cursor and a stale CSS `:hover` all painted the same wash, so arrowing through a list with the mouse resting on it left no way to tell which row `Enter` would commit. `:hover` was redundant in that rule — `pointerenter` already moves the keyboard cursor onto the hovered row — and it was the only thing keeping a passed-over row lit. Every individual state was, and remains, pixel-correct against the design file.
|
|
23
|
+
- **`rel-accordion` appeared to gain a border on hover.** There was no hover border rule: `.accordion` always carried a 1px border and `appearance="shadow"` merely made it transparent, so the header was inset 1px on all four sides and the hover wash stopped short of the card edge, leaving a ring of untinted surface. The stroke is now an `outline` with a negative offset, which is what Figma's inside-aligned stroke means and costs no layout.
|
|
24
|
+
- **`rel-upload`'s disabled cursor never appeared.** `cursor: not-allowed` was set on the dropzone, but an ancestor carried `pointer-events: none`, and an element that is not a hit target never has its cursor consulted. `getComputedStyle` reported `not-allowed` while the pointer showed an arrow. The cursor now sits on the host. Separately, the file list failed `aria-required-children`: its container has `role="list"` and its rows carried no `role="listitem"`.
|
|
25
|
+
- **`rel-menu`'s submenu chevron was dulled to 60% opacity**, where the design file has it at full strength on the same token as the row label beside it. Its colour is now declared rather than inherited — `rel-menu` renders to the light DOM, so an inherited colour travelled through the consumer's page cascade.
|
|
26
|
+
- **`rel-carousel`'s vertical orientation put the arrows on the horizontal axis.** A vertical carousel got a left-pointing chevron at mid-left and a right-pointing one at mid-right, flanking the caption while the motion ran up and down. The arrows now ride the vertical axis with rotated chevrons. Vertical slides also centre their content instead of stretching it and leaving the caption top-anchored in dead space.
|
|
27
|
+
- **`rel-checkbox`'s indeterminate dash rendered 1.6× too thin.** An 8:1 viewBox was being letterboxed into a square box, so the dash painted at 6.875% of the frame where the design file has 11.11% — and at a fractional thickness at every size, which is what made it look soft. Both marks now use the design file's own 16-unit geometry. Corner radius was hand-written per size and *omitted* for `large` and `extra-large`, so a 24px box and an 18px box shared a corner; radius and border are now derived from the drawn square.
|
|
28
|
+
- **All three pickers left their panel floating over sticky page furniture.** `rel-date-picker`, `rel-range-picker` and `rel-time-picker` repositioned their panel on scroll, tracking a trigger that had already scrolled out of view — and because the panel is promoted to the browser top layer it kept painting above everything. They now **close on an outside scroll**, matching `rel-select` and `rel-autocomplete`. Scrolling *inside* a panel does not close it, including the time picker's hour, minute and period columns, which are each their own scroller.
|
|
29
|
+
- **`rel-date-picker` and `rel-range-picker` drew a hand-written calendar glyph** that matched no icon in the set. Both triggers now render the design system's `calendar-month`.
|
|
30
|
+
- **`rel-alert` reserved a pixel no variant used.** A 1px transparent border sat on the shared base, so every appearance's content box was 1px tighter than the design file — only `outline` has a stroke there, and it is inside-aligned, which overlays rather than insets. The content column also hugged its text instead of filling the row it is specified to fill. Both fixed; every appearance keeps a visible boundary in forced-colors mode.
|
|
31
|
+
- **`rel-radio` warned about a value you never set.** `density="compact"` with `alignment="trailing"` logged a notice — but `compact` is the default, so setting only `alignment` produced a warning about a coercion of a value the author never wrote. It now warns only when `density` was explicitly chosen.
|
|
32
|
+
|
|
33
|
+
### Added
|
|
34
|
+
|
|
35
|
+
- **`rel-stepper` `connector`.** `solid` (default, unchanged) or `dot` — a 1px connector dashed 2px on / 2px off, per the design file's `.Line Types`. Note `connector="dot"` is the **line between steps** while the existing `type="dot"` is the **step indicator disc**; they are independent axes that both use the word.
|
|
36
|
+
- **`rel-checkbox` `field-aria-label`**, joining `rel-switch`, `rel-radio`, `rel-segmented-button` and `rel-autocomplete`. It was the only control in that family without one.
|
|
37
|
+
- **`rel-change` as an alias on the last three components that lacked it.** `rel-select` and `rel-segmented-button` emitted only `changed`; `rel-tab` emitted only `rel-tab-change`. All three now emit `rel-change` *alongside* their existing event, with the same detail and timing. **Both fire — neither replaces the other**, and the legacy names are kept for as long as 1.x. Every interactive component in the library now emits `rel-change`.
|
|
38
|
+
- **`--rel-overlay-fallback-z`** (default `1001`), shared by `rel-select`, `rel-autocomplete` and the three pickers. Consulted **only** on the no-Popover-API fallback path: wherever the browser top layer is available the panel is promoted into it and z-index is inert, because top-layer elements are not in the page stacking order at all.
|
|
39
|
+
- **`--rel-select-surface`** and **`--rel-select-surface-disabled`**, mirroring `--rel-textfield-surface` — the colour of the strip the floating label lays over the trigger's border. Set them when the field sits on a surface that is not the trigger's own fill.
|
|
40
|
+
- **`--rel-carousel-slide-display`** (default `flex`), the escape hatch for the vertical slide centring above. Set it to `block` if forcing flex reflows your slide's own children.
|
|
41
|
+
- **`rel-stepper` `textalign` now logs its deprecation.** It has been a documented alias of `label-placement` for some time but said nothing at runtime. The notice fires once, only for a non-default value, and only when `label-placement` is unset.
|
|
42
|
+
|
|
43
|
+
### Changed
|
|
44
|
+
|
|
45
|
+
<a id="changed-149"></a>Three changes are visible with no rename to warn you:
|
|
46
|
+
|
|
47
|
+
- **`rel-radio` `size="small"` renders larger again.** 1.4.8 gave Small its own 16px control on the premise that the ramp was 16/20/24. That premise was never read off the design file, which pins Small **and** Medium to one 20px control and separates them by type alone — verified across all forty-five variants. Small returns to a 20px control, 16px ring and 8px dot, keeping its smaller label type. This reverts a deliberate 1.4.8 change; the component's own README had said so correctly the whole time.
|
|
48
|
+
- **`rel-textfield`'s `::part(control)` is a `div` for `variant="legend"`,** where it used to be the `<fieldset>`. The fieldset became a decorative outline layer so the variant could sit on the size ladder. `::part(control)` still matches; only rules that depended on it *being* a fieldset do not.
|
|
49
|
+
- **`rel-alert` has no minimum width.** It carried `min-width: 360px`, which was code-invented — the design file's Alert is a fixed 400 with no minimum — and it stopped the alert fitting any column narrower than 360px. Set a floor on the host if you want one.
|
|
50
|
+
|
|
51
|
+
Also changed, but only observable on the fallback path: the picker panels' fallback `z-index` drops
|
|
52
|
+
from `2147483647` to the shared `--rel-overlay-fallback-z` (default `1001`). This value is consulted
|
|
53
|
+
only in browsers without the Popover API and has no effect wherever the top layer is available. The
|
|
54
|
+
old value unconditionally beat consumer application chrome.
|
|
55
|
+
|
|
56
|
+
### Not changed
|
|
57
|
+
|
|
58
|
+
<a id="notchanged-149"></a>Two reported items were investigated and found correct, recorded here so
|
|
59
|
+
they are not re-filed:
|
|
60
|
+
|
|
61
|
+
- **`rel-alert`'s colours and tones match the design file.** All twelve style × tone cells bind exactly the token the design file binds. The report came from comparing against a dark-mode rendering; the REST API resolves variable-bound fills in whichever mode is default, so a raw hex from it names no token and cannot be compared to a light-mode value.
|
|
62
|
+
- **`rel-checkbox`'s four sizes are the design file's own ramp.** The drawn squares land 1.6px apart at the lower steps, which does read as tight, but 16 / 18 / 20 / 24 is what the file specifies and the code already matched it.
|
|
63
|
+
|
|
3
64
|
## 1.4.8 (2026-09-21)
|
|
4
65
|
|
|
5
66
|
|
|
@@ -138,7 +138,7 @@ saved.dismissed = false;
|
|
|
138
138
|
|
|
139
139
|
- `tone` and `appearance` are independent axes; every tone renders in every appearance. Solid uses inverse text and carries the `--rel-shadows-md` elevation; subtle and outline have no shadow.
|
|
140
140
|
- The status icon is fixed per tone (`info` for neutral and info, `check-circle`, `alert-circle`, `error`) and cannot be replaced or removed — there are no slots.
|
|
141
|
-
- The alert has `min-width: 360px
|
|
141
|
+
- The alert stretches to its container (`display: block`) and has **no minimum width**. It carried a `min-width: 360px` until 1.4.9; that floor was code-invented (Figma's Alert is a fixed 400 with `minWidth` unset on all twelve variants) and stopped the alert fitting a narrower column. Set a floor on the host if you want one.
|
|
142
142
|
- The link renders only when both `url-text` and `url-href` are set; it opens in the same tab (no `target`).
|
|
143
143
|
- `dismissible` and `dismissed` are Lit booleans: any attribute value (including `"false"`) counts as true. Toggle them as properties from script.
|
|
144
144
|
- `dismissed` only hides the host; the element stays in the DOM and keeps its state. Resetting `dismissed = false` shows it again without re-firing `dismiss`.
|
|
@@ -166,6 +166,7 @@ Set these on the host (or an ancestor) to customise the element without touching
|
|
|
166
166
|
| `--rel-autocomplete-status-color` | `var(--rel-text-body)` | Colour of the loading / no-results status rows |
|
|
167
167
|
| `--rel-autocomplete-supporting-color` | `var(--rel-text-caption)` | — |
|
|
168
168
|
| `--rel-autocomplete-value-color` | `var(--rel-text-default)` | Suggestion label colour |
|
|
169
|
+
| `--rel-overlay-fallback-z` | `1001` | z-index of the panel on the **no-Popover-API fallback path only**. Wherever the browser top layer is available the panel is promoted into it and this value is inert, because top-layer elements are not in the page stacking order at all. Shared with the other overlays (select, autocomplete, the three pickers) so the whole family can be moved from one place. |
|
|
169
170
|
|
|
170
171
|
<!-- readme-gen:end -->
|
|
171
172
|
|
|
@@ -192,7 +193,7 @@ Focus: `focus()` on the host delegates to the inner field. Focusing the field wi
|
|
|
192
193
|
## Notes / Edge Cases
|
|
193
194
|
|
|
194
195
|
- **The second line is searchable, and that changes which rows appear.** `supportingText` is matched by the filter alongside the label, because the identifiers it usually carries (a DOT or MC number, a code) are the reason to show it at all. An item whose label does not match but whose supporting text does will now appear in the list, and the matched characters are emboldened on whichever line they were found. Set `filter="none"` or drop `supportingText` if you need the previous label-only behaviour.
|
|
195
|
-
- The second line truncates with an ellipsis rather than wrapping — the panel is pinned to the field's width, and a suggestion row is a fixed-height scanning target. Rows grow from 32 / 40 / 48 to roughly 46 / 58 / 70 when an item supplies one; a row without `supportingText` is unchanged.
|
|
196
|
+
- The second line truncates with an ellipsis rather than wrapping — the panel is pinned to the field's width, and a suggestion row is a fixed-height scanning target. Rows grow from 32 / 40 / 48 to roughly 46 / 58 / 70 when an item supplies one; a row without `supportingText` is unchanged. This holds in `multiple` mode too (the nested checkbox rows used to wrap, and an unbroken identifier could push the panel sideways). Nothing is hyphenated. A row that carries a second line also gets a native `title` with its full label and supporting text, so hovering a truncated row reveals what was cut.
|
|
196
197
|
- **In `multiple` mode the text input stays a search box.** It never displays the committed values: in single mode the input shows the selected label, but in multi there is no single label to show, and writing a value list into the field would overwrite the query being typed. The chips therefore render *beneath* the field (`rel-select` can carry them inside its trigger because a trigger is not editable), the query clears after each toggle so the next search starts fresh, and the panel stays open. Read the selection from `selectedValues`, not `value`; `changed` carries the array.
|
|
197
198
|
- `value` and the visible text are separate: typing only emits `rel-change`; `value` (and `changed`) update when a row is picked, or when the text exactly matches an item's label or value (case-insensitive) on Enter, Tab, blur or outside click. Free text that matches nothing stays in the field but never becomes `value`.
|
|
198
199
|
- Setting `value` externally (attribute, property, Angular `writeValue`) shows the matching item's label, or the raw string when no item matches (common with remote data on init) — provide the item in `items` to show its label.
|
|
@@ -142,6 +142,7 @@ Set these on the host (or an ancestor) to customise the element without touching
|
|
|
142
142
|
| `--rel-carousel-dot-size` | `8px` | Diameter of an inactive dot (default 8px). |
|
|
143
143
|
| `--rel-carousel-dot-size-active` | `16px` | Width of the active (brand-coloured, pill-shaped) dot (default 16px). |
|
|
144
144
|
| `--rel-carousel-height` | `320px` | Viewport height in `orientation="vertical"` (default 320px); the horizontal viewport takes the height of its tallest slide. |
|
|
145
|
+
| `--rel-carousel-slide-display` | `flex` | `display` given to each slide in `orientation="vertical"` only, which is what centres the slide's content in the fixed-height viewport as Figma draws it. Set it to `block` to opt out if forcing flex reflows your slide's own children. Horizontal slides are never touched. |
|
|
145
146
|
| `--rel-carousel-nav-size` | `32px` | Width / height of the arrow buttons (default 32px, matching Figma; applies to both side and bottom arrows). |
|
|
146
147
|
| `--rel-carousel-slide-bg` | `var(--rel-bg-neutral)` | — |
|
|
147
148
|
| `--rel-carousel-slide-height` | `100%` | Written by the component on its own host to the computed panel height (used for vertical slides); not meant to be set by consumers. |
|
|
@@ -67,9 +67,10 @@ A tri-state checkbox implementing the Figma "Checkbox" sheet: a hidden native `<
|
|
|
67
67
|
| `state` | `state` | `"default" \| "error" \| "disabled"` | `"default"` | Visual and interaction state (Figma State): `"error"` swaps in the danger colours, `"disabled"` blocks input and dims the whole control to 0.4 opacity (colours unchanged, as Figma specifies). |
|
|
68
68
|
| `status` | `status` | `"unchecked" \| "checked" \| "indeterminate"` | `"unchecked"` | Check state. `"indeterminate"` is programmatic-only — a user toggle always resolves to checked or unchecked. |
|
|
69
69
|
| `value` | `value` | `string` | `""` | Value reported in the `rel-change` event detail (it is not submitted with a form). |
|
|
70
|
-
| `size` | `size` | `"small" \| "medium" \| "large" \| "extra-large"` | `"medium"` | Control scale (Figma Size), on Figma's four-step ramp of control frames: `"small"` 16px / body-sm, `"medium"` (default) 18px / body-md, `"large"` 20px / body-lg, `"extra-large"` 24px / body-xl. The drawn square is inset to 80% of the frame, so the frame is the layout footprint (what the row gap and the compact indent measure from) rather than the visible square. Reflected. |
|
|
70
|
+
| `size` | `size` | `"small" \| "medium" \| "large" \| "extra-large"` | `"medium"` | Control scale (Figma Size), on Figma's four-step ramp of control frames: `"small"` 16px / body-sm, `"medium"` (default) 18px / body-md, `"large"` 20px / body-lg, `"extra-large"` 24px / body-xl. Supporting text sits one body step below the label at every size (body-xs / sm / md / lg, i.e. 10 / 12 / 14 / 16px). The drawn square is inset to 80% of the frame, so the frame is the layout footprint (what the row gap and the compact indent measure from) rather than the visible square. Reflected. |
|
|
71
71
|
| `density` | `density` | `"compact" \| "comfortable"` | `"compact"` | Supporting-text layout (Figma Density). `"compact"` (default) puts the supporting text on its own line under the row, indented past the box; `"comfortable"` stacks label + supporting text beside the box. Reflected. |
|
|
72
72
|
| `alignment` | `alignment` | `"leading" \| "trailing"` | `"leading"` | Box position (Figma Alignment). `"leading"` (default) renders the box before the label; `"trailing"` pushes it to the far edge of the row. Trailing has no compact layout in the spec, so it always renders with comfortable density. Reflected. |
|
|
73
|
+
| `fieldAriaLabel` | `field-aria-label` | `string` | `undefined` | Accessible name for the native checkbox, for when the visible `label` is absent or not a usable name on its own — a checkbox in a table row, or one whose meaning comes from its column header. Normally unnecessary: the input is wrapped in the `<label>` that renders `label`, so the visible text already names it natively. Setting this puts an `aria-label` on the input, which OVERRIDES that wrapping label — so set it only when the visible text is the wrong name, not to repeat it. Left unset the attribute is not emitted at all. |
|
|
73
74
|
|
|
74
75
|
### Public fields / accessors
|
|
75
76
|
|
|
@@ -146,6 +146,12 @@ Without `display-format` or `formatDate` the field shows the locale long date (`
|
|
|
146
146
|
|
|
147
147
|
<!-- readme-gen:end -->
|
|
148
148
|
|
|
149
|
+
|
|
150
|
+
## CSS Custom Properties
|
|
151
|
+
|
|
152
|
+
| Custom property | Default | Description |
|
|
153
|
+
| --- | --- | --- |
|
|
154
|
+
| `--rel-overlay-fallback-z` | `1001` | z-index of the panel on the **no-Popover-API fallback path only**. Wherever the browser top layer is available the panel is promoted into it and this value is inert, because top-layer elements are not in the page stacking order at all. Shared with the other overlays (select, autocomplete, the three pickers) so the whole family can be moved from one place. |
|
|
149
155
|
## Accessibility
|
|
150
156
|
|
|
151
157
|
- The text input is a `role="combobox"` with `aria-haspopup="dialog"`, `aria-expanded`, `aria-controls` pointing at the panel, `aria-required` from `required`, `aria-invalid` when `error` is set or typed text failed to parse, and `aria-describedby` pointing at the helper/error line. Its name is the `label` (via `aria-labelledby`) or the fixed string "Select date" when no label is set; the required `*` is `aria-hidden`.
|
|
@@ -191,7 +197,7 @@ Without `display-format` or `formatDate` the field shows the locale long date (`
|
|
|
191
197
|
- The years grid shows one 12-year page at a time, anchored so the year in view sits in the seventh cell; the chevrons page by 12. In the months grid the chevrons step a year, and in the days grid a month. A month or year cell is disabled only when *every* day it covers falls outside `min-date`/`max-date`.
|
|
192
198
|
- The panel always reopens on the days grid, whichever view was showing when it closed.
|
|
193
199
|
- The field is Figma's TextField instance: `border-radius: xs` (4 px), 12 px inline padding at `medium`, a 4 px label gap and an 18 px suffix icon — the same geometry `rel-textfield` uses, so the two line up in a shared form row. Icon and inline padding step with `size` (16/18/20 px and 8/12/16 px).
|
|
194
|
-
- The panel is promoted to the browser top layer (`popover="manual"`), so it renders above `rel-modal` / `rel-drawer` and transformed or `overflow: hidden` ancestors, and
|
|
200
|
+
- The panel is promoted to the browser top layer (`popover="manual"`), so it renders above `rel-modal` / `rel-drawer` and transformed or `overflow: hidden` ancestors. It repositions on resize, and **closes on an outside scroll** (changed in 1.4.9 — it used to track the trigger, which left the panel floating over sticky page furniture once the trigger had scrolled out of view). Scrolling inside the panel does not close it. It flips above the trigger when there is no room below, stays 8 px inside the viewport, is capped at 600 px tall (scrolls) and never wider than the viewport.
|
|
195
201
|
- Below-field text resolves as `errorMessage ?? typed-entry error ?? helperText` while in error, else `helperText`. A typed-entry error styles the field like `error` without setting the property.
|
|
196
202
|
- `readonly` and `disabled` both stop the popover from opening (field click, icon, Enter, ArrowDown); `disabled` also blocks all pointer events.
|
|
197
203
|
- `closeOnSelect` is a boolean attribute: it can only be turned off from JavaScript (`picker.closeOnSelect = false`). It affects grid picks only — typed entry and shortcuts always close the popover.
|
|
@@ -63,7 +63,7 @@ plan.addEventListener("rel-change", (e) => {
|
|
|
63
63
|
| `options` | `options` | `RadioOption[]` | `[]` | The options to render, in order. Each entry is a `RadioOption` (`value`, `label`, optional `supportingText`, `disabled`, `icon` or `avatar`). Set as a property (`.options=`) or as a JSON attribute. |
|
|
64
64
|
| `name` | `name` | `string` | `"radio-group"` | `name` given to every native radio input inside this element. It scopes native radio-group behaviour (arrow-key movement, mutual exclusion) to this group, so give each group on a page a distinct name. |
|
|
65
65
|
| `orientation` | `orientation` | `"horizontal" \| "vertical"` | `"vertical"` | Layout axis of the option list. `"vertical"` (default) stacks options in a column; `"horizontal"` lays them out in a wrapping row. Reflected. |
|
|
66
|
-
| `size` | `size` | `"small" \| "medium" \| "large" \| "sm" \| "md" \| "lg"` | `"medium"` | Control scale. `"medium"` (default
|
|
66
|
+
| `size` | `size` | `"small" \| "medium" \| "large" \| "sm" \| "md" \| "lg"` | `"medium"` | Control scale, matching Figma's Size axis. `"small"` and `"medium"` (default) both draw a 20px control — they differ by TYPE, body-md labels against body-lg — and `"large"` steps the control to 24px with body-xl labels. Supporting text sits one body step below the label at every size (body-sm / md / lg, i.e. 12 / 14 / 16px). `sm` / `md` / `lg` are alternative spellings and stay supported — normalised to the full words, not deprecated. Small and Medium sharing a control is Figma's design, not an oversight: verified against the live Published DS file on 2026-09-25, `.Radio Control` measures 20x20 in all 15 Small variants and all 15 Medium ones. v1.4.8 briefly gave Small its own 16px control on the mistaken premise that the ramp was 16/20/24; 1.4.9 reverts that. Reflected. |
|
|
67
67
|
| `density` | `density` | `"compact" \| "comfortable"` | `"compact"` | Supporting-text layout (Figma Density). `"compact"` (default) puts the supporting text on its own line under the row, indented past the control; `"comfortable"` stacks label + supporting text beside the control. Reflected. |
|
|
68
68
|
| `alignment` | `alignment` | `"leading" \| "trailing"` | `"leading"` | Control position (Figma Layout). `"leading"` (default) renders the control before the label; `"trailing"` pushes it to the far edge of the row. Trailing has no compact layout in the spec, so it always renders with comfortable density. Meant for vertical orientation — trailing options stretch to the group's full width. Reflected. |
|
|
69
69
|
| `selectedValue` | `selectedvalue` | `string \| null` | `null` | `value` of the currently selected option, or `null` when nothing is selected. Set it to preselect an option; it is updated on every user selection. Reflected. |
|
|
@@ -118,6 +118,12 @@ The left pane lists Today (`T`), Tomorrow (`M`), Yesterday (`Y`), This Week (`W`
|
|
|
118
118
|
|
|
119
119
|
<!-- readme-gen:end -->
|
|
120
120
|
|
|
121
|
+
|
|
122
|
+
## CSS Custom Properties
|
|
123
|
+
|
|
124
|
+
| Custom property | Default | Description |
|
|
125
|
+
| --- | --- | --- |
|
|
126
|
+
| `--rel-overlay-fallback-z` | `1001` | z-index of the panel on the **no-Popover-API fallback path only**. Wherever the browser top layer is available the panel is promoted into it and this value is inert, because top-layer elements are not in the page stacking order at all. Shared with the other overlays (select, autocomplete, the three pickers) so the whole family can be moved from one place. |
|
|
121
127
|
## Accessibility
|
|
122
128
|
|
|
123
129
|
- Default trigger: the input is a `role="combobox"` with `aria-haspopup="dialog"`, `aria-expanded`, `aria-controls` pointing at the panel, `aria-label` = `placeholder`, `aria-invalid` while typed text is in error and `aria-describedby` pointing at the error line. The icon buttons are labelled "Clear date range" and "Open date range panel" (with `aria-expanded`).
|
|
@@ -157,7 +163,7 @@ The left pane lists Today (`T`), Tomorrow (`M`), Yesterday (`Y`), This Week (`W`
|
|
|
157
163
|
- The trigger text, From / To readouts, month names and day labels are always rendered in `en-US` (`MM/DD/YYYY`); `locale` only affects month-name matching in typed input.
|
|
158
164
|
- Leading and trailing cells of adjacent months are visible but never selectable, and render at 40% label opacity (Figma "Day of a week = Other Month"). Today gets a 1 px brand ring; weekend cells render at 70% opacity (Figma "Day of a week = Weekend") unless they are range endpoints.
|
|
159
165
|
- Range styling follows Figma ".Date Range Days": the selected run is an `elevated-1` band across the full cell, rounded at the two ends (a one-day range is a single rounded pill), and the two endpoints are `inverse-2` discs with inverse semibold labels. Days between the endpoints keep their normal label on the band.
|
|
160
|
-
- The panel (and the popup backdrop) is promoted to the browser top layer (`popover="manual"`), so it works inside `rel-modal` / `rel-drawer` and above transformed or `overflow: hidden` ancestors. In `popover` mode it anchors below the host, flips above when there is no room, stays 8 px inside the viewport and
|
|
166
|
+
- The panel (and the popup backdrop) is promoted to the browser top layer (`popover="manual"`), so it works inside `rel-modal` / `rel-drawer` and above transformed or `overflow: hidden` ancestors. In `popover` mode it anchors below the host, flips above when there is no room, stays 8 px inside the viewport, repositions on resize and **closes on an outside scroll** (changed in 1.4.9 — it used to track the trigger, leaving the panel over sticky page furniture once the trigger had scrolled away); scrolling inside the panel does not close it. In `popup` mode it is fixed-centred behind a 40% black backdrop (part `overlay`) whose click cancels. It never exceeds the viewport and scrolls internally.
|
|
161
167
|
- `width`, `min-width`, `max-width`, `height`, `min-height` and `max-height` are copied to the host's inline style on every update, so they override stylesheet rules on the host.
|
|
162
168
|
- The icon trigger has no typed entry, no clear icon and no error line; parts `trigger` and `value` are not rendered in that variant.
|
|
163
169
|
- There is no helper-text or external error API; the only text below the field is the typed-entry error.
|
|
@@ -68,7 +68,7 @@ The control comes in three sizes that match the text field ramp — `small` 32px
|
|
|
68
68
|
{ label: 'Oldest', value: 'oldest' },
|
|
69
69
|
{ label: 'Popular', value: 'popular' }
|
|
70
70
|
];
|
|
71
|
-
sortOrder.addEventListener('
|
|
71
|
+
sortOrder.addEventListener('rel-change', (e) => {
|
|
72
72
|
console.log('Selected:', e.detail.value);
|
|
73
73
|
});
|
|
74
74
|
</script>
|
|
@@ -106,7 +106,8 @@ const options: SegmentOption[] = [
|
|
|
106
106
|
|
|
107
107
|
| Event | Detail | Bubbles / composed | Description |
|
|
108
108
|
| --- | --- | --- | --- |
|
|
109
|
-
| `
|
|
109
|
+
| `rel-change` | `{ value: option.value }` | bubbles / composed | Fired after a non-disabled segment is selected by click or arrow / Home / End key (also when it was already the active one). Detail: `{ value: string }`. The canonical name — prefer it over the legacy `changed`, which fires alongside it with the same detail. |
|
|
110
|
+
| `changed` | `{ value: option.value }` | bubbles / composed | Legacy alias of `rel-change`, fired with the same detail at the same time. Kept indefinitely so existing listeners keep working; new code should use `rel-change`. |
|
|
110
111
|
|
|
111
112
|
## Slots
|
|
112
113
|
|
|
@@ -131,7 +132,7 @@ const options: SegmentOption[] = [
|
|
|
131
132
|
- The container is a `role="radiogroup"`; give it a name with `field-aria-label` — the component renders no visible group label, so without it screen readers announce an unnamed group.
|
|
132
133
|
- Every segment is a native `<button role="radio">` with `aria-checked="true"` on the active one and `aria-checked="false"` on the rest. Disabled options get the native `disabled` attribute, so they are not focusable and do not fire `click`.
|
|
133
134
|
- Roving tabindex (APG radio-group pattern): the whole group is a single Tab stop. The active segment carries `tabindex="0"`; if `active` matches no enabled option, the first enabled segment takes the Tab stop instead (without becoming selected). All other segments are `tabindex="-1"`.
|
|
134
|
-
- Arrow keys both move focus and select, so they emit
|
|
135
|
+
- Arrow keys both move focus and select, so they emit the change event immediately. Focus is moved to the newly active segment after the re-render.
|
|
135
136
|
- Focus ring: a 2px `border-focus` outline drawn inside the segment (`outline-offset: -2px`) and raised with `z-index: 1` so it stays above the neighbouring segment.
|
|
136
137
|
- Nothing is announced beyond the native `aria-checked` change.
|
|
137
138
|
|
|
@@ -145,7 +146,7 @@ const options: SegmentOption[] = [
|
|
|
145
146
|
|
|
146
147
|
## Notes / Edge Cases
|
|
147
148
|
|
|
148
|
-
- The change event is
|
|
149
|
+
- **Two events fire for one selection — listen to ONE of them or your handler runs twice.** The change event is `rel-change`, matching the rest of the library. The legacy un-prefixed `changed` fires alongside it with the same detail and is kept so existing listeners keep working — listen to one, not both. Both bubble and are composed, so either can be caught on any ancestor, and both fire when the already-active segment is clicked again.
|
|
149
150
|
- `active` is a plain string compared against `option.value`; setting it to a value that is not in `options` (or to a disabled option's value) renders no active segment.
|
|
150
151
|
- `options` is compared by reference: mutate-in-place changes are not picked up — assign a new array.
|
|
151
152
|
- The host is `display: inline-block` and the group `inline-flex`, so it sizes to its content; `align` has no visible effect until the host is stretched (e.g. `style="display: block; width: 100%"`). Every segment has `min-width: 80px`, so a group is never narrower than 80px per segment.
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
country.addEventListener("changed", (e) => console.log(e.detail.value)); // "Canada"
|
|
14
14
|
</script>
|
|
15
15
|
|
|
16
|
-
<!-- Object options: value / label / icon / supportingText (
|
|
16
|
+
<!-- Object options: value / label / icon / supportingText (a second line under the label, in single and multi rows) -->
|
|
17
17
|
<script>
|
|
18
18
|
country.options = [
|
|
19
19
|
{ value: "air", label: "Air freight", icon: "air" },
|
|
@@ -123,8 +123,10 @@ Set these on the host (or an ancestor) to customise the element without touching
|
|
|
123
123
|
| Custom property | Default | Description |
|
|
124
124
|
| --- | --- | --- |
|
|
125
125
|
| `--rel-field-label-reserve` | `calc(var(--rel-body-md-regular-line-height) + var(--rel-spacing-2xs))` | Height held for the label row whenever no label renders above the trigger — `labelStyle="none"`, and `labelStyle="floating"` where the label sits inside the trigger — so the trigger still lines up with labelled siblings in a form row. Defaults to that size's label line-height plus the wrapper gap (24px at `small`, 26px at `medium` and `large`). Set to `0` to collapse the row entirely (toolbars, table cells). |
|
|
126
|
-
| `--rel-select-bg` | `var(--rel-bg-neutral)` | Background of the trigger, the
|
|
126
|
+
| `--rel-select-bg` | `var(--rel-bg-neutral)` | Background of the trigger, the panel and its sticky header. No longer paints behind the floating label — see `--rel-select-surface` |
|
|
127
127
|
| `--rel-select-bg-disabled` | `var(--rel-bg-neutral-elevated-2)` | Trigger background when `state="disabled"` |
|
|
128
|
+
| `--rel-select-surface` | `var(--rel-select-bg)` | Colour of the strip the floating label lays over the trigger's top border and focus ring, so the label appears to sit in a notch. It overshoots the band by 2px on the page side, so it must match the surface the field sits on. Set it when the field sits on a surface that is not the trigger's own fill, otherwise the strip shows as a short dash of the wrong colour. Mirrors `--rel-textfield-surface` |
|
|
129
|
+
| `--rel-select-surface-disabled` | `var(--rel-select-bg-disabled)` | The same strip while `state="disabled"`, where the trigger fill changes |
|
|
128
130
|
| `--rel-select-border-default` | `var(--rel-border-default)` | Trigger border colour at rest |
|
|
129
131
|
| `--rel-select-border-disabled` | `var(--rel-border-accent)` | Trigger border colour when `state="disabled"` |
|
|
130
132
|
| `--rel-select-border-error` | `var(--rel-border-danger)` | Trigger border colour when `state="error"` |
|
|
@@ -143,6 +145,7 @@ Set these on the host (or an ancestor) to customise the element without touching
|
|
|
143
145
|
| `--rel-select-search-border` | `var(--rel-border-default)` | Border colour of the search field inside the panel |
|
|
144
146
|
| `--rel-select-supporting-color` | `var(--rel-text-body)` | Supporting text colour (non-error) |
|
|
145
147
|
| `--rel-select-value-color` | `var(--rel-text-default)` | Colour of the selected value, the search text, option labels and the check mark |
|
|
148
|
+
| `--rel-overlay-fallback-z` | `1001` | z-index of the panel on the **no-Popover-API fallback path only**. Wherever the browser top layer is available the panel is promoted into it and this value is inert, because top-layer elements are not in the page stacking order at all. Shared with the other overlays (select, autocomplete, the three pickers) so the whole family can be moved from one place. |
|
|
146
149
|
|
|
147
150
|
<!-- readme-gen:end -->
|
|
148
151
|
|
|
@@ -176,7 +179,7 @@ Set these on the host (or an ancestor) to customise the element without touching
|
|
|
176
179
|
- `state="error"` clears itself to `"default"` on the next selection, toggle, Select-all or chip removal. `state="disabled"` dims the whole field to 50% opacity, makes chips non-removable and blocks pointer events on them.
|
|
177
180
|
- Turning `multiple` off resets `selectedValues` to `[]`. `selected` is ignored in multi mode and `selectedValues` in single mode.
|
|
178
181
|
- Chips never wrap: the number shown adapts to the trigger width (measured through a hidden copy of every chip and a `ResizeObserver`), with the remainder collapsed into a trailing "+N" chip. At least one chip always stays visible; `max-visible-chips` is an extra hard cap. A selected value with no matching option is shown as its raw value.
|
|
179
|
-
- Option `icon`
|
|
182
|
+
- Option `icon` and `supportingText` render in both single and multi rows. Both lines truncate to one line with an ellipsis in either mode (nothing wraps or hyphenates), and a row that carries a second line gets a native `title` with its full text so hovering reveals what was cut. An unknown icon name logs a `rel-icon` warning and renders nothing (no reserved space).
|
|
180
183
|
- A custom `right-icon` replaces the chevron and does not rotate when the panel opens; the default chevron rotates 180°.
|
|
181
184
|
- The search field uses the small field sizing for both `small` and `medium` selects; only `large` bumps it to medium. Empty results show "No matches found" (or "No options available" when `options` is empty).
|
|
182
185
|
- The `helper` part exists only while `supporting-text` is non-empty.
|
|
@@ -37,6 +37,11 @@ A multi-step progress indicator implementing the Figma "Stepper" component: its
|
|
|
37
37
|
<rel-stepper type="number" steps='[{"title":"Plan","status":"done"},{"title":"Build","status":"progress"},{"title":"Ship","status":"disabled"}]'></rel-stepper>
|
|
38
38
|
<rel-stepper type="icon" steps='[{"title":"Plan","status":"done"},{"title":"Build","status":"progress"},{"title":"Ship","status":"disabled"}]'></rel-stepper>
|
|
39
39
|
|
|
40
|
+
<!-- Figma .Line Types axis: the CONNECTOR between steps, dashed 2px on / 2px off.
|
|
41
|
+
Distinct from type="dot" above, which is the indicator disc — combine freely. -->
|
|
42
|
+
<rel-stepper connector="dot" steps='[{"title":"Plan","status":"done"},{"title":"Build","status":"progress"},{"title":"Ship","status":"disabled"}]'></rel-stepper>
|
|
43
|
+
<rel-stepper connector="dot" type="dot" steps='[{"title":"Plan","status":"done"},{"title":"Build","status":"progress"},{"title":"Ship","status":"disabled"}]'></rel-stepper>
|
|
44
|
+
|
|
40
45
|
<!-- Label placement (Figma Typel): top-aligned beside the indicator, or indicator above the label -->
|
|
41
46
|
<rel-stepper orientation="vertical" label-placement="top" steps='[{"title":"Review","status":"done"},{"title":"Approve","status":"progress"}]'></rel-stepper>
|
|
42
47
|
<rel-stepper label-placement="inverted" steps='[{"title":"Review","status":"done"},{"title":"Approve","status":"progress"}]'></rel-stepper>
|
|
@@ -65,11 +70,13 @@ A multi-step progress indicator implementing the Figma "Stepper" component: its
|
|
|
65
70
|
|
|
66
71
|
```html
|
|
67
72
|
<!-- textalign="right" → label-placement="centered"; textalign="below" → label-placement="inverted".
|
|
68
|
-
Still supported and still the fallback when label-placement is unset
|
|
73
|
+
Still supported and still the fallback when label-placement is unset. Since 1.4.9 a
|
|
74
|
+
non-default value logs a one-time console notice; it had been marked deprecated since the
|
|
75
|
+
rename but never said so at runtime. Setting label-placement supersedes it and stays silent. -->
|
|
69
76
|
<rel-stepper textalign="right" steps='[{"title":"Review","status":"done"},{"title":"Approve","status":"progress"}]'></rel-stepper>
|
|
70
77
|
```
|
|
71
78
|
|
|
72
|
-
The `Step` type and the `StepStatus`, `StepperType` and `LabelPlacement` unions are exported from the package:
|
|
79
|
+
The `Step` type and the `StepStatus`, `StepperType`, `StepperConnector` and `LabelPlacement` unions are exported from the package:
|
|
73
80
|
|
|
74
81
|
```typescript
|
|
75
82
|
import type { Step } from '@reliability-design/web';
|
|
@@ -93,7 +100,8 @@ const steps: Step[] = [
|
|
|
93
100
|
| --- | --- | --- | --- | --- |
|
|
94
101
|
| `steps` | `steps` | `Step[]` | `[]` | The steps in order, each `{ title: string; subtitle?: string; status: "done" \| "progress" \| "disabled" }`. Set as a property (or a JSON array attribute); the component never mutates it — update it from the `rel-step-change` handler. |
|
|
95
102
|
| `orientation` | `orientation` | `"horizontal" \| "vertical"` | `"horizontal"` | Layout axis: `horizontal` (default) lays the steps out in a centred row; `vertical` stacks them. |
|
|
96
|
-
| `type` | `type` | `"default" \| "dot" \| "number" \| "icon"` | `"default"` | Figma "Type" axis — what the
|
|
103
|
+
| `type` | `type` | `"default" \| "dot" \| "number" \| "icon"` | `"default"` | Figma "Type" axis — what the INDICATOR shows: `default` (step number, check mark once done), `dot` (8px dot, no glyph), `number` (always the number) or `icon` (always the check mark). Not the same axis as `connector="dot"`, which dashes the line between steps. Reflected. |
|
|
104
|
+
| `connector` | `connector` | `"solid" \| "dot"` | `"solid"` | Figma ".Line Types" axis — how the line BETWEEN steps is drawn: `solid` (an unbroken 1px rule) or `dot` (a 1px rule dashed 2px on, 2px off). Applies to both orientations. Not the same axis as `type="dot"`, which is the indicator disc; the two are independent and can be combined. Reflected. |
|
|
97
105
|
| `labelPlacement` | `label-placement` | `"top" \| "centered" \| "inverted"` | `undefined` | Figma "Typel" axis — where the label sits: `top` (top-aligned beside the indicator), `centered` (vertically centred beside it) or `inverted` (indicator above the label). When unset, the deprecated `textAlign` prop drives the layout (`below` ≈ `inverted`, `right` ≈ `centered`). Figma marks Top + Horizontal as an unsupported combination. Reflected. |
|
|
98
106
|
|
|
99
107
|
#### Deprecated (still supported — removal targeted for 3.0)
|
|
@@ -156,6 +164,7 @@ Set these on the host (or an ancestor) to customise the element without touching
|
|
|
156
164
|
- `label-placement` is the canonical layout switch (Figma `Typel`). When it is unset the deprecated `textalign` prop decides: `below` renders like `inverted` (indicator above the label), `right` renders like `centered` (label beside the indicator). `top` differs from `centered` only in aligning the label to the top of the indicator. Figma marks Top + Horizontal as an unsupported combination.
|
|
157
165
|
- Statuses map to the Figma `.Artwork` states: `done` = inverse fill with a check mark (or the number when `type="number"`, nothing when `type="dot"`), `progress` = neutral fill with a strong 1px border, `disabled` = the same drawn at 40% opacity. Connectors are `border-strong` after a `done` / `progress` step and `border-default` otherwise.
|
|
158
166
|
- `type="icon"` shows the check mark on every step regardless of status; `type="dot"` shrinks the indicator to 8px and fills it for `done` and `progress`.
|
|
167
|
+
- Two axes spell one value "dot" and they are independent: `type="dot"` is the INDICATOR (an 8px disc replacing the number), `connector="dot"` is the LINE between steps. `connector="dot"` draws a 1px rule dashed 2px on / 2px off, matching Figma `.Line Types` `Type=Dot` (`strokeWeight=1`, `strokeDashes=[2,2]`). It is a repeating gradient rather than a dashed border because no engine lets you state a 2/2 dash period. The dash takes the connector's own state colour, so it goes `border-strong` after a `done` / `progress` step like the solid line does.
|
|
159
168
|
- Connector length doubles as the gap between steps and depends on `size` and `orientation`: large = `--rel-stepper-connector-length-lg` (86px) in both orientations; small horizontal = `--rel-stepper-connector-length-md` (60px); small vertical = `--rel-stepper-connector-length-sm` (42px). Note the `md` / `sm` suffixes refer to those defaults, not to a size value.
|
|
160
169
|
- Labels never wrap (`white-space: nowrap`); keep titles short or widen the connector hooks.
|
|
161
170
|
- `steps` is compared by reference: mutate-in-place changes are not picked up — assign a new array.
|
|
@@ -81,6 +81,7 @@
|
|
|
81
81
|
| Event | Detail | Bubbles / composed | Description |
|
|
82
82
|
| --- | --- | --- | --- |
|
|
83
83
|
| `rel-tab-change` | `index` | bubbles / composed | Fired when the user selects a tab by click or keyboard (not on programmatic `activeIndex` changes). Detail: the new active index (number). |
|
|
84
|
+
| `rel-change` | `index` | bubbles / composed | Prefixed alias of `rel-tab-change`, added in 1.4.9 — fired on the same action with the same detail. Both events fire; listen to one. Prefer this in new code, since every interactive component in the library emits `rel-change`. `rel-tab-change` is kept for as long as 1.x. |
|
|
84
85
|
|
|
85
86
|
### Slots
|
|
86
87
|
|
|
@@ -153,7 +153,7 @@
|
|
|
153
153
|
| `suffix` | The suffix block (suffix text, trailing icon and the `suffix` slot) |
|
|
154
154
|
| `helper` | The helper / error text line below the control |
|
|
155
155
|
| `helper-icon` | The leading `rel-icon` in the helper line; rendered only when `helper-icon` is set and there is helper text |
|
|
156
|
-
| `control` | The bordered control box
|
|
156
|
+
| `control` | The bordered control box. **Changed in 1.4.9:** in `variant="legend"` this is now the same `div` as every other variant, not the `fieldset` — the fieldset was demoted to a decorative outline layer behind the content so the control sits on the 32/40/48 size ladder and centres its value like the other variants. `::part(control)` still matches; only rules that depended on it being a fieldset do not. |
|
|
157
157
|
|
|
158
158
|
## Tokens / CSS Variables
|
|
159
159
|
|
|
@@ -164,7 +164,7 @@ Set these on the host (or an ancestor) to customise the element without touching
|
|
|
164
164
|
| Custom property | Default | Description |
|
|
165
165
|
| --- | --- | --- |
|
|
166
166
|
| `--rel-field-label-reserve` | `calc(var(--rel-body-md-regular-line-height) + var(--rel-spacing-2xs))` | Height held for the label row whenever no label renders above the control — `labelStyle="none"`, and `labelStyle="floating"` where the label sits inside the control — so the control still lines up with labelled siblings in a form row. Defaults to that size's label line-height plus the wrapper gap (24px at `small`, 26px at `medium` and `large`). Set to `0` to collapse the row entirely (toolbars, table cells). |
|
|
167
|
-
| `--rel-textfield-surface` | `var(--rel-bg-neutral)` | Colour the floating label's
|
|
167
|
+
| `--rel-textfield-surface` | `var(--rel-bg-neutral)` | Colour of the strip the floating label lays over the control's top border and focus ring so the label appears to sit in a notch. It overshoots the band by 2px on the page side, so it must match the surface the field sits on; defaults to the control's own fill (`--rel-bg-neutral`). Set it on a tinted or elevated surface, otherwise the strip shows as a light dash across the border. |
|
|
168
168
|
|
|
169
169
|
<!-- readme-gen:end -->
|
|
170
170
|
|
|
@@ -127,6 +127,12 @@ Typing into the field and pressing `Enter` (or leaving the field) commits the te
|
|
|
127
127
|
|
|
128
128
|
<!-- readme-gen:end -->
|
|
129
129
|
|
|
130
|
+
|
|
131
|
+
## CSS Custom Properties
|
|
132
|
+
|
|
133
|
+
| Custom property | Default | Description |
|
|
134
|
+
| --- | --- | --- |
|
|
135
|
+
| `--rel-overlay-fallback-z` | `1001` | z-index of the panel on the **no-Popover-API fallback path only**. Wherever the browser top layer is available the panel is promoted into it and this value is inert, because top-layer elements are not in the page stacking order at all. Shared with the other overlays (select, autocomplete, the three pickers) so the whole family can be moved from one place. |
|
|
130
136
|
## Accessibility
|
|
131
137
|
|
|
132
138
|
- The editable field is an `<input type="text" role="combobox">` with `aria-haspopup="dialog"`, `aria-expanded`, `aria-controls` pointing at the panel, `aria-required` (from `required`), `aria-invalid` (from `error` / `invalid` / a typed-entry error) and `aria-describedby` pointing at the helper / error text. Its accessible name is the `label` (via `aria-labelledby`), falling back to `aria-label="Select time"`.
|