@reliability-design/web 1.4.7 → 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 +98 -2
- package/dist/components/rel-accordion/README.md +2 -1
- package/dist/components/rel-alert/README.md +1 -1
- package/dist/components/rel-autocomplete/README.md +22 -5
- package/dist/components/rel-avatar/README.md +9 -7
- package/dist/components/rel-badge/README.md +4 -5
- package/dist/components/rel-button/README.md +32 -2
- package/dist/components/rel-card/README.md +3 -3
- package/dist/components/rel-carousel/README.md +3 -0
- package/dist/components/rel-checkbox/README.md +8 -4
- package/dist/components/rel-chip/README.md +13 -7
- package/dist/components/rel-container/README.md +8 -8
- package/dist/components/rel-date-picker/README.md +8 -2
- package/dist/components/rel-divider/README.md +11 -1
- package/dist/components/rel-kbd/README.md +2 -2
- package/dist/components/rel-layout/README.md +34 -0
- package/dist/components/rel-menu/README.md +3 -1
- package/dist/components/rel-menubar/README.md +1 -1
- package/dist/components/rel-otp/README.md +4 -3
- package/dist/components/rel-pagination/README.md +2 -2
- package/dist/components/rel-popover/README.md +5 -5
- package/dist/components/rel-progress/README.md +3 -3
- package/dist/components/rel-radio/README.md +1 -1
- package/dist/components/rel-range-picker/README.md +11 -2
- package/dist/components/rel-segmented-button/README.md +7 -5
- package/dist/components/rel-select/README.md +11 -6
- package/dist/components/rel-sheet/README.md +3 -3
- package/dist/components/rel-skeleton/README.md +5 -4
- package/dist/components/rel-slider/README.md +3 -2
- package/dist/components/rel-snackbar/README.md +13 -7
- package/dist/components/rel-stepper/README.md +23 -10
- package/dist/components/rel-switch/README.md +16 -7
- package/dist/components/rel-tab/README.md +5 -2
- package/dist/components/rel-textfield/README.md +19 -2
- package/dist/components/rel-time-picker/README.md +6 -0
- package/dist/components/rel-toast/README.md +14 -6
- package/dist/components/rel-tooltip/README.md +13 -2
- package/dist/components/rel-upload/README.md +2 -2
- package/dist/index.d.mts +825 -240
- package/dist/index.d.ts +825 -240
- package/dist/index.js +8617 -5213
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +8424 -5020
- package/dist/index.mjs.map +1 -1
- package/dist/styles/focus-ring.ts +84 -0
- package/dist/styles/vars.css +75 -60
- package/dist/{vars-JHTUGNRG.css → vars-DXX6QOAG.css} +75 -60
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,11 +1,107 @@
|
|
|
1
1
|
# Changelog — @reliability-design/web
|
|
2
2
|
|
|
3
|
-
## 1.4.
|
|
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
|
+
|
|
64
|
+
## 1.4.8 (2026-09-21)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
Consumer-ticket release. Twelve tickets from the testing and implementation team, built development-side and documented from source; the design file is being updated to follow, not the other way round. **Nothing is deprecated in this release** — every new spelling is an *alternative* that normalises, with no console notice. Four changes are visible without a rename to warn you; they are listed under [Changed](#changed-148) and on the docs site at `/docs/migration#v148-visual`.
|
|
68
|
+
|
|
69
|
+
Paired releases: `@reliability-design/angular` 1.4.8, which now requires web `^1.4.8` rather than `^1.4.1`. `@reliability-design/icons` is unchanged and stays on 1.4.4.
|
|
70
|
+
|
|
71
|
+
### Added
|
|
72
|
+
|
|
73
|
+
- **`rel-switch` text labels and a size axis.** `label` plus a default slot for rich content, `label-placement` (`end` default | `start`), and `size` (`small | medium | large`, with `sm`/`md`/`lg` accepted). The root element is now the `<label>` that owns the input, so clicking the text toggles natively. **`line` and `pill` ramp differently on purpose** — 16/20/24 against 20/24/28 — because Line's height *is* the handle while Pill adds a 2px inset each side. `medium` is unchanged, so no existing switch moves. There is no breakpoint logic: the ramp ships, choosing a size per breakpoint is the application's job.
|
|
74
|
+
- **`rel-toast` positions, sizes and stacking.** `position` takes `top-left | top-center | top-right | bottom-left | bottom-center | bottom-right`, defaulting to today's `bottom-center`; top-anchored toasts now animate downward instead of upward out of their own anchor. `size` is `small | medium | large` with `medium` unchanged. Multiple toasts at one position **stack** instead of overlapping: newest against the anchor edge, `max-visible` (default 3), and overflow **queues rather than drops** — so the `duration` countdown starts when a toast becomes *visible*, not when `show` is set. `max-visible` is read from the first toast to arrive at a position.
|
|
75
|
+
- **`rel-tooltip` `tone`.** `inverse` (default, pixel-identical to 1.4.7) and `neutral`, a light-surface bubble with a 1px border — and the tail carries the border too.
|
|
76
|
+
- **Second lines in suggestion rows.** A `supportingText` key on an object item renders a smaller second line under the label, on `rel-autocomplete` (new) and `rel-select` (see Changed). On **rel-autocomplete** it is **matched by the filter and highlighted**, not merely displayed, so a record can be found by its subtitle — rename the key with `supporting-field`. On **rel-select** the second line is displayed but NOT searched: the panel's search matches the label only. The two differ deliberately, because an autocomplete's second line carries identifying data a user types to find the row while a select's is descriptive. New parts `option-label` and `option-supporting-text` — **single-select rows only**; under `multiple` the row is a nested `rel-checkbox` and the second line has no part path at all.
|
|
77
|
+
- **`rel-autocomplete` multi-select.** `multiple` with `selected-values`, a Select-all row and checkbox rows, mirroring `rel-select`. One deliberate difference: the committed chips sit **below** the field rather than inside it, because a select's trigger is a button that displays a value while an autocomplete's is an editable input the caret lives in.
|
|
78
|
+
- **`rel-range-picker` month and year navigation.** The header month label is now a period switcher with Months and Years views and an unbounded year pager, ported from `rel-date-picker`. Reaching January 2023 is 3 clicks instead of roughly 44.
|
|
79
|
+
- **Full-word size values** on `rel-avatar`, `rel-container` (`padding`), `rel-popover-content` and `rel-modal`, joining badge, chip and slider. `sm` / `md` / `lg` / `xs` / `xl` remain accepted, normalised to the full words. **Not deprecations** — no console notice.
|
|
80
|
+
- **`--rel-shadow-color`** (`rgba(11,11,11,0.10)`), the shadow colour on its own. `filter: drop-shadow()` takes a bare `<color>` and cannot consume a box-shadow-shaped token, so components needing it had no semantic token to bind.
|
|
81
|
+
|
|
82
|
+
### Fixed
|
|
83
|
+
|
|
84
|
+
- **`rel-radio` sizes rendered identically.** `sm` / `md` / `lg` matched no rule and fell through to the base scale — which *is* medium — so three differently-spelled groups were byte-identical. They now normalise. Separately, `small` gets its own 16px control (see Changed).
|
|
85
|
+
- **Fields did not line up in a form row.** `label-style="floating"` put its control 26px above a labelled sibling, because it reserved no label row. All three label styles now share one baseline. Also fixed: the floating host was consistently 4px *shorter* than the control inside it, and `label-style="none"` ran 2px tall at `small` because its reserve was hard-bound to the medium label role. Affects `rel-textfield` and `rel-select`; `rel-autocomplete` inherits through composition.
|
|
86
|
+
- **`rel-otp` drew a hyphen before a lone cell.** With the default grouping of 3, a 4-character code rendered `[][][]-[]`. Separators now render only when the length divides evenly by the group size, which also fixes lengths 5, 7 and 8.
|
|
87
|
+
- **The calendars' Months and Years grids could lose every tab stop.** Cells carried `tabindex="0"` only when selected, and the year pager moves the page without moving the selected year — so paging back once left all twelve cells at `-1` and the grid unreachable by keyboard. Both `rel-date-picker` and `rel-range-picker`; the day grids were already correct. Arrow, Home/End and Escape navigation added to the range picker's period grids to match the date picker's, and the tab stop now follows focus in both.
|
|
88
|
+
- **`rel-slider`** documented a Medium size that the docs never showed. The component was always correct.
|
|
89
|
+
|
|
90
|
+
### Changed
|
|
91
|
+
|
|
92
|
+
<a id="changed-148"></a>Four changes are visible with no rename to warn you:
|
|
93
|
+
|
|
94
|
+
- **`rel-radio` `small` shrinks.** Its control was 20px, identical to `medium`; it is now 16px, so the ramp reads 16/20/24. This is a deliberate divergence from the design file, which pins Small and Medium to the same control — a size axis where two of three sizes look identical is a defective API. 16px is `rel-checkbox`'s Small, its sibling selection control.
|
|
95
|
+
- **`rel-select` option `supportingText` now renders in single-select rows.** It previously rendered only in multi-select rows, so a single-select built from options that already carried the key gains a second line it did not have. Rows are then two lines, which makes 32/40/48px the *one-line* height rather than the row height.
|
|
96
|
+
- **Floating-label fields are taller**, because they now reserve the label row so they line up with labelled siblings. `--rel-field-label-reserve: 0` collapses it for toolbars and table cells.
|
|
97
|
+
- **Toasts stack.** Five toasts at one position used to paint on top of each other; now three show and two wait. An app-level toast queue built to work around the old behaviour will still work, but the queueing it exists to provide is now built in.
|
|
98
|
+
|
|
99
|
+
## 1.4.7 (2026-09-12)
|
|
4
100
|
|
|
5
101
|
|
|
6
102
|
Designer-audit release. Every rename is **additive** — the new prop or value is canonical, the old one keeps working and is mapped internally, and its first use logs **one** `console.warn` per component per page (never one per instance). Two breaking removals: `rel-topbar` and `rel-navbar` are gone. The full old → new table is under [Migrating](#migrating-to-147) below and on the docs site at `/docs/migration`.
|
|
7
103
|
|
|
8
|
-
Paired releases: `@reliability-design/angular` 1.4.7
|
|
104
|
+
Paired releases: `@reliability-design/angular` 1.4.7. `@reliability-design/icons` is unchanged in this release and stays on 1.4.4 — icons keep their own version counter.
|
|
9
105
|
|
|
10
106
|
### Added
|
|
11
107
|
|
|
@@ -76,8 +76,9 @@ accordion.open = true;
|
|
|
76
76
|
| `title` | `title` | `string` | `"Accordion"` | Header text. Note it is also the native HTML `title` attribute, so browsers show it as a tooltip on hover. |
|
|
77
77
|
| `open` | `open` | `boolean` | `false` | Whether the body is expanded; toggled by the header and reported via `open-change`. Setting it from code does not fire the event. Reflected. |
|
|
78
78
|
| `size` | `size` | `"md" \| "sm" \| "large" \| "medium" \| "small"` | `"md"` | Header scale (Figma "Size"): `large` = 56px header / heading-md title, `medium` = 48px / heading-sm, `small` = 44px / heading-xs (new in 1.4.7). The historical spellings stay supported as aliases: `md` = large (the default), `sm` = medium. Reflected. |
|
|
79
|
-
| `type` | `type` | `"default" \| "action"` | `"default"` | Header layout (Figma "Type"): `default` = title + trailing rotating chevron; `action` = leading icon slot (rotating chevron by default) + static trailing arrow-expansion icon, with the body indented under the title. Reflected. |
|
|
79
|
+
| `type` | `type` | `"default" \| "action"` | `"default"` | Header layout (Figma "Type"): `default` = title + trailing rotating chevron; `action` = leading icon slot (rotating chevron by default) + static trailing arrow-expansion icon, with the body indented under the title. DELIBERATE DIVERGENCE — do not "fix" this to a button. For `Type=Action` Figma draws the trailing affordance as a full ghost icon Button instance (Size=Medium 40x40, Size=Small 32x32, Style=Ghost, Type=Icon Only); the code keeps a bare icon. Ruled by the user: our behaviour stands and the DESIGN FILE follows our docs here, not the other way round. The whole header is the click target, so a nested button would add a second focusable control inside it and two competing hit areas. Reflected. |
|
|
80
80
|
| `appearance` | `appearance` | `"shadow" \| "plain"` | `"shadow"` | Figma "Style" axis — named appearance because `style` collides with the HTML style attribute. `shadow` = 4px-radius card with the shadow-sm effect and no border; `plain` = square corners with a 1px border-subtle stroke and no elevation. Reflected. |
|
|
81
|
+
| `disabled` | `disabled` | `boolean` | `false` | Figma "State=Disabled": dims the whole component to 40% and blocks toggling by pointer and keyboard. The header keeps its fill and stroke — the dulling is opacity alone, as Figma specifies. Reflected. |
|
|
81
82
|
|
|
82
83
|
## Events
|
|
83
84
|
|
|
@@ -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`.
|
|
@@ -67,22 +67,26 @@
|
|
|
67
67
|
|
|
68
68
|
| Property | Attribute | Type | Default | Description |
|
|
69
69
|
| --- | --- | --- | --- | --- |
|
|
70
|
-
| `items` | `items` | `(string \| Record<string, unknown>)[]` | `[]` | Local (or fallback initial) suggestion list. Ignored while searchFn drives results. Object items read
|
|
70
|
+
| `items` | `items` | `(string \| Record<string, unknown>)[]` | `[]` | Local (or fallback initial) suggestion list. Ignored while searchFn drives results. Object items are read through `displayField` / `valueField` plus the fixed keys `icon`, `disabled` and `supportingText` (the last renamable via `supporting-field`); `supportingText` renders as a second line in the row and is matched by the filter alongside the label. |
|
|
71
71
|
| `value` | `value` | `string` | `""` | Committed value — the selected item's valueField string ("" = none). |
|
|
72
72
|
| `displayField` | `display-field` | `string` | `"label"` | Object key read for the row label when items are objects. |
|
|
73
73
|
| `valueField` | `value-field` | `string` | `"value"` | Object key read for the committed value when items are objects. |
|
|
74
|
+
| `supportingField` | `supporting-field` | `string` | `"supportingText"` | Object key read for the row's optional second line. Defaults to `supportingText`, the fixed key `rel-select` and `rel-radio` already use; point it elsewhere when your records cannot be reshaped. |
|
|
74
75
|
| `filter` | `filter` | `"contains" \| "starts-with" \| "none"` | `"contains"` | Local filtering mode. Ignored when searchFn is set (results arrive pre-filtered). |
|
|
75
76
|
| `searchFn` | — | `(query: string) => Promise<AutocompleteItem[]>` | `undefined` | Async data source. When set, local filtering is bypassed entirely. Property only. |
|
|
76
77
|
| `debounceMs` | `debounce-ms` | `number` | `300` | Debounce for searchFn calls, in ms. |
|
|
77
78
|
| `minSearchLength` | `min-search-length` | `number` | `1` | Minimum typed characters before the panel opens / searchFn fires. 0 = open on focus. |
|
|
78
79
|
| `maxSuggestions` | `max-suggestions` | `number` | `Infinity` | Optional cap on rendered suggestions (panel scrolls regardless). |
|
|
80
|
+
| `multiple` | `multiple` | `boolean` | `false` | Multi-select mode: rows become checkboxes with a Select-all row, and the committed values live in `selectedValues` rather than `value`. Mirrors `rel-select`'s `multiple` rather than introducing a second pattern. THE TEXT INPUT STAYS A SEARCH BOX. In single mode the input shows the selected label, which is the point of a type-ahead; in multi mode there is no single label to show, and writing a value list into the field would destroy the query the user is typing. `rel-select` can put chips inside its trigger because a trigger is not an editable surface — an input is. So the chips render beneath the field, the query clears after each toggle so the next search starts fresh, and the panel stays open. Reflected. |
|
|
81
|
+
| `selectedValues` | `selected-values` | `string[]` | `[]` | Committed values in `multiple` mode (ignored otherwise). Mirrors `rel-select.selectedValues`. |
|
|
82
|
+
| `selectAllLabel` | `select-all-label` | `string` | `"Select all"` | Label of the Select-all row shown at the top of the panel in `multiple` mode. |
|
|
79
83
|
| `clearable` | `clearable` | `boolean` | `false` | Shows a trailing ✕ that clears the text and the committed value. |
|
|
80
84
|
| `loading` | `loading` | `boolean` | `false` | Externally-controlled loading state (internal searchFn loading is automatic). |
|
|
81
85
|
| `noResultsText` | `no-results-text` | `string` | `"No results found"` | Row shown when a query yields nothing. |
|
|
82
86
|
| `loadingText` | `loading-text` | `string` | `"Loading…"` | Text shown next to the spinner while suggestions load. |
|
|
83
87
|
| `label` | `label` | `string` | `undefined` | Visible label, forwarded to the inner `rel-textfield`. Also names the suggestion panel. |
|
|
84
88
|
| `placeholder` | `placeholder` | `string` | `""` | Placeholder of the input, forwarded to the inner `rel-textfield`. |
|
|
85
|
-
| `size` | `size` | `"small" \| "medium" \| "large"` | `"medium"` | Field height — `small` 32px, `medium` 40px, `large` 48px — forwarded to the inner `rel-textfield`. |
|
|
89
|
+
| `size` | `size` | `"small" \| "medium" \| "large"` | `"medium"` | Field height — `small` 32px, `medium` 40px, `large` 48px — forwarded to the inner `rel-textfield`. Also drives the suggestion panel's row ramp. Reflected. |
|
|
86
90
|
| `variant` | `variant` | `"default" \| "underline" \| "legend"` | `"default"` | Field chrome (`default` outlined, `underline`, `legend` notched outline), forwarded to the inner `rel-textfield`. |
|
|
87
91
|
| `labelStyle` | `label-style` | `"default" \| "floating" \| "none"` | `undefined` | Label presentation (`default` / `floating` / `none`), forwarded to the inner field. Left `undefined`, the deprecated `floatingLabel` decides. Reflected. |
|
|
88
92
|
| `fieldAriaLabel` | `field-aria-label` | `string` | `undefined` | Accessible name for the input. Required when `labelStyle="none"`. |
|
|
@@ -113,7 +117,7 @@
|
|
|
113
117
|
|
|
114
118
|
| Event | Detail | Bubbles / composed | Description |
|
|
115
119
|
| --- | --- | --- | --- |
|
|
116
|
-
| `changed` | `{ value: this.value, item: item ? item.item : null }` | bubbles / composed | Fired when the committed value changes — an item was selected (click, Enter, or an exact text match on Enter / Tab / blur) or the field was cleared. Detail: `{ value, item }`, where `item` is the original string or object, or `null` after a clear. |
|
|
120
|
+
| `changed` | `{ value: this.selectedValues, item: null } / { value: this.value, item: item ? item.item : null }` | bubbles / composed | Fired when the committed value changes — an item was selected (click, Enter, or an exact text match on Enter / Tab / blur) or the field was cleared. Detail: `{ value, item }`, where `item` is the original string or object, or `null` after a clear. |
|
|
117
121
|
| `rel-change` | `inputText / ""` | bubbles / composed | Fired on every keystroke with the raw input text as the detail (and with `""` after a clear). The committed `value` does not change until an item is selected. |
|
|
118
122
|
| `rel-clear` | — | bubbles / composed | Fired when the clear icon empties a non-empty field (followed by `changed` and `rel-change`). No detail. |
|
|
119
123
|
| `rel-search-error` | `{ query, error: err }` | bubbles / composed | Fired when `searchFn` rejects; the panel then shows the no-results row. Detail: `{ query, error }`. |
|
|
@@ -128,9 +132,14 @@
|
|
|
128
132
|
|
|
129
133
|
| Part | Description |
|
|
130
134
|
| --- | --- |
|
|
135
|
+
| `chips` | — |
|
|
136
|
+
| `chip` | — |
|
|
131
137
|
| `panel` | The suggestion listbox container (same node as `list`) |
|
|
132
138
|
| `list` | Alias of `panel` |
|
|
133
|
-
| `
|
|
139
|
+
| `select-all` | — |
|
|
140
|
+
| `option` | A suggestion row. Emitted in BOTH single and `multiple` mode, unlike the two parts below |
|
|
141
|
+
| `option-label` | The row's first line (the matched label). SINGLE-SELECT ROWS ONLY |
|
|
142
|
+
| `option-supporting-text` | The row's optional second line, rendered only when the item supplies one. SINGLE-SELECT ROWS ONLY — under `multiple` the row is a nested `rel-checkbox` and the second line is that component's own `helper` part, inside its shadow root with nothing forwarding it out, so it cannot be reached with `::part()` at all. Style both modes with the `--rel-*` tokens instead |
|
|
134
143
|
| `label` | The inner field's label (re-exported from `rel-textfield`) |
|
|
135
144
|
| `control` | The inner field's bordered control box (re-exported from `rel-textfield`) |
|
|
136
145
|
| `input` | The inner native input (re-exported from `rel-textfield`) |
|
|
@@ -146,15 +155,18 @@ Set these on the host (or an ancestor) to customise the element without touching
|
|
|
146
155
|
| Custom property | Default | Description |
|
|
147
156
|
| --- | --- | --- |
|
|
148
157
|
| `--rel-autocomplete-bg` | `var(--rel-bg-neutral)` | Panel and row background |
|
|
149
|
-
| `--rel-autocomplete-divider` | `
|
|
158
|
+
| `--rel-autocomplete-divider` | `transparent` | Hairline between rows |
|
|
150
159
|
| `--rel-autocomplete-icon-color` | `var(--rel-text-default)` | Colour of a row's leading `icon` |
|
|
151
160
|
| `--rel-autocomplete-menu-border` | `var(--rel-border-default)` | Panel border colour. The panel is borderless by design and only draws this on dark surfaces, where its shadow alone can't separate it from the page. |
|
|
161
|
+
| `--rel-autocomplete-menu-max-height` | `360px` | Scroll ceiling of the suggestion panel; default `360px`. Code-owned rather than Figma-derived — every Figma Menu variant hugs its content with no ceiling, which is not shippable for an unbounded result list. |
|
|
152
162
|
| `--rel-autocomplete-menu-radius` | `var(--rel-border-radius-xs)` | Panel corner radius |
|
|
153
163
|
| `--rel-autocomplete-menu-shadow` | `var(--rel-shadows-md)` | Panel box-shadow |
|
|
154
164
|
| `--rel-autocomplete-option-hover` | `var(--rel-action-overlay-hover)` | Background of the hovered / keyboard-highlighted / selected row |
|
|
155
165
|
| `--rel-autocomplete-option-pressed` | `var(--rel-action-overlay-pressed)` | Background of a row while it is being pressed |
|
|
156
166
|
| `--rel-autocomplete-status-color` | `var(--rel-text-body)` | Colour of the loading / no-results status rows |
|
|
167
|
+
| `--rel-autocomplete-supporting-color` | `var(--rel-text-caption)` | — |
|
|
157
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. |
|
|
158
170
|
|
|
159
171
|
<!-- readme-gen:end -->
|
|
160
172
|
|
|
@@ -180,6 +192,9 @@ Focus: `focus()` on the host delegates to the inner field. Focusing the field wi
|
|
|
180
192
|
|
|
181
193
|
## Notes / Edge Cases
|
|
182
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.
|
|
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.
|
|
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.
|
|
183
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`.
|
|
184
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.
|
|
185
200
|
- Clearing emits, in order, `rel-clear`, `changed` (`value: ""`, `item: null`) and `rel-change` (`""`), and only when the field actually had text or a value.
|
|
@@ -190,3 +205,5 @@ Focus: `focus()` on the host delegates to the inner field. Focusing the field wi
|
|
|
190
205
|
- `full-width` layout is keyed off the **attribute** (`:host(:not([full-width]))` renders inline-block) even though the property defaults to `true` and is not reflected — add the `full-width` attribute in markup when you need the block layout. This differs from `rel-textfield`, whose `fullwidth` attribute is reflected.
|
|
191
206
|
- The suggestion panel mirrors `rel-select`'s listbox (same tokens, radius, shadow, row styling) and is promoted to the browser top layer (native Popover API, `popover="manual"`), so it renders correctly inside transformed or `overflow: hidden` containers such as a centered `rel-modal` or a `rel-drawer`; engines without the Popover API fall back to `position: fixed` with a measure-and-cancel correction. It matches the field width, flips above when short on space, and closes on outside pointerdown or outside scroll.
|
|
192
207
|
- Only `label`, `control`, `input`, `helper` and `right-icon` are re-exported from the inner `rel-textfield`; its `prefix` / `suffix` parts and slots are not exposed.
|
|
208
|
+
- The field passthroughs are a fixed subset, forwarded as **property** bindings (`.prefixIcon=${…}`), so they are unaffected by attribute-spelling differences between the two components. `rel-textfield` props outside that subset — `prefixText`, `prefixStyle`, `suffixText`, `suffixDivider`, `helperIcon`, `maxLength`, `multiline`, `rows`, `type` — are not forwarded and cannot be set through `rel-autocomplete`.
|
|
209
|
+
- Several of the passthroughs take a **kebab attribute here and an all-lowercase one on `rel-textfield`**: `prefix-icon` / `prefixicon`, `helper-text` / `helpertext`, `error-message` / `errormessage`, `floating-label` / `floatinglabel`, `full-width` / `fullwidth`. Moving markup between the two elements therefore needs the attribute renamed, and a wrong spelling fails silently rather than erroring. `rel-textfield`'s README carries the full table; 3.0 unifies on the kebab spellings used here.
|
|
@@ -19,10 +19,10 @@
|
|
|
19
19
|
</rel-avatar>
|
|
20
20
|
|
|
21
21
|
<!-- Sizes: xs 20px, sm 24px, md 32px (default), lg 40px -->
|
|
22
|
-
<rel-avatar type="text" text="XS" size="
|
|
23
|
-
<rel-avatar type="text" text="SM" size="
|
|
24
|
-
<rel-avatar type="text" text="MD" size="
|
|
25
|
-
<rel-avatar type="text" text="LG" size="
|
|
22
|
+
<rel-avatar type="text" text="XS" size="extra-small"></rel-avatar>
|
|
23
|
+
<rel-avatar type="text" text="SM" size="small"></rel-avatar>
|
|
24
|
+
<rel-avatar type="text" text="MD" size="medium"></rel-avatar>
|
|
25
|
+
<rel-avatar type="text" text="LG" size="large"></rel-avatar>
|
|
26
26
|
|
|
27
27
|
<!-- Variants -->
|
|
28
28
|
<rel-avatar type="text" text="AA" variant="fill"></rel-avatar>
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
|
|
51
51
|
| Property | Attribute | Type | Default | Description |
|
|
52
52
|
| --- | --- | --- | --- | --- |
|
|
53
|
-
| `size` | `size` | `"lg" \| "md" \| "sm" \| "xs"` | `"
|
|
53
|
+
| `size` | `size` | `"large" \| "medium" \| "small" \| "extra-small" \| "lg" \| "md" \| "sm" \| "xs"` | `"medium"` | Diameter step: `large` 40px, `medium` 32px, `small` 24px, `extra-small` 20px; each binds the matching body-*-semibold text role for initials. `lg` / `md` / `sm` / `xs` are alternative spellings and stay supported — they are normalised to the full words, not deprecated. Reflected. |
|
|
54
54
|
| `type` | `type` | `"image" \| "text" \| "icon"` | `"image"` | What is rendered inside the circle: `image` shows `src`, `text` shows the `text` initials, `icon` shows the default slot. Reflected. |
|
|
55
55
|
| `variant` | `variant` | `"fill" \| "subtle" \| "outline"` | `"fill"` | Fill style: `fill` = solid inverse background with light text, `subtle` = elevated neutral background, `outline` = transparent with a 1px border. Reflected. |
|
|
56
56
|
| `src` | `src` | `string` | `""` | Image URL for `type="image"`; when empty nothing is rendered inside the circle (there is no fallback to initials). |
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
|
|
70
70
|
| Slot | Description |
|
|
71
71
|
| --- | --- |
|
|
72
|
-
| (default) | Icon content rendered when `type="icon"` (typically an inline SVG, sized
|
|
72
|
+
| (default) | Icon content rendered when `type="icon"` (typically an inline SVG, sized 20 / 18 / 16 / 12px by `size`) |
|
|
73
73
|
|
|
74
74
|
## CSS Parts
|
|
75
75
|
|
|
@@ -88,12 +88,14 @@ Set these on the host (or an ancestor) to customise the element without touching
|
|
|
88
88
|
| Custom property | Default | Description |
|
|
89
89
|
| --- | --- | --- |
|
|
90
90
|
| `--rel-avatar-bg-fill` | `var(--rel-bg-neutral-inverse-2)` | Background colour of the `fill` variant |
|
|
91
|
+
| `--rel-avatar-bg-outline` | `var(--rel-bg-neutral)` | Background colour of the `outline` variant; defaults to --rel-bg-neutral |
|
|
91
92
|
| `--rel-avatar-bg-subtle` | `var(--rel-bg-neutral-elevated-1)` | Background colour of the `subtle` variant |
|
|
92
93
|
| `--rel-avatar-border-outline` | `var(--rel-border-default)` | 1px border colour of the `outline` variant |
|
|
93
94
|
| `--rel-avatar-color-fill` | `var(--rel-text-inverse)` | Text / icon colour of the `fill` variant |
|
|
94
95
|
| `--rel-avatar-color-outline` | `var(--rel-text-default)` | Text / icon colour of the `outline` variant |
|
|
95
96
|
| `--rel-avatar-color-subtle` | `var(--rel-text-default)` | Text / icon colour of the `subtle` variant |
|
|
96
97
|
| `--rel-avatar-font-size` | `var(--_rel-avatar-font-size)` | Overrides the initials' font-size, for use alongside `--rel-avatar-size`; defaults to the preset's text role |
|
|
98
|
+
| `--rel-avatar-icon-size` | `var(--_rel-avatar-icon-size)` | Overrides the slotted icon's size; defaults to the preset ramp (20 / 18 / 16 / 12px) |
|
|
97
99
|
| `--rel-avatar-size` | `var(--_rel-avatar-size)` | Overrides the circle's width/height for sizes between (or below) the `size` presets; defaults to the preset's diameter |
|
|
98
100
|
|
|
99
101
|
<!-- readme-gen:end -->
|
|
@@ -112,5 +114,5 @@ Set these on the host (or an ancestor) to customise the element without touching
|
|
|
112
114
|
- `type="image"` with an empty `src` renders an empty circle — there is no automatic fallback to initials. Switch `type` yourself when the photo is missing.
|
|
113
115
|
- Every variant keeps a 1px border (`--rel-stroke-sm`); `fill` and `subtle` make it transparent so the outer size is identical across variants.
|
|
114
116
|
- `bg` and `color` are written as inline styles on the `base` part, so they win over both the `--rel-avatar-*` hooks and the variant tokens.
|
|
115
|
-
- Initials are forced uppercase and `user-select: none`. `size`, `type` and `variant` are reflected, so `rel-avatar[size="
|
|
117
|
+
- Initials are forced uppercase and `user-select: none`. `size`, `type` and `variant` are reflected, so `rel-avatar[size="large"]` works as a selector. Because `lg` / `md` / `sm` / `xs` are normalised to the full words before reflection, the selector matches whichever spelling was written.
|
|
116
118
|
- The `icon` part name is shared by the `<img>` (`type="image"`) and the slot wrapper (`type="icon"`); `::part(icon)` matches whichever one is rendered.
|
|
@@ -12,9 +12,7 @@
|
|
|
12
12
|
<rel-badge number="120" size="xl"></rel-badge>
|
|
13
13
|
|
|
14
14
|
<!-- Dot badges (no text) -->
|
|
15
|
-
<rel-badge variant="dot"
|
|
16
|
-
<rel-badge variant="dot" size="md"></rel-badge>
|
|
17
|
-
<rel-badge variant="dot" size="lg"></rel-badge>
|
|
15
|
+
<rel-badge variant="dot"></rel-badge>
|
|
18
16
|
|
|
19
17
|
<!-- Positioned over an icon button -->
|
|
20
18
|
<div style="position: relative; display: inline-block;">
|
|
@@ -45,8 +43,8 @@
|
|
|
45
43
|
| `number` | `number` | `string` | `""` | Text shown in the `number` variant (a count such as "5" or "99+"); not rendered by `variant="dot"` but still used to build the accessible name. |
|
|
46
44
|
| `ariaLabel` | `arialabel` | `string` | `""` | Explicit accessible name; when empty it is composed as "<number> <suffix>" or, with no number, "New <suffix>". Attribute is `arialabel`. |
|
|
47
45
|
| `suffix` | `suffix` | `string` | `"notifications"` | Noun appended to the count in the generated accessible name, e.g. "3 notifications". |
|
|
48
|
-
| `size` | `size` | `"sm" \| "md" \| "lg" \| "xl"` | `"
|
|
49
|
-
| `variant` | `variant` | `"dot" \| "number"` | `"number"` | `number` renders the `number` text in a pill that widens with its content; `dot` renders a
|
|
46
|
+
| `size` | `size` | `"small" \| "medium" \| "large" \| "extra-large" \| "sm" \| "md" \| "lg" \| "xl"` | `"medium"` | Badge scale (Figma Badge S/M/L/XL): number badges are 20 / 24 / 28 / 40px tall. `sm` / `md` / `lg` / `xl` are accepted and normalised to the full words. Ignored when `variant="dot"` — Figma models `Dot` as a fifth option of the same Size axis, so a dot has one size. Reflected. |
|
|
47
|
+
| `variant` | `variant` | `"dot" \| "number"` | `"number"` | `number` renders the `number` text in a pill that widens with its content; `dot` renders a 6px disc with no text (Figma `Size=Dot`), ignoring `size`. Reflected. |
|
|
50
48
|
| `color` | `color` | `string` | `""` | Direct CSS color override for the badge background. Accepts any valid CSS color value: hex, rgb(...), hsl(...), named color, or var(--token). Clear it (set to `''`) to fall back to the default token. Reflected. |
|
|
51
49
|
| `textColor` | `text-color` | `string` | `""` | Direct CSS color override for the badge text/icon color. Accepts any valid CSS color value: hex, rgb(...), hsl(...), named color, or var(--token). Clear it (set to `''`) to fall back to the default (white). Reflected. |
|
|
52
50
|
|
|
@@ -78,6 +76,7 @@ Set these on the host (or an ancestor) to customise the element without touching
|
|
|
78
76
|
| --- | --- | --- |
|
|
79
77
|
| `--rel-badge-bg` | `var(--rel-bg-danger)` | Background colour of the badge; the `color` prop writes this on the host |
|
|
80
78
|
| `--rel-badge-color` | `var(--rel-text-static-light)` | Text colour of the count; the `text-color` prop writes this on the host |
|
|
79
|
+
| `--rel-badge-dot-size` | `6px` | Diameter of the `dot` variant; default 6px, the single dot size Figma draws. Set it to restore the larger discs shipped previously. |
|
|
81
80
|
|
|
82
81
|
<!-- readme-gen:end -->
|
|
83
82
|
|
|
@@ -93,7 +93,7 @@ Action button implementing the Figma "Button" component: a native `<button>` wit
|
|
|
93
93
|
| Property | Attribute | Type | Default | Description |
|
|
94
94
|
| --- | --- | --- | --- | --- |
|
|
95
95
|
| `variant` | `variant` | `"default" \| "primary" \| "secondary" \| "text" \| "link" \| "icon" \| "static" \| "outline" \| "ghost"` | `"default"` | Style axis (Figma "Style"). Canonical: `default` (legacy neutral fill), `primary`, `secondary` (outlined), `ghost` (text-only), `link` (underlined inline text), `static` (transparent, static-light label for dark/colored surfaces). Deprecated aliases still accepted: `outline` = secondary, `text` = ghost, and `icon` (pre-2.0 40px circular icon button — use `icon-only` with a style instead). |
|
|
96
|
-
| `tone` | `tone` | `"default" \| "primary" \| "secondary" \| "critical" \| "success" \| "neutral" \| "brand" \| "danger" \| "destructive"` | `
|
|
96
|
+
| `tone` | `tone` | `"default" \| "primary" \| "secondary" \| "critical" \| "success" \| "neutral" \| "brand" \| "danger" \| "destructive"` | `undefined` | Tone axis (Figma "Tone"). Canonical: `neutral` (= `default`), `brand` (= `primary`), `destructive`. Deprecated aliases `critical` and `danger` map to destructive and log a one-time console notice. `secondary` and `success` are legacy extras with no Figma counterpart (secondary only affects the legacy `icon` variant). Left UNSET it means `neutral`, except on `variant="primary"`, where it still means `brand` for one release — see `effectiveTone`. Write the tone explicitly and you always get what you wrote. |
|
|
97
97
|
| `iconOnly` | `icon-only` | `boolean` | `false` | Figma "Type=Icon Only": renders a square button (32/40/48px per `size`) of the current style and tone with no padding or min-width. Composes with every `variant`; slot a single `rel-icon` and set `aria-label`. Reflected. |
|
|
98
98
|
| `loading` | `loading` | `boolean` | `false` | Figma "State=Loading": replaces the label with a spinner (`indicator` part), sets `aria-busy`, and blocks pointer and programmatic clicks. The button keeps its width. Reflected. |
|
|
99
99
|
| `size` | `size` | `"small" \| "medium" \| "large"` | `"medium"` | Figma "Size": `small` = 32px, `medium` = 40px, `large` = 48px height, with matching padding, typography and minimum width. |
|
|
@@ -150,8 +150,38 @@ Focus: `:focus-visible` shows the shared 2px focus outline plus inner/outer halo
|
|
|
150
150
|
|
|
151
151
|
## Notes / Edge Cases
|
|
152
152
|
|
|
153
|
+
### The `variant="primary"` tone transition
|
|
154
|
+
|
|
155
|
+
Figma's `Style=Primary` is the **filled** style, and `Tone` picks its colour — so Primary + Neutral is the neutral fill and Primary + Brand is the brand fill. This component's `variant="primary"` historically meant *both* "filled" and "brand-coloured", which left the Figma-canonical Primary/Neutral cell unreachable by its own names.
|
|
156
|
+
|
|
157
|
+
That cell now works: **`variant="primary" tone="neutral"` renders the neutral fill.**
|
|
158
|
+
|
|
159
|
+
For one release there is a deliberate wrinkle, and it is worth knowing before you debug it:
|
|
160
|
+
|
|
161
|
+
| Markup | Renders | Why |
|
|
162
|
+
| --- | --- | --- |
|
|
163
|
+
| `<rel-button>` | neutral fill | Unchanged. `variant` defaults to `default`, which is Figma's Primary/Neutral. |
|
|
164
|
+
| `<rel-button variant="primary">` | **brand** fill | Unset tone still means brand *here only*, so existing brand buttons do not silently turn neutral. Logs a one-time console notice. |
|
|
165
|
+
| `<rel-button variant="primary" tone="brand">` | brand fill | The explicit form. **Write this.** |
|
|
166
|
+
| `<rel-button variant="primary" tone="neutral">` | neutral fill | The Figma-canonical cell, now reachable. |
|
|
167
|
+
|
|
168
|
+
So for this release, writing the default value explicitly changes the result: `variant="primary"` alone and `variant="primary" tone="neutral"` render differently. Setting `tone` explicitly always gives you exactly what you wrote. A future major will make an unset tone mean `neutral` everywhere, matching every other style.
|
|
169
|
+
|
|
170
|
+
### Tone × Style matrix
|
|
171
|
+
|
|
172
|
+
Figma populates only part of the grid. Combinations outside it have no design-system definition and fall back to the style's neutral treatment:
|
|
173
|
+
|
|
174
|
+
| Tone | Primary | Secondary | Ghost | Link | Static |
|
|
175
|
+
| --- | --- | --- | --- | --- | --- |
|
|
176
|
+
| Neutral | yes | yes | yes | yes | yes |
|
|
177
|
+
| Brand | yes | — | — | — | — |
|
|
178
|
+
| Destructive | yes | yes | yes | yes | — |
|
|
179
|
+
|
|
180
|
+
`Link` additionally has no icon-only and no loading state in the design.
|
|
181
|
+
|
|
153
182
|
- **Events.** The element dispatches no custom events. The native `click` is `composed`, so a listener on `<rel-button>` (or an ancestor) receives exactly one event per activation — do not re-dispatch it.
|
|
154
|
-
- **Minimum widths** come from the Figma master: small/medium/large = 80/100/120px,
|
|
183
|
+
- **Minimum widths** come from the Figma master: small/medium/large = 80/100/120px, static = 40/50/60px. `link` has no minimum, `icon-only` is a fixed square (32/40/48px) and the legacy `icon` variant is always a 40px circle regardless of `size`. Override per instance with `--rel-button-min-width`.
|
|
184
|
+
- **`border`** exposes `small-corner-radius` and `no-corner-radius`, which have no design-system backing — every one of Figma's 624 button nodes is fully rounded. They are a code-only extension, kept for existing layouts.
|
|
155
185
|
- **Legacy `icon` variant vs `icon-only`.** `variant="icon"` keeps its pre-2.0 look (40px circle, tone-driven fill, `tone="secondary"` gives it an outline) and ignores `size` and `border`. `icon-only` is an axis that composes with any style and size — prefer it.
|
|
156
186
|
- **`tone` on `secondary` / `ghost` / `link`** changes the label, border or text colour only; the fill stays on the surface. The destructive outline border is deliberately subtle at rest and only goes solid on hover/pressed, matching Figma.
|
|
157
187
|
- **`static`** is a neutral-only style meant for coloured or dark backgrounds; other tones have no static treatment.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# rel-card
|
|
2
2
|
|
|
3
|
-
`<rel-card>` is
|
|
3
|
+
`<rel-card>` is an elevated, padded surface that groups related content — the Figma "Card" component. `type="custom"` (the default) pads whatever you slot in and applies the Figma card typography to slotted headings and paragraphs; `type="media"` adds a flush media region above the body, meant for a `<rel-card-media>` image header. Use it for dashboard tiles, summary blocks and list items that pair a title with body copy. It is a static container with no interaction of its own — for transient content use `rel-modal` or `rel-drawer` instead, and for a surface with its own tone/elevation variants use `rel-container`.
|
|
4
4
|
|
|
5
5
|
## Usage
|
|
6
6
|
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
|
|
56
56
|
| Part | Description |
|
|
57
57
|
| --- | --- |
|
|
58
|
-
| `base` | The outer
|
|
58
|
+
| `base` | The outer elevated card surface |
|
|
59
59
|
| `header` | The media region wrapper (present only when `type="media"`) |
|
|
60
60
|
| `content` | The padded body that wraps the default slot |
|
|
61
61
|
|
|
@@ -72,4 +72,4 @@
|
|
|
72
72
|
- `type="media"` removes the card's own padding and clips overflow so the media sits flush against the rounded border; the body (`content` part) keeps the standard padding. Content placed in the `media` slot without `type="media"` is not rendered, because the slot only exists in that mode.
|
|
73
73
|
- The `header` part exists only while `type="media"`; `::part(header)` matches nothing otherwise.
|
|
74
74
|
- Slotted `h1`–`h4` and `p` elements get the Figma card roles (heading md / body lg) with their default margins reset; wrap other content in your own elements to style it.
|
|
75
|
-
-
|
|
75
|
+
- The card is elevated with Figma's `shadow/sm` and draws no border, in both light and dark themes. There is no hover lift or transition.
|
|
@@ -142,8 +142,11 @@ 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). |
|
|
147
|
+
| `--rel-carousel-slide-bg` | `var(--rel-bg-neutral)` | — |
|
|
146
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. |
|
|
149
|
+
| `--rel-carousel-slide-radius` | `var(--rel-border-radius-sm)` | — |
|
|
147
150
|
| `--rel-carousel-slide-width` | `100%` | Written by the component on its own host to the computed panel width (viewport width for `panels="1"`, a fraction of it above that); not meant to be set by consumers (an inline value is overwritten on resize). |
|
|
148
151
|
|
|
149
152
|
<!-- readme-gen:end -->
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# rel-checkbox
|
|
2
2
|
|
|
3
|
-
A tri-state checkbox implementing the Figma "Checkbox" sheet: a hidden native `<input type="checkbox">` behind a custom box, with a label, an optional label-prefix icon and optional supporting text. It exposes the sheet's vocabulary verbatim — `size` (small / medium / large), `density` (compact / comfortable) and `alignment` (leading / trailing) — plus `state` for error and disabled styling. Use it for independent yes/no choices and for "select all" parents (via `status="indeterminate"`); for a single choice among several options use `rel-radio`, and for an immediate on/off setting use `rel-switch`.
|
|
3
|
+
A tri-state checkbox implementing the Figma "Checkbox" sheet: a hidden native `<input type="checkbox">` behind a custom box, with a label, an optional label-prefix icon and optional supporting text. It exposes the sheet's vocabulary verbatim — `size` (small / medium / large / extra-large), `density` (compact / comfortable) and `alignment` (leading / trailing) — plus `state` for error and disabled styling. Use it for independent yes/no choices and for "select all" parents (via `status="indeterminate"`); for a single choice among several options use `rel-radio`, and for an immediate on/off setting use `rel-switch`.
|
|
4
4
|
|
|
5
5
|
## Usage
|
|
6
6
|
|
|
@@ -64,12 +64,13 @@ A tri-state checkbox implementing the Figma "Checkbox" sheet: a hidden native `<
|
|
|
64
64
|
| `label` | `label` | `string` | `"Label"` | Label text rendered next to the box. |
|
|
65
65
|
| `icon` | `icon` | `string` | `""` | rel-icon name rendered between the checkbox and the label (label prefix icon); scales with `size`. Unset renders no icon and reserves no space. |
|
|
66
66
|
| `supportingtext` | `supportingtext` | `string` | `""` | Supporting text under or beside the label; where it renders depends on `density`. |
|
|
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 control to 0.
|
|
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"` | `"medium"` | Control scale (Figma Size)
|
|
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
|
|
|
@@ -121,7 +122,8 @@ A tri-state checkbox implementing the Figma "Checkbox" sheet: a hidden native `<
|
|
|
121
122
|
- The host is `display: block`, so a trailing checkbox stretches to its container width; constrain the parent to control row width.
|
|
122
123
|
- `value` is only reported in the `rel-change` detail — the element does not participate in native form submission.
|
|
123
124
|
- The hidden native input is absolutely positioned against the `.row` containing block; keep `position: relative` on `.row` (Tab-scroll page-jump fix, shared with rel-radio).
|
|
124
|
-
-
|
|
125
|
+
- Control frames are 16 / 18 / 20 / 24px for small / medium / large / extra-large, matching Figma. The frame is the layout footprint — the row gap and the compact indent measure from it — while the drawn square is inset to 80% of the frame (12.8 / 14.4 / 16 / 19.2px), as Figma draws it.
|
|
126
|
+
- The square's 2px border weight is **not** verified against Figma: the control is flattened artwork (a filled vector rather than a frame with a stroke), so the sheet gives the square's outer size but says nothing about its stroke. Carried forward from the previous implementation.
|
|
125
127
|
|
|
126
128
|
### Layout matrix (mirrors the Figma sheet)
|
|
127
129
|
|
|
@@ -133,6 +135,8 @@ A tri-state checkbox implementing the Figma "Checkbox" sheet: a hidden native `<
|
|
|
133
135
|
| S | Compact | yes | renders comfortable |
|
|
134
136
|
| L | Comfortable | yes | yes |
|
|
135
137
|
| L | Compact | yes | renders comfortable |
|
|
138
|
+
| XL | Comfortable | yes | yes |
|
|
139
|
+
| XL | Compact | yes | renders comfortable |
|
|
136
140
|
|
|
137
141
|
### Recipes (intentionally not component props)
|
|
138
142
|
|
|
@@ -5,22 +5,28 @@
|
|
|
5
5
|
## Usage
|
|
6
6
|
|
|
7
7
|
```html
|
|
8
|
-
<!-- Basic chip (tone="neutral" variant="fill" size="
|
|
8
|
+
<!-- Basic chip (tone="neutral" variant="fill" size="medium" border="default") -->
|
|
9
9
|
<rel-chip label="Active"></rel-chip>
|
|
10
10
|
|
|
11
11
|
<!-- Tones -->
|
|
12
12
|
<rel-chip label="Info" tone="info"></rel-chip>
|
|
13
13
|
<rel-chip label="Warning" tone="warning"></rel-chip>
|
|
14
|
-
<rel-chip label="Error" tone="
|
|
14
|
+
<rel-chip label="Error" tone="destructive"></rel-chip>
|
|
15
15
|
<rel-chip label="Success" tone="success"></rel-chip>
|
|
16
16
|
|
|
17
17
|
<!-- Subtle variant: tinted background, tone-coloured text -->
|
|
18
18
|
<rel-chip label="Subtle" tone="info" variant="subtle"></rel-chip>
|
|
19
19
|
|
|
20
20
|
<!-- Sizes -->
|
|
21
|
+
<rel-chip label="Small" size="small"></rel-chip>
|
|
22
|
+
<rel-chip label="Medium" size="medium"></rel-chip>
|
|
23
|
+
<rel-chip label="Large" size="large"></rel-chip>
|
|
24
|
+
|
|
25
|
+
<!-- Deprecated aliases — these still render, but prefer the canonical spellings above.
|
|
26
|
+
tone="danger" is an alias of tone="destructive" and logs a one-time console notice.
|
|
27
|
+
size="sm" / "md" / "lg" are alternative spellings (NOT deprecated, no notice). -->
|
|
28
|
+
<rel-chip label="Error" tone="danger"></rel-chip>
|
|
21
29
|
<rel-chip label="Small" size="sm"></rel-chip>
|
|
22
|
-
<rel-chip label="Medium" size="md"></rel-chip>
|
|
23
|
-
<rel-chip label="Large" size="lg"></rel-chip>
|
|
24
30
|
|
|
25
31
|
<!-- Fully rounded corners -->
|
|
26
32
|
<rel-chip label="Rounded" border="rounded"></rel-chip>
|
|
@@ -60,9 +66,9 @@
|
|
|
60
66
|
| Property | Attribute | Type | Default | Description |
|
|
61
67
|
| --- | --- | --- | --- | --- |
|
|
62
68
|
| `label` | `label` | `string` | `"Label"` | Text shown in the chip; also its accessible name and the drag payload. |
|
|
63
|
-
| `tone` | `tone` | `"neutral" \| "
|
|
64
|
-
| `variant` | `variant` | `"fill" \| "subtle"` | `"fill"` | Emphasis (Figma
|
|
65
|
-
| `size` | `size` | `"sm" \| "md" \| "lg"` | `"
|
|
69
|
+
| `tone` | `tone` | `"neutral" \| "destructive" \| "warning" \| "success" \| "info" \| "danger"` | `"neutral"` | Colour tone (Figma Tone): `neutral`, `destructive`, `warning`, `success` or `info`. Combined with `variant`. `"danger"` is a deprecated alias of `"destructive"`: it keeps working, logs a one-time console notice, and is normalised to `"destructive"` on the property and the reflected attribute. Reflected. |
|
|
70
|
+
| `variant` | `variant` | `"fill" \| "subtle" \| "primary" \| "secondary"` | `"fill"` | Emphasis (Figma Style): `fill` = solid tone background, `subtle` = tinted background with tone-coloured text. Figma's own spellings `primary` and `secondary` are accepted and normalised to `fill` / `subtle`. Reflected. |
|
|
71
|
+
| `size` | `size` | `"small" \| "medium" \| "large" \| "sm" \| "md" \| "lg"` | `"medium"` | Height scale (Figma Size): `small` 24px / body-sm, `medium` 28px / body-md, `large` 32px / body-lg. Icon size follows (12 / 14 / 16px). `sm` / `md` / `lg` are accepted and normalised to the full words. Reflected. |
|
|
66
72
|
| `border` | `border` | `"default" \| "rounded"` | `"default"` | Corner shape (Figma Border): `default` = 4px radius, `rounded` = fully rounded with 12px horizontal padding. Reflected. |
|
|
67
73
|
| `leadingIcon` | `leading-icon` | `string` | `undefined` | Icon name from the generated icon dictionary (e.g. "settings") — rendered via <rel-icon> before the label. Ignored when `avatar` is set. |
|
|
68
74
|
| `trailingIcon` | `trailing-icon` | `string` | `undefined` | Icon name from the generated icon dictionary, rendered after the label. |
|