@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.
Files changed (48) hide show
  1. package/CHANGELOG.md +98 -2
  2. package/dist/components/rel-accordion/README.md +2 -1
  3. package/dist/components/rel-alert/README.md +1 -1
  4. package/dist/components/rel-autocomplete/README.md +22 -5
  5. package/dist/components/rel-avatar/README.md +9 -7
  6. package/dist/components/rel-badge/README.md +4 -5
  7. package/dist/components/rel-button/README.md +32 -2
  8. package/dist/components/rel-card/README.md +3 -3
  9. package/dist/components/rel-carousel/README.md +3 -0
  10. package/dist/components/rel-checkbox/README.md +8 -4
  11. package/dist/components/rel-chip/README.md +13 -7
  12. package/dist/components/rel-container/README.md +8 -8
  13. package/dist/components/rel-date-picker/README.md +8 -2
  14. package/dist/components/rel-divider/README.md +11 -1
  15. package/dist/components/rel-kbd/README.md +2 -2
  16. package/dist/components/rel-layout/README.md +34 -0
  17. package/dist/components/rel-menu/README.md +3 -1
  18. package/dist/components/rel-menubar/README.md +1 -1
  19. package/dist/components/rel-otp/README.md +4 -3
  20. package/dist/components/rel-pagination/README.md +2 -2
  21. package/dist/components/rel-popover/README.md +5 -5
  22. package/dist/components/rel-progress/README.md +3 -3
  23. package/dist/components/rel-radio/README.md +1 -1
  24. package/dist/components/rel-range-picker/README.md +11 -2
  25. package/dist/components/rel-segmented-button/README.md +7 -5
  26. package/dist/components/rel-select/README.md +11 -6
  27. package/dist/components/rel-sheet/README.md +3 -3
  28. package/dist/components/rel-skeleton/README.md +5 -4
  29. package/dist/components/rel-slider/README.md +3 -2
  30. package/dist/components/rel-snackbar/README.md +13 -7
  31. package/dist/components/rel-stepper/README.md +23 -10
  32. package/dist/components/rel-switch/README.md +16 -7
  33. package/dist/components/rel-tab/README.md +5 -2
  34. package/dist/components/rel-textfield/README.md +19 -2
  35. package/dist/components/rel-time-picker/README.md +6 -0
  36. package/dist/components/rel-toast/README.md +14 -6
  37. package/dist/components/rel-tooltip/README.md +13 -2
  38. package/dist/components/rel-upload/README.md +2 -2
  39. package/dist/index.d.mts +825 -240
  40. package/dist/index.d.ts +825 -240
  41. package/dist/index.js +8617 -5213
  42. package/dist/index.js.map +1 -1
  43. package/dist/index.mjs +8424 -5020
  44. package/dist/index.mjs.map +1 -1
  45. package/dist/styles/focus-ring.ts +84 -0
  46. package/dist/styles/vars.css +75 -60
  47. package/dist/{vars-JHTUGNRG.css → vars-DXX6QOAG.css} +75 -60
  48. package/package.json +1 -1
package/dist/index.d.ts CHANGED
@@ -26,10 +26,22 @@ declare class RelAccordion extends LitElement {
26
26
  * `md` = large (the default), `sm` = medium.
27
27
  */
28
28
  size: "md" | "sm" | "large" | "medium" | "small";
29
- /** 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. */
29
+ /**
30
+ * 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.
31
+ *
32
+ * DELIBERATE DIVERGENCE — do not "fix" this to a button. For `Type=Action`
33
+ * Figma draws the trailing affordance as a full ghost icon Button instance
34
+ * (Size=Medium 40x40, Size=Small 32x32, Style=Ghost, Type=Icon Only); the
35
+ * code keeps a bare icon. Ruled by the user: our behaviour stands and the
36
+ * DESIGN FILE follows our docs here, not the other way round. The whole
37
+ * header is the click target, so a nested button would add a second
38
+ * focusable control inside it and two competing hit areas.
39
+ */
30
40
  type: "default" | "action";
31
41
  /** 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. */
32
42
  appearance: "shadow" | "plain";
43
+ /** 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. */
44
+ disabled: boolean;
33
45
  static styles: lit.CSSResult;
34
46
  /** Stable id prefix wiring the header to its panel. */
35
47
  private accordionId;
@@ -106,13 +118,15 @@ type FieldLabelStyle = "default" | "floating" | "none";
106
118
  * @slot prefix - Custom content at the far left of the control (e.g. a unit dropdown); shares the prefix block with `prefixIcon` / `prefixText`. Hidden when `multiline` is set.
107
119
  * @slot suffix - Custom content at the far right of the control (e.g. an action button); shares the suffix block with `rightIcon` / `suffixText`. Hidden when `multiline` is set.
108
120
  * @csspart label - The visible label element (default and floating label styles)
109
- * @csspart control - The bordered control box (the fieldset in the legend variant)
121
+ * @csspart 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.
110
122
  * @csspart input - The native input / textarea
111
123
  * @csspart prefix - The prefix block (leading icon, prefix text and the `prefix` slot)
112
124
  * @csspart suffix - The suffix block (suffix text, trailing icon and the `suffix` slot)
113
125
  * @csspart right-icon - The trailing `rel-icon`
114
126
  * @csspart helper - The helper / error text line below the control
115
- * @cssprop --rel-field-label-reserve - Height held for the absent label row under `labelStyle="none"` so the control bottom-aligns with labelled siblings; defaults to the label line-height plus the wrapper gap. Set to `0` to collapse the row entirely (toolbars, table cells).
127
+ * @csspart helper-icon - The leading `rel-icon` in the helper line; rendered only when `helper-icon` is set and there is helper text
128
+ * @cssprop --rel-field-label-reserve - 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).
129
+ * @cssprop --rel-textfield-surface - 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.
116
130
  */
117
131
  declare class RelTextfield extends LitElement {
118
132
  /**
@@ -193,6 +207,8 @@ declare class RelTextfield extends LitElement {
193
207
  errorMessage?: string;
194
208
  /** Helper line below the control (preferred over the legacy `supportingText`); linked to the input via `aria-describedby`. */
195
209
  helperText?: string;
210
+ /** Icon name from the generated icon dictionary, rendered before the helper line (Figma "Supporting Icon"). Sized 16px, or 14px at `size="small"`, and it takes the helper's colour — so it turns `--rel-text-danger` in the error state. Decorative: it is hidden from screen readers, since the helper text it sits beside is already announced. */
211
+ helperIcon?: string;
196
212
  /** Makes the input read-only: focusable and selectable but not editable. */
197
213
  readonly: boolean;
198
214
  /** Maximum number of characters (native `maxlength`). */
@@ -248,6 +264,197 @@ declare class RelTextfield extends LitElement {
248
264
  render(): lit_html.TemplateResult<1>;
249
265
  }
250
266
 
267
+ type CheckboxState = "default" | "error" | "disabled";
268
+ type CheckboxStatus = "unchecked" | "checked" | "indeterminate";
269
+ type CheckboxSize = "small" | "medium" | "large" | "extra-large";
270
+ type CheckboxDensity = "compact" | "comfortable";
271
+ type CheckboxAlignment = "leading" | "trailing";
272
+ /**
273
+ * Tri-state checkbox (Figma "Checkbox"): a hidden native checkbox behind a custom box, with label, optional prefix icon and supporting text.
274
+ * @csspart base - The outer wrapper that carries the `state` / `status` classes
275
+ * @csspart label - The visible label text
276
+ * @csspart helper - The supporting text (rendered beside the box in comfortable density, under the row in compact)
277
+ */
278
+ declare class RelCheckbox extends LitElement {
279
+ /** Label text rendered next to the box. */
280
+ label: string;
281
+ /** rel-icon name rendered between the checkbox and the label (label prefix icon); scales with `size`. Unset renders no icon and reserves no space. */
282
+ icon: string;
283
+ /** Supporting text under or beside the label; where it renders depends on `density`. */
284
+ supportingtext: string;
285
+ /** 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). */
286
+ state: CheckboxState;
287
+ /** Check state. `"indeterminate"` is programmatic-only — a user toggle always resolves to checked or unchecked. */
288
+ status: CheckboxStatus;
289
+ /** Value reported in the `rel-change` event detail (it is not submitted with a form). */
290
+ value: string;
291
+ /** Control scale (Figma Size), on Figma's four-step ramp of control frames:
292
+ * `"small"` 16px / body-sm, `"medium"` (default) 18px / body-md,
293
+ * `"large"` 20px / body-lg, `"extra-large"` 24px / body-xl. Supporting
294
+ * text sits one body step below the label at every size (body-xs / sm /
295
+ * md / lg, i.e. 10 / 12 / 14 / 16px). The drawn square is inset to 80% of
296
+ * the frame, so the frame is the layout footprint (what the row gap and the
297
+ * compact indent measure from) rather than the visible square. */
298
+ size: CheckboxSize;
299
+ /** Supporting-text layout (Figma Density). `"compact"` (default) puts the
300
+ * supporting text on its own line under the row, indented past the box;
301
+ * `"comfortable"` stacks label + supporting text beside the box. */
302
+ density: CheckboxDensity;
303
+ /** Box position (Figma Alignment). `"leading"` (default) renders the box
304
+ * before the label; `"trailing"` pushes it to the far edge of the row.
305
+ * Trailing has no compact layout in the spec, so it always renders with
306
+ * comfortable density. */
307
+ alignment: CheckboxAlignment;
308
+ /** Accessible name for the native checkbox, for when the visible `label` is
309
+ * absent or not a usable name on its own — a checkbox in a table row, or one
310
+ * whose meaning comes from its column header.
311
+ *
312
+ * Normally you do not need it: the input is wrapped in the `<label>` that
313
+ * renders `label`, so the visible text already names it natively. Setting
314
+ * this puts an `aria-label` on the input, which OVERRIDES that wrapping
315
+ * label — so set it only when the visible text is the wrong name, not to
316
+ * repeat it. Left unset the attribute is not emitted at all and the native
317
+ * labelling is untouched. */
318
+ fieldAriaLabel?: string;
319
+ /** Boolean view of `status`: `true` when `status === "checked"`; setting it writes `"checked"` / `"unchecked"`. */
320
+ get checked(): boolean;
321
+ set checked(val: boolean);
322
+ private _onChange;
323
+ private _onTouched;
324
+ static styles: lit.CSSResult;
325
+ protected willUpdate(changed: PropertyValues<this>): void;
326
+ /** Trailing has no compact layout in the spec — trailing wins. */
327
+ private get effectiveDensity();
328
+ private toggle;
329
+ /** @internal Angular ControlValueAccessor bridge. */
330
+ registerOnChange(fn: (value: boolean) => void): void;
331
+ /** @internal Angular ControlValueAccessor bridge. */
332
+ registerOnTouched(fn: () => void): void;
333
+ /** @internal Angular ControlValueAccessor bridge. */
334
+ writeValue(checked: boolean): void;
335
+ render(): lit_html.TemplateResult<1>;
336
+ }
337
+
338
+ /** Diameter step, spelled out. */
339
+ type AvatarSize = "large" | "medium" | "small" | "extra-small";
340
+ /** Alternative spellings, not deprecations — the short names keep working. */
341
+ type AvatarSizeAlias = "lg" | "md" | "sm" | "xs";
342
+ type AvatarSizeInput = AvatarSize | AvatarSizeAlias;
343
+ /**
344
+ * Circular avatar (Figma "Avatar"): shows a photo, text initials or a slotted icon in one of four sizes and three fill variants.
345
+ * @slot - Icon content rendered when `type="icon"` (typically an inline SVG, sized 20 / 18 / 16 / 12px by `size`)
346
+ * @csspart base - The circular wrapper that carries the size, variant and inline `bg` / `color` overrides
347
+ * @csspart icon - The `<img>` (`type="image"`) or the icon wrapper span around the slot (`type="icon"`)
348
+ * @csspart label - The `<span>` holding the initials when `type="text"`
349
+ * @cssprop --rel-avatar-bg-fill - Background colour of the `fill` variant
350
+ * @cssprop --rel-avatar-color-fill - Text / icon colour of the `fill` variant
351
+ * @cssprop --rel-avatar-bg-subtle - Background colour of the `subtle` variant
352
+ * @cssprop --rel-avatar-color-subtle - Text / icon colour of the `subtle` variant
353
+ * @cssprop --rel-avatar-border-outline - 1px border colour of the `outline` variant
354
+ * @cssprop --rel-avatar-color-outline - Text / icon colour of the `outline` variant
355
+ * @cssprop --rel-avatar-icon-size - Overrides the slotted icon's size; defaults to the preset ramp (20 / 18 / 16 / 12px)
356
+ * @cssprop --rel-avatar-bg-outline - Background colour of the `outline` variant; defaults to --rel-bg-neutral
357
+ * @cssprop --rel-avatar-size - Overrides the circle's width/height for sizes between (or below) the `size` presets; defaults to the preset's diameter
358
+ * @cssprop --rel-avatar-font-size - Overrides the initials' font-size, for use alongside `--rel-avatar-size`; defaults to the preset's text role
359
+ */
360
+ declare class RelAvatar extends LitElement {
361
+ /** 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. */
362
+ size: AvatarSizeInput;
363
+ /** What is rendered inside the circle: `image` shows `src`, `text` shows the `text` initials, `icon` shows the default slot. */
364
+ type: "image" | "text" | "icon";
365
+ /** Fill style: `fill` = solid inverse background with light text, `subtle` = elevated neutral background, `outline` = transparent with a 1px border. */
366
+ variant: "fill" | "subtle" | "outline";
367
+ /** Image URL for `type="image"`; when empty nothing is rendered inside the circle (there is no fallback to initials). */
368
+ src: string;
369
+ /** Initials shown for `type="text"`; rendered uppercase via `text-transform`. */
370
+ text: string;
371
+ /** Alt text of the `<img>` for `type="image"`; the default is the generic word "Avatar", so pass the person's name. */
372
+ alt: string;
373
+ /** Any CSS colour applied inline as `background-color` on the circle, overriding the variant background. */
374
+ bg: string;
375
+ /** Any CSS colour applied inline as `color` on the circle (initials / icon), overriding the variant text colour. */
376
+ color: string;
377
+ static styles: lit.CSSResult;
378
+ /** Alternative spellings, NOT deprecations — no console notice. Normalising
379
+ * the property rather than duplicating every selector keeps the reflected
380
+ * attribute and the rendered class canonical, so only one spelling reaches
381
+ * the CSS. Same shape as rel-chip and rel-slider. */
382
+ protected willUpdate(changed: Map<string, unknown>): void;
383
+ render(): lit_html.TemplateResult<1>;
384
+ }
385
+
386
+ /** Canonical semantic tones (Figma "Tone"): neutral | destructive | warning | success | info. */
387
+ type ChipTone = "neutral" | "destructive" | "warning" | "success" | "info";
388
+ /** Deprecated spelling: "danger" → destructive. */
389
+ type ChipToneAlias = "danger";
390
+ type ChipToneInput = ChipTone | ChipToneAlias;
391
+ /** Canonical emphasis (Figma "Style"): fill | subtle. */
392
+ type ChipVariant = "fill" | "subtle";
393
+ /** Figma's own spellings of the Style axis, accepted as alternatives. */
394
+ type ChipVariantAlias = "primary" | "secondary";
395
+ type ChipVariantInput = ChipVariant | ChipVariantAlias;
396
+ /** Canonical size steps — full words, as elsewhere in the library. */
397
+ type ChipSize = "small" | "medium" | "large";
398
+ /** Short size spellings, accepted as alternatives. */
399
+ type ChipSizeAlias = "sm" | "md" | "lg";
400
+ type ChipSizeInput = ChipSize | ChipSizeAlias;
401
+ type ChipBorder = "default" | "rounded";
402
+ /**
403
+ * Compact chip (Figma "Chip") for tags, filters and selected values: tone x variant
404
+ * colouring, optional leading icon / avatar / trailing icon, a remove control and
405
+ * opt-in drag-and-drop.
406
+ * @csspart base - The chip body (`.chip`) that carries size, tone, variant and border styling
407
+ * @csspart icon - The leading `<rel-icon>` (rendered only when `leading-icon` is set and no avatar)
408
+ * @csspart label - The label text
409
+ * @cssprop --rel-chip-focus-ring - Colour of the keyboard focus outline; default `var(--rel-border-focus)`
410
+ */
411
+ declare class RelChip extends LitElement {
412
+ static shadowRootOptions: ShadowRootInit;
413
+ /** Text shown in the chip; also its accessible name and the drag payload. */
414
+ label: string;
415
+ /** 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. */
416
+ tone: ChipToneInput;
417
+ /** 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`. */
418
+ variant: ChipVariantInput;
419
+ /** 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. */
420
+ size: ChipSizeInput;
421
+ /** Corner shape (Figma Border): `default` = 4px radius, `rounded` = fully rounded with 12px horizontal padding. */
422
+ border: ChipBorder;
423
+ /** Icon name from the generated icon dictionary (e.g. "settings") — rendered via <rel-icon> before the label. Ignored when `avatar` is set. */
424
+ leadingIcon?: string;
425
+ /** Icon name from the generated icon dictionary, rendered after the label. */
426
+ trailingIcon?: string;
427
+ /** Initials text for the avatar (e.g. "JD"), rendered as a `<rel-avatar type="text">` before the label. Mutually exclusive with leadingIcon (avatar wins). */
428
+ avatar?: string;
429
+ /** Shows a remove (close) button after the label; clicking it sets `removed` and fires `removed`. */
430
+ removable: boolean;
431
+ /** `true` once the chip has been removed — the host is hidden via `:host([removed])`. Reflected so CSS and Angular can see it; reset to `false` to show the chip again. */
432
+ removed: boolean;
433
+ /** Opt-in native drag-and-drop: sets `draggable="true"` on the host, shows the Figma "Dragged" state during a real drag gesture, and makes Left/Right arrow keys fire `chip-move`. */
434
+ draggableChip: boolean;
435
+ /** Reflected so CSS can target the Figma "Dragged" visual state. Driven automatically by dragstart/dragend when draggableChip is set. */
436
+ private dragging;
437
+ static styles: lit.CSSResult[];
438
+ private get classes();
439
+ private handleRemove;
440
+ /** The chip body is announced as a button, so it must also act on keys:
441
+ * Enter/Space re-dispatch as a host click (consumers' @click handlers
442
+ * fire), Delete/Backspace remove when removable, and arrow keys ask the
443
+ * container to reorder a draggable chip (`chip-move`) — the keyboard
444
+ * alternative to pointer drag-and-drop. */
445
+ private handleKeydown;
446
+ private onDragStart;
447
+ private onDragEnd;
448
+ protected willUpdate(changed: Map<string, unknown>): void;
449
+ protected updated(changed: Map<string, unknown>): void;
450
+ render(): lit_html.TemplateResult<1>;
451
+ }
452
+ declare global {
453
+ interface HTMLElementTagNameMap {
454
+ "rel-chip": RelChip;
455
+ }
456
+ }
457
+
251
458
  /**
252
459
  * An item is a plain string or any object read through displayField/valueField.
253
460
  * Two keys are read off objects directly, whatever those fields are named:
@@ -272,7 +479,9 @@ type AutocompleteSearchFn = (query: string) => Promise<AutocompleteItem[]>;
272
479
  * item's label/value on Enter/blur).
273
480
  * @csspart panel - The suggestion listbox container (same node as `list`)
274
481
  * @csspart list - Alias of `panel`
275
- * @csspart option - A suggestion row
482
+ * @csspart option - A suggestion row. Emitted in BOTH single and `multiple` mode, unlike the two parts below
483
+ * @csspart option-label - The row's first line (the matched label). SINGLE-SELECT ROWS ONLY
484
+ * @csspart 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
276
485
  * @csspart label - The inner field's label (re-exported from `rel-textfield`)
277
486
  * @csspart control - The inner field's bordered control box (re-exported from `rel-textfield`)
278
487
  * @csspart input - The inner native input (re-exported from `rel-textfield`)
@@ -288,9 +497,11 @@ type AutocompleteSearchFn = (query: string) => Promise<AutocompleteItem[]>;
288
497
  * @cssprop --rel-autocomplete-value-color - Suggestion label colour
289
498
  * @cssprop --rel-autocomplete-icon-color - Colour of a row's leading `icon`
290
499
  * @cssprop --rel-autocomplete-status-color - Colour of the loading / no-results status rows
500
+ * @cssprop --rel-autocomplete-menu-max-height - 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.
501
+ * @cssprop --rel-overlay-fallback-z - 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 rel-select, rel-autocomplete and the other pickers so the whole overlay family can be moved from one place; default 1001.
291
502
  */
292
503
  declare class RelAutocomplete extends LitElement {
293
- /** Local (or fallback initial) suggestion list. Ignored while searchFn drives results. */
504
+ /** 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. */
294
505
  items: AutocompleteItem[];
295
506
  /** Committed value — the selected item's valueField string ("" = none). */
296
507
  value: string;
@@ -298,6 +509,8 @@ declare class RelAutocomplete extends LitElement {
298
509
  displayField: string;
299
510
  /** Object key read for the committed value when items are objects. */
300
511
  valueField: string;
512
+ /** 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. */
513
+ supportingField: string;
301
514
  /** Local filtering mode. Ignored when searchFn is set (results arrive pre-filtered). */
302
515
  filter: AutocompleteFilter;
303
516
  /** Async data source. When set, local filtering is bypassed entirely. */
@@ -308,6 +521,24 @@ declare class RelAutocomplete extends LitElement {
308
521
  minSearchLength: number;
309
522
  /** Optional cap on rendered suggestions (panel scrolls regardless). */
310
523
  maxSuggestions: number;
524
+ /**
525
+ * Multi-select mode: rows become checkboxes with a Select-all row, and the
526
+ * committed values live in `selectedValues` rather than `value`. Mirrors
527
+ * `rel-select`'s `multiple` rather than introducing a second pattern.
528
+ *
529
+ * THE TEXT INPUT STAYS A SEARCH BOX. In single mode the input shows the
530
+ * selected label, which is the point of a type-ahead; in multi mode there is
531
+ * no single label to show, and writing a value list into the field would
532
+ * destroy the query the user is typing. `rel-select` can put chips inside its
533
+ * trigger because a trigger is not an editable surface — an input is. So the
534
+ * chips render beneath the field, the query clears after each toggle so the
535
+ * next search starts fresh, and the panel stays open.
536
+ */
537
+ multiple: boolean;
538
+ /** Committed values in `multiple` mode (ignored otherwise). Mirrors `rel-select.selectedValues`. */
539
+ selectedValues: string[];
540
+ /** Label of the Select-all row shown at the top of the panel in `multiple` mode. */
541
+ selectAllLabel: string;
311
542
  /** Shows a trailing ✕ that clears the text and the committed value. */
312
543
  clearable: boolean;
313
544
  /** Externally-controlled loading state (internal searchFn loading is automatic). */
@@ -387,6 +618,14 @@ declare class RelAutocomplete extends LitElement {
387
618
  private openPanel;
388
619
  private closePanel;
389
620
  private showNativePopover;
621
+ /**
622
+ * Gap between the field and the panel. Figma composes the two in a column
623
+ * with gap=--rel-spacing-2xs, so this is a bound token rather than a magic
624
+ * number; the position is computed in JS (the panel is in the top layer), so
625
+ * it is read from the resolved custom property instead of being hardcoded.
626
+ * That way a spacing re-export carries through here as it does in CSS.
627
+ */
628
+ private get panelOffset();
390
629
  private hideNativePopover;
391
630
  private updatePosition;
392
631
  /** Re-evaluates whether the panel should be showing for the current state. */
@@ -394,6 +633,22 @@ declare class RelAutocomplete extends LitElement {
394
633
  private onFieldInput;
395
634
  private announceResults;
396
635
  private runSearch;
636
+ /**
637
+ * Multi rows nest the checkbox at the SAME step as the field, not one below.
638
+ * rel-select records why (rel-select.ts, optionCheckboxSize): the old
639
+ * one-step-down compensated for rel-checkbox sitting a size above the system,
640
+ * and once that ramp was realigned to Figma the compensation became the bug.
641
+ */
642
+ private get optionCheckboxSize();
643
+ /** Native tooltip carrying the row's full text. Both lines truncate to one
644
+ * line with an ellipsis, so a long supporting line is otherwise unrecoverable
645
+ * by sight; hovering the row reveals it. Only rows that carry a second line
646
+ * get one, so plain lists do not sprout tooltips. */
647
+ private rowTitle;
648
+ private get selectAllStatus();
649
+ private toggleMultiOption;
650
+ private toggleSelectAll;
651
+ private emitMultiChanged;
397
652
  private selectItem;
398
653
  /** Silent commit when typed text exactly matches an item (label or value, case-insensitive). */
399
654
  private commitExactMatch;
@@ -417,47 +672,17 @@ declare global {
417
672
  }
418
673
  }
419
674
 
420
- /**
421
- * Circular avatar (Figma "Avatar"): shows a photo, text initials or a slotted icon in one of four sizes and three fill variants.
422
- * @slot - Icon content rendered when `type="icon"` (typically an inline SVG, sized to 50% of the circle)
423
- * @csspart base - The circular wrapper that carries the size, variant and inline `bg` / `color` overrides
424
- * @csspart icon - The `<img>` (`type="image"`) or the icon wrapper span around the slot (`type="icon"`)
425
- * @csspart label - The `<span>` holding the initials when `type="text"`
426
- * @cssprop --rel-avatar-bg-fill - Background colour of the `fill` variant
427
- * @cssprop --rel-avatar-color-fill - Text / icon colour of the `fill` variant
428
- * @cssprop --rel-avatar-bg-subtle - Background colour of the `subtle` variant
429
- * @cssprop --rel-avatar-color-subtle - Text / icon colour of the `subtle` variant
430
- * @cssprop --rel-avatar-border-outline - 1px border colour of the `outline` variant
431
- * @cssprop --rel-avatar-color-outline - Text / icon colour of the `outline` variant
432
- * @cssprop --rel-avatar-size - Overrides the circle's width/height for sizes between (or below) the `size` presets; defaults to the preset's diameter
433
- * @cssprop --rel-avatar-font-size - Overrides the initials' font-size, for use alongside `--rel-avatar-size`; defaults to the preset's text role
434
- */
435
- declare class RelAvatar extends LitElement {
436
- /** Diameter step: `lg` 40px, `md` 32px, `sm` 24px, `xs` 20px; each binds the matching body-*-semibold text role for initials. */
437
- size: "lg" | "md" | "sm" | "xs";
438
- /** What is rendered inside the circle: `image` shows `src`, `text` shows the `text` initials, `icon` shows the default slot. */
439
- type: "image" | "text" | "icon";
440
- /** Fill style: `fill` = solid inverse background with light text, `subtle` = elevated neutral background, `outline` = transparent with a 1px border. */
441
- variant: "fill" | "subtle" | "outline";
442
- /** Image URL for `type="image"`; when empty nothing is rendered inside the circle (there is no fallback to initials). */
443
- src: string;
444
- /** Initials shown for `type="text"`; rendered uppercase via `text-transform`. */
445
- text: string;
446
- /** Alt text of the `<img>` for `type="image"`; the default is the generic word "Avatar", so pass the person's name. */
447
- alt: string;
448
- /** Any CSS colour applied inline as `background-color` on the circle, overriding the variant background. */
449
- bg: string;
450
- /** Any CSS colour applied inline as `color` on the circle (initials / icon), overriding the variant text colour. */
451
- color: string;
452
- static styles: lit.CSSResult;
453
- render(): lit_html.TemplateResult<1>;
454
- }
455
-
675
+ /** Canonical size steps — full words, as elsewhere in the library. */
676
+ type BadgeSize = "small" | "medium" | "large" | "extra-large";
677
+ /** Short size spellings, accepted as alternatives. */
678
+ type BadgeSizeAlias = "sm" | "md" | "lg" | "xl";
679
+ type BadgeSizeInput = BadgeSize | BadgeSizeAlias;
456
680
  /**
457
681
  * Notification badge (Figma "Badge"): a small count pill or an unlabeled dot, usually positioned over an icon or avatar.
458
682
  * @csspart base - The `role="status"` pill / dot that carries the size, variant and colour overrides
459
683
  * @cssprop --rel-badge-bg - Background colour of the badge; the `color` prop writes this on the host
460
684
  * @cssprop --rel-badge-color - Text colour of the count; the `text-color` prop writes this on the host
685
+ * @cssprop --rel-badge-dot-size - Diameter of the `dot` variant; default 6px, the single dot size Figma draws. Set it to restore the larger discs shipped previously.
461
686
  */
462
687
  declare class RelBadge extends LitElement {
463
688
  /** 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. */
@@ -466,9 +691,9 @@ declare class RelBadge extends LitElement {
466
691
  ariaLabel: string;
467
692
  /** Noun appended to the count in the generated accessible name, e.g. "3 notifications". */
468
693
  suffix: string;
469
- /** Badge scale (Figma Badge S/M/L/XL): number badges are 20 / 24 / 28 / 40px tall, dot badges 8 / 10 / 12 / 12px. */
470
- size: "sm" | "md" | "lg" | "xl";
471
- /** `number` renders the `number` text in a pill that widens with its content; `dot` renders a small disc with no text. */
694
+ /** 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. */
695
+ size: BadgeSizeInput;
696
+ /** `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`. */
472
697
  variant: "dot" | "number";
473
698
  /**
474
699
  * Direct CSS color override for the badge background. Accepts any valid
@@ -485,6 +710,7 @@ declare class RelBadge extends LitElement {
485
710
  */
486
711
  textColor: string;
487
712
  static styles: lit.CSSResult;
713
+ protected willUpdate(changed: PropertyValues<this>): void;
488
714
  private get classes();
489
715
  private get computedAriaLabel();
490
716
  updated(changedProperties: Map<PropertyKey, unknown>): void;
@@ -534,8 +760,14 @@ type ButtonTone = "default" | "primary" | "secondary" | "critical" | "success" |
534
760
  declare class RelButton extends LitElement {
535
761
  /** 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). */
536
762
  variant: ButtonVariant;
537
- /** 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). */
538
- tone: ButtonTone;
763
+ /**
764
+ * 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).
765
+ *
766
+ * Left UNSET it means `neutral`, except on `variant="primary"`, where it
767
+ * still means `brand` for one release — see `effectiveTone`. Write the tone
768
+ * explicitly and you always get what you wrote.
769
+ */
770
+ tone?: ButtonTone;
539
771
  /** 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`. */
540
772
  iconOnly: boolean;
541
773
  /** 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. */
@@ -552,7 +784,7 @@ declare class RelButton extends LitElement {
552
784
  badge: string;
553
785
  /** Renders a small dot badge in the top-right corner instead of a numbered badge; takes precedence over `badge`. The dot is inset 2px from the corner so it lands on a circular icon button's edge. */
554
786
  badgeDot: boolean;
555
- static styles: lit.CSSResult;
787
+ static styles: lit.CSSResult[];
556
788
  connectedCallback(): void;
557
789
  disconnectedCallback(): void;
558
790
  private handleHostClick;
@@ -622,11 +854,11 @@ declare class RelCalendar extends LitElement {
622
854
  }
623
855
 
624
856
  /**
625
- * `<rel-card>` — bordered surface that groups related content (Figma "Card").
857
+ * `<rel-card>` — elevated surface that groups related content (Figma "Card").
626
858
  * Use `type="media"` with a `<rel-card-media>` in the `media` slot for a flush image header.
627
859
  * @slot - Card body; slotted headings and paragraphs receive the Figma card typography
628
860
  * @slot media - Flush media region above the body; rendered only when `type="media"`
629
- * @csspart base - The outer bordered card surface
861
+ * @csspart base - The outer elevated card surface
630
862
  * @csspart header - The media region wrapper (present only when `type="media"`)
631
863
  * @csspart content - The padded body that wraps the default slot
632
864
  */
@@ -655,6 +887,7 @@ declare class RelCard extends LitElement {
655
887
  * @cssprop --rel-carousel-dot-size - Diameter of an inactive dot (default 8px).
656
888
  * @cssprop --rel-carousel-dot-size-active - Width of the active (brand-coloured, pill-shaped) dot (default 16px).
657
889
  * @cssprop --rel-carousel-slide-width - 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).
890
+ * @cssprop --rel-carousel-slide-display - `display` given to each slide in `orientation="vertical"` only; defaults to `flex`, 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.
658
891
  * @cssprop --rel-carousel-slide-height - Written by the component on its own host to the computed panel height (used for vertical slides); not meant to be set by consumers.
659
892
  */
660
893
  declare class RelCarousel extends LitElement {
@@ -786,63 +1019,6 @@ declare class RelCardMedia extends LitElement {
786
1019
  render(): lit_html.TemplateResult<1>;
787
1020
  }
788
1021
 
789
- type CheckboxState = "default" | "error" | "disabled";
790
- type CheckboxStatus = "unchecked" | "checked" | "indeterminate";
791
- type CheckboxSize = "small" | "medium" | "large";
792
- type CheckboxDensity = "compact" | "comfortable";
793
- type CheckboxAlignment = "leading" | "trailing";
794
- /**
795
- * Tri-state checkbox (Figma "Checkbox"): a hidden native checkbox behind a custom box, with label, optional prefix icon and supporting text.
796
- * @csspart base - The outer wrapper that carries the `state` / `status` classes
797
- * @csspart label - The visible label text
798
- * @csspart helper - The supporting text (rendered beside the box in comfortable density, under the row in compact)
799
- */
800
- declare class RelCheckbox extends LitElement {
801
- /** Label text rendered next to the box. */
802
- label: string;
803
- /** rel-icon name rendered between the checkbox and the label (label prefix icon); scales with `size`. Unset renders no icon and reserves no space. */
804
- icon: string;
805
- /** Supporting text under or beside the label; where it renders depends on `density`. */
806
- supportingtext: string;
807
- /** Visual and interaction state (Figma State): `"error"` swaps in the danger colours, `"disabled"` blocks input and dims the control to 0.55 opacity. */
808
- state: CheckboxState;
809
- /** Check state. `"indeterminate"` is programmatic-only — a user toggle always resolves to checked or unchecked. */
810
- status: CheckboxStatus;
811
- /** Value reported in the `rel-change` event detail (it is not submitted with a form). */
812
- value: string;
813
- /** Control scale (Figma Size). `"medium"` (default) = 18px box with body-lg
814
- * label; `"small"` = 14px box with body-md label; `"large"` (Figma L,
815
- * added 2026-09-04) = 20px box with body-lg label and body-sm supporting
816
- * text — the same additive step rel-radio took for its large size. */
817
- size: CheckboxSize;
818
- /** Supporting-text layout (Figma Density). `"compact"` (default) puts the
819
- * supporting text on its own line under the row, indented past the box;
820
- * `"comfortable"` stacks label + supporting text beside the box. */
821
- density: CheckboxDensity;
822
- /** Box position (Figma Alignment). `"leading"` (default) renders the box
823
- * before the label; `"trailing"` pushes it to the far edge of the row.
824
- * Trailing has no compact layout in the spec, so it always renders with
825
- * comfortable density. */
826
- alignment: CheckboxAlignment;
827
- /** Boolean view of `status`: `true` when `status === "checked"`; setting it writes `"checked"` / `"unchecked"`. */
828
- get checked(): boolean;
829
- set checked(val: boolean);
830
- private _onChange;
831
- private _onTouched;
832
- static styles: lit.CSSResult;
833
- protected willUpdate(changed: PropertyValues<this>): void;
834
- /** Trailing has no compact layout in the spec — trailing wins. */
835
- private get effectiveDensity();
836
- private toggle;
837
- /** @internal Angular ControlValueAccessor bridge. */
838
- registerOnChange(fn: (value: boolean) => void): void;
839
- /** @internal Angular ControlValueAccessor bridge. */
840
- registerOnTouched(fn: () => void): void;
841
- /** @internal Angular ControlValueAccessor bridge. */
842
- writeValue(checked: boolean): void;
843
- render(): lit_html.TemplateResult<1>;
844
- }
845
-
846
1022
  /**
847
1023
  * Single-date form field (Figma "Date Picker"): a text field with typed date entry plus a
848
1024
  * popover calendar whose header pills drill into month and year grids, above
@@ -853,6 +1029,7 @@ declare class RelCheckbox extends LitElement {
853
1029
  * @csspart trigger - The field box that wraps the text input and the clear/calendar icons
854
1030
  * @csspart value - The editable text input inside the trigger
855
1031
  * @csspart shortcuts - The Today / Tomorrow / Yesterday chip row at the top of the popover
1032
+ * @cssprop --rel-overlay-fallback-z - 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 rel-select, rel-autocomplete and the other pickers so the whole overlay family can be moved from one place; default 1001.
856
1033
  */
857
1034
  declare class RelDatePicker extends LitElement {
858
1035
  /** Selected date as an ISO `YYYY-MM-DD` string, or `null` when empty; setting it also moves the popover to that month. */
@@ -861,7 +1038,7 @@ declare class RelDatePicker extends LitElement {
861
1038
  placeholder: string;
862
1039
  /** Visible label rendered above the field and wired to the input via `aria-labelledby`; `null` renders no label. */
863
1040
  label: string | null;
864
- /** Disables the field, the icons and the popover; the host dims to 60% opacity. */
1041
+ /** Disables the field, the icons and the popover; the host dims to 40% opacity. */
865
1042
  disabled: boolean;
866
1043
  /** Appends a `*` to the label and sets `aria-required`; no validation is performed. */
867
1044
  required: boolean;
@@ -926,6 +1103,10 @@ declare class RelDatePicker extends LitElement {
926
1103
  private panelView;
927
1104
  /** First year of the visible Years page; re-anchored whenever that view opens. */
928
1105
  private yearPageStart;
1106
+ /** Period-grid cell the keyboard last moved to; -1 until the user moves. Kept
1107
+ * across a year page turn, since it is a grid POSITION and every page has the
1108
+ * same twelve slots. */
1109
+ private periodRoved;
929
1110
  /** Uncommitted typed text; null → the input shows the committed value. */
930
1111
  private inputText;
931
1112
  /** Parse/range error message for the typed text (cleared on next input). */
@@ -949,6 +1130,7 @@ declare class RelDatePicker extends LitElement {
949
1130
  private showTopLayer;
950
1131
  private hideTopLayer;
951
1132
  private onReposition;
1133
+ private onPageScroll;
952
1134
  private updatePopoverPosition;
953
1135
  /** Focus the roving day cell (selected day, else today, else first). */
954
1136
  private focusPanelSelection;
@@ -1012,16 +1194,48 @@ declare class RelDatePicker extends LitElement {
1012
1194
  private announceYearPage;
1013
1195
  /** A pill toggles its own view; tapping it again returns to the day grid. */
1014
1196
  private switchView;
1015
- /** Focus the selected month/year cell so the keyboard lands somewhere useful. */
1197
+ /** Focus the month/year cell that holds the tab stop, so the keyboard lands
1198
+ * somewhere useful and focus can never disagree with the tab stop. */
1016
1199
  private focusPanelGridSelection;
1017
1200
  private selectMonth;
1018
1201
  private selectYear;
1019
1202
  /** Arrow-key roving across the 3-column month / year grid. */
1020
1203
  private handlePeriodGridKeydown;
1204
+ /**
1205
+ * The tab stop follows focus, wherever focus came from — arrows, a click, Tab,
1206
+ * or a cell that simply kept focus across a re-render. Doing this on focusin
1207
+ * rather than in the key handler is what keeps the two from disagreeing: a
1208
+ * year page turn reuses the same buttons with new labels, so the focused cell
1209
+ * survives while `selected` moves off-page, and deriving the tab stop from
1210
+ * anything other than focus itself puts them on different cells.
1211
+ */
1212
+ private handlePeriodGridFocusin;
1021
1213
  /**
1022
1214
  * Index of the single grid cell carrying tabindex="0". The old expression
1023
1215
  * (`isSelected || (index === 0 && !value)`) left the grid with ZERO tab
1024
1216
  * stops once the user arrowed around without a selection.
1217
+ *
1218
+ * KNOWN GAP (deferred from 1.4.9, needs a ticket covering BOTH calendars):
1219
+ * `focusedDate` is read here as the first candidate but is never assigned
1220
+ * anywhere in this file, so it is permanently null and the tab stop tracks
1221
+ * the SELECTION rather than focus. Tab out of the grid and back and you
1222
+ * return to the selected day, not the day you left.
1223
+ *
1224
+ * Wiring it from each cell's `@focus` was tried and is NOT sufficient, which
1225
+ * is why this is still open. Measured 2026-09-25 by driving real keys: from
1226
+ * Sun 27 Sep, one ArrowDown lands focus on Sun 25 Oct instead of Sun 4 Oct,
1227
+ * because handleDayKeydown moves by grid INDEX and then lets the view flip
1228
+ * month, so index+7 addresses a different date in the new grid. lit reuses
1229
+ * the same button elements across that re-render, so the focused element
1230
+ * never changes and NO focus event fires — the handler cannot observe the
1231
+ * move it is supposed to record.
1232
+ *
1233
+ * The real fix is to navigate by DATE and set the rove explicitly before
1234
+ * focusing, the way rel-range-picker.moveGridFocusTo already does (verified
1235
+ * correct in the same run: Sep 27 → Oct 4 → Oct 11, tab stop and focus
1236
+ * agreeing at every step). That is a rewrite of this component's arrow-key
1237
+ * model, not a contained fix, and it must land on both calendars together or
1238
+ * they diverge again.
1025
1239
  */
1026
1240
  private getRovingIndex;
1027
1241
  /** Days view: weekday header + the six week rows. */
@@ -1033,6 +1247,11 @@ declare class RelDatePicker extends LitElement {
1033
1247
  render(): lit_html.TemplateResult<1>;
1034
1248
  }
1035
1249
 
1250
+ /** Field scale, matching the other pickers. */
1251
+ type RangePickerSize = "small" | "medium" | "large";
1252
+ /** Alternative spellings, not deprecations. */
1253
+ type RangePickerSizeAlias = "sm" | "md" | "lg";
1254
+ type RangePickerSizeInput = RangePickerSize | RangePickerSizeAlias;
1036
1255
  /**
1037
1256
  * Date-range form field (Figma "Date Range Picker"): a text field with typed "from → to"
1038
1257
  * entry and a two-pane panel — quick-select presets with single-key shortcuts on the left,
@@ -1044,8 +1263,9 @@ declare class RelDatePicker extends LitElement {
1044
1263
  * @csspart panel - The floating panel (`role="dialog"`) holding presets and calendar
1045
1264
  * @csspart list - The preset column and the day grid
1046
1265
  * @csspart option - Each quick-select preset button
1047
- * @csspart action - The previous-month and next-month buttons
1266
+ * @csspart action - The header's previous and next buttons; they step a month, a year or a 12-year page depending on which view the panel is showing
1048
1267
  * @csspart footer - The Reset / Cancel / Apply button row
1268
+ * @cssprop --rel-overlay-fallback-z - 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 rel-select, rel-autocomplete and the other pickers so the whole overlay family can be moved from one place; default 1001.
1049
1269
  */
1050
1270
  declare class RelRangePicker extends LitElement {
1051
1271
  /** Committed range start as an ISO `YYYY-MM-DD` string; empty string when no range is set. */
@@ -1088,9 +1308,19 @@ declare class RelRangePicker extends LitElement {
1088
1308
  * mirroring rel-time-picker / rel-date-picker. Off by default so the
1089
1309
  * existing intrinsic-width (`inline-block`) rendering is unchanged. */
1090
1310
  fullWidth: boolean;
1311
+ /** Trigger height on the shared field scale — `small` 32 / `medium` 40 / `large` 48px border-box — matching rel-textfield, rel-date-picker and rel-time-picker so the four align in a form row. Also ramps the trigger's text and inline padding. The panel is unaffected: Figma specifies one panel size. `sm` / `md` / `lg` are alternative spellings and stay supported — normalised to the full words, not deprecated. */
1312
+ size: RangePickerSizeInput;
1091
1313
  private draftFrom;
1092
1314
  private draftTo;
1093
1315
  private draftActivePreset;
1316
+ /** Which grid the panel shows. Ported from rel-date-picker so the two
1317
+ * calendars behave the same; reaching January 2023 was ~44 chevron clicks. */
1318
+ private panelView;
1319
+ private yearPageStart;
1320
+ /** Period-grid cell the keyboard last moved to; -1 until the user moves. Kept
1321
+ * across a year page turn, since it is a grid POSITION and every page has the
1322
+ * same twelve slots. */
1323
+ private periodRoved;
1094
1324
  private viewYear;
1095
1325
  private viewMonth;
1096
1326
  private isSelectingRange;
@@ -1111,10 +1341,15 @@ declare class RelRangePicker extends LitElement {
1111
1341
  private showTopLayer;
1112
1342
  private hideTopLayer;
1113
1343
  private onReposition;
1344
+ private onPageScroll;
1114
1345
  private updatePopoverPosition;
1115
1346
  private handleClickOutside;
1116
1347
  private handleGlobalKeydown;
1117
1348
  private toggleOpen;
1349
+ /** Alternative spellings, NOT deprecations — no console notice. Normalising
1350
+ * keeps the reflected attribute canonical so the :host([size=…]) rules only
1351
+ * need the full words. */
1352
+ protected willUpdate(changed: Map<string, unknown>): void;
1118
1353
  private openPopover;
1119
1354
  private cancelPopover;
1120
1355
  private applySelection;
@@ -1154,6 +1389,48 @@ declare class RelRangePicker extends LitElement {
1154
1389
  private isDateDisabled;
1155
1390
  private getQuickSelects;
1156
1391
  private selectQuickSelect;
1392
+ private goPrev;
1393
+ private goNext;
1394
+ private announceYearPage;
1395
+ /** A pill toggles its own view; tapping it again returns to the day grid. */
1396
+ private switchView;
1397
+ private selectMonth;
1398
+ private selectYear;
1399
+ /**
1400
+ * Arrow-key roving across the Months / Years grid, matching rel-date-picker
1401
+ * exactly. Without this the grid had ONE reachable cell — the selected one —
1402
+ * because every other cell carries tabindex="-1" under the roving pattern and
1403
+ * nothing moved the focus. A keyboard user could open the view and go nowhere,
1404
+ * while the 44 chevron presses still worked: strictly worse than before the
1405
+ * switcher existed.
1406
+ *
1407
+ * Movement clamps at the edges (shared with the date picker through
1408
+ * periodGridTarget); Escape steps back to the day grid rather than falling
1409
+ * through to the panel's own Escape, which closes the whole picker.
1410
+ */
1411
+ private handlePeriodGridKeydown;
1412
+ /**
1413
+ * The tab stop follows focus, wherever focus came from — arrows, a click, Tab,
1414
+ * or a cell that simply kept focus across a re-render. Doing this on focusin
1415
+ * rather than in the key handler is what keeps the two from disagreeing: a
1416
+ * year page turn reuses the same buttons with new labels, so the focused cell
1417
+ * survives while `selected` moves off-page, and deriving the tab stop from
1418
+ * anything other than focus itself puts them on different cells.
1419
+ */
1420
+ private handlePeriodGridFocusin;
1421
+ /**
1422
+ * Return focus to the day grid after leaving Months / Years. Prefers the
1423
+ * roving tab stop and falls back to the first selectable day, so a month with
1424
+ * every leading cell disabled still lands somewhere. Disabled days here carry
1425
+ * a CLASS, not the :disabled state, because they are not buttons.
1426
+ */
1427
+ private focusDayGrid;
1428
+ /** Put focus in the grid when a view opens, so the keyboard has somewhere to
1429
+ * start; otherwise the arrows have nothing to move from. */
1430
+ private focusPeriodGrid;
1431
+ /** Months or Years, in the same 3x4 grid rel-date-picker uses. */
1432
+ private renderPeriodGrid;
1433
+ private getMonthsList;
1157
1434
  private previousMonth;
1158
1435
  private nextMonth;
1159
1436
  private getMonthName;
@@ -1167,6 +1444,21 @@ declare class RelRangePicker extends LitElement {
1167
1444
  * The cell that carries tabindex="0". Prefers the last roved date, then the
1168
1445
  * draft start, then today, then the first selectable day — always exactly
1169
1446
  * one Tab stop, even after arrowing away with nothing selected.
1447
+ *
1448
+ * `focusedDate` is written by moveGridFocusTo, which sets it from the DATE
1449
+ * before moving focus rather than reading it back off a focus event. That
1450
+ * ordering is what makes the arrow keys correct across a month boundary —
1451
+ * verified 2026-09-25 by driving real keys: Sep 27 → Oct 4 → Oct 11 with the
1452
+ * tab stop and focus agreeing at every step, including the view change.
1453
+ * rel-date-picker moves by grid INDEX instead and lands on the wrong date
1454
+ * there; see the note on its getRovingIndex. Do not "simplify" this to a
1455
+ * focus listener — lit reuses the same cell elements across the re-render, so
1456
+ * the focused element does not change and no focus event fires.
1457
+ *
1458
+ * Still open for BOTH calendars: focus arriving from a click or from Tab does
1459
+ * not update the rove, only the arrow keys do. Deferred from 1.4.9 as a
1460
+ * shared ticket, since fixing one calendar alone reintroduces the divergence
1461
+ * the shared period-page module exists to prevent.
1170
1462
  */
1171
1463
  private getRovingIso;
1172
1464
  private handleGridKeydown;
@@ -1179,65 +1471,11 @@ declare global {
1179
1471
  }
1180
1472
  }
1181
1473
 
1182
- type ChipTone = "neutral" | "danger" | "warning" | "success" | "info";
1183
- type ChipVariant = "fill" | "subtle";
1184
- type ChipSize = "sm" | "md" | "lg";
1185
- type ChipBorder = "default" | "rounded";
1186
- /**
1187
- * Compact chip (Figma "Chip") for tags, filters and selected values: tone x variant
1188
- * colouring, optional leading icon / avatar / trailing icon, a remove control and
1189
- * opt-in drag-and-drop.
1190
- * @csspart base - The chip body (`.chip`) that carries size, tone, variant and border styling
1191
- * @csspart icon - The leading `<rel-icon>` (rendered only when `leading-icon` is set and no avatar)
1192
- * @csspart label - The label text
1193
- * @cssprop --rel-chip-focus-ring - Colour of the keyboard focus outline; default `var(--rel-border-focus)`
1194
- */
1195
- declare class RelChip extends LitElement {
1196
- static shadowRootOptions: ShadowRootInit;
1197
- /** Text shown in the chip; also its accessible name and the drag payload. */
1198
- label: string;
1199
- /** Colour tone (Figma Tone): `neutral`, `danger`, `warning`, `success` or `info`. Combined with `variant`. */
1200
- tone: ChipTone;
1201
- /** Emphasis (Figma Variant): `fill` = solid tone background, `subtle` = tinted background with tone-coloured text. */
1202
- variant: ChipVariant;
1203
- /** Height scale (Figma Size): `sm` 24px / body-sm, `md` 28px / body-md, `lg` 32px / body-lg. Icon size follows (12 / 14 / 16px). */
1204
- size: ChipSize;
1205
- /** Corner shape (Figma Border): `default` = 4px radius, `rounded` = fully rounded with 12px horizontal padding. */
1206
- border: ChipBorder;
1207
- /** Icon name from the generated icon dictionary (e.g. "settings") — rendered via <rel-icon> before the label. Ignored when `avatar` is set. */
1208
- leadingIcon?: string;
1209
- /** Icon name from the generated icon dictionary, rendered after the label. */
1210
- trailingIcon?: string;
1211
- /** Initials text for the avatar (e.g. "JD"), rendered as a `<rel-avatar type="text">` before the label. Mutually exclusive with leadingIcon (avatar wins). */
1212
- avatar?: string;
1213
- /** Shows a remove (close) button after the label; clicking it sets `removed` and fires `removed`. */
1214
- removable: boolean;
1215
- /** `true` once the chip has been removed — the host is hidden via `:host([removed])`. Reflected so CSS and Angular can see it; reset to `false` to show the chip again. */
1216
- removed: boolean;
1217
- /** Opt-in native drag-and-drop: sets `draggable="true"` on the host, shows the Figma "Dragged" state during a real drag gesture, and makes Left/Right arrow keys fire `chip-move`. */
1218
- draggableChip: boolean;
1219
- /** Reflected so CSS can target the Figma "Dragged" visual state. Driven automatically by dragstart/dragend when draggableChip is set. */
1220
- private dragging;
1221
- static styles: lit.CSSResult;
1222
- private get classes();
1223
- private handleRemove;
1224
- /** The chip body is announced as a button, so it must also act on keys:
1225
- * Enter/Space re-dispatch as a host click (consumers' @click handlers
1226
- * fire), Delete/Backspace remove when removable, and arrow keys ask the
1227
- * container to reorder a draggable chip (`chip-move`) — the keyboard
1228
- * alternative to pointer drag-and-drop. */
1229
- private handleKeydown;
1230
- private onDragStart;
1231
- private onDragEnd;
1232
- protected updated(changed: Map<string, unknown>): void;
1233
- render(): lit_html.TemplateResult<1>;
1234
- }
1235
- declare global {
1236
- interface HTMLElementTagNameMap {
1237
- "rel-chip": RelChip;
1238
- }
1239
- }
1240
-
1474
+ /** Inner padding step, spelled out. */
1475
+ type ContainerPadding = "small" | "medium" | "large" | "extra-large";
1476
+ /** Alternative spellings, not deprecations — the short names keep working. */
1477
+ type ContainerPaddingAlias = "sm" | "md" | "lg" | "xl";
1478
+ type ContainerPaddingInput = ContainerPadding | ContainerPaddingAlias;
1241
1479
  /** Canonical surface names (audit 12). */
1242
1480
  type ContainerSurface = "default" | "elevated" | "transparent";
1243
1481
  /** @deprecated since 1.4.7 — use `ContainerSurface` / the `surface` prop. */
@@ -1249,8 +1487,8 @@ type ContainerBackground = "light" | "white" | "none";
1249
1487
  * @csspart container - Alias of `base`, kept for existing `::part(container)` selectors
1250
1488
  */
1251
1489
  declare class RelContainer extends LitElement {
1252
- /** Inner padding step: `sm` = spacing-xs, `md` = spacing-md, `lg` = spacing-xl, `xl` = spacing-2xl. */
1253
- padding: "sm" | "md" | "lg" | "xl";
1490
+ /** Inner padding step: `small` = spacing-xs, `medium` = spacing-md, `large` = spacing-lg. `extra-large` = spacing-2xl is a code-only extension — Figma publishes only the three steps above — and is supported, not deprecated. `sm` / `md` / `lg` / `xl` are alternative spellings and stay supported, normalised to the full words. */
1491
+ padding: ContainerPaddingInput;
1254
1492
  /** Adds the shadow-300 elevation box-shadow to the surface. */
1255
1493
  shadow: boolean;
1256
1494
  /**
@@ -1270,7 +1508,8 @@ declare class RelContainer extends LitElement {
1270
1508
 
1271
1509
  /**
1272
1510
  * Thin separator rule that splits content horizontally or vertically. Use it between sections, list groups or toolbar clusters; do not use it as a spacer.
1273
- * @csspart base - The rendered rule (`role="separator"` div) — restyle thickness, colour or margin here.
1511
+ * @csspart base - The rendered rule — restyle thickness, colour or margin here.
1512
+ * @cssprop --rel-divider-spacing - Cross-axis margin around the rule; default 16px. Figma specifies none, so set it to `0` for strict parity — the default is kept for back-compat and is planned to become `0` in 3.0.
1274
1513
  */
1275
1514
  declare class RelDivider extends LitElement {
1276
1515
  /** Axis of the rule: `horizontal` spans the full width with vertical margin; `vertical` is a full-height line with horizontal margin (the parent needs a defined height or a flex row). Mirrored to `aria-orientation` on the host. */
@@ -1352,12 +1591,23 @@ declare class RelDrawer extends LitElement {
1352
1591
  private previousFocus;
1353
1592
  private startY;
1354
1593
  private currentY;
1594
+ /** Scrim opacity at rest, sampled once per drag so the fade starts from it. */
1595
+ private scrimRestingOpacity;
1355
1596
  private originalBodyOverflow;
1356
1597
  private originalBodyPaddingRight;
1357
1598
  static styles: lit.CSSResult;
1358
1599
  connectedCallback(): void;
1359
1600
  disconnectedCallback(): void;
1360
1601
  updated(changed: Map<string, any>): void;
1602
+ /**
1603
+ * The footer divider is conditional: short content (a confirmation message)
1604
+ * has no divider, but a body that scrolls does — it is a scroll affordance,
1605
+ * not decoration (designer, confirmed). CSS cannot ask whether an element
1606
+ * scrolls, so it is measured and reflected onto the host as `body-scrolls`,
1607
+ * re-measured whenever the body or its contents resize.
1608
+ */
1609
+ private bodyScrollObserver?;
1610
+ private watchBodyScroll;
1361
1611
  private handleOpen;
1362
1612
  private handleClose;
1363
1613
  private lockScroll;
@@ -1393,7 +1643,7 @@ type KbdSize = "sm" | "md" | "lg" | "small" | "medium" | "large";
1393
1643
  declare class RelKbd extends LitElement {
1394
1644
  /** Key-cap scale (Figma Size): `small` 20px tall / label-sm, `medium` 24px / label-md, `large` 28px / label-lg; 4px radius at every size. `sm` / `md` / `lg` are the legacy short spellings and stay accepted. */
1395
1645
  size: KbdSize;
1396
- /** Fill / text contrast: `default` = elevated-1 background with label text; `muted` = elevated-2 background with placeholder-tone text for secondary shortcuts. Both are flat with a 1px border. */
1646
+ /** Fill / text contrast: `default` = elevated-1 background with label text; `muted` = elevated-2 background with placeholder-tone text for secondary shortcuts. Both carry a 0.5px stroke and the hard 2px bottom edge Figma draws on the key cap. */
1397
1647
  variant: "default" | "muted";
1398
1648
  /**
1399
1649
  * Fully rounded corners. Logs a one-time console notice when set.
@@ -1443,10 +1693,15 @@ type Orientation = "horizontal" | "vertical";
1443
1693
  type StepperSize = "small" | "large";
1444
1694
  /** Figma "Type": indicator content — default (number, check when done), dot, number, icon. */
1445
1695
  type StepperType = "default" | "dot" | "number" | "icon";
1696
+ /** How the line BETWEEN steps is drawn (Figma ".Line Types"). Distinct from
1697
+ * `StepperType`, which is the indicator disc — both spell one value "dot". */
1698
+ type StepperConnector = "solid" | "dot";
1446
1699
  /** Figma "Typel": label placement relative to the indicator. */
1447
1700
  type LabelPlacement = "top" | "centered" | "inverted";
1448
1701
  interface Step {
1449
1702
  title: string;
1703
+ /** Optional second line under the title (Figma "Subtitle" / "Show Subtitle"). Omit it and the step stays single-line. */
1704
+ subtitle?: string;
1450
1705
  status: StepStatus;
1451
1706
  }
1452
1707
  /**
@@ -1454,13 +1709,15 @@ interface Step {
1454
1709
  * @csspart indicator - The 24px circle (8px for `type="dot"`) that holds the step number, check mark or icon.
1455
1710
  * @csspart track - The 1px connector line from a step to the next one (not rendered on the last step).
1456
1711
  * @csspart label - The step title text.
1712
+ * @csspart subtitle - The optional second line under the title; rendered only when a step sets `subtitle`.
1457
1713
  * @cssprop --rel-stepper-indicator-size - Indicator diameter (default 24px; `type="dot"` is fixed at 8px).
1458
- * @cssprop --rel-stepper-connector-length-lg - Connector length and step gap for `size="large"` in both orientations (default 86px).
1459
- * @cssprop --rel-stepper-connector-length-md - Connector length and step gap for `size="small"` in `orientation="horizontal"` (default 60px).
1460
- * @cssprop --rel-stepper-connector-length-sm - Connector length and step gap for `size="small"` in `orientation="vertical"` (default 42px).
1714
+ * @cssprop --rel-stepper-connector-length - Length of the connector line; the step gap is derived from it by adding Figma's clear space at both ends. Defaults to Figma's fixed geometry: 96px horizontally, 34px vertically with `label-placement="centered"`, 51px with `inverted`.
1715
+ * @cssprop --rel-stepper-connector-length-lg - Superseded by `--rel-stepper-connector-length`, which it still falls back to, so an existing override keeps working.
1716
+ * @cssprop --rel-stepper-connector-length-md - No longer applies. It parameterised the code-only `size` axis, and Figma has one connector geometry rather than a scale.
1717
+ * @cssprop --rel-stepper-connector-length-sm - No longer applies, for the same reason as `-md`.
1461
1718
  */
1462
1719
  declare class RelStepper extends LitElement {
1463
- /** The steps in order, each `{ title: 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. */
1720
+ /** 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. */
1464
1721
  steps: Step[];
1465
1722
  /**
1466
1723
  * Legacy label placement (attribute `textalign`): `below` (default) puts the label under the indicator, `right` puts it beside the indicator. Ignored whenever `label-placement` is set.
@@ -1469,13 +1726,40 @@ declare class RelStepper extends LitElement {
1469
1726
  textAlign: TextAlign;
1470
1727
  /** Layout axis: `horizontal` (default) lays the steps out in a centred row; `vertical` stacks them. */
1471
1728
  orientation: Orientation;
1472
- /** `large` (default) or `small`: controls the label type (body-md vs body-sm) and the connector length / step gap (see the `--rel-stepper-connector-length-*` hooks). */
1729
+ /**
1730
+ * No longer has any effect. It used to set the connector length and step gap
1731
+ * (86px for `large`, 60 / 42px for `small`) and to swap the step label to
1732
+ * body/sm/regular at `small`.
1733
+ * @deprecated Figma has no Size axis on the Stepper — the connector geometry
1734
+ * is fixed — so this was a code-only scale. It stays accepted so existing
1735
+ * markup keeps working, and now resolves to the one Figma geometry. Retune it
1736
+ * with `--rel-stepper-connector-length` instead. Logs a one-time notice.
1737
+ */
1473
1738
  size: StepperSize;
1474
- /** 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). */
1739
+ /**
1740
+ * Figma "Type" axis — what the INDICATOR shows: `default` (step number, check
1741
+ * mark once done), `dot` (8px dot, no glyph), `number` (always the number) or
1742
+ * `icon` (always the check mark).
1743
+ *
1744
+ * Note `type="dot"` and `connector="dot"` are different axes that both use the
1745
+ * word "dot": this one replaces the numbered disc, `connector` dashes the line
1746
+ * BETWEEN discs. They are independent and can be combined.
1747
+ */
1475
1748
  type: StepperType;
1749
+ /**
1750
+ * Figma ".Line Types" axis — how the connector line between steps is drawn:
1751
+ * `solid` (default, an unbroken 1px rule) or `dot` (a 1px rule dashed 2px on,
1752
+ * 2px off).
1753
+ *
1754
+ * Not to be confused with `type="dot"`, which is the INDICATOR disc. This axis
1755
+ * only ever affects the line between steps, and it applies to both
1756
+ * orientations.
1757
+ */
1758
+ connector: StepperConnector;
1476
1759
  /** 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. */
1477
1760
  labelPlacement?: LabelPlacement;
1478
1761
  static styles: lit.CSSResult;
1762
+ protected willUpdate(changed: Map<string, unknown>): void;
1479
1763
  private isDone;
1480
1764
  private isProgress;
1481
1765
  private isDisabled;
@@ -1497,7 +1781,11 @@ interface RadioOption {
1497
1781
  * Ignored when `icon` is also set. */
1498
1782
  avatar?: string;
1499
1783
  }
1784
+ /** Control scale, spelled out. */
1500
1785
  type RadioSize = "small" | "medium" | "large";
1786
+ /** Alternative spellings, not deprecations — the short names work too. */
1787
+ type RadioSizeAlias = "sm" | "md" | "lg";
1788
+ type RadioSizeInput = RadioSize | RadioSizeAlias;
1501
1789
  type RadioDensity = "compact" | "comfortable";
1502
1790
  type RadioAlignment = "leading" | "trailing";
1503
1791
  /**
@@ -1518,11 +1806,21 @@ declare class RelRadio extends LitElement {
1518
1806
  /** Layout axis of the option list. `"vertical"` (default) stacks options in
1519
1807
  * a column; `"horizontal"` lays them out in a wrapping row. */
1520
1808
  orientation: Orientation;
1521
- /** Control scale (Figma Size). `"medium"` (default, Figma M) = 20px control
1522
- * with body-lg labels; `"large"` (Figma L) = 24px control with body-xl
1523
- * labels; `"small"` (Figma S) = 20px control with body-md labels — S and M
1524
- * share the control size in the spec, only typography steps down. */
1525
- size: RadioSize;
1809
+ /** Control scale, matching Figma's Size axis. `"small"` and `"medium"`
1810
+ * (default) both draw a 20px control — they differ by TYPE, body-md labels
1811
+ * against body-lg — and `"large"` steps the control to 24px with body-xl
1812
+ * labels. Supporting text sits one body step below the label at every
1813
+ * size (body-sm / md / lg, i.e. 12 / 14 / 16px). `sm` / `md` / `lg` are
1814
+ * alternative spellings and stay supported — normalised to the full words,
1815
+ * not deprecated.
1816
+ *
1817
+ * Small and Medium sharing a control is Figma's design, not an oversight:
1818
+ * verified against the live Published DS file on 2026-09-25, `.Radio
1819
+ * Control` measures 20x20 in all 15 Small variants and all 15 Medium ones.
1820
+ * v1.4.8 briefly gave Small its own 16px control on the mistaken premise
1821
+ * that the ramp was 16/20/24; 1.4.9 reverts that. Do not reintroduce it
1822
+ * without re-reading the live file. */
1823
+ size: RadioSizeInput;
1526
1824
  /** Supporting-text layout (Figma Density). `"compact"` (default) puts the
1527
1825
  * supporting text on its own line under the row, indented past the control;
1528
1826
  * `"comfortable"` stacks label + supporting text beside the control. */
@@ -1559,6 +1857,11 @@ declare class RelRadio extends LitElement {
1559
1857
  private _onTouched;
1560
1858
  static styles: lit.CSSResult;
1561
1859
  protected willUpdate(changed: PropertyValues<this>): void;
1860
+ /** Whether the author wrote `density` themselves, as opposed to inheriting
1861
+ * the default. Read once at connect: Lit reflects during update, which is
1862
+ * after this runs, so the attribute is present here only if authored. */
1863
+ private densityExplicit;
1864
+ connectedCallback(): void;
1562
1865
  /** Trailing has no compact layout in the spec — trailing wins. */
1563
1866
  private get effectiveDensity();
1564
1867
  private onChange;
@@ -1574,7 +1877,15 @@ declare class RelRadio extends LitElement {
1574
1877
  render(): lit_html.TemplateResult<1>;
1575
1878
  }
1576
1879
 
1577
- type MenuType = "default" | "checkbox" | "arrow" | "select-option" | "fixed" | "fit";
1880
+ /**
1881
+ * Row shape (Figma ".Menu Item" Type): list | checkbox | select-option.
1882
+ * `default` is the deprecated spelling of `list`. `arrow` is a code-only
1883
+ * extension with no Figma counterpart — it turns on the submenu flyout.
1884
+ * `fixed` / `fit` are deprecated here and now live on `width`.
1885
+ */
1886
+ type MenuType = "list" | "checkbox" | "select-option" | "arrow" | "default" | "fixed" | "fit";
1887
+ /** Sizing mode (Figma "Menu" Type). Both modes are --rel-menu-width wide; the axis varies the HEIGHT: fixed pins the panel to --rel-menu-height and scrolls, fit hugs its rows. */
1888
+ type MenuWidth = "fixed" | "fit";
1578
1889
  /** Figma ".Menu Item" Size: large (48px rows) | medium (40px) | small (32px). */
1579
1890
  type MenuSize = "large" | "medium" | "small";
1580
1891
  interface MenuItem {
@@ -1607,15 +1918,36 @@ declare class RelMenuItem extends LitElement {
1607
1918
  * rows, checkbox rows, radio-style select-option rows, or hover/keyboard submenus.
1608
1919
  * @csspart list - The `role="menu"` root list (a light-DOM `[part="list"]` attribute)
1609
1920
  * @csspart panel - A submenu flyout (`type="arrow"` only; light-DOM `[part="panel"]` attribute)
1610
- * @cssprop --rel-menu-width - Width of the menu when `type="fixed"`; default 300px
1921
+ * @cssprop --rel-menu-width - Width of the menu in BOTH sizing modes; default 300px
1922
+ * @cssprop --rel-menu-height - Height of the menu when `width="fixed"`, which then scrolls; default 500px. `fit` ignores it and hugs its rows.
1611
1923
  */
1612
1924
  declare class RelMenu extends LitElement {
1613
- /** Menu behaviour / width mode (Figma Type). `default` = single-select rows
1614
- * (`rel-select`); `checkbox` = toggling rows with a checkbox (`rel-check`);
1615
- * `select-option` = single-select rows with a radio control; `arrow` = rows whose
1616
- * `submenu` opens a flyout (`rel-submenu-select`); `fixed` / `fit` behave like
1617
- * `default` but size the menu to `--rel-menu-width` (300px) or to its content. */
1925
+ /**
1926
+ * Row shape (Figma ".Menu Item" Type). `list` (default) = plain rows;
1927
+ * `checkbox` = toggling rows with a checkbox; `select-option` = single-select
1928
+ * rows with a trailing check.
1929
+ * `arrow` is a code-only extension with no Figma counterpart: rows whose
1930
+ * `submenu` opens a flyout. Because it rides this axis, submenus cannot
1931
+ * currently be combined with checkbox or select-option rows — see the README.
1932
+ * DEPRECATED values, all still working: `default` is the old spelling of
1933
+ * `list`; `fixed` / `fit` were width modes and now set `width` instead,
1934
+ * leaving the row shape at `list`.
1935
+ */
1618
1936
  type: MenuType;
1937
+ /**
1938
+ * Sizing mode (Figma "Menu" Type). Both modes are `--rel-menu-width` (300px)
1939
+ * wide — Figma's Fixed and Fit variants are both 300px — and the axis varies
1940
+ * the HEIGHT: `fixed` pins the panel to `--rel-menu-height` (500px) and
1941
+ * scrolls, `fit` hugs its rows. `fit` previously varied the width instead,
1942
+ * which was the wrong dimension. This was also fused into `type`, where
1943
+ * choosing a width silently forced plain rows and choosing a row shape
1944
+ * silently forced the default width.
1945
+ */
1946
+ width?: MenuWidth;
1947
+ /** Row shape actually rendered: the deprecated width values fall back to `list`. */
1948
+ private get resolvedType();
1949
+ /** Width mode actually applied: explicit `width` wins, else the deprecated `type` spelling. */
1950
+ private get resolvedWidth();
1619
1951
  /**
1620
1952
  * Row size (Figma ".Menu Item" Size, 6326:616): large = 48px rows with
1621
1953
  * body-lg labels (Figma default), medium = 40px / body-md, small = 32px /
@@ -1632,7 +1964,7 @@ declare class RelMenu extends LitElement {
1632
1964
  private _items;
1633
1965
  /** Stable id prefix for aria-activedescendant targets. */
1634
1966
  private menuId;
1635
- static readonly _css = "\n :host {\n color: var(--rel-text-default);\n display: inline-block;\n }\n\n .rel-menu {\n position: relative;\n display: inline-block;\n background: var(--rel-bg-neutral);\n color: var(--rel-text-default);\n /* Audit 17: 8px (border-radius/sm), was 16px; Figma Menu (6326:2931) = shadow/md */\n border-radius: var(--rel-border-radius-sm);\n box-shadow: var(--rel-shadows-md);\n min-width: 220px;\n /* No vertical padding: items fill edge-to-edge so a first/last-item hover\n reaches the menu's top/bottom. NOT overflow:hidden \u2014 that clips the\n type=arrow flyout (position:absolute at left:100%). Instead the first\n and last items carry the outer corner radius so hover backgrounds clip\n to the rounded shape without any overflow. */\n padding: 0;\n outline: none;\n overflow: visible;\n font-family: var(--rel-body-lg-regular-font-family);\n font-weight: var(--rel-body-lg-regular-font-weight);\n font-size: var(--rel-body-lg-regular-font-size);\n line-height: var(--rel-body-lg-regular-line-height);\n letter-spacing: var(--rel-body-lg-regular-letter-spacing);\n }\n\n .rel-menu:focus-visible,\n :host(:focus-visible) .rel-menu {\n outline: 2px solid var(--rel-border-focus);\n outline-offset: 2px;\n }\n\n /* Rows follow Figma \".Menu Item\" (6326:616) per size:\n Large 48px = pad 12/12 + body-lg (24 line-height)\n Medium 40px = pad 8/12 + body-md (22)\n Small 32px = pad 4/8 + body-sm (20)\n (audit 17a \u2014 before 1.4.7 every row was 36px: pad 8/12 + a 20px line-height) */\n .rel-menu-item {\n position: relative;\n display: flex;\n align-items: center;\n gap: var(--rel-spacing-md);\n padding: var(--rel-spacing-sm) var(--rel-spacing-sm);\n line-height: var(--rel-body-lg-regular-line-height);\n min-height: 48px;\n cursor: pointer;\n transition: background 0.15s ease, color 0.15s ease;\n user-select: none;\n box-sizing: border-box;\n }\n\n /* Light-DOM component: :host never matches here, so size rules key off the\n reflected attribute on the <rel-menu> element itself. */\n rel-menu[size=\"medium\"] .rel-menu,\n rel-menu[size=\"medium\"] .rel-submenu {\n font-family: var(--rel-body-md-regular-font-family);\n font-weight: var(--rel-body-md-regular-font-weight);\n font-size: var(--rel-body-md-regular-font-size);\n line-height: var(--rel-body-md-regular-line-height);\n letter-spacing: var(--rel-body-md-regular-letter-spacing);\n }\n\n /* Figma Medium / Small frames are fixed at 40 / 32 (content sums to 38 / 28) */\n rel-menu[size=\"medium\"] .rel-menu-item {\n padding: var(--rel-spacing-xs) var(--rel-spacing-sm);\n line-height: var(--rel-body-md-regular-line-height);\n min-height: 40px;\n }\n\n rel-menu[size=\"small\"] .rel-menu,\n rel-menu[size=\"small\"] .rel-submenu {\n font-family: var(--rel-body-sm-regular-font-family);\n font-weight: var(--rel-body-sm-regular-font-weight);\n font-size: var(--rel-body-sm-regular-font-size);\n line-height: var(--rel-body-sm-regular-line-height);\n letter-spacing: var(--rel-body-sm-regular-letter-spacing);\n }\n\n rel-menu[size=\"small\"] .rel-menu-item {\n padding: var(--rel-spacing-2xs) var(--rel-spacing-xs);\n line-height: var(--rel-body-sm-regular-line-height);\n min-height: 32px;\n }\n\n /* First/last items carry the menu's outer radius so their hover fill clips\n to the rounded corners (replaces the removed overflow:hidden). */\n .rel-menu > .rel-menu-item:first-child {\n border-top-left-radius: var(--rel-border-radius-sm);\n border-top-right-radius: var(--rel-border-radius-sm);\n }\n\n .rel-menu > .rel-menu-item:last-child {\n border-bottom-left-radius: var(--rel-border-radius-sm);\n border-bottom-right-radius: var(--rel-border-radius-sm);\n }\n\n /* Figma states (6326:616): Hover = action overlay 0.05, Pressed = 0.10\n (nearest token: overlay-pressed), Active/selected list row = the hover\n overlay + medium label; Checkbox / Select Option rows keep no fill when\n selected. Disabled = 40% opacity. (audit 17) */\n .rel-menu-item:hover {\n background: var(--rel-action-overlay-hover);\n }\n\n .rel-menu-item:active {\n background: var(--rel-action-overlay-pressed);\n }\n\n .rel-menu-item.active {\n background: var(--rel-action-overlay-hover);\n font-weight: var(--rel-body-lg-medium-font-weight);\n }\n\n .rel-menu.type-checkbox .rel-menu-item.active,\n .rel-menu.type-select-option .rel-menu-item.active {\n background: transparent;\n }\n\n .rel-menu.type-checkbox .rel-menu-item.active:hover,\n .rel-menu.type-select-option .rel-menu-item.active:hover {\n background: var(--rel-action-overlay-hover);\n }\n\n .rel-menu-item.disabled {\n background-color: transparent;\n opacity: 0.4;\n /* Keep the item pointer-interactive so cursor:not-allowed actually\n renders; clicks are already neutralised by the item.disabled guard in\n onItemClick/onItemEnter. pointer-events:none here would make the row\n transparent to the pointer and suppress the cursor. */\n cursor: not-allowed;\n }\n\n /* [data-focused] (presence, not =\"true\"): the ?data-focused boolean\n binding renders the attribute with an EMPTY value, so a =\"true\"\n selector never matches. */\n .rel-menu-item[data-focused] {\n outline: 2px solid var(--rel-border-focus);\n outline-offset: -2px;\n }\n\n /* Width modes (Figma Type=Fixed / Type=Fit) */\n .rel-menu.fixed {\n width: var(--rel-menu-width, 300px);\n min-width: 0;\n box-sizing: border-box;\n }\n\n .rel-menu.fit {\n min-width: 0;\n width: max-content;\n }\n\n /* Checkbox (Type=Checkbox) / radio (Type=Select Option) rows render the real\n rel-checkbox / rel-radio. pointer-events:none keeps the whole row the click\n target so onItemClick stays the single source of truth. */\n .menu-control {\n pointer-events: none;\n width: 100%;\n }\n\n .menu-label {\n flex: 1 1 auto;\n }\n\n /* Submenu affordance \u2014 same rel-icon chevron the menubar uses. */\n .submenu-chevron {\n margin-left: auto;\n display: inline-flex;\n align-items: center;\n opacity: 0.6;\n pointer-events: none;\n }\n\n /* Shortcut / hint text (Figma body-sm-regular, caption tone) */\n .menu-shortcut,\n ::slotted([slot=\"shortcut\"]) {\n font-family: var(--rel-body-sm-regular-font-family);\n font-weight: var(--rel-body-sm-regular-font-weight);\n font-size: var(--rel-body-sm-regular-font-size);\n line-height: var(--rel-body-sm-regular-line-height);\n letter-spacing: var(--rel-body-sm-regular-letter-spacing);\n color: var(--rel-text-caption);\n margin-left: auto;\n padding-left: var(--rel-spacing-md);\n }\n\n \n .rel-submenu {\n border: 1px solid var(--rel-border-subtle);\n position: absolute;\n top: calc(-1 * var(--rel-spacing-3xs));\n /* flush against the trigger (no gap) so moving the pointer into the flyout\n never crosses a dead zone that would fire the trigger's mouseleave */\n left: 100%;\n min-width: 200px;\n background: var(--rel-bg-neutral);\n color: var(--rel-text-default);\n border-radius: var(--rel-border-radius-sm);\n box-shadow: var(--rel-shadows-md);\n /* padding:0 + overflow:hidden \u2014 safe here (a submenu has no further nested\n flyout to clip) \u2014 so sub-item hovers fill fully and clip to the corners */\n padding: 0;\n overflow: hidden;\n visibility: hidden;\n pointer-events: none;\n z-index: 20;\n }\n\n .rel-submenu.visible {\n visibility: visible;\n pointer-events: auto;\n }\n\n .rel-submenu .rel-menu-item {\n padding: var(--rel-spacing-sm) var(--rel-spacing-sm);\n }\n\n rel-menu[size=\"medium\"] .rel-submenu .rel-menu-item {\n padding: var(--rel-spacing-xs) var(--rel-spacing-sm);\n }\n\n rel-menu[size=\"small\"] .rel-submenu .rel-menu-item {\n padding: var(--rel-spacing-2xs) var(--rel-spacing-xs);\n }\n ";
1967
+ static readonly _css = "\n :host {\n color: var(--rel-text-default);\n display: inline-block;\n }\n\n .rel-menu {\n position: relative;\n display: inline-block;\n background: var(--rel-bg-neutral);\n color: var(--rel-text-default);\n /* Audit 17: 8px (border-radius/sm), was 16px; Figma Menu (6326:2931) = shadow/md */\n border-radius: var(--rel-border-radius-sm);\n box-shadow: var(--rel-shadows-md);\n /* Figma's Menu container is 300px around 288px rows. */\n min-width: 300px;\n /* No vertical padding: items fill edge-to-edge so a first/last-item hover\n reaches the menu's top/bottom. NOT overflow:hidden \u2014 that clips the\n type=arrow flyout (position:absolute at left:100%). Instead the first\n and last items carry the outer corner radius so hover backgrounds clip\n to the rounded shape without any overflow. */\n padding: 0;\n outline: none;\n overflow: visible;\n font-family: var(--rel-body-lg-regular-font-family);\n font-weight: var(--rel-body-lg-regular-font-weight);\n font-size: var(--rel-body-lg-regular-font-size);\n line-height: var(--rel-body-lg-regular-line-height);\n letter-spacing: var(--rel-body-lg-regular-letter-spacing);\n }\n\n .rel-menu:focus-visible,\n :host(:focus-visible) .rel-menu {\n outline: 2px solid var(--rel-border-focus);\n outline-offset: 0;\n }\n\n /* Rows follow Figma \".Menu Item\" (6326:616) per size:\n Large 48px = pad 12/12 + body-lg (24 line-height)\n Medium 40px = pad 8/12 + body-md (22)\n Small 32px = pad 4/8 + body-sm (20)\n (audit 17a \u2014 before 1.4.7 every row was 36px: pad 8/12 + a 20px line-height) */\n .rel-menu-item {\n position: relative;\n display: flex;\n align-items: center;\n gap: var(--rel-spacing-md);\n padding: var(--rel-spacing-sm) var(--rel-spacing-sm);\n line-height: var(--rel-body-lg-regular-line-height);\n min-height: 48px;\n cursor: pointer;\n transition: background 0.15s ease, color 0.15s ease;\n user-select: none;\n box-sizing: border-box;\n /* Figma strokes every item with --rel-border-subtle. Drawn as an inset\n shadow on the BOTTOM edge only (the pattern rel-menubar already uses)\n so the rows read as a divided list rather than 45 boxed cells, and so\n it costs no layout height. */\n box-shadow: inset 0 calc(-1 * var(--rel-stroke-sm)) 0 var(--rel-border-subtle);\n }\n\n .rel-menu-item:last-child {\n box-shadow: none;\n }\n\n /* Light-DOM component: :host never matches here, so size rules key off the\n reflected attribute on the <rel-menu> element itself. */\n rel-menu[size=\"medium\"] .rel-menu,\n rel-menu[size=\"medium\"] .rel-submenu {\n font-family: var(--rel-body-md-regular-font-family);\n font-weight: var(--rel-body-md-regular-font-weight);\n font-size: var(--rel-body-md-regular-font-size);\n line-height: var(--rel-body-md-regular-line-height);\n letter-spacing: var(--rel-body-md-regular-letter-spacing);\n }\n\n /* Figma Medium / Small frames are fixed at 40 / 32 (content sums to 38 / 28) */\n rel-menu[size=\"medium\"] .rel-menu-item {\n padding: var(--rel-spacing-xs) var(--rel-spacing-sm);\n line-height: var(--rel-body-md-regular-line-height);\n min-height: 40px;\n }\n\n rel-menu[size=\"small\"] .rel-menu,\n rel-menu[size=\"small\"] .rel-submenu {\n font-family: var(--rel-body-sm-regular-font-family);\n font-weight: var(--rel-body-sm-regular-font-weight);\n font-size: var(--rel-body-sm-regular-font-size);\n line-height: var(--rel-body-sm-regular-line-height);\n letter-spacing: var(--rel-body-sm-regular-letter-spacing);\n }\n\n rel-menu[size=\"small\"] .rel-menu-item {\n padding: var(--rel-spacing-2xs) var(--rel-spacing-xs);\n line-height: var(--rel-body-sm-regular-line-height);\n min-height: 32px;\n }\n\n /* First/last items carry the menu's outer radius so their hover fill clips\n to the rounded corners (replaces the removed overflow:hidden). */\n .rel-menu > .rel-menu-item:first-child {\n border-top-left-radius: var(--rel-border-radius-sm);\n border-top-right-radius: var(--rel-border-radius-sm);\n }\n\n .rel-menu > .rel-menu-item:last-child {\n border-bottom-left-radius: var(--rel-border-radius-sm);\n border-bottom-right-radius: var(--rel-border-radius-sm);\n }\n\n /* Figma states (6326:616): Hover = action overlay 0.05, Pressed = 0.10\n (nearest token: overlay-pressed), Active/selected list row = the hover\n overlay + medium label; Checkbox / Select Option rows keep no fill when\n selected. Disabled = 40% opacity. (audit 17) */\n .rel-menu-item:hover {\n background: var(--rel-action-overlay-hover);\n }\n\n .rel-menu-item:active {\n background: var(--rel-action-overlay-pressed);\n }\n\n .rel-menu-item.active {\n background: var(--rel-action-overlay-hover);\n }\n\n /* The active weight bump follows the row's own size, and applies to plain\n rows only \u2014 a checkbox row carries its state in the control, and Figma\n does not bold its label. */\n .rel-menu:not(.type-checkbox) .rel-menu-item.active {\n font-weight: var(--rel-body-lg-medium-font-weight);\n }\n\n rel-menu[size=\"medium\"] .rel-menu:not(.type-checkbox) .rel-menu-item.active {\n font-weight: var(--rel-body-md-medium-font-weight);\n }\n\n /* Small's active row is Figma's mixed tuple: an xs-medium WEIGHT with\n sm-medium METRICS on the sm-regular family. Large and Medium step only\n their weight and keep regular metrics, which is why they are one line\n each and this one is four. */\n rel-menu[size=\"small\"] .rel-menu:not(.type-checkbox) .rel-menu-item.active {\n font-weight: var(--rel-body-xs-medium-font-weight);\n font-size: var(--rel-body-sm-medium-font-size);\n line-height: var(--rel-body-sm-medium-line-height);\n letter-spacing: var(--rel-body-sm-medium-letter-spacing);\n }\n\n .rel-menu.type-checkbox .rel-menu-item.active,\n .rel-menu.type-select-option .rel-menu-item.active {\n background: transparent;\n }\n\n .rel-menu.type-checkbox .rel-menu-item.active:hover,\n .rel-menu.type-select-option .rel-menu-item.active:hover {\n background: var(--rel-action-overlay-hover);\n }\n\n .rel-menu-item.disabled {\n background-color: transparent;\n opacity: 0.4;\n /* Keep the item pointer-interactive so cursor:not-allowed actually\n renders; clicks are already neutralised by the item.disabled guard in\n onItemClick/onItemEnter. pointer-events:none here would make the row\n transparent to the pointer and suppress the cursor. */\n cursor: not-allowed;\n }\n\n /* [data-focused] (presence, not =\"true\"): the ?data-focused boolean\n binding renders the attribute with an EMPTY value, so a =\"true\"\n selector never matches. */\n /* Inset ring: items run the full width of the panel and butt against its\n padding, so an outside ring would cross the panel edge. rel-menu has no\n State=Focused variant in Figma; kept deliberately. */\n .rel-menu-item[data-focused] {\n outline: 2px solid var(--rel-border-focus);\n outline-offset: -2px;\n }\n\n /* Width modes (Figma Type=Fixed / Type=Fit). BOTH are 300px wide \u2014\n COMPONENT \"Type=Fixed\" is 300x500 sizing=FIXED/FIXED and\n COMPONENT \"Type=Fit\" is 300x48 sizing=FIXED/HUG \u2014 so the axis varies the\n HEIGHT, not the width: Fixed pins a scrolling panel, Fit hugs its rows.\n This had it inverted: fit varied the width (max-content) and neither mode\n constrained the height, so the two modes differed on the wrong dimension. */\n .rel-menu.fixed {\n width: var(--rel-menu-width, 300px);\n height: var(--rel-menu-height, 500px);\n overflow-y: auto;\n min-width: 0;\n box-sizing: border-box;\n }\n\n .rel-menu.fit {\n width: var(--rel-menu-width, 300px);\n min-width: 0;\n box-sizing: border-box;\n }\n\n /* Checkbox (Type=Checkbox) / radio (Type=Select Option) rows render the real\n rel-checkbox / rel-radio. pointer-events:none keeps the whole row the click\n target so onItemClick stays the single source of truth. */\n .menu-control {\n pointer-events: none;\n width: 100%;\n }\n\n .menu-label {\n flex: 1 1 auto;\n }\n\n /* Submenu affordance \u2014 same rel-icon chevron the menubar uses.\n\n No opacity here: verified against the live Figma file, \"Chevron Right\"\n is opacity 1 on both the instance and its vector, on every State and\n Size, bound to the same colour variable as the row label beside it.\n The 0.6 that used to sit here dulled it to a mid grey.\n\n The colour is declared rather than inherited on purpose. rel-menu\n renders to the LIGHT DOM (createRenderRoot returns this), so these\n selectors are unscoped globals and an inherited colour would travel\n through the consumer's page cascade \u2014 an app setting a colour on any\n ancestor would have been repainting this chevron. */\n .submenu-chevron {\n margin-left: auto;\n display: inline-flex;\n align-items: center;\n color: var(--rel-text-default);\n pointer-events: none;\n }\n\n /* Shortcut / hint text (Figma body-sm-regular, caption tone) */\n .menu-shortcut,\n ::slotted([slot=\"shortcut\"]) {\n font-family: var(--rel-body-sm-regular-font-family);\n font-weight: var(--rel-body-sm-regular-font-weight);\n font-size: var(--rel-body-sm-regular-font-size);\n line-height: var(--rel-body-sm-regular-line-height);\n letter-spacing: var(--rel-body-sm-regular-letter-spacing);\n color: var(--rel-text-caption);\n margin-left: auto;\n padding-left: var(--rel-spacing-md);\n }\n\n \n /* No border: the flyout is the same surface as the parent menu, which has\n none \u2014 overlapping surfaces are separated by shadow. Min-width tracks the\n parent's 300px rather than a narrower 200px. */\n .rel-submenu {\n position: absolute;\n top: calc(-1 * var(--rel-spacing-3xs));\n /* flush against the trigger (no gap) so moving the pointer into the flyout\n never crosses a dead zone that would fire the trigger's mouseleave */\n left: 100%;\n min-width: 300px;\n background: var(--rel-bg-neutral);\n color: var(--rel-text-default);\n border-radius: var(--rel-border-radius-sm);\n box-shadow: var(--rel-shadows-md);\n /* padding:0 + overflow:hidden \u2014 safe here (a submenu has no further nested\n flyout to clip) \u2014 so sub-item hovers fill fully and clip to the corners */\n padding: 0;\n overflow: hidden;\n visibility: hidden;\n pointer-events: none;\n z-index: 20;\n }\n\n .rel-submenu.visible {\n visibility: visible;\n pointer-events: auto;\n }\n\n .rel-submenu .rel-menu-item {\n padding: var(--rel-spacing-sm) var(--rel-spacing-sm);\n }\n\n rel-menu[size=\"medium\"] .rel-submenu .rel-menu-item {\n padding: var(--rel-spacing-xs) var(--rel-spacing-sm);\n }\n\n rel-menu[size=\"small\"] .rel-submenu .rel-menu-item {\n padding: var(--rel-spacing-2xs) var(--rel-spacing-xs);\n }\n ";
1636
1968
  createRenderRoot(): this;
1637
1969
  connectedCallback(): void;
1638
1970
  disconnectedCallback(): void;
@@ -1666,7 +1998,7 @@ declare class RelMenubar extends LitElement {
1666
1998
  */
1667
1999
  bindShortcuts: boolean;
1668
2000
  /**
1669
- * Width mode (Figma "Menu Bar" Type, 8699:4): `fit` sizes the bar to its triggers, `fixed` stretches it across its container (override the stretched width with `--rel-menubar-width`). Added in 1.4.8; the default stays `fit`, which is how the bar has always rendered.
2001
+ * Width mode (Figma "Menu Bar" Type, 8699:4): `fit` sizes the bar to its triggers, `fixed` stretches it across its container (override the stretched width with `--rel-menubar-width`). The default stays `fit`, which is how the bar has always rendered.
1670
2002
  */
1671
2003
  type: MenubarType;
1672
2004
  private menus;
@@ -1758,7 +2090,7 @@ declare class RelOtp extends LitElement {
1758
2090
  value: string;
1759
2091
  /** Total number of cells (characters in the code). */
1760
2092
  length: number;
1761
- /** Cells per visual group; a separator renders between groups. `0` (or `show-separator` off) renders one ungrouped row. */
2093
+ /** Cells per visual group; a separator renders between groups. Grouping applies only when `length` divides evenly by this — otherwise the row renders ungrouped, because a ragged final group would put a separator before a lone cell. So the default `length="6"` is 3–3, `length="4"` is one ungrouped row of four, and `group-size="2"` with `length="4"` is 2–2. `0` (or `show-separator` off) also renders one ungrouped row. */
1762
2094
  groupSize: number;
1763
2095
  /** Placeholder character shown in every empty cell. */
1764
2096
  placeholder: string;
@@ -1775,7 +2107,7 @@ declare class RelOtp extends LitElement {
1775
2107
  type: OtpType;
1776
2108
  /** Renders the cells as `type="password"` so entered characters are masked. */
1777
2109
  mask: boolean;
1778
- /** Disables every cell and dims the control to 0.6 opacity. */
2110
+ /** Disables every cell and dims the control to 0.4 opacity (cell fill and border unchanged, as Figma specifies). */
1779
2111
  disabled: boolean;
1780
2112
  /** Makes every cell read-only: the value is shown but typing, paste and keyboard editing are ignored. */
1781
2113
  readonly: boolean;
@@ -1793,7 +2125,7 @@ declare class RelOtp extends LitElement {
1793
2125
  ariaLabel: string | null;
1794
2126
  /** Error styling (danger border on every cell) and `aria-invalid="true"`. */
1795
2127
  invalid: boolean;
1796
- /** Renders the dash separator between groups. Boolean attribute: its presence means true — set the property to `false` to hide separators. */
2128
+ /** Renders the dash separator between groups. Boolean attribute: its presence means true — set the property to `false` to hide separators. Even when on, separators appear only if `length` divides evenly by `group-size`; see `groupSize`. */
1797
2129
  showSeparator: boolean;
1798
2130
  /** Cell size on the shared field scale (square, border-box): `small` 32px, `medium` 40px, `large` 48px (the Figma OTP Input cell). `sm` / `md` / `lg` are deprecated aliases of the full words. */
1799
2131
  size: OtpSize;
@@ -1853,8 +2185,17 @@ declare class RelPagination extends LitElement {
1853
2185
  /** Items per page, used to compute the page count from `totalItems` and the "start–end" range in the count-text readout. Attribute `perPage`, also read once as `per-page` on connect. */
1854
2186
  perPage: number;
1855
2187
  /** Shows a "Viewing page X of Y" caption under the extended layout (ignored by the other types). */
2188
+ /**
2189
+ * DELIBERATE DIVERGENCE — do not flip this default to `true`. Figma's
2190
+ * `Type=Extended` has no boolean for the caption: the "Viewing page 1 of 10"
2191
+ * text is a fixed child of both Extended variants and is load-bearing for
2192
+ * their 78px height (40 + spacing-md + 22). Here it stays opt-in. Ruled by
2193
+ * the user: our behaviour stands and the DESIGN FILE follows our docs.
2194
+ * Defaulting it on would silently add 38px to every existing extended
2195
+ * pagination.
2196
+ */
1856
2197
  showLabel: boolean;
1857
- /** Dims the whole control to 50% opacity, shows a `not-allowed` cursor and ignores every page change. */
2198
+ /** Dims the whole control to 40% opacity, shows a `not-allowed` cursor and ignores every page change. */
1858
2199
  disabled: boolean;
1859
2200
  /** Reserved for link-mode pagination. Currently accepted but not rendered: every page control is a `<button>`, so navigate in your `rel-change` handler instead. */
1860
2201
  hrefTemplate: string;
@@ -1888,7 +2229,7 @@ type AlignType = "start" | "center" | "end";
1888
2229
  * @slot content - The panel body, usually a `<rel-popover-content>`; rendered inside the fixed, top-layer wrapper only while open.
1889
2230
  * @csspart arrow - The 14×14 chevron box rendered on the panel edge facing the trigger when `arrow` is set (absent otherwise).
1890
2231
  * @cssprop --rel-popover-arrow-bg - Fill of the `arrow` chevron (default `--rel-bg-neutral`, matching `<rel-popover-content>`).
1891
- * @cssprop --rel-popover-arrow-border - Stroke of the `arrow` chevron's two slanted edges (default `--rel-border-default`).
2232
+ * @cssprop --rel-popover-arrow-border - **Deprecated.** Stroke of the `arrow` chevron's two slanted edges. The panel no longer draws a border — overlapping surfaces are separated by shadow — so this defaults to `transparent` and the chevron has no edge. Set it to restore a stroke.
1892
2233
  */
1893
2234
  declare class RelPopover extends LitElement {
1894
2235
  /** Controlled open state. Leave unset for uncontrolled mode; once set (true or false) the component only emits `rel-open-change` and you must update `open` yourself. */
@@ -1974,6 +2315,11 @@ declare global {
1974
2315
  }
1975
2316
  }
1976
2317
 
2318
+ /** Figma Size axis, spelled out. */
2319
+ type PopoverContentSize = "small" | "medium" | "large";
2320
+ /** Alternative spellings, not deprecations — the short names keep working. */
2321
+ type PopoverContentSizeAlias = "sm" | "md" | "lg";
2322
+ type PopoverContentSizeInput = PopoverContentSize | PopoverContentSizeAlias;
1977
2323
  /**
1978
2324
  * Styled panel body for `<rel-popover>` (Figma "Popover" surface): neutral background, 1px border, 8px radius, shadow-300, max-width 400px, with heading / paragraph typography for slotted content. Place it in the popover's `content` slot; any other element works there too.
1979
2325
  * @slot - The panel content (headings, paragraphs, form controls, buttons).
@@ -1982,8 +2328,12 @@ declare global {
1982
2328
  declare class RelPopoverContent extends LitElement {
1983
2329
  /** Accessible name applied to the inner `role="dialog"` box (attribute `aria-label`); describe what the panel is for. */
1984
2330
  ariaLabel: string;
1985
- /** Figma Size axis (S/M/L): panel padding 12/16/20px. */
1986
- size: "sm" | "md" | "lg";
2331
+ /** Figma Size axis: panel padding 12px (`small`), 16px (`medium`, default) or 20px (`large`). `sm` / `md` / `lg` are alternative spellings and stay supported — they are normalised to the full words, not deprecated. */
2332
+ size: PopoverContentSizeInput;
2333
+ /** Alternative spellings, NOT deprecations — no console notice. Normalising
2334
+ * keeps the reflected attribute canonical, so the :host([size=…]) rules only
2335
+ * need the full words. */
2336
+ protected willUpdate(changed: Map<string, unknown>): void;
1987
2337
  static styles: lit.CSSResult;
1988
2338
  render(): lit_html.TemplateResult<1>;
1989
2339
  }
@@ -1995,7 +2345,7 @@ type ProgressMode = "linear-determinate" | "linear-indeterminate" | "circular-de
1995
2345
  * @csspart indicator - The filled / sliding bar inside the linear track (linear modes only)
1996
2346
  */
1997
2347
  declare class RelProgress extends LitElement {
1998
- /** Shape and behaviour: `linear-*` is a full-width 6px bar, `circular-*` a 48px ring; `*-determinate` fills to `value`, `*-indeterminate` loops an animation and ignores `value`. */
2348
+ /** Shape and behaviour: `linear-*` is a full-width 2px bar, `circular-*` a 36px ring; `*-determinate` fills to `value`, `*-indeterminate` loops an animation and ignores `value`. */
1999
2349
  mode: ProgressMode;
2000
2350
  /** Completion percentage 0–100 for the determinate modes; out-of-range values are clamped before rendering and before `aria-valuenow`. */
2001
2351
  value: number;
@@ -2107,16 +2457,24 @@ interface SegmentOption {
2107
2457
  disabled?: boolean;
2108
2458
  /** `rel-icon` name rendered before the label (Figma "Left Icon"), sized 16 / 18 / 20 with the control. */
2109
2459
  icon?: string;
2460
+ /**
2461
+ * Trailing badge (Figma "Show Badge" / "Badge"), rendered after the label
2462
+ * inside the segment's own 8px gap. A number or string shows the count at
2463
+ * `medium` and `large`; at `small` Figma switches to the 6px dot, so the
2464
+ * value is not displayed there. Use `true` for a dot at any size.
2465
+ */
2466
+ badge?: number | string | boolean;
2110
2467
  }
2111
2468
  /**
2112
2469
  * Single-select segmented button group: renders one `<button role="radio">` per entry in `options` on a 2px-inset track (`role="radiogroup"`), with exactly one segment active at a time.
2113
2470
  * @csspart base - The `role="radiogroup"` track that lays out the segments.
2114
2471
  * @csspart option - Each segment's native `<button role="radio">`; the active one carries the `active` class and `aria-checked="true"`.
2115
2472
  * @csspart icon - A segment's optional leading `rel-icon` element.
2473
+ * @csspart badge - A segment's optional trailing `rel-badge`; rendered only when the option sets `badge`.
2116
2474
  * @csspart label - The wrapper around a segment's label text.
2117
2475
  */
2118
2476
  declare class RelSegmentedButton extends LitElement {
2119
- /** The segments in order, each `{ label: string; value: string; disabled?: boolean; icon?: string }`. Set as a property (or a JSON array attribute); a disabled entry is rendered but cannot be selected, and `icon` names a `rel-icon` drawn before the label. */
2477
+ /** The segments in order, each `{ label: string; value: string; disabled?: boolean; icon?: string; badge?: number | string | boolean }`. Set as a property (or a JSON array attribute); a disabled entry is rendered but cannot be selected, and `icon` names a `rel-icon` drawn before the label. */
2120
2478
  options: SegmentOption[];
2121
2479
  /** `value` of the currently selected segment. Updated by the component on click / arrow keys; set it to pre-select a segment (an unknown value selects nothing). */
2122
2480
  active: string;
@@ -2132,6 +2490,13 @@ declare class RelSegmentedButton extends LitElement {
2132
2490
  * one Tab stop (roving tabindex on the selected segment). */
2133
2491
  private onKeyDown;
2134
2492
  private renderSegment;
2493
+ /**
2494
+ * Figma's Show Badge axis: a 20x20 numeric badge at medium and large, and a
2495
+ * 6px dot at small — where the count is not shown at all, so a numeric value
2496
+ * degrades to a dot rather than being squeezed in. `badge: true` asks for the
2497
+ * dot at any size.
2498
+ */
2499
+ private renderBadge;
2135
2500
  render(): lit_html.TemplateResult<1>;
2136
2501
  }
2137
2502
 
@@ -2159,9 +2524,13 @@ type SelectOption = string | SelectOptionObject;
2159
2524
  * @csspart list - Alias of `panel`
2160
2525
  * @csspart input - The search input inside the panel
2161
2526
  * @csspart option - A single-select option row
2162
- * @cssprop --rel-field-label-reserve - Height held for the absent label row under `labelStyle="none"` so the trigger bottom-aligns with labelled siblings; defaults to the label line-height plus the wrapper gap. Set to `0` to collapse the row entirely (toolbars, table cells).
2163
- * @cssprop --rel-select-bg - Background of the trigger, the floating label, the panel and its sticky header
2527
+ * @csspart option-label - A single-select row's first line (the option label)
2528
+ * @csspart option-supporting-text - A single-select row's optional second line; rendered only when the option supplies `supportingText`
2529
+ * @cssprop --rel-field-label-reserve - 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).
2530
+ * @cssprop --rel-select-bg - Background of the trigger, the panel and its sticky header. No longer paints behind the floating label — see `--rel-select-surface`.
2164
2531
  * @cssprop --rel-select-bg-disabled - Trigger background when `state="disabled"`
2532
+ * @cssprop --rel-select-surface - 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. Defaults to `--rel-select-bg`. 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`.
2533
+ * @cssprop --rel-select-surface-disabled - The same strip while `state="disabled"`, where the trigger fill changes. Defaults to `--rel-select-bg-disabled`.
2165
2534
  * @cssprop --rel-select-border-default - Trigger border colour at rest
2166
2535
  * @cssprop --rel-select-border-disabled - Trigger border colour when `state="disabled"`
2167
2536
  * @cssprop --rel-select-border-focus - Trigger border colour on hover, while focused (where it also thickens to 2px) and while the panel is open
@@ -2180,6 +2549,7 @@ type SelectOption = string | SelectOptionObject;
2180
2549
  * @cssprop --rel-select-divider - Hairline under the search field, the Select-all row and every option row
2181
2550
  * @cssprop --rel-select-option-hover - Background of the hovered / keyboard-highlighted / selected option and Select-all row
2182
2551
  * @cssprop --rel-select-option-pressed - Background of an option row while it is being pressed
2552
+ * @cssprop --rel-overlay-fallback-z - 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 rel-select, rel-autocomplete and the other pickers so the whole overlay family can be moved from one place; default 1001.
2183
2553
  */
2184
2554
  declare class RelSelect extends LitElement {
2185
2555
  /** Visible label. Rendered above the trigger (`labelStyle="default"`) or floating inside it; omitted under `labelStyle="none"`. */
@@ -2200,7 +2570,14 @@ declare class RelSelect extends LitElement {
2200
2570
  /**
2201
2571
  * Options to list. Each entry is a plain string (used as both value and label) or
2202
2572
  * `{ value, label?, supportingText?, icon? }`; `icon` is a `rel-icon` name rendered
2203
- * before the label, `supportingText` shows only in multi-select rows.
2573
+ * before the label, `supportingText` renders as a second line in both single- and multi-select rows.
2574
+ *
2575
+ * NOTE — the panel's search matches the LABEL ONLY. `supportingText` is displayed but not
2576
+ * searched, so an option is not findable by its second line here. This differs deliberately from
2577
+ * `rel-autocomplete`, whose filter matches the supporting line as well: there the second line
2578
+ * carries identifying data (a DOT or MC number) that a user types to find the row, whereas a
2579
+ * select's is descriptive ("Read-only", "Can edit, not delete") and matching it would return rows
2580
+ * whose visible label has nothing to do with the query.
2204
2581
  */
2205
2582
  options: SelectOption[];
2206
2583
  /** Enables multi-select: selected values render as removable chips, options as checkboxes, and a Select-all row appears in the panel. */
@@ -2245,6 +2622,16 @@ declare class RelSelect extends LitElement {
2245
2622
  */
2246
2623
  maxVisibleChips: number;
2247
2624
  /** Hides the in-panel search field. Figma's "Single Option" dropdown has no search bar at all — use this for short, non-searchable lists. */
2625
+ /**
2626
+ * DELIBERATE DIVERGENCE — do not default this to `!multiple`. Figma's
2627
+ * `.Menu Dropdown` shows the search row only on `Type=Multioption`; its
2628
+ * `Type=Single Option` panels contain five Select Option items and nothing
2629
+ * else. Here the search row renders for single-select too unless this is set.
2630
+ * Ruled by the user: our behaviour stands and the DESIGN FILE follows our
2631
+ * docs. A long single-select list is unusable without search, and making the
2632
+ * default depend on `multiple` would change the panel of every existing
2633
+ * single-select.
2634
+ */
2248
2635
  noSearch: boolean;
2249
2636
  private open;
2250
2637
  private highlightedIndex;
@@ -2290,18 +2677,41 @@ declare class RelSelect extends LitElement {
2290
2677
  */
2291
2678
  private get chipSize();
2292
2679
  /**
2293
- * Multi-select rows nest the checkbox one step down its own ramp: rel-checkbox
2294
- * sits a size above the rest of the system (its `large` is body/xl where a
2295
- * `large` field is body/lg), so large select -> medium checkbox and both
2296
- * medium and small select -> small checkbox, the smallest it ships. The rows
2297
- * still land on 48 / 40 / 32 through their own padding and min-height.
2680
+ * Multi-select rows nest the checkbox at the SAME step as the field. This
2681
+ * used to shift one down, compensating for rel-checkbox sitting a size above
2682
+ * the rest of the system — but that ramp has since been realigned to Figma
2683
+ * (16/18/20/24 with body-sm/md/lg/xl labels), so the compensation is now the
2684
+ * bug: it is what made a small multi-select render 14px labels where Figma
2685
+ * asks for 12px. Figma's option content heights are 20 / 22 / 24 for
2686
+ * small / medium / large, which is exactly body-sm / body-md / body-lg — the
2687
+ * checkbox step of the same name. The rows still land on 32 / 40 / 48
2688
+ * through their own padding and min-height.
2298
2689
  */
2299
2690
  private get optionCheckboxSize();
2691
+ /** Native tooltip carrying the row's full text. Both lines truncate to one
2692
+ * line with an ellipsis, so a long supporting line is otherwise unrecoverable
2693
+ * by sight; hovering the row reveals it. Only rows that carry a second line
2694
+ * get one, so plain lists do not sprout tooltips. */
2695
+ private rowTitle;
2300
2696
  private get selectAllStatus();
2301
2697
  private addGlobalListeners;
2302
2698
  private removeGlobalListeners;
2303
2699
  private onOutsidePointerDown;
2304
2700
  private onPageScroll;
2701
+ /**
2702
+ * BOTH events fire, deliberately — do not delete either.
2703
+ *
2704
+ * This repo is split down the middle on the convention, not inconsistent in
2705
+ * one place: rel-select and rel-segmented-button emit `changed`, while
2706
+ * rel-switch, rel-checkbox and rel-radio emit `rel-change`. Renaming
2707
+ * `changed` would break every existing rel-select consumer, so 1.4.9 adds
2708
+ * the prefixed name alongside it rather than moving anyone. New code should
2709
+ * listen for `rel-change`; `changed` stays for as long as 1.x does.
2710
+ *
2711
+ * One `detail` object is shared by both dispatches so the two events cannot
2712
+ * drift apart — a listener on one sees exactly what a listener on the other
2713
+ * sees.
2714
+ */
2305
2715
  private emitChange;
2306
2716
  private openListbox;
2307
2717
  private closeListbox;
@@ -2345,18 +2755,19 @@ type SkeletonAnimation = "pulse" | "shimmer" | "none";
2345
2755
  * Loading placeholder: a neutral block, circle or stack of text lines that pulses or shimmers while real content loads, then hides itself once `loaded` is set. Use it in place of content whose size you already know; use a spinner for indeterminate waits.
2346
2756
  * @csspart base - Each placeholder surface: the single block/circle, or every bar when `shape="line"`.
2347
2757
  * @cssprop --rel-skeleton-color - Fill colour of the placeholder (defaults to the elevated-3 neutral background token).
2758
+ * @cssprop --rel-skeleton-line-height - Height of each text bar when `shape="line"`; default 16px (Figma's Line shape).
2348
2759
  * @cssprop --rel-skeleton-highlight - Lighter band that sweeps across the placeholder when `animation="shimmer"` (defaults to the elevated-2 neutral background token).
2349
2760
  */
2350
2761
  declare class RelSkeleton extends LitElement {
2351
- /** Placeholder shape: `block` fills the host (min 16px tall), `circle` is a square-aspect disc sized by the host width, `line` renders `lines` 14px text bars with the last one at 65% width. */
2762
+ /** Placeholder shape: `block` fills the host (min 16px tall), `circle` is a square-aspect disc sized by the host width, `line` renders `lines` 16px text bars with the last one at 65% width. */
2352
2763
  shape: SkeletonShape;
2353
- /** Number of text bars when `shape="line"` (minimum 1, 8px apart); ignored by the other shapes. */
2764
+ /** Number of text bars when `shape="line"` (minimum 1, --rel-spacing-xs apart); ignored by the other shapes. */
2354
2765
  lines: number;
2355
2766
  /** Any CSS width (e.g. `"240px"`, `"50%"`) applied to the host's inline style. Empty leaves the host at its natural block width. */
2356
2767
  width: string;
2357
2768
  /** Any CSS height (e.g. `"120px"`) applied to the host's inline style. Not needed for `circle`, which derives its height from the width. */
2358
2769
  height: string;
2359
- /** Motion: `pulse` fades opacity between 100% and 40%, `shimmer` sweeps a highlight band left to right, `none` is static. Both animations are disabled under `prefers-reduced-motion: reduce`. */
2770
+ /** Motion: `pulse` fades opacity between 20% and 40% (Figma's Start/End states), `shimmer` sweeps a highlight band left to right, `none` is static. Both animations are disabled under `prefers-reduced-motion: reduce`. */
2360
2771
  animation: SkeletonAnimation;
2361
2772
  /** Set once the real content is ready: hides the skeleton (`display: none`) and removes its `role="status"` / "Loading" label. */
2362
2773
  loaded: boolean;
@@ -2371,7 +2782,11 @@ declare global {
2371
2782
  }
2372
2783
 
2373
2784
  type ModalSide = "top" | "bottom" | "center";
2374
- type ModalSize = "sm" | "md" | "lg" | "xl" | "fullscreen";
2785
+ /** Panel width scale, spelled out. `fullscreen` has no short form. */
2786
+ type ModalSize = "small" | "medium" | "large" | "extra-large" | "fullscreen";
2787
+ /** Alternative spellings, not deprecations — the short names keep working. */
2788
+ type ModalSizeAlias = "sm" | "md" | "lg" | "xl";
2789
+ type ModalSizeInput = ModalSize | ModalSizeAlias;
2375
2790
  /**
2376
2791
  * `"default"` — the body is a free-form slot (24px padding, your own layout).
2377
2792
  * `"text"` — the Figma Modal Sheet "Text" slot: a message-only modal whose body
@@ -2413,8 +2828,8 @@ declare class RelModal extends LitElement {
2413
2828
  dismissible: boolean;
2414
2829
  /** Modal mode: renders the scrim, sets `role="dialog"` + `aria-modal`, traps Tab inside the panel and moves focus in on open. When false the panel is a `region` with no scrim and no focus trap. */
2415
2830
  modal: boolean;
2416
- /** Panel width scale. `md` matches the Figma Modal Sheet width; `fullscreen` covers the viewport. */
2417
- size: ModalSize;
2831
+ /** Panel width scale. `medium` (default) matches the Figma Modal Sheet width; `fullscreen` covers the viewport. `sm` / `md` / `lg` / `xl` are alternative spellings of `small` / `medium` / `large` / `extra-large` and stay supported — they are normalised to the full words, not deprecated. */
2832
+ size: ModalSizeInput;
2418
2833
  /** Body treatment — see `ModalVariant`. `"text"` styles the default slot as
2419
2834
  * the Figma "Text" message slot; the header (and its divider) render as
2420
2835
  * usual above it. Default rendering is unchanged. */
@@ -2449,8 +2864,18 @@ declare class RelModal extends LitElement {
2449
2864
  private originalBodyPaddingRight;
2450
2865
  static styles: lit.CSSResult;
2451
2866
  connectedCallback(): void;
2867
+ /**
2868
+ * The footer divider is conditional: the designer confirmed that short
2869
+ * content (a confirmation message) has no divider, but a body that scrolls
2870
+ * does — it is a scroll affordance, not decoration. There is no CSS way to
2871
+ * ask "does this element scroll", so it is measured and reflected onto the
2872
+ * host as `body-scrolls`, re-measured whenever the body or its contents
2873
+ * resize.
2874
+ */
2875
+ private bodyScrollObserver?;
2876
+ private watchBodyScroll;
2452
2877
  disconnectedCallback(): void;
2453
- protected willUpdate(): void;
2878
+ protected willUpdate(changed: Map<string, unknown>): void;
2454
2879
  updated(changed: Map<string, unknown>): void;
2455
2880
  private handleOpen;
2456
2881
  private handleClose;
@@ -2473,6 +2898,11 @@ declare global {
2473
2898
  */
2474
2899
  type SliderType = "continuous" | "discrete" | "default" | "range";
2475
2900
  type TrackStyle = "solid" | "dotted";
2901
+ /** Figma Size axis, spelled out as in the design file. */
2902
+ type SliderSize = "small" | "medium" | "large";
2903
+ /** Alternative spellings, not deprecations — the original short names keep working. */
2904
+ type SliderSizeAlias = "sm" | "md" | "lg";
2905
+ type SliderSizeInput = SliderSize | SliderSizeAlias;
2476
2906
  /**
2477
2907
  * Slider (Figma "Slider", 6417:1340): a pill track with a stroked round thumb,
2478
2908
  * optional value tooltip, discrete tick marks and a dual-thumb range mode.
@@ -2491,8 +2921,19 @@ type TrackStyle = "solid" | "dotted";
2491
2921
  declare class RelSlider extends LitElement {
2492
2922
  /** Slider mode: `continuous` (or its alias `default`) accepts any integer value, `discrete` snaps to `step` and draws tick marks, `range` shows two thumbs bounding `value`…`valueEnd`. */
2493
2923
  type: SliderType;
2494
- /** Figma Size axis (S/M/L): scales track, thumb and ticks. */
2495
- size: "sm" | "md" | "lg";
2924
+ /**
2925
+ * Draws the tick marks (Figma's separate `Is Determinated` boolean). Works
2926
+ * with ANY `type` — Figma shows ticks on every variant, where the code used
2927
+ * to gate them behind the code-only `type="discrete"`, so a `default` or
2928
+ * `range` slider could never show them.
2929
+ * Left unset it follows `type="discrete"`, so nothing changes for existing
2930
+ * sliders; set it explicitly to override in either direction.
2931
+ */
2932
+ showTicks?: boolean;
2933
+ /** Whether tick marks render: explicit `show-ticks` wins, else the legacy `type="discrete"`. */
2934
+ private get ticksVisible();
2935
+ /** Figma Size axis: `small` (6px track / 16px thumb), `medium` (8 / 20, default) or `large` (10 / 24), which also scales the ticks and the value bubble. `sm` / `md` / `lg` are alternative spellings and stay supported — they are normalised to the full words, not deprecated. */
2936
+ size: SliderSizeInput;
2496
2937
  /** Lowest selectable value. */
2497
2938
  min: number;
2498
2939
  /** Highest selectable value. */
@@ -2518,6 +2959,10 @@ declare class RelSlider extends LitElement {
2518
2959
  private _onTouched;
2519
2960
  static styles: lit.CSSResult;
2520
2961
  connectedCallback(): void;
2962
+ /** Alternative spellings, NOT deprecations — no console notice. Normalising
2963
+ * the property rather than duplicating every [size=] selector keeps the
2964
+ * reflected attribute canonical, so only one spelling can reach the CSS. */
2965
+ protected willUpdate(changed: Map<string, unknown>): void;
2521
2966
  updated(changedProperties: Map<string, any>): void;
2522
2967
  private get isRange();
2523
2968
  private get percentage();
@@ -2549,6 +2994,8 @@ declare class RelSlider extends LitElement {
2549
2994
  }
2550
2995
 
2551
2996
  type SnackbarLines = "single" | "double";
2997
+ /** Canonical layout axis (Figma "Type"): default | longer-action. */
2998
+ type SnackbarVariant = "default" | "longer-action";
2552
2999
  /** Canonical semantic tones (Figma "Tone"): neutral | success | warning | destructive. */
2553
3000
  type SnackbarTone = "neutral" | "success" | "warning" | "destructive";
2554
3001
  /** Deprecated spellings: "default" → neutral; "error" | "danger" | "critical" → destructive. */
@@ -2567,13 +3014,17 @@ declare class RelSnackbar extends LitElement {
2567
3014
  message: string;
2568
3015
  /** Label of the optional action button (Figma trailing button, Static / Small); omitted when unset. Clicking it fires `actionClick` and then dismisses the snackbar. */
2569
3016
  action?: string;
2570
- /** Doubles the auto-dismiss delay (`duration * 2`) so the user has time to read and use the action button. Pair it with `lines="double"` for the Figma Type=Longer Action layout. */
3017
+ /**
3018
+ * Layout and timing (Figma "Type"): `"default"` (message and actions on one row) | `"longer-action"` (message stacked above the actions, which align to the end, with the auto-dismiss delay doubled so there is time to use the action). Takes precedence over the deprecated `lines` and `longerAction` props.
3019
+ */
3020
+ variant?: SnackbarVariant;
3021
+ /** @deprecated — use `variant="longer-action"`, which also sets the stacked layout. Doubles the auto-dismiss delay (`duration * 2`) on its own. Ignored when `variant` is set. Logs a one-time console notice. */
2571
3022
  longerAction: boolean;
2572
3023
  /** Shows the trailing close icon button. Defaults to true; because it is a Lit boolean, `iconclose="false"` is still true — set the property to `false` from script to hide the button. */
2573
3024
  iconClose: boolean;
2574
- /** Layout: `"single"` (default) puts the message and the actions on one row; `"double"` stacks the message above the actions, which align to the end (Figma Type=Longer Action). */
3025
+ /** @deprecated — use `variant`; `lines="double"` maps to `variant="longer-action"`. Sets the stacked layout on its own but not the doubled delay. Ignored when `variant` is set. Logs a one-time console notice. */
2575
3026
  lines: SnackbarLines;
2576
- /** Auto-dismiss delay in milliseconds (doubled by `longerAction`), started when `show` becomes true and restarted when `duration` or `longerAction` changes while shown. `0` (or a negative value) disables auto-dismiss. */
3027
+ /** Auto-dismiss delay in milliseconds (doubled by `variant="longer-action"`), started when `show` becomes true and restarted when `duration` or the layout changes while shown. `0` (or a negative value) disables auto-dismiss. */
2577
3028
  duration: number;
2578
3029
  /**
2579
3030
  * Semantic tone (Figma "Tone"): `"neutral"` (default: no accent, no icon) | `"success"` | `"warning"` | `"destructive"` — the status tones add a 4px left accent and a leading icon (check-circle / warning / error) in the tone colour. `"default"` is a deprecated alias of `"neutral"`; `"error"`, `"danger"` and `"critical"` are deprecated aliases of `"destructive"`; each logs a one-time console notice. Takes precedence over the deprecated `type` prop.
@@ -2583,6 +3034,8 @@ declare class RelSnackbar extends LitElement {
2583
3034
  type?: SnackbarToneInput;
2584
3035
  /** Whether the snackbar is visible. Set it to true to show the snackbar and start the auto-dismiss timer; it is set back to false by the close icon, the action button and the timer. There is no open/close method — toggle this property. */
2585
3036
  show: boolean;
3037
+ /** Read-only canonical layout actually rendered: explicit `variant` wins, else either deprecated prop (`lines="double"` or `longerAction`) promotes to `"longer-action"`, else `"default"`. */
3038
+ get resolvedVariant(): SnackbarVariant;
2586
3039
  /** Read-only canonical tone actually rendered: explicit `tone` wins, else `type`, else `"neutral"`; deprecated aliases are resolved to `"neutral"` / `"destructive"`. */
2587
3040
  get resolvedTone(): SnackbarTone;
2588
3041
  private visible;
@@ -2600,15 +3053,25 @@ declare class RelSnackbar extends LitElement {
2600
3053
  render(): lit_html.TemplateResult<1>;
2601
3054
  }
2602
3055
 
3056
+ /** Control scale, spelled out. Figma has no Size axis on Switch; see the size ramp in the styles. */
3057
+ type SwitchSize = "small" | "medium" | "large";
3058
+ /** Alternative spellings, not deprecations — the short names work too. */
3059
+ type SwitchSizeAlias = "sm" | "md" | "lg";
3060
+ type SwitchSizeInput = SwitchSize | SwitchSizeAlias;
3061
+ /** Which side of the control the label sits on. */
3062
+ type SwitchLabelPlacement = "end" | "start";
2603
3063
  /**
2604
3064
  * Toggle switch (Figma "Switch", 5917:1174, Type=Line): a 44×20 control with a
2605
3065
  * thin 10px track and a 20px handle that slides 20px. Wraps a native checkbox
2606
3066
  * with `role="switch"`, so keyboard and screen-reader behaviour are native.
2607
- * @csspart base - The `<label>` wrapper around the hidden input and the track
3067
+ * @slot - Rich label content beside the control, as an alternative to the `label` property. Clicking it toggles the switch.
3068
+ * @csspart root - The `<label>` holding the control and its optional text; carries the `label-placement` order
3069
+ * @csspart base - The control box around the hidden input and the track
2608
3070
  * @csspart track - The thin rail; the handle is its `::before` pseudo-element
2609
- * @cssprop --rel-switch-track-width - Overall control width; default 44px
2610
- * @cssprop --rel-switch-track-height - Overall control height; default 20px
2611
- * @cssprop --rel-switch-thumb-size - Handle diameter; default 20px (the handle always travels 20px)
3071
+ * @csspart label - The text beside the control; not rendered when there is neither a `label` nor slotted content
3072
+ * @cssprop --rel-switch-track-width - Overall control width; defaults to the `size` ramp (36 / 44 / 52px)
3073
+ * @cssprop --rel-switch-track-height - Overall control height; defaults to the `size` ramp — 16 / 20 / 24px for `type="line"`, 20 / 24 / 28px for `type="pill"`
3074
+ * @cssprop --rel-switch-thumb-size - Handle diameter; defaults to the `size` ramp (16 / 20 / 24px), and the handle always travels its own width minus the 2px inset on each side
2612
3075
  * @cssprop --rel-switch-track - Rail colour; default `var(--rel-bg-neutral-elevated-2)`
2613
3076
  * @cssprop --rel-switch-knob-on - Handle colour when checked; default `var(--rel-bg-neutral-inverse-2)`
2614
3077
  * @cssprop --rel-switch-knob-off - Handle colour when unchecked; default `var(--rel-text-static-light)`
@@ -2617,16 +3080,54 @@ declare class RelSnackbar extends LitElement {
2617
3080
  declare class RelSwitch extends LitElement {
2618
3081
  /** Whether the switch is on. Set it to control the state programmatically; user toggles update it and fire `rel-change`. */
2619
3082
  checked: boolean;
2620
- /** Disables interaction, dims the control to 50% and removes the handle shadow. */
3083
+ /**
3084
+ * Shape (Figma "Type"): `"line"` (default) is the 44x20 control with a thin
3085
+ * 40x10 rail and the handle riding on it; `"pill"` is the 44x24 filled track
3086
+ * with the handle inside it.
3087
+ * NOTE: Figma's own default is Pill. We default to `line` deliberately —
3088
+ * flipping it would restyle every existing switch — so this is a recorded
3089
+ * divergence from the design default, not an oversight.
3090
+ */
3091
+ type: "line" | "pill";
3092
+ /** Disables interaction and swaps in the dulled rail / handle fills; the handle shadow is removed. */
2621
3093
  disabled: boolean;
2622
3094
  /** Arbitrary string echoed in the `rel-change` event detail — useful when one handler serves several switches. */
2623
3095
  value: string;
2624
3096
  /** Accessible name for the native input. Without it the switch has no label of its own, so pair it with a visible `<label>` or set this. */
2625
3097
  fieldAriaLabel?: string;
3098
+ /** Visible text beside the control. Clicking it toggles the switch, and it becomes the accessible name — use `fieldAriaLabel` instead when the switch has no visible text. For rich content (a bold word, an icon) slot it in the default slot rather than setting this. */
3099
+ label?: string;
3100
+ /**
3101
+ * Which side the label sits on: `end` (default) puts it after the control, `start` before it.
3102
+ *
3103
+ * Note this is a PHYSICAL side, not a writing-direction one, matching every
3104
+ * other placement prop in the library.
3105
+ */
3106
+ labelPlacement: SwitchLabelPlacement;
3107
+ /**
3108
+ * Control scale: `small`, `medium` (default) or `large`. `sm` / `md` / `lg`
3109
+ * are alternative spellings and stay supported — normalised to the full
3110
+ * words, not deprecated.
3111
+ *
3112
+ * Figma's Switch has axes Type / State / Enabled only, with no Size, so this
3113
+ * ramp is ours. `medium` is exactly the control that shipped before it
3114
+ * existed, so an existing switch is unchanged.
3115
+ *
3116
+ * It does NOT respond to viewport width. Picking a size per breakpoint is the
3117
+ * consuming application's decision — a control that silently resized itself
3118
+ * at some width would be undocumentable behaviour, and the breakpoint that
3119
+ * matters belongs to the layout around it, not to the switch.
3120
+ */
3121
+ size: SwitchSizeInput;
2626
3122
  private _onChange;
2627
3123
  private _onTouched;
2628
3124
  private isFocused;
2629
3125
  private isPressed;
3126
+ private hasSlottedLabel;
3127
+ /** Alternative spellings, NOT deprecations — no console notice. Normalising
3128
+ * keeps the reflected attribute canonical so the :host([size=…]) rules only
3129
+ * need the full words. Same shape as rel-chip, rel-slider and rel-radio. */
3130
+ protected willUpdate(changed: Map<string, unknown>): void;
2630
3131
  static styles: lit.CSSResult;
2631
3132
  private toggle;
2632
3133
  /** @internal Angular ControlValueAccessor bridge. */
@@ -2640,10 +3141,16 @@ declare class RelSwitch extends LitElement {
2640
3141
  private handleMouseDown;
2641
3142
  private handleMouseUp;
2642
3143
  render(): lit_html.TemplateResult<1>;
3144
+ /** Rich label content can be slotted instead of set as text; this keeps the
3145
+ * label span out of the layout entirely when neither is present, so an
3146
+ * unlabelled switch has no stray gap beside it. */
3147
+ private onSlotChange;
2643
3148
  }
2644
3149
 
2645
3150
  interface TabItem {
2646
3151
  label: string;
3152
+ /** Icon name from the generated icon dictionary, rendered before the label (Figma "Show Icon" / "Icon"). Sized 20 / 18 / 16px with the tab's own size step. */
3153
+ icon?: string;
2647
3154
  badge?: number;
2648
3155
  badgeDot?: boolean;
2649
3156
  disabled?: boolean;
@@ -2661,6 +3168,8 @@ type TabSize = "sm" | "md" | "lg" | "small" | "medium" | "large";
2661
3168
  declare class RelTabItem extends LitElement {
2662
3169
  /** Text shown on the tab. */
2663
3170
  label: string;
3171
+ /** Icon name from the generated icon dictionary, rendered before the label (Figma "Show Icon" / "Icon"). Sized 20 / 18 / 16px with the tab's own size step. */
3172
+ icon: string;
2664
3173
  /** Optional identifier for the tab. `<rel-tab>` does not read it; look it up from the item at `event.detail` when handling `rel-tab-change`. */
2665
3174
  value: string;
2666
3175
  /** Count shown in a small badge after the label; `0` hides the badge. */
@@ -2682,7 +3191,7 @@ declare class RelTabItem extends LitElement {
2682
3191
  * @csspart item - Each `role="tab"` element
2683
3192
  * @csspart label - The text span inside each tab
2684
3193
  * @cssprop --rel-tab-active-color - Underline colour of the active tab; default `var(--rel-bg-brand)`
2685
- * @cssprop --rel-tab-underline-height - Thickness of the active underline; default 3px
3194
+ * @cssprop --rel-tab-underline-height - Thickness of the active underline; default 1px (`--rel-stroke-sm`, the weight Figma draws). Set it to restore the heavier 3px bar shipped previously.
2686
3195
  * @cssprop --rel-tab-text-default - Label colour of the active tab; default `var(--rel-text-default)`
2687
3196
  * @cssprop --rel-tab-text-hover - Label colour while hovering an enabled tab; default `var(--rel-text-label)`
2688
3197
  * @cssprop --rel-tab-text-disabled - Label colour of a disabled tab; default `var(--rel-text-disabled)`
@@ -2735,6 +3244,7 @@ declare class RelTab extends LitElement {
2735
3244
  * @csspart option - Each hour / minute / second / AM-PM entry button
2736
3245
  * @csspart footer - The Now / Cancel / OK row
2737
3246
  * @csspart helper - The helper / error text live region under the field
3247
+ * @cssprop --rel-overlay-fallback-z - 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 rel-select, rel-autocomplete and the other pickers so the whole overlay family can be moved from one place; default 1001.
2738
3248
  * @fires rel-focus - Fired when the text input receives focus. No detail.
2739
3249
  */
2740
3250
  declare class RelTimePicker extends LitElement {
@@ -2806,6 +3316,7 @@ declare class RelTimePicker extends LitElement {
2806
3316
  private showTopLayer;
2807
3317
  private hideTopLayer;
2808
3318
  private onReposition;
3319
+ private onPageScroll;
2809
3320
  private updatePopoverPosition;
2810
3321
  /** Focus the selected (or first enabled) option in the columns. */
2811
3322
  private focusPanelSelection;
@@ -2882,19 +3393,38 @@ declare global {
2882
3393
  }
2883
3394
  }
2884
3395
 
2885
- /** Canonical semantic tones. */
2886
- type ToastTone = "default" | "destructive";
2887
- /** Deprecated tone spellings, all mapped to "destructive". */
2888
- type ToastToneAlias = "error" | "danger" | "critical";
3396
+ /** Canonical semantic tones (Figma "Tone"): neutral | destructive. */
3397
+ type ToastTone = "neutral" | "destructive";
3398
+ /** Deprecated spellings: "default" → neutral; "error" | "danger" | "critical" → destructive. */
3399
+ type ToastToneAlias = "default" | "error" | "danger" | "critical";
2889
3400
  type ToastToneInput = ToastTone | ToastToneAlias;
2890
3401
  /** @deprecated since 1.4.7 — use `ToastToneInput`. */
2891
3402
  type ToastType = "default" | "error";
2892
3403
  /**
2893
- * Transient bottom-centre notification: a fixed-position dark pill with a message, an optional action button and a dismiss button, shown via `show` / `open()` and hidden again after `duration` milliseconds. Use it for brief, non-blocking confirmations; use `rel-alert` for persistent messages.
3404
+ * Where the toast is anchored in the viewport. PHYSICAL sides, not logical
3405
+ * start/end: nothing else in this library adapts to writing direction, and a
3406
+ * logical model in one component would make it the odd one out.
3407
+ */
3408
+ type ToastPosition = "top-left" | "top-center" | "top-right" | "bottom-left" | "bottom-center" | "bottom-right";
3409
+ /** Toast scale, spelled out. Figma has no Size axis on Toast; see the ramp in the styles. */
3410
+ type ToastSize = "small" | "medium" | "large";
3411
+ /** Alternative spellings, not deprecations — the short names work too. */
3412
+ type ToastSizeAlias = "sm" | "md" | "lg";
3413
+ type ToastSizeInput = ToastSize | ToastSizeAlias;
3414
+ /**
3415
+ * Transient notification: a fixed-position card with a message, an optional action button and a dismiss button, shown via `show` / `open()` and hidden again after `duration` milliseconds. Anchors to any of six viewport positions (`position`, default `bottom-center`) at three scales (`size`, default `medium`). Use it for brief, non-blocking confirmations; use `rel-alert` for persistent messages.
3416
+ *
3417
+ * STACKING. Toasts sharing a `position` stack automatically — no wrapper element
3418
+ * and no change to your markup. Up to `max-visible` (default 3) show at once,
3419
+ * newest against the anchor edge; any beyond that WAIT rather than being dropped,
3420
+ * and a waiting toast's `duration` does not start until it is actually on screen.
3421
+ * Dismissing one closes the gap for the rest. The six positions stack
3422
+ * independently. Behaviour change: five toasts at one position used to render on
3423
+ * top of each other; now three show and two wait.
2894
3424
  * @csspart base - The `role="status"` toast container that carries the tone class
2895
3425
  * @csspart label - The message text span
2896
3426
  * @csspart action - The action button; rendered only when `actionlabel` is set
2897
- * @csspart close-button - The dismiss (✕) button
3427
+ * @csspart close-button - The dismiss button; rendered while `dismissible` is true
2898
3428
  * @cssprop --rel-toast-z - `z-index` of the fixed host (default `9000`); raise it when a modal or drawer layer covers the toast
2899
3429
  * @cssprop --rel-z-toast - Deprecated alias of `--rel-toast-z`, still read as its fallback
2900
3430
  */
@@ -2902,12 +3432,12 @@ declare class RelToast extends LitElement {
2902
3432
  /** Text of the toast. It is also announced to screen readers through the shared live-region announcer each time `show` becomes true. */
2903
3433
  message: string;
2904
3434
  /**
2905
- * Semantic tone: `"default"` (dark neutral pill) | `"destructive"` (danger fill, announced assertively). `"error"`, `"danger"` and `"critical"` are deprecated aliases of `"destructive"` and log a one-time console notice. Takes precedence over the deprecated `type` prop.
3435
+ * Semantic tone (Figma "Tone"): `"neutral"` (default: neutral surface) | `"destructive"` (danger fill, announced assertively). `"default"` is a deprecated alias of `"neutral"`; `"error"`, `"danger"` and `"critical"` are deprecated aliases of `"destructive"`; each logs a one-time console notice. Takes precedence over the deprecated `type` prop.
2906
3436
  */
2907
3437
  tone?: ToastToneInput;
2908
- /** @deprecated Deprecated since 1.4.7 — alias of `tone`; use `tone`. Same values as `tone`; `type="error"` maps to `tone="destructive"`. Ignored when `tone` is set. Logs a one-time console notice. */
3438
+ /** @deprecated Deprecated since 1.4.7 — alias of `tone`; use `tone`. Same values as `tone`; `type="error"` maps to `tone="destructive"` and `type="default"` to `tone="neutral"`. Ignored when `tone` is set. Logs a one-time console notice. */
2909
3439
  type?: ToastToneInput;
2910
- /** Read-only canonical tone actually rendered: explicit `tone` wins, else `type`, else `"default"`; deprecated aliases are resolved to `"destructive"`. */
3440
+ /** Read-only canonical tone actually rendered: explicit `tone` wins, else `type`, else `"neutral"`; deprecated aliases are resolved to `"neutral"` / `"destructive"`. */
2911
3441
  get resolvedTone(): ToastTone;
2912
3442
  /** Label of the optional action button rendered between the message and the dismiss button; the button is omitted when unset. Clicking it fires `action` and then closes the toast. */
2913
3443
  actionLabel?: string;
@@ -2915,10 +3445,54 @@ declare class RelToast extends LitElement {
2915
3445
  duration: number;
2916
3446
  /** Whether the toast is visible. Set it to true (or call `open()`) to show the toast and start the auto-dismiss timer; it is set back to false by `close()`, the dismiss button, the action button and the timer. */
2917
3447
  show: boolean;
3448
+ /** Shows the trailing dismiss button (Figma "Show Close Button"). Defaults to true; because it is a Lit boolean, `dismissible="false"` is still true — set the property to `false` from script to hide the button. */
3449
+ dismissible: boolean;
3450
+ /**
3451
+ * Where the toast is anchored: `bottom-center` (default, unchanged from
3452
+ * before this prop existed), `bottom-left`, `bottom-right`, `top-left`,
3453
+ * `top-center` or `top-right`. Top anchors also animate in from the top edge
3454
+ * rather than upward from the bottom.
3455
+ *
3456
+ * Figma's Toast has axes Content / Show Button / Show Close Button / Tone and
3457
+ * no position, so this axis is ours.
3458
+ */
3459
+ position: ToastPosition;
3460
+ /**
3461
+ * Toast scale: `small`, `medium` (default, unchanged) or `large`. `sm` / `md`
3462
+ * / `lg` are alternative spellings and stay supported — normalised to the
3463
+ * full words, not deprecated. Scales the padding, the inner gap, the message
3464
+ * type role and the close icon. Figma has no Size axis on Toast.
3465
+ */
3466
+ size: ToastSizeInput;
3467
+ /**
3468
+ * How many toasts may be on screen at once AT THIS POSITION. Further ones
3469
+ * wait their turn rather than being dropped — a notification the user never
3470
+ * saw is worse than a late one — and each toast's `duration` only starts
3471
+ * once it actually becomes visible, so a queued message cannot expire unseen.
3472
+ * Default 3; unbounded stacking just fills the viewport.
3473
+ */
3474
+ maxVisible: number;
2918
3475
  private visible;
2919
3476
  private _dismissTimer;
3477
+ /** The stack this instance is currently registered in, so a `position`
3478
+ * change while shown can leave the old stack before joining the new one. */
3479
+ private _registeredAt;
2920
3480
  static styles: lit.CSSResult;
2921
3481
  willUpdate(changed: Map<string, unknown>): void;
3482
+ private joinStack;
3483
+ private leaveStack;
3484
+ /**
3485
+ * Called by the reflow with this instance's admission state. The
3486
+ * auto-dismiss timer starts HERE, on becoming visible — not when `show` is
3487
+ * set — so a toast waiting its turn cannot expire before anyone has seen it.
3488
+ */
3489
+ private _setVisible;
3490
+ /** Offset from the anchor edge, written by the reflow. Index 0 gets no
3491
+ * transform at all, so a lone toast is untouched by the stacking code. */
3492
+ private _setStackOffset;
3493
+ /** Measured height of the rendered card, for the stack arithmetic. */
3494
+ private get _cardHeight();
3495
+ private get anchorsTop();
2922
3496
  disconnectedCallback(): void;
2923
3497
  /** Shows the toast: sets `show = true`, which starts the auto-dismiss timer and announces `message`. */
2924
3498
  open(): void;
@@ -2930,21 +3504,31 @@ declare class RelToast extends LitElement {
2930
3504
 
2931
3505
  type TooltipPosition = "above" | "below" | "left" | "right";
2932
3506
  type TooltipType = "single-line" | "multi-line";
3507
+ /**
3508
+ * Bubble appearance. `inverse` is the original look — an inverse-surface bubble
3509
+ * that is dark-on-light in light mode and light-on-dark in dark mode.
3510
+ * `neutral` is a light-surface bubble, the same sense the word already carries
3511
+ * on rel-toast, where `tone="neutral"` is likewise the light surface.
3512
+ */
3513
+ type TooltipTone = "inverse" | "neutral";
2933
3514
  /**
2934
3515
  * Hover/focus tooltip (Figma "Tooltip"): wraps a trigger element and shows a short inverse-coloured text bubble above, below, left or right of it. Use it for supplementary hints on icons and truncated labels only — the text is not reachable on touch and must never be the only place critical information lives.
2935
3516
  * @slot - The trigger element (one focusable element such as a button or icon button). It receives `aria-describedby` pointing at the bubble.
2936
3517
  * @csspart panel - The tooltip bubble (`role="tooltip"`): background, padding, radius, shadow and max-width.
2937
3518
  * @csspart indicator - The triangular tail pointing at the trigger, rendered only when `show-tail` is set.
3519
+ * @cssprop --rel-tooltip-offset - Gap between the trigger and the bubble; defaults to `6px`, or `13px` when `show-tail` is set so the tail clears the trigger.
2938
3520
  */
2939
3521
  declare class RelTooltip extends LitElement {
2940
3522
  /** Plain-text content of the bubble. Empty text still renders an (empty) bubble on hover, so always set it. */
2941
3523
  text: string;
2942
- /** Side of the trigger the bubble appears on, 6px away and centred: `above`, `below`, `left` or `right`. No collision detection — pick a side with room in the viewport. */
3524
+ /** Side of the trigger the bubble appears on, centred and 6px away (13px when `show-tail` is set, so the tail clears the trigger; override with `--rel-tooltip-offset`): `above`, `below`, `left` or `right`. No collision detection — pick a side with room in the viewport. */
2943
3525
  position: TooltipPosition;
2944
3526
  /** Renders the triangular tail (`indicator` part) on the bubble's edge facing the trigger. */
2945
3527
  showTail: boolean;
2946
- /** Figma "Type": `single-line` keeps the text on one line and sizes to content; `multi-line` wraps inside a 220px max-width bubble. */
3528
+ /** Figma "Type": `single-line` keeps the text on one line and sizes to content; `multi-line` wraps inside a 200px max-width bubble. */
2947
3529
  type: TooltipType;
3530
+ /** Bubble appearance: `inverse` (default) is the original inverse-surface bubble, unchanged; `neutral` is a light surface — `--rel-bg-neutral` with `--rel-text-default` and a 1px `--rel-border-default`, tail included. Figma has no such variant; this is a deliberate addition. */
3531
+ tone: TooltipTone;
2948
3532
  private uniqueId;
2949
3533
  static styles: lit.CSSResult;
2950
3534
  connectedCallback(): void;
@@ -3035,7 +3619,8 @@ interface UploadState {
3035
3619
  * @fires rel-file-error - Fired when one file's `uploader` rejects in self-driving mode. Detail: `{ id: string, file: File, error: string }`.
3036
3620
  * @fires rel-complete - Fired when the whole self-driving batch settles without being cancelled. Detail: `{ succeeded: UploadFile[], failed: UploadFile[] }`.
3037
3621
  * @fires rel-state - Fired when the derived `state` snapshot changes (and once after connecting). Detail: `UploadState`.
3038
- * @cssprop --rel-upload-browse-hover-bg - Background of the Browse button on hover; falls back to `--rel-action-neutral-outline-hover`, then `--rel-bg-surface-elevated`
3622
+ * @cssprop --rel-upload-browse-hover-bg - Background of the Browse button on hover. Defaults to `transparent`, which is what the underlying `rel-button` secondary style draws (Figma's outline button reacts on its border, not its fill), so this only takes effect when you set it.
3623
+ * @csspart browse - The `rel-button` that opens the file picker (Figma "Browse Button": Size=Medium, Style=Secondary).
3039
3624
  */
3040
3625
  declare class RelUpload extends LitElement {
3041
3626
  /** Comma-separated accept list, e.g. ".pdf,.png,.jpg" or "image/*". */
@@ -3319,4 +3904,4 @@ type Politeness = "polite" | "assertive";
3319
3904
  */
3320
3905
  declare function announce(message: string, politeness?: Politeness): void;
3321
3906
 
3322
- export { type AlertTone, type AlertToneAlias, type AlertToneInput, type AutocompleteFilter, type AutocompleteItem, type AutocompleteSearchFn, type BindShortcutOptions, type BreadcrumbItem, type ButtonTone, type ButtonVariant, type ChipBorder, type ChipSize, type ChipTone, type ChipVariant, type ContainerBackground, type ContainerSurface, type DrawerCloseReason, type DrawerSide, type FieldLabelStyle, FocusTrap, type KbdSize, type LabelPlacement, type MenuItem, type MenuSize, type MenuType, type MenubarType, type ModalSide, type ModalSize, type ModalVariant, type Orientation, type OtpInputMode, type OtpSize, type OtpType, type ProgressMode, type RadioOption, RelAccordion, RelAlert, RelAutocomplete, RelAvatar, RelBadge, RelBreadcrumbs, RelButton, RelCalendar, RelCard, RelCardMedia, RelCarousel, RelCheckbox, RelChip, RelContainer, RelDatePicker, RelDivider, RelDrawer, RelDrawerFooter, RelKbd, RelKbdGroup, RelLayout, RelMenu, RelMenuItem, RelMenubar, RelMenubarMenu, RelModal, RelOtp, RelPagination, RelPopover, RelPopoverContent, RelProgress, RelRadio, RelRangePicker, RelResizableGroup, RelResizableHandle, RelResizablePanel, RelSegmentedButton, RelSelect, RelSkeleton, RelSlider, RelSnackbar, RelStepper, RelSwitch, RelTab, RelTabItem, RelTextfield, RelTimePicker, RelToast, RelTooltip, RelUpload, RelUploadActions, type SegmentAlign, type SegmentOption, type SegmentSize, type SelectBorder, type SelectLabelStyle, type SelectOption, type SelectOptionObject, type SelectSize, type SelectState, type ShortcutHandler, type SkeletonAnimation, type SkeletonShape, type SliderType, type SnackbarLines, type SnackbarTone, type SnackbarToneAlias, type SnackbarToneInput, type SnackbarType, type Step, type StepStatus, type StepperSize, type StepperType, type TabItem, type TabSize, type TextAlign, type TextFieldPrefixStyle, type TextFieldSize, type TextFieldState, type TextFieldVariant, type ToastTone, type ToastToneAlias, type ToastToneInput, type ToastType, type TooltipPosition, type TooltipType, type TrackStyle, type UploadFile, type UploadPhase, type UploadRejection, type UploadState, type UploadStatus, type Uploader, type UploaderContext, announce, bindShortcut, formatShortcutParts, getDeepActiveElement, getFocusableElements, isApplePlatform, isEditableTarget, matchesShortcut, parseShortcut, trapTabKey };
3907
+ export { type AlertTone, type AlertToneAlias, type AlertToneInput, type AutocompleteFilter, type AutocompleteItem, type AutocompleteSearchFn, type AvatarSize, type AvatarSizeAlias, type AvatarSizeInput, type BadgeSize, type BadgeSizeAlias, type BadgeSizeInput, type BindShortcutOptions, type BreadcrumbItem, type ButtonTone, type ButtonVariant, type ChipBorder, type ChipSize, type ChipSizeAlias, type ChipSizeInput, type ChipTone, type ChipToneAlias, type ChipToneInput, type ChipVariant, type ChipVariantAlias, type ChipVariantInput, type ContainerBackground, type ContainerPadding, type ContainerPaddingAlias, type ContainerPaddingInput, type ContainerSurface, type DrawerCloseReason, type DrawerSide, type FieldLabelStyle, FocusTrap, type KbdSize, type LabelPlacement, type MenuItem, type MenuSize, type MenuType, type MenuWidth, type MenubarType, type ModalSide, type ModalSize, type ModalSizeAlias, type ModalSizeInput, type ModalVariant, type Orientation, type OtpInputMode, type OtpSize, type OtpType, type PopoverContentSize, type PopoverContentSizeAlias, type PopoverContentSizeInput, type ProgressMode, type RadioOption, type RadioSize, type RadioSizeAlias, type RadioSizeInput, type RangePickerSize, type RangePickerSizeAlias, type RangePickerSizeInput, RelAccordion, RelAlert, RelAutocomplete, RelAvatar, RelBadge, RelBreadcrumbs, RelButton, RelCalendar, RelCard, RelCardMedia, RelCarousel, RelCheckbox, RelChip, RelContainer, RelDatePicker, RelDivider, RelDrawer, RelDrawerFooter, RelKbd, RelKbdGroup, RelLayout, RelMenu, RelMenuItem, RelMenubar, RelMenubarMenu, RelModal, RelOtp, RelPagination, RelPopover, RelPopoverContent, RelProgress, RelRadio, RelRangePicker, RelResizableGroup, RelResizableHandle, RelResizablePanel, RelSegmentedButton, RelSelect, RelSkeleton, RelSlider, RelSnackbar, RelStepper, RelSwitch, RelTab, RelTabItem, RelTextfield, RelTimePicker, RelToast, RelTooltip, RelUpload, RelUploadActions, type SegmentAlign, type SegmentOption, type SegmentSize, type SelectBorder, type SelectLabelStyle, type SelectOption, type SelectOptionObject, type SelectSize, type SelectState, type ShortcutHandler, type SkeletonAnimation, type SkeletonShape, type SliderSize, type SliderSizeAlias, type SliderSizeInput, type SliderType, type SnackbarLines, type SnackbarTone, type SnackbarToneAlias, type SnackbarToneInput, type SnackbarType, type SnackbarVariant, type Step, type StepStatus, type StepperConnector, type StepperSize, type StepperType, type SwitchLabelPlacement, type SwitchSize, type SwitchSizeAlias, type SwitchSizeInput, type TabItem, type TabSize, type TextAlign, type TextFieldPrefixStyle, type TextFieldSize, type TextFieldState, type TextFieldVariant, type ToastPosition, type ToastSize, type ToastSizeAlias, type ToastSizeInput, type ToastTone, type ToastToneAlias, type ToastToneInput, type ToastType, type TooltipPosition, type TooltipTone, type TooltipType, type TrackStyle, type UploadFile, type UploadPhase, type UploadRejection, type UploadState, type UploadStatus, type Uploader, type UploaderContext, announce, bindShortcut, formatShortcutParts, getDeepActiveElement, getFocusableElements, isApplePlatform, isEditableTarget, matchesShortcut, parseShortcut, trapTabKey };