@reliability-design/web 1.4.4 → 1.4.6

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 (58) hide show
  1. package/dist/components/rel-accordion/rel-accordion.ts +13 -1
  2. package/dist/components/rel-alert/rel-alert.ts +1 -1
  3. package/dist/components/rel-autocomplete/rel-autocomplete.ts +33 -2
  4. package/dist/components/rel-badge/rel-badge.ts +6 -1
  5. package/dist/components/rel-button/rel-button.ts +18 -2
  6. package/dist/components/rel-calendar/rel-calendar.ts +86 -18
  7. package/dist/components/rel-carousel/rel-carousel.ts +45 -2
  8. package/dist/components/rel-checkbox/rel-checkbox.ts +4 -1
  9. package/dist/components/rel-chip/rel-chip.ts +59 -19
  10. package/dist/components/rel-date-picker/date-parse.test.ts +116 -0
  11. package/dist/components/rel-date-picker/date-parse.ts +178 -0
  12. package/dist/components/rel-date-picker/rel-date-picker.ts +675 -93
  13. package/dist/components/rel-drawer/rel-drawer.ts +28 -61
  14. package/dist/components/rel-menu/rel-menu.ts +130 -15
  15. package/dist/components/rel-menubar/rel-menubar.ts +221 -63
  16. package/dist/components/rel-nav-menu/rel-nav-menu.ts +39 -3
  17. package/dist/components/rel-otp/rel-otp.ts +14 -4
  18. package/dist/components/rel-pagination/rel-pagination.ts +13 -0
  19. package/dist/components/rel-popover/rel-popover.ts +64 -5
  20. package/dist/components/rel-progress/rel-progress.ts +13 -4
  21. package/dist/components/rel-radio/README.md +6 -3
  22. package/dist/components/rel-radio/rel-radio.ts +236 -60
  23. package/dist/components/rel-range-picker/rel-range-picker.ts +581 -91
  24. package/dist/components/rel-resizable/rel-resizable-group.ts +20 -0
  25. package/dist/components/rel-segmented-button/rel-segmented-button.ts +55 -13
  26. package/dist/components/rel-select/rel-select.ts +203 -24
  27. package/dist/components/rel-sheet/rel-sheet.ts +184 -18
  28. package/dist/components/rel-sidebar/rel-sidebar.ts +50 -40
  29. package/dist/components/rel-skeleton/rel-skeleton.ts +9 -0
  30. package/dist/components/rel-slider/rel-slider.ts +6 -3
  31. package/dist/components/rel-snackbar/rel-snackbar.ts +20 -16
  32. package/dist/components/rel-stepper/rel-stepper.ts +47 -2
  33. package/dist/components/rel-switch/rel-switch.ts +3 -1
  34. package/dist/components/rel-tab/rel-tab.ts +32 -7
  35. package/dist/components/rel-textfield/rel-textfield.ts +263 -75
  36. package/dist/components/rel-time-picker/rel-time-picker.ts +749 -261
  37. package/dist/components/rel-time-picker/time-parse.test.ts +67 -0
  38. package/dist/components/rel-time-picker/time-parse.ts +85 -0
  39. package/dist/components/rel-toast/rel-toast.ts +4 -2
  40. package/dist/components/rel-tooltip/README.md +13 -5
  41. package/dist/components/rel-tooltip/rel-tooltip.ts +33 -0
  42. package/dist/components/rel-topbar/rel-topbar-actions.ts +1 -1
  43. package/dist/components/rel-topbar/rel-topbar-brand.ts +1 -1
  44. package/dist/components/rel-topbar/rel-topbar-dropdown-item.ts +1 -1
  45. package/dist/components/rel-topbar/rel-topbar-dropdown.ts +1 -1
  46. package/dist/components/rel-topbar/rel-topbar-item.ts +318 -319
  47. package/dist/components/rel-topbar/rel-topbar-mobile-panel.ts +1 -1
  48. package/dist/components/rel-topbar/rel-topbar-profile.ts +224 -224
  49. package/dist/components/rel-topbar/rel-topbar-utilities.ts +1 -1
  50. package/dist/components/rel-topbar/rel-topbar.ts +440 -440
  51. package/dist/components/rel-upload/rel-upload.ts +25 -0
  52. package/dist/index.d.mts +461 -23
  53. package/dist/index.d.ts +461 -23
  54. package/dist/index.js +3906 -807
  55. package/dist/index.js.map +1 -1
  56. package/dist/index.mjs +4143 -1055
  57. package/dist/index.mjs.map +1 -1
  58. package/package.json +2 -2
package/dist/index.d.ts CHANGED
@@ -11,6 +11,8 @@ declare class RelAccordion extends LitElement {
11
11
  /** Figma "Style" axis — named appearance because `style` collides with the HTML style attribute. */
12
12
  appearance: "shadow" | "plain";
13
13
  static styles: lit.CSSResult;
14
+ /** Stable id prefix wiring the header to its panel. */
15
+ private accordionId;
14
16
  private toggle;
15
17
  render(): lit_html.TemplateResult<1>;
16
18
  }
@@ -34,6 +36,13 @@ declare class RelAlert extends LitElement {
34
36
  type TextFieldState = "default" | "error" | "disabled";
35
37
  type TextFieldSize = "small" | "medium" | "large";
36
38
  type TextFieldVariant = "default" | "underline" | "legend";
39
+ /**
40
+ * How the prefix block is drawn (Figma `.Preffix` → Style):
41
+ * - `"clear"` — bare icon/text, no separator (the legacy `leftIcon` look)
42
+ * - `"divider"` — hairline between the prefix and the input
43
+ * - `"fill"` — tinted block flush with the control's left edge
44
+ */
45
+ type TextFieldPrefixStyle = "clear" | "divider" | "fill";
37
46
  /**
38
47
  * How a field presents its label. Shared by `<rel-textfield>`, `<rel-select>`
39
48
  * and `<rel-autocomplete>` so the three stay in step.
@@ -55,6 +64,17 @@ declare class RelTextfield extends LitElement {
55
64
  clickableRightIcon: boolean;
56
65
  /** Accessible label for the trailing icon when it is clickable. */
57
66
  rightIconAriaLabel?: string;
67
+ /** Short text rendered before the input, e.g. `"http://"` or `"$"`. Sits in
68
+ * the same prefix block as `prefixIcon` and is styled by `prefixStyle`. */
69
+ prefixText?: string;
70
+ /** Visual treatment of the prefix block — applies to the prefix icon, text
71
+ * and slotted `prefix` content alike. See `TextFieldPrefixStyle`. */
72
+ prefixStyle: TextFieldPrefixStyle;
73
+ /** Short text rendered after the input, e.g. `"kg"` or `".00"`. Shares the
74
+ * suffix block with `rightIcon` and slotted `suffix` content. */
75
+ suffixText?: string;
76
+ /** Draws a hairline between the input and the suffix block. */
77
+ suffixDivider: boolean;
58
78
  supportingText?: string;
59
79
  state: TextFieldState;
60
80
  size: TextFieldSize;
@@ -89,7 +109,10 @@ declare class RelTextfield extends LitElement {
89
109
  name?: string;
90
110
  fullWidth: boolean;
91
111
  private internalState;
112
+ /** Whether the `prefix` / `suffix` named slots currently have content. */
113
+ private slotted;
92
114
  private _wasError;
115
+ private _prefixObserver?;
93
116
  private uniqueId;
94
117
  private get isDisabled();
95
118
  private get isError();
@@ -101,6 +124,14 @@ declare class RelTextfield extends LitElement {
101
124
  private get labelHidden();
102
125
  static styles: lit.CSSResult;
103
126
  connectedCallback(): void;
127
+ disconnectedCallback(): void;
128
+ private handleSlotChange;
129
+ /** Clicking the (non-interactive) prefix/suffix area focuses the input, as a
130
+ * label would. Clickable icons and slotted controls keep their own clicks. */
131
+ private handleAffixClick;
132
+ /** Publishes the prefix block's width as --_rel-tf-prefix-w on its parent so
133
+ * the floated label can slide back over a clear-style prefix (see CSS). */
134
+ private observePrefix;
104
135
  willUpdate(changed: PropertyValues<this>): void;
105
136
  private _warnedNoName;
106
137
  updated(): void;
@@ -200,6 +231,8 @@ declare class RelAutocomplete extends LitElement {
200
231
  static styles: lit.CSSResult;
201
232
  disconnectedCallback(): void;
202
233
  protected willUpdate(changed: PropertyValues<this>): void;
234
+ private wasLoading;
235
+ protected updated(): void;
203
236
  /** Full normalized source list (remote results while searchFn drives, else items). */
204
237
  private get allItems();
205
238
  private normalizeItem;
@@ -219,6 +252,7 @@ declare class RelAutocomplete extends LitElement {
219
252
  /** Re-evaluates whether the panel should be showing for the current state. */
220
253
  private syncPanel;
221
254
  private onFieldInput;
255
+ private announceResults;
222
256
  private runSearch;
223
257
  private selectItem;
224
258
  /** Silent commit when typed text exactly matches an item (label or value, case-insensitive). */
@@ -226,6 +260,7 @@ declare class RelAutocomplete extends LitElement {
226
260
  private emitChanged;
227
261
  private clear;
228
262
  private onKeyDown;
263
+ private announceHighlighted;
229
264
  private onFocusIn;
230
265
  private onFocusOut;
231
266
  /** Delegates focus to the inner textfield. */
@@ -275,6 +310,7 @@ declare class RelBadge extends LitElement {
275
310
  textColor: string;
276
311
  static styles: lit.CSSResult;
277
312
  private get classes();
313
+ private get computedAriaLabel();
278
314
  updated(changedProperties: Map<PropertyKey, unknown>): void;
279
315
  render(): lit_html.TemplateResult<1>;
280
316
  }
@@ -352,10 +388,21 @@ declare class RelCalendar extends LitElement {
352
388
  private getCalendarDays;
353
389
  private getWeekdayLabels;
354
390
  private getMonthName;
391
+ private dayLabelFormatter;
392
+ private dayLabelLocale;
393
+ private formatDayLabel;
394
+ private announceMonth;
355
395
  private previousMonth;
356
396
  private nextMonth;
357
397
  private selectDay;
358
398
  private handleKeyDown;
399
+ /**
400
+ * Index of the single grid cell carrying tabindex="0". The old expression
401
+ * (`isSelected || (index === 0 && !focusedDate)`) left the grid with ZERO
402
+ * tab stops once the user arrowed around without a selection — Tab could
403
+ * never re-enter the calendar.
404
+ */
405
+ private getRovingIndex;
359
406
  render(): lit_html.TemplateResult<1>;
360
407
  }
361
408
 
@@ -402,6 +449,7 @@ declare class RelCarousel extends LitElement {
402
449
  private prefersReducedMotion;
403
450
  private clonesBefore;
404
451
  private _silentJumping;
452
+ private _autoplayTick;
405
453
  private _ro?;
406
454
  static styles: lit.CSSResult;
407
455
  connectedCallback(): void;
@@ -424,6 +472,8 @@ declare class RelCarousel extends LitElement {
424
472
  private handlePointerMove;
425
473
  private handlePointerUp;
426
474
  private handleKeyDown;
475
+ private handleFocusIn;
476
+ private handleFocusOut;
427
477
  private handleMouseEnter;
428
478
  private handleMouseLeave;
429
479
  updated(changedProperties: Map<string, any>): void;
@@ -523,6 +573,17 @@ declare class RelDatePicker extends LitElement {
523
573
  private focusedDate;
524
574
  private yearDropdownOpen;
525
575
  private monthDropdownOpen;
576
+ /** Uncommitted typed text; null → the input shows the committed value. */
577
+ private inputText;
578
+ /** Parse/range error message for the typed text (cleared on next input). */
579
+ private inputError;
580
+ /**
581
+ * Whether the next popover open should move focus into the calendar grid.
582
+ * Icon-click / ArrowDown / external `open` do (previous behavior); opening
583
+ * by clicking into the text input must NOT, or typing would be impossible.
584
+ */
585
+ private focusPanelOnOpen;
586
+ private triggerInput?;
526
587
  private triggerButton?;
527
588
  private popoverElement?;
528
589
  private calendarGrid?;
@@ -532,6 +593,12 @@ declare class RelDatePicker extends LitElement {
532
593
  private handleClickOutside;
533
594
  private handleEscapeKey;
534
595
  updated(changedProperties: Map<string, any>): void;
596
+ private showTopLayer;
597
+ private hideTopLayer;
598
+ private onReposition;
599
+ private updatePopoverPosition;
600
+ /** Focus the roving day cell (selected day, else today, else first). */
601
+ private focusPanelSelection;
535
602
  private parseISODate;
536
603
  private formatISODate;
537
604
  private formatDisplayDate;
@@ -540,6 +607,10 @@ declare class RelDatePicker extends LitElement {
540
607
  private getCalendarDays;
541
608
  private getWeekdayLabels;
542
609
  private getMonthName;
610
+ private dayLabelFormatter;
611
+ private dayLabelLocale;
612
+ private formatDayLabel;
613
+ private announceMonth;
543
614
  private getMonthsList;
544
615
  private getYearsList;
545
616
  private toggleCalendarPopover;
@@ -547,13 +618,43 @@ declare class RelDatePicker extends LitElement {
547
618
  private previousMonth;
548
619
  private nextMonth;
549
620
  private selectDay;
621
+ /** Commit a date: set value, reset typed text/error, fire rel-change. */
622
+ private commitDate;
550
623
  private clearValue;
551
- private handleTriggerKeydown;
624
+ /** Ambiguous "a/b" day/month order: display format wins, then locale. */
625
+ private get dayFirst();
626
+ /**
627
+ * Parse and commit the typed text (Enter / blur). Invalid or out-of-range
628
+ * text keeps the field in an error state with the text preserved for
629
+ * correction; the committed value is untouched (Escape reverts the text).
630
+ */
631
+ private commitTypedValue;
632
+ /** Today ± offset days, committed directly (T shortcuts / popover chips). */
633
+ private applyDayOffsetShortcut;
634
+ private handleInputInput;
635
+ private handleInputKeydown;
636
+ /** Clicking anywhere on the field opens the popover, keeping focus typed-in. */
637
+ private handleFieldClick;
638
+ /** The calendar icon toggles the popover with the previous focus behavior. */
639
+ private handleIconClick;
640
+ /** Commit typed text when focus leaves the component entirely. */
641
+ private handleInputBlur;
642
+ /** T inside the panel (grid/header focus) applies Today directly. */
643
+ private handlePanelKeydown;
552
644
  private handleDayKeydown;
553
645
  private toggleMonthDropdown;
554
646
  private toggleYearDropdown;
647
+ private handleDropdownTriggerKeydown;
648
+ private focusOpenDropdownSelection;
649
+ private handleDropdownKeydown;
555
650
  private selectMonth;
556
651
  private selectYear;
652
+ /**
653
+ * Index of the single grid cell carrying tabindex="0". The old expression
654
+ * (`isSelected || (index === 0 && !value)`) left the grid with ZERO tab
655
+ * stops once the user arrowed around without a selection.
656
+ */
657
+ private getRovingIndex;
557
658
  render(): lit_html.TemplateResult<1>;
558
659
  }
559
660
 
@@ -575,6 +676,7 @@ declare class RelRangePicker extends LitElement {
575
676
  maxHeight: string;
576
677
  placeholder: string;
577
678
  triggerVariant: "default" | "icon";
679
+ clearable: boolean;
578
680
  private draftFrom;
579
681
  private draftTo;
580
682
  private draftActivePreset;
@@ -582,7 +684,13 @@ declare class RelRangePicker extends LitElement {
582
684
  private viewMonth;
583
685
  private isSelectingRange;
584
686
  private hoverDate;
585
- private triggerButton?;
687
+ /** ISO date of the cell that owns the grid's single Tab stop (roving tabindex). */
688
+ private focusedDate;
689
+ /** Uncommitted typed text; null → the input shows the committed range. */
690
+ private inputText;
691
+ /** Parse/range error message for the typed text (cleared on next input). */
692
+ private inputError;
693
+ private triggerInput?;
586
694
  private popoverEl?;
587
695
  private backdropEl?;
588
696
  static styles: lit.CSSResult;
@@ -600,6 +708,31 @@ declare class RelRangePicker extends LitElement {
600
708
  private cancelPopover;
601
709
  private applySelection;
602
710
  private resetDraft;
711
+ private clearRange;
712
+ /**
713
+ * Parse "date → date" text into an ISO from/to pair, or null. Each side
714
+ * goes through rel-date-picker's parseDateInput, so ISO / numeric /
715
+ * month-name dates and the t / t±N shortcuts all work per side.
716
+ * Separators: "→", "..", spaced "-", or the word "to" ("t - t+7",
717
+ * "21/7 to 28/7"). A single date yields a one-day range. Reversed input
718
+ * is normalized (from ≤ to). dayFirst is hard-coded false because the
719
+ * displayed format is always MM/DD/YYYY.
720
+ */
721
+ private parseRangeText;
722
+ /**
723
+ * Parse and commit the typed text (Enter / blur). Invalid or out-of-range
724
+ * text keeps the field in an error state with the text preserved for
725
+ * correction; the committed range is untouched (Escape reverts the text).
726
+ */
727
+ private commitTypedValue;
728
+ private handleInputInput;
729
+ private handleInputKeydown;
730
+ /** Clicking anywhere on the field opens the panel (never closes). */
731
+ private handleFieldClick;
732
+ /** The calendar icon toggles the panel (previous trigger behavior). */
733
+ private handleIconClick;
734
+ /** Commit typed text when focus leaves the component entirely. */
735
+ private handleInputBlur;
603
736
  private getTriggerLabel;
604
737
  /** Formatted "MM/DD/YYYY → MM/DD/YYYY" (or single date / "") for the current committed `from`/`to`. */
605
738
  get displayLabel(): string;
@@ -613,10 +746,20 @@ declare class RelRangePicker extends LitElement {
613
746
  private previousMonth;
614
747
  private nextMonth;
615
748
  private getMonthName;
749
+ private dayLabelFormatter;
750
+ private formatDayLabel;
616
751
  private getWeekdayLabels;
617
752
  private getCalendarDays;
618
753
  private handleDayClick;
619
754
  private handleDayHover;
755
+ /**
756
+ * The cell that carries tabindex="0". Prefers the last roved date, then the
757
+ * draft start, then today, then the first selectable day — always exactly
758
+ * one Tab stop, even after arrowing away with nothing selected.
759
+ */
760
+ private getRovingIso;
761
+ private handleGridKeydown;
762
+ private moveGridFocusTo;
620
763
  render(): lit_html.TemplateResult<1>;
621
764
  }
622
765
  declare global {
@@ -651,6 +794,12 @@ declare class RelChip extends LitElement {
651
794
  static styles: lit.CSSResult;
652
795
  private get classes();
653
796
  private handleRemove;
797
+ /** The chip body is announced as a button, so it must also act on keys:
798
+ * Enter/Space re-dispatch as a host click (consumers' @click handlers
799
+ * fire), Delete/Backspace remove when removable, and arrow keys ask the
800
+ * container to reorder a draggable chip (`chip-move`) — the keyboard
801
+ * alternative to pointer drag-and-drop. */
802
+ private handleKeydown;
654
803
  private onDragStart;
655
804
  private onDragEnd;
656
805
  protected updated(changed: Map<string, unknown>): void;
@@ -723,7 +872,6 @@ declare class RelDrawer extends LitElement {
723
872
  private handleClose;
724
873
  private lockScroll;
725
874
  private unlockScroll;
726
- private getFocusableElements;
727
875
  private handleKeydown;
728
876
  private handleOverlayClick;
729
877
  private handleCloseButtonClick;
@@ -804,6 +952,7 @@ declare class RelStepper extends LitElement {
804
952
  private isProgress;
805
953
  private isDisabled;
806
954
  private getStepClasses;
955
+ private onStepKeyDown;
807
956
  private onStepClick;
808
957
  private renderIndicator;
809
958
  render(): lit_html.TemplateResult<1>;
@@ -814,30 +963,56 @@ interface RadioOption {
814
963
  label: string;
815
964
  supportingText?: string;
816
965
  disabled?: boolean;
966
+ /** rel-icon name rendered before the label (Figma Has Icon/Avatar, Type=Icon). */
967
+ icon?: string;
968
+ /** 1–2 initials rendered as a circular chip before the label (Type=Avatar).
969
+ * Ignored when `icon` is also set. */
970
+ avatar?: string;
817
971
  }
972
+ type RadioSize = "small" | "medium" | "large";
973
+ type RadioDensity = "compact" | "comfortable";
974
+ type RadioAlignment = "leading" | "trailing";
818
975
  declare class RelRadio extends LitElement {
819
976
  options: RadioOption[];
820
977
  name: string;
821
978
  /** Layout axis of the option list. `"vertical"` (default) stacks options in
822
979
  * a column; `"horizontal"` lays them out in a wrapping row. */
823
980
  orientation: Orientation;
824
- /** Control scale. `"medium"` (default) = 20px circle with body-lg labels;
825
- * `"small"` = 16px circle with body-md labels. */
826
- size: "small" | "medium";
981
+ /** Control scale (Figma Size). `"medium"` (default, Figma M) = 20px control
982
+ * with body-lg labels; `"large"` (Figma L) = 24px control with body-xl
983
+ * labels; `"small"` (Figma S) = 20px control with body-md labels — S and M
984
+ * share the control size in the spec, only typography steps down. */
985
+ size: RadioSize;
986
+ /** Supporting-text layout (Figma Density). `"compact"` (default) puts the
987
+ * supporting text on its own line under the row, indented past the control;
988
+ * `"comfortable"` stacks label + supporting text beside the control. */
989
+ density: RadioDensity;
990
+ /** Control position (Figma Layout). `"leading"` (default) renders the
991
+ * control before the label; `"trailing"` pushes it to the far edge of the
992
+ * row. Trailing has no compact layout in the spec, so it always renders
993
+ * with comfortable density. Meant for vertical orientation — trailing
994
+ * options stretch to the group's full width. */
995
+ alignment: RadioAlignment;
827
996
  selectedValue: string | null;
828
997
  disabled: boolean;
829
998
  error: boolean;
830
999
  errorMessage: string;
1000
+ /** Accessible name for the radiogroup (it renders no visible group label). */
1001
+ fieldAriaLabel?: string;
831
1002
  value: string;
832
1003
  checked: boolean;
833
1004
  private _onChange;
834
1005
  private _onTouched;
835
1006
  static styles: lit.CSSResult;
1007
+ protected willUpdate(changed: PropertyValues<this>): void;
1008
+ /** Trailing has no compact layout in the spec — trailing wins. */
1009
+ private get effectiveDensity();
836
1010
  private onChange;
837
1011
  registerOnChange(fn: (value: string) => void): void;
838
1012
  registerOnTouched(fn: () => void): void;
839
1013
  writeValue(value: string): void;
840
1014
  private get hasSelection();
1015
+ private renderPrefix;
841
1016
  private renderOption;
842
1017
  render(): lit_html.TemplateResult<1>;
843
1018
  }
@@ -863,8 +1038,12 @@ declare class RelMenu extends LitElement {
863
1038
  type: MenuType;
864
1039
  items: MenuItem[];
865
1040
  private focusedIndex;
1041
+ /** Index inside the open submenu that virtual focus sits on; -1 = main list. */
1042
+ private subFocusedIndex;
866
1043
  private _items;
867
- 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 border-radius: var(--rel-border-radius-md);\n box-shadow: var(--rel-shadows-shadow-300);\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 .rel-menu-item {\n position: relative;\n display: flex;\n align-items: center;\n gap: var(--rel-spacing-xs);\n padding: var(--rel-spacing-xs) var(--rel-spacing-sm);\n line-height: var(--rel-body-sm-regular-line-height);\n cursor: pointer;\n transition: background 0.15s ease, color 0.15s ease;\n user-select: none;\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-md);\n border-top-right-radius: var(--rel-border-radius-md);\n }\n\n .rel-menu > .rel-menu-item:last-child {\n border-bottom-left-radius: var(--rel-border-radius-md);\n border-bottom-right-radius: var(--rel-border-radius-md);\n }\n\n .rel-menu-item:hover {\n background: var(--rel-bg-neutral-elevated-1);\n }\n\n .rel-menu-item:active {\n background: var(--rel-bg-neutral-elevated-1);\n }\n\n .rel-menu-item.active {\n background: var(--rel-bg-neutral-elevated-2);\n font-weight: var(--rel-body-lg-medium-font-weight);\n }\n\n .rel-menu-item.disabled {\n background-color: transparent;\n color: var(--rel-text-disabled);\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 .rel-menu-item[data-focused=\"true\"] {\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-md);\n box-shadow: var(--rel-shadows-shadow-300);\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-xs) var(--rel-spacing-sm);\n }\n ";
1044
+ /** Stable id prefix for aria-activedescendant targets. */
1045
+ private menuId;
1046
+ 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 border-radius: var(--rel-border-radius-md);\n box-shadow: var(--rel-shadows-shadow-300);\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 .rel-menu-item {\n position: relative;\n display: flex;\n align-items: center;\n gap: var(--rel-spacing-xs);\n padding: var(--rel-spacing-xs) var(--rel-spacing-sm);\n line-height: var(--rel-body-sm-regular-line-height);\n cursor: pointer;\n transition: background 0.15s ease, color 0.15s ease;\n user-select: none;\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-md);\n border-top-right-radius: var(--rel-border-radius-md);\n }\n\n .rel-menu > .rel-menu-item:last-child {\n border-bottom-left-radius: var(--rel-border-radius-md);\n border-bottom-right-radius: var(--rel-border-radius-md);\n }\n\n .rel-menu-item:hover {\n background: var(--rel-bg-neutral-elevated-1);\n }\n\n .rel-menu-item:active {\n background: var(--rel-bg-neutral-elevated-1);\n }\n\n .rel-menu-item.active {\n background: var(--rel-bg-neutral-elevated-2);\n font-weight: var(--rel-body-lg-medium-font-weight);\n }\n\n .rel-menu-item.disabled {\n background-color: transparent;\n color: var(--rel-text-disabled);\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-md);\n box-shadow: var(--rel-shadows-shadow-300);\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-xs) var(--rel-spacing-sm);\n }\n ";
868
1047
  createRenderRoot(): this;
869
1048
  connectedCallback(): void;
870
1049
  disconnectedCallback(): void;
@@ -876,19 +1055,35 @@ declare class RelMenu extends LitElement {
876
1055
  private onItemLeave;
877
1056
  private onItemClick;
878
1057
  private onSubClick;
1058
+ /** Keyboard-open locks the submenu (no hover-out to close it) and moves
1059
+ * virtual focus onto its first entry. */
1060
+ private openSubmenuAt;
1061
+ private closeSubmenus;
879
1062
  private onKeydown;
880
1063
  private renderMenuItem;
881
1064
  render(): lit_html.TemplateResult<1>;
882
1065
  }
883
1066
 
884
1067
  declare class RelMenubar extends LitElement {
1068
+ /**
1069
+ * Opt-in: actually BIND the keys declared via data-shortcut (e.g.
1070
+ * data-shortcut="Ctrl+Z"), not just display them. "Ctrl" matches Cmd on
1071
+ * Apple platforms when written as "Mod". Keystrokes originating in
1072
+ * inputs/textareas/contenteditable are ignored.
1073
+ */
1074
+ bindShortcuts: boolean;
885
1075
  private menus;
886
1076
  private openMenuIndex;
887
1077
  private focusedTriggerIndex;
888
1078
  private focusedItemIndex;
1079
+ /** Virtual-focus index inside the open submenu; -1 = focus is in the panel. */
1080
+ private focusedSubItemIndex;
889
1081
  private openSubmenuElement;
890
1082
  private hoverSwitchEnabled;
891
1083
  private _onScroll?;
1084
+ /** Stable id prefix for aria-activedescendant targets (light DOM, so ids
1085
+ * must be unique per menubar instance on the page). */
1086
+ private menubarId;
892
1087
  static readonly _css = "\n /* This component renders into LIGHT DOM (createRenderRoot returns this), so\n :host / <slot> don't work here \u2014 the raw <rel-menubar-menu> definitions\n are data holders that must be hidden with a plain selector. Relying on the\n global rel-menubar-menu.css is not enough: it can't cross a consumer's\n shadow boundary AND it's a no-op in the tsup-built package (css:\"copy\"). */\n rel-menubar {\n color: var(--rel-text-default);\n display: block;\n }\n\n rel-menubar-menu {\n display: none;\n }\n\n .menubar-container {\n display: flex;\n align-items: stretch;\n background: var(--rel-bg-surface-elevated);\n border: var(--rel-stroke-sm) solid var(--rel-border-default);\n border-radius: var(--rel-border-radius-sm);\n overflow: hidden;\n padding: 0;\n gap: var(--rel-spacing-3xs);\n height: 40px;\n width: fit-content;\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 .menubar-trigger {\n display: flex;\n align-items: center;\n align-self: stretch;\n height: 100%;\n padding: 0 var(--rel-spacing-sm);\n background: transparent;\n border: none;\n cursor: pointer;\n font-family: inherit;\n font-size: inherit;\n color: var(--rel-text-label);\n transition: background 0.15s ease;\n user-select: none;\n outline: none;\n }\n\n .menubar-trigger:hover,\n .menubar-trigger.active {\n background: var(--rel-bg-neutral-elevated-2);\n }\n\n .menubar-trigger:focus-visible {\n outline: 2px solid var(--rel-border-focus);\n outline-offset: 2px;\n }\n\n .menubar-panel {\n position: fixed;\n min-width: 220px;\n background: var(--rel-bg-surface);\n border: var(--rel-stroke-sm) solid var(--rel-border-default);\n border-radius: var(--rel-border-radius-sm);\n box-shadow: var(--rel-shadows-shadow-300);\n padding: 0;\n overflow: hidden;\n z-index: 1000;\n display: none;\n }\n\n .menubar-panel.open {\n display: block;\n }\n\n .menu-item {\n position: relative;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--rel-spacing-sm);\n padding: var(--rel-spacing-xs) var(--rel-spacing-md);\n cursor: pointer;\n transition: background 0.15s ease;\n user-select: none;\n color: var(--rel-text-label);\n line-height: var(--rel-body-sm-regular-line-height);\n }\n\n .menu-item:hover,\n .menu-item.focused {\n background: var(--rel-bg-neutral-elevated-2);\n }\n\n .menu-item[data-disabled=\"true\"] {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n .menu-separator {\n height: var(--rel-stroke-sm);\n background: var(--rel-border-default);\n margin: 0;\n padding: 0;\n }\n\n rel-icon {\n display: inline-flex;\n pointer-events: none;\n }\n\n .menu-submenu-trigger {\n position: relative;\n }\n\n .menu-submenu-trigger rel-icon {\n opacity: 0.6;\n }\n\n .submenu-panel {\n position: fixed;\n min-width: 200px;\n background: var(--rel-bg-surface);\n border: var(--rel-stroke-sm) solid var(--rel-border-default);\n border-radius: var(--rel-border-radius-sm);\n box-shadow: var(--rel-shadows-shadow-300);\n padding: 0;\n overflow: hidden;\n z-index: 1001;\n display: none;\n }\n\n .submenu-panel.open {\n display: block;\n }\n\n .menu-shortcut {\n opacity: 0.6;\n margin-left: auto;\n display: flex;\n align-items: center;\n gap: var(--rel-spacing-3xs);\n font-family: var(--rel-body-xs-regular-font-family);\n font-weight: var(--rel-body-xs-regular-font-weight);\n font-size: var(--rel-body-xs-regular-font-size);\n line-height: var(--rel-body-xs-regular-line-height);\n letter-spacing: var(--rel-body-xs-regular-letter-spacing);\n }\n\n .menubar-panel rel-checkbox {\n display: block;\n width: 100%;\n padding: var(--rel-spacing-xs, 8px) var(--rel-spacing-md, 16px);\n box-sizing: border-box;\n cursor: pointer;\n }\n ";
893
1088
  createRenderRoot(): this;
894
1089
  connectedCallback(): void;
@@ -896,17 +1091,24 @@ declare class RelMenubar extends LitElement {
896
1091
  private _repositionOpenSubmenu;
897
1092
  private _repositionOpenMenu;
898
1093
  private discoverMenus;
1094
+ private handleShortcutKeyDown;
899
1095
  private handleOutsideClick;
900
1096
  private closeAllMenus;
1097
+ /** Close everything and put DOM focus back on the trigger that owned the
1098
+ * open menu. (Capturing the index BEFORE closing — closeAllMenus resets
1099
+ * openMenuIndex to -1, which used to drop focus to <body>.) */
1100
+ private closeAllMenusAndRefocusTrigger;
901
1101
  private openMenu;
902
1102
  private handleTriggerClick;
903
1103
  private handleTriggerMouseEnter;
904
1104
  private handleTriggerKeyDown;
905
1105
  private focusTrigger;
906
1106
  private handlePanelKeyDown;
1107
+ private getSubmenuItems;
907
1108
  private getFocusableItems;
908
1109
  private focusMenuItem;
909
1110
  private handleItemClick;
1111
+ private dispatchSelectEvent;
910
1112
  private openSubmenu;
911
1113
  private handleSubmenuItemClick;
912
1114
  private dispatchChangeEvent;
@@ -938,7 +1140,7 @@ declare class RelNavbar extends LitElement {
938
1140
 
939
1141
  /**
940
1142
  * @deprecated Part of the deprecated rel-topbar family. Use a plain
941
- * `<a>`/logo markup inside the "App Topbar" recipe at /docs/recipes/app-topbar.
1143
+ * `<a>`/logo markup inside the "Navbar" recipe at /docs/recipes/navbar.
942
1144
  */
943
1145
  declare class RelTopbarBrand extends LitElement {
944
1146
  src: string;
@@ -955,7 +1157,7 @@ declare global {
955
1157
 
956
1158
  /**
957
1159
  * @deprecated Part of the deprecated rel-topbar family. Use plain
958
- * `rel-button` elements inside the "App Topbar" recipe at /docs/recipes/app-topbar.
1160
+ * `rel-button` elements inside the "Navbar" recipe at /docs/recipes/navbar.
959
1161
  */
960
1162
  declare class RelTopbarActions extends LitElement {
961
1163
  static styles: lit.CSSResult;
@@ -969,7 +1171,7 @@ declare global {
969
1171
 
970
1172
  /**
971
1173
  * @deprecated Part of the deprecated rel-topbar family. Use plain
972
- * `rel-button` (icon variant) elements inside the "App Topbar" recipe at /docs/recipes/app-topbar.
1174
+ * `rel-button` (icon variant) elements inside the "Navbar" recipe at /docs/recipes/navbar.
973
1175
  */
974
1176
  declare class RelTopbarUtilities extends LitElement {
975
1177
  static styles: lit.CSSResult;
@@ -983,7 +1185,7 @@ declare global {
983
1185
 
984
1186
  /**
985
1187
  * @deprecated Part of the deprecated rel-topbar family. Use `rel-drawer`
986
- * for the mobile nav panel in the "App Topbar" recipe at /docs/recipes/app-topbar.
1188
+ * for the mobile nav panel in the "Navbar" recipe at /docs/recipes/navbar.
987
1189
  */
988
1190
  declare class RelTopbarMobilePanel extends LitElement {
989
1191
  static styles: lit.CSSResult;
@@ -997,7 +1199,7 @@ declare global {
997
1199
 
998
1200
  /**
999
1201
  * @deprecated Part of the deprecated rel-topbar family. Use `rel-menu` or
1000
- * `rel-popover` for nav dropdowns in the "App Topbar" recipe at /docs/recipes/app-topbar.
1202
+ * `rel-popover` for nav dropdowns in the "Navbar" recipe at /docs/recipes/navbar.
1001
1203
  */
1002
1204
  declare class RelTopbarDropdown extends LitElement {
1003
1205
  static styles: lit.CSSResult;
@@ -1011,7 +1213,7 @@ declare global {
1011
1213
 
1012
1214
  /**
1013
1215
  * @deprecated Part of the deprecated rel-topbar family. Use `rel-menu`'s
1014
- * item slots or `rel-popover` content in the "App Topbar" recipe at /docs/recipes/app-topbar.
1216
+ * item slots or `rel-popover` content in the "Navbar" recipe at /docs/recipes/navbar.
1015
1217
  */
1016
1218
  declare class RelTopbarDropdownItem extends LitElement {
1017
1219
  label: string;
@@ -1034,7 +1236,7 @@ type TopbarForceLayout = "auto" | "desktop" | "tablet" | "mobile";
1034
1236
  *
1035
1237
  * @deprecated This component's fixed slot structure, hardcoded breakpoint,
1036
1238
  * and built-in overflow logic are too rigid for most real layouts. Use the
1037
- * "App Topbar" recipe at /docs/recipes/app-topbar instead, composed from
1239
+ * "Navbar" recipe at /docs/recipes/navbar instead, composed from
1038
1240
  * rel-menu/rel-popover, rel-drawer, and rel-button. Still supported, but
1039
1241
  * will be removed in a future major version.
1040
1242
  *
@@ -1087,8 +1289,7 @@ declare class RelTopbar extends LitElement {
1087
1289
  /**
1088
1290
  * `rel-topbar-item` — A navigation item within rel-topbar.
1089
1291
  *
1090
- * @deprecated Part of the deprecated rel-topbar family. Use the "App
1091
- * Topbar" recipe at /docs/recipes/app-topbar, which composes nav links and
1292
+ * @deprecated Part of the deprecated rel-topbar family. Use the "Navbar" recipe at /docs/recipes/navbar, which composes nav links and
1092
1293
  * `rel-menu`/`rel-popover` dropdowns directly instead of a dedicated item component.
1093
1294
  *
1094
1295
  * Renders as an `<a>` when `href` is supplied, otherwise a `<button>`.
@@ -1138,7 +1339,7 @@ type TopbarRewardTier = "gold" | "silver" | "platinum" | "bronze" | "regular";
1138
1339
  * `rel-topbar-profile` — Profile / user tile for the topbar right region.
1139
1340
  *
1140
1341
  * @deprecated Part of the deprecated rel-topbar family. Compose `rel-avatar`
1141
- * directly inside the "App Topbar" recipe at /docs/recipes/app-topbar.
1342
+ * directly inside the "Navbar" recipe at /docs/recipes/navbar.
1142
1343
  *
1143
1344
  * type="avatar" Customer avatar with initials circle + optional name/subtitle.
1144
1345
  * type="carrier" Carrier reward tile with reward-tier badge + name/subtitle.
@@ -1279,8 +1480,18 @@ declare class RelPopover extends LitElement {
1279
1480
  private get activeOpen();
1280
1481
  private setOpen;
1281
1482
  private onHostPointerDown;
1483
+ private onHostKeyDown;
1282
1484
  private onOpen;
1283
1485
  private onClose;
1486
+ /**
1487
+ * Light-dismiss every other open popover that isn't an ancestor of this
1488
+ * one (nested popovers keep their parent open). This is what the outside-
1489
+ * pointerdown listener would do for a click on our trigger — except the
1490
+ * trigger handler stops that pointerdown in the capture phase, and a
1491
+ * keyboard (Enter/Space) or programmatic open never produces one at all.
1492
+ * Honors each popover's own `closeOnOutsideClick` setting.
1493
+ */
1494
+ private closeOtherPopovers;
1284
1495
  private showNativePopover;
1285
1496
  private hideNativePopover;
1286
1497
  private addGlobalListeners;
@@ -1314,10 +1525,12 @@ type ProgressMode = "linear-determinate" | "linear-indeterminate" | "circular-de
1314
1525
  declare class RelProgress extends LitElement {
1315
1526
  mode: ProgressMode;
1316
1527
  value: number;
1528
+ label: string;
1317
1529
  private radius;
1318
1530
  private circumference;
1319
1531
  static styles: lit.CSSResult;
1320
1532
  private get safeValue();
1533
+ private get accessibleName();
1321
1534
  private renderLinearDeterminate;
1322
1535
  private renderLinearIndeterminate;
1323
1536
  private renderCircularDeterminate;
@@ -1404,8 +1617,13 @@ declare class RelSegmentedButton extends LitElement {
1404
1617
  active: string;
1405
1618
  size: SegmentSize;
1406
1619
  align: SegmentAlign;
1620
+ /** Accessible name for the radiogroup (it renders no visible group label). */
1621
+ fieldAriaLabel?: string;
1407
1622
  static styles: lit.CSSResult;
1408
1623
  private setActive;
1624
+ /** APG radio-group navigation: arrows move focus AND select, the group is
1625
+ * one Tab stop (roving tabindex on the selected segment). */
1626
+ private onKeyDown;
1409
1627
  private renderSegment;
1410
1628
  render(): lit_html.TemplateResult<1>;
1411
1629
  }
@@ -1459,6 +1677,10 @@ declare class RelSelect extends LitElement {
1459
1677
  noSearch: boolean;
1460
1678
  private open;
1461
1679
  private highlightedIndex;
1680
+ /** Virtual highlight on the Select-All header row (multi mode). */
1681
+ private selectAllHighlighted;
1682
+ private typeaheadBuffer;
1683
+ private typeaheadTimer;
1462
1684
  private panelStyle;
1463
1685
  private searchValue;
1464
1686
  private internalState;
@@ -1515,6 +1737,12 @@ declare class RelSelect extends LitElement {
1515
1737
  private onSearchInput;
1516
1738
  private onSearchKeyDown;
1517
1739
  private handleListNavigation;
1740
+ /** APG typeahead for the no-search mode (with a search field, typing
1741
+ * filters instead). Accumulates printable keys for 500 ms and jumps the
1742
+ * highlight to the next option whose label starts with the buffer. */
1743
+ private handleTypeahead;
1744
+ private activateHighlighted;
1745
+ private scrollHighlightedIntoView;
1518
1746
  private renderChips;
1519
1747
  /**
1520
1748
  * Hidden, unconstrained copy of every selected chip used purely for width
@@ -1551,19 +1779,40 @@ declare global {
1551
1779
  }
1552
1780
 
1553
1781
  type ModalSide = "top" | "bottom" | "center";
1782
+ type ModalSize = "sm" | "md" | "lg" | "xl" | "fullscreen";
1554
1783
  declare class RelModal extends LitElement {
1555
1784
  open: boolean;
1556
1785
  side: ModalSide;
1557
1786
  title: string;
1558
1787
  description: string;
1788
+ /** Alias for `description`; rendered only when `description` is empty. */
1789
+ subtitle: string;
1559
1790
  dismissible: boolean;
1560
1791
  modal: boolean;
1792
+ /** Panel width scale. `md` matches the Figma Modal Sheet width; `fullscreen` covers the viewport. */
1793
+ size: ModalSize;
1794
+ closeOnBackdropClick: boolean;
1795
+ closeOnEscape: boolean;
1796
+ showCloseButton: boolean;
1797
+ /** Move focus into the panel when opened (modal mode only). */
1798
+ autoFocus: boolean;
1799
+ /** Return focus to the opener when closed. */
1800
+ restoreFocus: boolean;
1801
+ /** When false, the body no longer scrolls internally — tall content clips at the panel's max height. */
1802
+ scrollable: boolean;
1803
+ /** Shows a spinner overlay and blocks all interaction inside the panel. */
1804
+ loading: boolean;
1805
+ /** Blocks user-initiated closes (X / backdrop / Escape) and footer actions. */
1806
+ disabled: boolean;
1807
+ /** Stacking order of the whole modal (backdrop + panel). */
1808
+ zIndex: number;
1561
1809
  /**
1562
1810
  * Figma "Breakpoint" axis. responsive (default) collapses to a full-width
1563
1811
  * bottom sheet below 480px; web keeps the fixed desktop panel at every size.
1564
1812
  */
1565
1813
  breakpoint: "responsive" | "web";
1566
1814
  private overlayElement;
1815
+ private panelElement;
1567
1816
  private previousFocus;
1568
1817
  private originalBodyOverflow;
1569
1818
  private originalBodyPaddingRight;
@@ -1607,6 +1856,7 @@ declare class RelSlider extends LitElement {
1607
1856
  value: number;
1608
1857
  /** Upper value when type="range" (attribute value-end). */
1609
1858
  valueEnd: number;
1859
+ fieldAriaLabel?: string;
1610
1860
  private ticks;
1611
1861
  private _onChange;
1612
1862
  private _onTouched;
@@ -1669,6 +1919,7 @@ declare class RelSwitch extends LitElement {
1669
1919
  checked: boolean;
1670
1920
  disabled: boolean;
1671
1921
  value: string;
1922
+ fieldAriaLabel?: string;
1672
1923
  private _onChange;
1673
1924
  private _onTouched;
1674
1925
  private isFocused;
@@ -1706,11 +1957,14 @@ declare class RelTab extends LitElement {
1706
1957
  /** Tab size — styled via :host([size]) rules. */
1707
1958
  size: "sm" | "md" | "lg";
1708
1959
  private _tabItems;
1960
+ /** Stable id prefix linking tabs to the shared tabpanel. */
1961
+ private tabsId;
1709
1962
  static styles: lit.CSSResult;
1710
1963
  private _onSlotChange;
1711
1964
  private get _effectiveTabs();
1712
1965
  private selectTab;
1713
1966
  private onKeyDown;
1967
+ private selectAndFocusTab;
1714
1968
  render(): lit_html.TemplateResult<1>;
1715
1969
  }
1716
1970
 
@@ -1722,19 +1976,27 @@ declare class RelTab extends LitElement {
1722
1976
  * column selections are held in draft state until Apply is clicked; Cancel,
1723
1977
  * Escape, or an outside click discards the draft.
1724
1978
  *
1725
- * The committed `value` is ALWAYS 24-hour "HH:mm" regardless of the display
1726
- * `format`, so consumers get a consistent machine-readable string.
1979
+ * The committed `value` is ALWAYS 24-hour "HH:mm" (or "HH:mm:ss" when
1980
+ * `seconds` is enabled) regardless of the display `format`, so consumers get
1981
+ * a consistent machine-readable string.
1982
+ *
1983
+ * Panel visuals follow the Figma "Time Picker" component (Reliability DS
1984
+ * V1.0, node 8301:3771): fixed panel widths 220/250/280 by size, full-bleed
1985
+ * columns with 1px dividers, neutral selected state, and a Now / Cancel / OK
1986
+ * footer whose buttons are always size S.
1727
1987
  */
1728
1988
  declare class RelTimePicker extends LitElement {
1729
- /** Committed time, 24-hour "HH:mm" (e.g. "09:30", "17:05"). */
1989
+ /** Committed time, 24-hour "HH:mm" or "HH:mm:ss" (e.g. "09:30", "17:05:30"). */
1730
1990
  value: string | null;
1731
1991
  placeholder: string;
1732
1992
  label: string | null;
1733
1993
  disabled: boolean;
1734
1994
  required: boolean;
1735
1995
  readonly: boolean;
1736
- /** Display format of the trigger text and hour column. Value stays "HH:mm". */
1996
+ /** Display format of the trigger text and hour column. Value stays 24-hour. */
1737
1997
  format: "12h" | "24h";
1998
+ /** Shows the seconds column; committed value becomes "HH:mm:ss". */
1999
+ seconds: boolean;
1738
2000
  /** Minute granularity of the minutes column (1, 5, 10, 15, 30, …). */
1739
2001
  minuteStep: number;
1740
2002
  /** Earliest selectable time, "HH:mm" (inclusive). */
@@ -1754,7 +2016,20 @@ declare class RelTimePicker extends LitElement {
1754
2016
  fullWidth: boolean;
1755
2017
  private draftHour;
1756
2018
  private draftMinute;
2019
+ private draftSecond;
2020
+ /** Uncommitted typed text; null → the input shows the committed value. */
2021
+ private inputText;
2022
+ /** Parse/range error message for the typed text (cleared on next input). */
2023
+ private inputError;
2024
+ /**
2025
+ * Whether the next popover open should move focus into the columns.
2026
+ * Icon-click / ArrowDown / external `open` do (previous behavior); opening
2027
+ * by clicking into the text input must NOT, or typing would be impossible.
2028
+ */
2029
+ private focusPanelOnOpen;
2030
+ private triggerInput?;
1757
2031
  private triggerButton?;
2032
+ private popoverElement?;
1758
2033
  private columnsElement?;
1759
2034
  static styles: lit.CSSResult;
1760
2035
  connectedCallback(): void;
@@ -1762,6 +2037,12 @@ declare class RelTimePicker extends LitElement {
1762
2037
  private handleClickOutside;
1763
2038
  private handleEscapeKey;
1764
2039
  updated(changedProperties: Map<string, any>): void;
2040
+ private showTopLayer;
2041
+ private hideTopLayer;
2042
+ private onReposition;
2043
+ private updatePopoverPosition;
2044
+ /** Focus the selected (or first enabled) option in the columns. */
2045
+ private focusPanelSelection;
1765
2046
  private parseTime;
1766
2047
  private formatValue;
1767
2048
  private formatDisplay;
@@ -1777,6 +2058,8 @@ declare class RelTimePicker extends LitElement {
1777
2058
  private isPeriodEnabled;
1778
2059
  /** Hour column entries: display label + the 0–23 hour it maps to. */
1779
2060
  private getHourOptions;
2061
+ /** Seconds shown in the seconds column (always step 1). */
2062
+ private getSecondOptions;
1780
2063
  private get draftPeriod();
1781
2064
  private resetDraftFromValue;
1782
2065
  /** Snap the draft minute to the nearest valid step minute for the hour. */
@@ -1785,14 +2068,40 @@ declare class RelTimePicker extends LitElement {
1785
2068
  /** Close discarding the draft (Cancel / Escape / outside click). */
1786
2069
  private cancelPopover;
1787
2070
  private applySelection;
2071
+ /** Commit a time: set value, reset typed text/error, fire rel-change. */
2072
+ private commitTime;
1788
2073
  /** Set the draft to the current time, snapped to the minute step and range. */
1789
2074
  private selectNow;
1790
2075
  private clearValue;
2076
+ /**
2077
+ * Parse and commit the typed text (Enter / blur). Invalid or out-of-range
2078
+ * text keeps the field in an error state with the text preserved for
2079
+ * correction; the committed value is untouched (Escape reverts the text).
2080
+ */
2081
+ private commitTypedValue;
2082
+ /** "Now", committed directly (N shortcut / popover chip), clamped to range. */
2083
+ private applyNowShortcut;
1791
2084
  private selectHour;
1792
2085
  private selectMinute;
2086
+ private selectSecond;
1793
2087
  private selectPeriod;
2088
+ /**
2089
+ * Scrolls the column's own scroll container only. scrollIntoView is off
2090
+ * limits here: it walks ALL scrollable ancestors, so it yanked the page
2091
+ * up/down whenever a column recentred (user-reported jump bug).
2092
+ */
2093
+ private scrollOptionInColumn;
1794
2094
  private scrollColumnsToSelection;
1795
- private handleTriggerKeydown;
2095
+ private handleInputInput;
2096
+ private handleInputKeydown;
2097
+ /** Clicking anywhere on the field opens the popover, keeping focus typed-in. */
2098
+ private handleFieldClick;
2099
+ /** The clock icon toggles the popover with the previous focus behavior. */
2100
+ private handleIconClick;
2101
+ /** Commit typed text when focus leaves the component entirely. */
2102
+ private handleInputBlur;
2103
+ /** N inside the panel (columns/footer focus) applies Now directly. */
2104
+ private handlePanelKeydown;
1796
2105
  /**
1797
2106
  * Column navigation: Up/Down move within the column (and select), Left/Right
1798
2107
  * jump between columns, Home/End go to the first/last option, Enter applies.
@@ -1835,6 +2144,12 @@ declare class RelTooltip extends LitElement {
1835
2144
  type: TooltipType;
1836
2145
  private uniqueId;
1837
2146
  static styles: lit.CSSResult;
2147
+ connectedCallback(): void;
2148
+ disconnectedCallback(): void;
2149
+ /** APG: Escape dismisses the tooltip. Not swallowed — an enclosing dialog
2150
+ * may legitimately close on the same keystroke. */
2151
+ private onKeydown;
2152
+ private clearDismissed;
1838
2153
  firstUpdated(): void;
1839
2154
  private linkTrigger;
1840
2155
  render(): lit_html.TemplateResult<1>;
@@ -1950,6 +2265,8 @@ declare class RelUpload extends LitElement {
1950
2265
  private aborter?;
1951
2266
  private delayTimer?;
1952
2267
  private lastSignature;
2268
+ private lastHeading;
2269
+ private lastAlertText;
1953
2270
  static styles: lit.CSSResult;
1954
2271
  /**
1955
2272
  * Aggregate state. Read-only; corresponds to the Figma modal variants.
@@ -2074,4 +2391,125 @@ declare global {
2074
2391
  }
2075
2392
  }
2076
2393
 
2077
- export { type AutocompleteFilter, type AutocompleteItem, type AutocompleteSearchFn, type BreadcrumbItem, type ButtonTone, type ButtonVariant, type ChipBorder, type ChipSize, type ChipTone, type ChipVariant, type DrawerCloseReason, type DrawerSide, type FieldLabelStyle, type LabelPlacement, type MenuItem, type MenuType, type ModalSide, type Orientation, 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, RelNavbar, RelOtp, RelPagination, RelPopover, RelPopoverContent, RelProgress, RelRadio, RelRangePicker, RelResizableGroup, RelResizableHandle, RelResizablePanel, RelSegmentedButton, RelSelect, RelSkeleton, RelSlider, RelSnackbar, RelStepper, RelSwitch, RelTab, RelTabItem, RelTextfield, RelTimePicker, RelToast, RelTooltip, RelTopbar, RelTopbarActions, RelTopbarBrand, RelTopbarDropdown, RelTopbarDropdownItem, RelTopbarItem, RelTopbarMobilePanel, RelTopbarProfile, RelTopbarUtilities, RelUpload, RelUploadActions, type SegmentAlign, type SegmentOption, type SegmentSize, type SelectBorder, type SelectLabelStyle, type SelectOption, type SelectOptionObject, type SelectSize, type SelectState, type SkeletonAnimation, type SkeletonShape, type SliderType, type SnackbarLines, type SnackbarType, type Step, type StepStatus, type StepperSize, type StepperType, type TabItem, type TextAlign, type TextFieldSize, type TextFieldState, type TextFieldVariant, type ToastType, type TooltipPosition, type TooltipType, type TopbarForceLayout, type TopbarProfileType, type TopbarRewardTier, type TopbarVariant, type TrackStyle, type UploadFile, type UploadPhase, type UploadRejection, type UploadState, type UploadStatus, type Uploader, type UploaderContext };
2394
+ /**
2395
+ * Shared focus utilities (shadow-DOM aware).
2396
+ *
2397
+ * Most components in this package render into shadow roots, so the classic
2398
+ * `container.querySelectorAll(focusableSelector)` + `document.activeElement`
2399
+ * approach silently fails in two ways:
2400
+ * 1. querySelectorAll never descends into child components' shadow roots,
2401
+ * so slotted rel-buttons etc. are invisible to a focus trap.
2402
+ * 2. document.activeElement retargets to the outermost host, so equality
2403
+ * checks against inner elements never match.
2404
+ * These helpers traverse the *composed* tree and resolve the *deep* active
2405
+ * element instead.
2406
+ */
2407
+ /**
2408
+ * All keyboard-focusable elements under `root`, in composed tree order —
2409
+ * descends into shadow roots and follows slot assignment.
2410
+ */
2411
+ declare function getFocusableElements(root: Element | ShadowRoot): HTMLElement[];
2412
+ /** The actually-focused element, resolved through nested shadow roots. */
2413
+ declare function getDeepActiveElement(): HTMLElement | null;
2414
+ /**
2415
+ * Keep Tab / Shift+Tab cycling inside `container`. Call from a keydown
2416
+ * listener; no-op for non-Tab keys. If focus has already escaped the
2417
+ * container, the next Tab pulls it back in.
2418
+ */
2419
+ declare function trapTabKey(e: KeyboardEvent, container: Element | ShadowRoot): void;
2420
+ /**
2421
+ * Reusable focus trap for dialog-like components (drawer, modal, popover…).
2422
+ *
2423
+ * private trap = new FocusTrap(() => this.panelEl);
2424
+ * onOpen() { this.trap.activate(); }
2425
+ * onClose() { this.trap.deactivate(); }
2426
+ *
2427
+ * activate() records the previously focused element, moves focus to the
2428
+ * first focusable inside the container (or the container itself), and wraps
2429
+ * Tab at the edges. deactivate() restores focus to where it was.
2430
+ */
2431
+ declare class FocusTrap {
2432
+ private getContainer;
2433
+ private previousFocus;
2434
+ private active;
2435
+ constructor(getContainer: () => Element | ShadowRoot | null);
2436
+ activate(options?: {
2437
+ initialFocus?: HTMLElement | null;
2438
+ }): void;
2439
+ deactivate(options?: {
2440
+ restoreFocus?: boolean;
2441
+ }): void;
2442
+ private onKeyDown;
2443
+ }
2444
+
2445
+ /**
2446
+ * Shared keyboard-shortcut utilities.
2447
+ *
2448
+ * Shortcut strings are written as "+"-joined tokens, e.g. "mod+b",
2449
+ * "ctrl+shift+p", "escape". The "mod" token means Cmd on Apple platforms
2450
+ * and Ctrl everywhere else (this is the pattern rel-sidebar's Ctrl/Cmd+B
2451
+ * toggle established). Matching is case-insensitive.
2452
+ */
2453
+ type ShortcutHandler = (e: KeyboardEvent) => void;
2454
+ interface ParsedShortcut {
2455
+ key: string;
2456
+ ctrl: boolean;
2457
+ meta: boolean;
2458
+ alt: boolean;
2459
+ shift: boolean;
2460
+ }
2461
+ declare function isApplePlatform(): boolean;
2462
+ declare function parseShortcut(shortcut: string): ParsedShortcut;
2463
+ /** Does this keydown event match the shortcut string? */
2464
+ declare function matchesShortcut(e: KeyboardEvent, shortcut: string): boolean;
2465
+ /**
2466
+ * True when the event originates from a text-editing context (input,
2467
+ * textarea, select, contenteditable) — global single-key shortcuts should
2468
+ * stay out of the way there. Uses composedPath so it sees through shadow
2469
+ * roots (e.g. the real <input> inside rel-textfield).
2470
+ */
2471
+ declare function isEditableTarget(e: Event): boolean;
2472
+ interface BindShortcutOptions {
2473
+ /** Listen on this target instead of `document`. */
2474
+ target?: EventTarget;
2475
+ /** Fire even when focus is in an input/textarea/contenteditable. */
2476
+ allowInEditable?: boolean;
2477
+ /** preventDefault on match (default true). */
2478
+ preventDefault?: boolean;
2479
+ }
2480
+ /**
2481
+ * Bind a shortcut, returns the unbind function.
2482
+ *
2483
+ * private unbind = bindShortcut("mod+b", () => this.toggle());
2484
+ * disconnectedCallback() { this.unbind(); }
2485
+ */
2486
+ declare function bindShortcut(shortcut: string, handler: ShortcutHandler, options?: BindShortcutOptions): () => void;
2487
+ /**
2488
+ * Display labels for a shortcut, one entry per key — feed straight into
2489
+ * <rel-kbd-group .items=${formatShortcutParts("mod+b")}>.
2490
+ * Platform-aware: "mod+b" → ["⌘","B"] on Apple, ["Ctrl","B"] elsewhere.
2491
+ */
2492
+ declare function formatShortcutParts(shortcut: string): string[];
2493
+
2494
+ /**
2495
+ * Shared screen-reader announcer.
2496
+ *
2497
+ * Maintains two persistent, visually-hidden live regions on document.body
2498
+ * (one polite, one assertive). Because the regions exist before any message
2499
+ * is written into them, text changes are reliably announced — unlike live
2500
+ * regions that are inserted into the DOM at the same moment as their content,
2501
+ * which most screen reader / browser pairs ignore.
2502
+ *
2503
+ * Messages are written on a short delay after clearing the region so that
2504
+ * repeating the same string (e.g. two identical toasts in a row) is
2505
+ * re-announced instead of deduplicated by the browser's accessibility tree.
2506
+ */
2507
+ type Politeness = "polite" | "assertive";
2508
+ /**
2509
+ * Announce a message to screen reader users without moving focus.
2510
+ * Use "assertive" only for time-sensitive information (errors); everything
2511
+ * else should be "polite" so it does not interrupt the user mid-word.
2512
+ */
2513
+ declare function announce(message: string, politeness?: Politeness): void;
2514
+
2515
+ export { type AutocompleteFilter, type AutocompleteItem, type AutocompleteSearchFn, type BindShortcutOptions, type BreadcrumbItem, type ButtonTone, type ButtonVariant, type ChipBorder, type ChipSize, type ChipTone, type ChipVariant, type DrawerCloseReason, type DrawerSide, type FieldLabelStyle, FocusTrap, type LabelPlacement, type MenuItem, type MenuType, type ModalSide, type ModalSize, type Orientation, 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, RelNavbar, RelOtp, RelPagination, RelPopover, RelPopoverContent, RelProgress, RelRadio, RelRangePicker, RelResizableGroup, RelResizableHandle, RelResizablePanel, RelSegmentedButton, RelSelect, RelSkeleton, RelSlider, RelSnackbar, RelStepper, RelSwitch, RelTab, RelTabItem, RelTextfield, RelTimePicker, RelToast, RelTooltip, RelTopbar, RelTopbarActions, RelTopbarBrand, RelTopbarDropdown, RelTopbarDropdownItem, RelTopbarItem, RelTopbarMobilePanel, RelTopbarProfile, RelTopbarUtilities, 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 SnackbarType, type Step, type StepStatus, type StepperSize, type StepperType, type TabItem, type TextAlign, type TextFieldPrefixStyle, type TextFieldSize, type TextFieldState, type TextFieldVariant, type ToastType, type TooltipPosition, type TooltipType, type TopbarForceLayout, type TopbarProfileType, type TopbarRewardTier, type TopbarVariant, 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 };