@reliability-design/web 1.4.6 → 1.4.8

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 (148) hide show
  1. package/CHANGELOG.md +246 -49
  2. package/dist/components/rel-accordion/README.md +133 -132
  3. package/dist/components/rel-alert/README.md +145 -141
  4. package/dist/components/rel-autocomplete/README.md +172 -61
  5. package/dist/components/rel-avatar/README.md +118 -130
  6. package/dist/components/rel-badge/README.md +97 -93
  7. package/dist/components/rel-breadcrumb/README.md +96 -102
  8. package/dist/components/rel-button/README.md +190 -130
  9. package/dist/components/rel-calendar/README.md +109 -109
  10. package/dist/components/rel-card/README.md +75 -94
  11. package/dist/components/rel-cardmedia/README.md +100 -127
  12. package/dist/components/rel-carousel/README.md +186 -166
  13. package/dist/components/rel-checkbox/README.md +96 -75
  14. package/dist/components/rel-chip/README.md +138 -121
  15. package/dist/components/rel-container/README.md +107 -99
  16. package/dist/components/rel-date-picker/README.md +200 -289
  17. package/dist/components/rel-divider/README.md +88 -100
  18. package/dist/components/rel-drawer/README.md +187 -200
  19. package/dist/components/rel-kbd/README.md +141 -151
  20. package/dist/components/rel-layout/README.md +34 -0
  21. package/dist/components/rel-menu/README.md +180 -151
  22. package/dist/components/rel-menubar/README.md +218 -329
  23. package/dist/components/rel-otp/README.md +171 -440
  24. package/dist/components/rel-pagination/README.md +100 -424
  25. package/dist/components/rel-popover/README.md +203 -568
  26. package/dist/components/rel-progress/README.md +88 -110
  27. package/dist/components/rel-radio/README.md +131 -138
  28. package/dist/components/rel-range-picker/README.md +165 -0
  29. package/dist/components/rel-resizable/README.md +199 -140
  30. package/dist/components/rel-segmented-button/README.md +156 -141
  31. package/dist/components/rel-select/README.md +156 -93
  32. package/dist/components/rel-sheet/README.md +168 -224
  33. package/dist/components/rel-skeleton/README.md +104 -0
  34. package/dist/components/rel-slider/README.md +131 -135
  35. package/dist/components/rel-snackbar/README.md +168 -134
  36. package/dist/components/rel-stepper/README.md +162 -160
  37. package/dist/components/rel-switch/README.md +117 -102
  38. package/dist/components/rel-tab/README.md +173 -163
  39. package/dist/components/rel-textfield/README.md +176 -69
  40. package/dist/components/rel-time-picker/README.md +122 -157
  41. package/dist/components/rel-toast/README.md +165 -148
  42. package/dist/components/rel-tooltip/README.md +119 -111
  43. package/dist/components/rel-upload/README.md +220 -0
  44. package/dist/index.d.mts +1726 -447
  45. package/dist/index.d.ts +1726 -447
  46. package/dist/index.js +13715 -10870
  47. package/dist/index.js.map +1 -1
  48. package/dist/index.mjs +14018 -11163
  49. package/dist/index.mjs.map +1 -1
  50. package/dist/styles/focus-ring.ts +84 -0
  51. package/dist/styles/vars.css +892 -866
  52. package/dist/{vars-XNMTSRVN.css → vars-DXX6QOAG.css} +892 -866
  53. package/package.json +3 -2
  54. package/dist/components/rel-accordion/rel-accordion.ts +0 -336
  55. package/dist/components/rel-alert/rel-alert.ts +0 -305
  56. package/dist/components/rel-autocomplete/rel-autocomplete.ts +0 -720
  57. package/dist/components/rel-avatar/rel-avatar.ts +0 -142
  58. package/dist/components/rel-badge/rel-badge.ts +0 -175
  59. package/dist/components/rel-breadcrumb/rel-breadcrumb.ts +0 -106
  60. package/dist/components/rel-button/rel-button.ts +0 -560
  61. package/dist/components/rel-calendar/index.ts +0 -1
  62. package/dist/components/rel-calendar/rel-calendar.ts +0 -565
  63. package/dist/components/rel-card/rel-card.ts +0 -88
  64. package/dist/components/rel-cardmedia/rel-cardmedia.ts +0 -88
  65. package/dist/components/rel-carousel/index.ts +0 -1
  66. package/dist/components/rel-carousel/rel-carousel.ts +0 -723
  67. package/dist/components/rel-chart/README.md +0 -286
  68. package/dist/components/rel-chart/index.ts +0 -1
  69. package/dist/components/rel-chart/rel-chart.ts +0 -754
  70. package/dist/components/rel-checkbox/rel-checkbox.ts +0 -382
  71. package/dist/components/rel-chip/rel-chip.ts +0 -483
  72. package/dist/components/rel-container/rel-container.ts +0 -95
  73. package/dist/components/rel-date-picker/date-format.test.ts +0 -43
  74. package/dist/components/rel-date-picker/date-format.ts +0 -47
  75. package/dist/components/rel-date-picker/date-parse.test.ts +0 -116
  76. package/dist/components/rel-date-picker/date-parse.ts +0 -178
  77. package/dist/components/rel-date-picker/index.ts +0 -1
  78. package/dist/components/rel-date-picker/rel-date-picker.ts +0 -1676
  79. package/dist/components/rel-divider/rel-divider.ts +0 -54
  80. package/dist/components/rel-drawer/index.ts +0 -1
  81. package/dist/components/rel-drawer/rel-drawer-footer.ts +0 -27
  82. package/dist/components/rel-drawer/rel-drawer.ts +0 -607
  83. package/dist/components/rel-kbd/rel-kbd.ts +0 -188
  84. package/dist/components/rel-layout/rel-layout.ts +0 -49
  85. package/dist/components/rel-menu/rel-menu.ts +0 -541
  86. package/dist/components/rel-menubar/index.ts +0 -2
  87. package/dist/components/rel-menubar/rel-menubar-menu.css +0 -3
  88. package/dist/components/rel-menubar/rel-menubar-menu.ts +0 -17
  89. package/dist/components/rel-menubar/rel-menubar.ts +0 -777
  90. package/dist/components/rel-nav-menu/README.md +0 -289
  91. package/dist/components/rel-nav-menu/index.ts +0 -3
  92. package/dist/components/rel-nav-menu/rel-nav-menu-item.ts +0 -243
  93. package/dist/components/rel-nav-menu/rel-nav-menu-panel.ts +0 -72
  94. package/dist/components/rel-nav-menu/rel-nav-menu.ts +0 -270
  95. package/dist/components/rel-navbar/README.md +0 -126
  96. package/dist/components/rel-navbar/rel-navbar.ts +0 -105
  97. package/dist/components/rel-otp/index.ts +0 -1
  98. package/dist/components/rel-otp/rel-otp.ts +0 -527
  99. package/dist/components/rel-pagination/index.ts +0 -1
  100. package/dist/components/rel-pagination/rel-pagination.ts +0 -522
  101. package/dist/components/rel-popover/index.ts +0 -2
  102. package/dist/components/rel-popover/rel-popover-content.ts +0 -81
  103. package/dist/components/rel-popover/rel-popover.ts +0 -475
  104. package/dist/components/rel-progress/rel-progress.ts +0 -234
  105. package/dist/components/rel-radio/rel-radio.ts +0 -481
  106. package/dist/components/rel-range-picker/rel-range-picker.ts +0 -1688
  107. package/dist/components/rel-resizable/index.ts +0 -3
  108. package/dist/components/rel-resizable/rel-resizable-group.ts +0 -405
  109. package/dist/components/rel-resizable/rel-resizable-handle.ts +0 -150
  110. package/dist/components/rel-resizable/rel-resizable-panel.ts +0 -58
  111. package/dist/components/rel-segmented-button/rel-segmented-button.ts +0 -217
  112. package/dist/components/rel-select/rel-select.ts +0 -1416
  113. package/dist/components/rel-sheet/rel-sheet.ts +0 -610
  114. package/dist/components/rel-sidebar/README.md +0 -209
  115. package/dist/components/rel-sidebar/rel-sidebar.ts +0 -1191
  116. package/dist/components/rel-skeleton/rel-skeleton.ts +0 -143
  117. package/dist/components/rel-slider/rel-slider.ts +0 -673
  118. package/dist/components/rel-snackbar/rel-snackbar.ts +0 -329
  119. package/dist/components/rel-stepper/rel-stepper.ts +0 -567
  120. package/dist/components/rel-switch/rel-switch.ts +0 -199
  121. package/dist/components/rel-tab/rel-tab.ts +0 -264
  122. package/dist/components/rel-table/README.md +0 -178
  123. package/dist/components/rel-table/rel-table-body.ts +0 -22
  124. package/dist/components/rel-table/rel-table-caption.ts +0 -30
  125. package/dist/components/rel-table/rel-table-cell.ts +0 -43
  126. package/dist/components/rel-table/rel-table-footer.ts +0 -24
  127. package/dist/components/rel-table/rel-table-head.ts +0 -47
  128. package/dist/components/rel-table/rel-table-header.ts +0 -22
  129. package/dist/components/rel-table/rel-table-row.ts +0 -37
  130. package/dist/components/rel-table/rel-table.ts +0 -62
  131. package/dist/components/rel-textfield/rel-textfield.ts +0 -1054
  132. package/dist/components/rel-time-picker/rel-time-picker.ts +0 -1482
  133. package/dist/components/rel-time-picker/time-parse.test.ts +0 -67
  134. package/dist/components/rel-time-picker/time-parse.ts +0 -85
  135. package/dist/components/rel-toast/rel-toast.ts +0 -206
  136. package/dist/components/rel-tooltip/rel-tooltip.ts +0 -193
  137. package/dist/components/rel-topbar/rel-topbar-actions.ts +0 -27
  138. package/dist/components/rel-topbar/rel-topbar-brand.ts +0 -54
  139. package/dist/components/rel-topbar/rel-topbar-dropdown-item.ts +0 -69
  140. package/dist/components/rel-topbar/rel-topbar-dropdown.ts +0 -31
  141. package/dist/components/rel-topbar/rel-topbar-item.ts +0 -324
  142. package/dist/components/rel-topbar/rel-topbar-mobile-panel.ts +0 -27
  143. package/dist/components/rel-topbar/rel-topbar-profile.ts +0 -243
  144. package/dist/components/rel-topbar/rel-topbar-utilities.ts +0 -27
  145. package/dist/components/rel-topbar/rel-topbar.ts +0 -446
  146. package/dist/components/rel-upload/UPLOAD-DESIGN-CHANGES.md +0 -134
  147. package/dist/components/rel-upload/rel-upload-actions.ts +0 -163
  148. package/dist/components/rel-upload/rel-upload.ts +0 -968
@@ -1,160 +1,162 @@
1
- # rel-stepper
2
-
3
- ## Overview
4
- A comprehensive stepper component for visualizing multi-step processes. Supports horizontal and vertical orientations, text positioning (below or right of indicator), two size variants, and three step states (done, progress, disabled). Features clickable steps, automatic connector lines, and checkmark icons for completed steps.
5
-
6
- ## Usage
7
- ```html
8
- <!-- Basic horizontal stepper -->
9
- <rel-stepper id="checkout"></rel-stepper>
10
- <script>
11
- const stepper = document.getElementById('checkout');
12
- stepper.steps = [
13
- { title: 'Cart', status: 'done' },
14
- { title: 'Shipping', status: 'progress' },
15
- { title: 'Payment', status: 'disabled' },
16
- { title: 'Confirmation', status: 'disabled' }
17
- ];
18
- </script>
19
-
20
- <!-- Vertical stepper with text to the right -->
21
- <rel-stepper
22
- id="onboarding"
23
- orientation="vertical"
24
- textAlign="right">
25
- </rel-stepper>
26
- <script>
27
- const onboarding = document.getElementById('onboarding');
28
- onboarding.steps = [
29
- { title: 'Create Account', status: 'done' },
30
- { title: 'Verify Email', status: 'done' },
31
- { title: 'Profile Setup', status: 'progress' },
32
- { title: 'Complete', status: 'disabled' }
33
- ];
34
- </script>
35
-
36
- <!-- Small size horizontal stepper -->
37
- <rel-stepper
38
- id="wizard"
39
- size="small"
40
- textAlign="below">
41
- </rel-stepper>
42
- <script>
43
- const wizard = document.getElementById('wizard');
44
- wizard.steps = [
45
- { title: 'Select', status: 'done' },
46
- { title: 'Configure', status: 'progress' },
47
- { title: 'Review', status: 'disabled' }
48
- ];
49
- </script>
50
-
51
- <!-- Listen for step clicks -->
52
- <rel-stepper id="interactive"></rel-stepper>
53
- <script>
54
- const interactive = document.getElementById('interactive');
55
- interactive.steps = [
56
- { title: 'Step 1', status: 'done' },
57
- { title: 'Step 2', status: 'progress' },
58
- { title: 'Step 3', status: 'disabled' }
59
- ];
60
- interactive.addEventListener('step-change', (e) => {
61
- console.log('Clicked step:', e.detail.index, e.detail.step);
62
- // Update step statuses or navigate to step content
63
- });
64
- </script>
65
- ```
66
-
67
- ## API
68
-
69
- ### Attributes/Properties
70
- | Name | Type | Default | Description |
71
- |------|------|---------|-------------|
72
- | `steps` | `Step[]` | `[]` | Array of step items (see Step interface below) |
73
- | `textAlign` | `"right" \| "below"` | `"below"` | Position of step labels relative to indicators |
74
- | `orientation` | `"horizontal" \| "vertical"` | `"horizontal"` | Layout direction |
75
- | `size` | `"small" \| "large"` | `"large"` | Size variant (controls connector line length and spacing) |
76
-
77
- ### Step Interface
78
- ```typescript
79
- interface Step {
80
- title: string; // Step label text
81
- status: "done" | "progress" | "disabled"; // Step state
82
- }
83
- ```
84
-
85
- ## Events
86
- | Event | Detail | Description |
87
- |-------|--------|-------------|
88
- | `step-change` | `{ index: number, step: Step }` | Fired when a non-disabled step is clicked |
89
-
90
- ## Slots
91
- Default slot not used.
92
-
93
- ## CSS Parts
94
- None exposed.
95
-
96
- ## Tokens / CSS Variables
97
-
98
- ### Semantic Tokens
99
- - `--rel-fonts-font-family` - Font family
100
- - `--rel-body-sm-regular-font-size` - Label font size
101
- - `--rel-fonts-weight-body-medium` - Default label font weight
102
- - `--rel-fonts-weight-body-semibold` - Active/done label font weight
103
-
104
- ### Spacing Tokens
105
- - `--rel-spacing-2xs` - Gap between indicators and labels (text-right mode)
106
-
107
- ### Color Tokens
108
- - `--rel-bg-surface` - Indicator background (progress, disabled states)
109
- - `--rel-text-default` - Active/done indicator background, text, and connector color
110
- - `--rel-border-default` - Disabled indicator border and inactive connector
111
- - `--rel-bg-surface-elevated` - Disabled indicator background
112
- - `--rel-text-disabled` - Disabled label text color
113
- - `--rel-text-default` - Default label text color
114
-
115
- ## Accessibility
116
- - Steps can be clicked unless in disabled state
117
- - Checkmark SVG uses `currentColor` for proper theming
118
- - Focus states could be enhanced with keyboard navigation (not currently implemented)
119
-
120
- ### Keyboard Interactions
121
- - Click-based interaction only (no keyboard navigation implemented)
122
-
123
- ### Focus Behavior
124
- - No specific focus handling (steps are div elements, not buttons)
125
-
126
- ## Notes / Edge Cases
127
- - **Indicator states**:
128
- - **done**: Black circle with white checkmark SVG icon
129
- - **progress**: White circle with black border and step number
130
- - **disabled**: Light gray circle with gray border and step number
131
-
132
- - **Connector lines**:
133
- - Positioned absolutely relative to the indicator
134
- - **Horizontal**: 2px height, extends from indicator to next step
135
- - **Vertical**: 2px width, extends from indicator to next step
136
- - **done/progress** connectors are black; **disabled** connectors are gray
137
- - Last step has no connector
138
-
139
- - **Size variants** (control connector length and gap):
140
- - **large horizontal**: 86px connector, 86px gap
141
- - **small horizontal**: 60px connector, 60px gap
142
- - **large vertical**: 86px connector, 86px gap
143
- - **small vertical**: 42px connector, 42px gap
144
-
145
- - **Text positioning**:
146
- - **below**: Labels appear centered below indicators
147
- - **right**: Labels appear to the right of indicators with a small gap
148
-
149
- - **Layout combinations**:
150
- - **horizontal + text-below**: Steps arranged in a row, labels centered below
151
- - **horizontal + text-right**: Steps in a row, labels to the right of each indicator
152
- - **vertical + text-below**: Steps stacked vertically, labels centered below each indicator
153
- - **vertical + text-right**: Steps stacked vertically, labels to the right with indicators on the left
154
-
155
- - The indicator is 22px × 22px with a 2px border
156
- - Step numbers are 1-indexed (displayed as `index + 1`)
157
- - Clicking a disabled step does nothing (no event dispatched)
158
- - All transitions use `0.2s ease`
159
- - The component uses flexbox extensively for layout management
160
- - The stepper is `display: block` and `width: 100%`
1
+ # rel-stepper
2
+
3
+ A multi-step progress indicator implementing the Figma "Stepper" component: its `Type` axis (default, dot, number, icon indicators), its `Typel` label-placement axis (top, centered, inverted), two sizes and both orientations. Each entry in `steps` renders as a focusable step with a circular indicator, a label and a connector to the next step, styled by its status (`done`, `progress`, `disabled`). Use it to show where the user is in a linear flow (checkout, onboarding, a wizard) and to let them jump back to completed steps; the component is presentation only — it never changes a step's status itself, so drive `steps` from your own state.
4
+
5
+ ## Usage
6
+
7
+ ```html
8
+ <!-- Horizontal (default), labels under the indicators -->
9
+ <rel-stepper id="checkout"></rel-stepper>
10
+ <script>
11
+ document.getElementById('checkout').steps = [
12
+ { title: 'Cart', status: 'done' },
13
+ { title: 'Shipping', status: 'progress' },
14
+ { title: 'Payment', status: 'disabled' },
15
+ { title: 'Confirmation', status: 'disabled' }
16
+ ];
17
+ </script>
18
+
19
+ <!-- Vertical, label beside the indicator (Figma Type = Centered).
20
+ `size` is DEPRECATED and deliberately omitted: Figma has no Size axis on the
21
+ Stepper — the connector geometry is fixed — so it was a code-only scale. It is
22
+ still accepted so existing markup keeps working, but it now resolves to the one
23
+ Figma geometry and logs a one-time notice. Retune with
24
+ `--rel-stepper-connector-length` instead. -->
25
+ <rel-stepper id="onboarding" orientation="vertical" label-placement="centered"></rel-stepper>
26
+ <script>
27
+ document.getElementById('onboarding').steps = [
28
+ { title: 'Create account', status: 'done' },
29
+ { title: 'Verify email', status: 'done' },
30
+ { title: 'Set up profile', status: 'progress' },
31
+ { title: 'Done', status: 'disabled' }
32
+ ];
33
+ </script>
34
+
35
+ <!-- Figma Type axis: dot / number / icon indicators -->
36
+ <rel-stepper type="dot" steps='[{"title":"Plan","status":"done"},{"title":"Build","status":"progress"},{"title":"Ship","status":"disabled"}]'></rel-stepper>
37
+ <rel-stepper type="number" steps='[{"title":"Plan","status":"done"},{"title":"Build","status":"progress"},{"title":"Ship","status":"disabled"}]'></rel-stepper>
38
+ <rel-stepper type="icon" steps='[{"title":"Plan","status":"done"},{"title":"Build","status":"progress"},{"title":"Ship","status":"disabled"}]'></rel-stepper>
39
+
40
+ <!-- Label placement (Figma Typel): top-aligned beside the indicator, or indicator above the label -->
41
+ <rel-stepper orientation="vertical" label-placement="top" steps='[{"title":"Review","status":"done"},{"title":"Approve","status":"progress"}]'></rel-stepper>
42
+ <rel-stepper label-placement="inverted" steps='[{"title":"Review","status":"done"},{"title":"Approve","status":"progress"}]'></rel-stepper>
43
+
44
+ <!-- Let users jump back to a completed step -->
45
+ <rel-stepper id="wizard"></rel-stepper>
46
+ <script>
47
+ const wizard = document.getElementById('wizard');
48
+ wizard.steps = [
49
+ { title: 'Select', status: 'done' },
50
+ { title: 'Configure', status: 'progress' },
51
+ { title: 'Review', status: 'disabled' }
52
+ ];
53
+ wizard.addEventListener('rel-step-change', (e) => {
54
+ console.log('Activated step', e.detail.index, e.detail.step.title);
55
+ // Recompute statuses and assign a new array so the stepper re-renders
56
+ wizard.steps = wizard.steps.map((s, i) => ({
57
+ ...s,
58
+ status: i < e.detail.index ? 'done' : i === e.detail.index ? 'progress' : 'disabled'
59
+ }));
60
+ });
61
+ </script>
62
+ ```
63
+
64
+ ### Deprecated aliases
65
+
66
+ ```html
67
+ <!-- textalign="right" → label-placement="centered"; textalign="below" → label-placement="inverted".
68
+ Still supported and still the fallback when label-placement is unset; no console notice is logged. -->
69
+ <rel-stepper textalign="right" steps='[{"title":"Review","status":"done"},{"title":"Approve","status":"progress"}]'></rel-stepper>
70
+ ```
71
+
72
+ The `Step` type and the `StepStatus`, `StepperType` and `LabelPlacement` unions are exported from the package:
73
+
74
+ ```typescript
75
+ import type { Step } from '@reliability-design/web';
76
+
77
+ const steps: Step[] = [
78
+ { title: 'Cart', status: 'done' }, // filled indicator with a check mark
79
+ { title: 'Shipping', status: 'progress' }, // current step (aria-current="step")
80
+ { title: 'Payment', status: 'disabled' } // not yet reachable; rendered at 40% opacity, not focusable
81
+ ];
82
+ ```
83
+
84
+ <!-- readme-gen:start -->
85
+
86
+ <!-- Generated from rel-stepper.ts — do not edit; run readme-generate.mjs -->
87
+
88
+ ## API
89
+
90
+ ### Attributes / Properties
91
+
92
+ | Property | Attribute | Type | Default | Description |
93
+ | --- | --- | --- | --- | --- |
94
+ | `steps` | `steps` | `Step[]` | `[]` | The steps in order, each `{ title: string; subtitle?: string; status: "done" \| "progress" \| "disabled" }`. Set as a property (or a JSON array attribute); the component never mutates it — update it from the `rel-step-change` handler. |
95
+ | `orientation` | `orientation` | `"horizontal" \| "vertical"` | `"horizontal"` | Layout axis: `horizontal` (default) lays the steps out in a centred row; `vertical` stacks them. |
96
+ | `type` | `type` | `"default" \| "dot" \| "number" \| "icon"` | `"default"` | Figma "Type" axis — what the indicator shows: `default` (step number, check mark once done), `dot` (8px dot, no glyph), `number` (always the number) or `icon` (always the check mark). Reflected. |
97
+ | `labelPlacement` | `label-placement` | `"top" \| "centered" \| "inverted"` | `undefined` | Figma "Typel" axis — where the label sits: `top` (top-aligned beside the indicator), `centered` (vertically centred beside it) or `inverted` (indicator above the label). When unset, the deprecated `textAlign` prop drives the layout (`below` ≈ `inverted`, `right` ≈ `centered`). Figma marks Top + Horizontal as an unsupported combination. Reflected. |
98
+
99
+ #### Deprecated (still supported — removal targeted for 3.0)
100
+
101
+ | Property | Attribute | Type | Use instead |
102
+ | --- | --- | --- | --- |
103
+ | `textAlign` | `textalign` | `"right" \| "below"` | Deprecated alias of `label-placement`; use `label-placement="inverted"` instead of `textalign="below"` and `label-placement="centered"` instead of `textalign="right"`. |
104
+ | `size` | `size` | `"small" \| "large"` | Figma has no Size axis on the Stepper — the connector geometry is fixed — so this was a code-only scale. It stays accepted so existing markup keeps working, and now resolves to the one Figma geometry. Retune it with `--rel-stepper-connector-length` instead. Logs a one-time notice. |
105
+
106
+ ## Events
107
+
108
+ | Event | Detail | Bubbles / composed | Description |
109
+ | --- | --- | --- | --- |
110
+ | `rel-step-change` | `{ index, step, }` | bubbles / composed | Fired when a step whose status is not `disabled` is clicked or activated with Enter / Space. Detail: `{ index: number, step: Step }`. Statuses are not changed by the component — update `steps` in the handler. |
111
+
112
+ ## Slots
113
+
114
+ | Slot | Description |
115
+ | --- | --- |
116
+ | — | This element has no slots. |
117
+
118
+ ## CSS Parts
119
+
120
+ | Part | Description |
121
+ | --- | --- |
122
+ | `indicator` | The 24px circle (8px for `type="dot"`) that holds the step number, check mark or icon. |
123
+ | `track` | The 1px connector line from a step to the next one (not rendered on the last step). |
124
+ | `label` | The step title text. |
125
+ | `subtitle` | The optional second line under the title; rendered only when a step sets `subtitle`. |
126
+
127
+ ## Tokens / CSS Variables
128
+
129
+ ### Component hooks
130
+
131
+ Set these on the host (or an ancestor) to customise the element without touching design tokens.
132
+
133
+ | Custom property | Default | Description |
134
+ | --- | --- | --- |
135
+ | `--rel-stepper-connector-length` | `var(--rel-stepper-connector-length-lg, 96px)` | 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`. |
136
+ | `--rel-stepper-indicator-size` | `24px` | Indicator diameter (default 24px; `type="dot"` is fixed at 8px). |
137
+
138
+ <!-- readme-gen:end -->
139
+
140
+ ## Accessibility
141
+
142
+ - The list renders as a `role="group"` with the fixed name "Progress steps" (not configurable).
143
+ - Every step is a `role="button"` element with `aria-label="<title>, step n of N"`, plus ", completed" for `done` steps. The `progress` step carries `aria-current="step"`; `disabled` steps carry `aria-disabled="true"` and `tabindex="-1"` so they are skipped in the Tab order and by the arrow keys.
144
+ - Enabled steps are individual Tab stops (`tabindex="0"`) with a 2px focus ring; the indicators and connectors are purely visual (the check mark SVG is `aria-hidden`).
145
+ - Activating a step only emits `rel-step-change`; nothing is announced and no status changes until you update `steps`.
146
+
147
+ | Key | Action |
148
+ | --- | --- |
149
+ | `Enter` / `Space` | Activate the focused step (emits `rel-step-change`; ignored on `disabled` steps) |
150
+ | `ArrowRight` / `ArrowDown` | Move focus to the next enabled step (wraps to the first) |
151
+ | `ArrowLeft` / `ArrowUp` | Move focus to the previous enabled step (wraps to the last) |
152
+ | `Home` / `End` | Move focus to the first / last enabled step |
153
+
154
+ ## Notes / Edge Cases
155
+
156
+ - `label-placement` is the canonical layout switch (Figma `Typel`). When it is unset the deprecated `textalign` prop decides: `below` renders like `inverted` (indicator above the label), `right` renders like `centered` (label beside the indicator). `top` differs from `centered` only in aligning the label to the top of the indicator. Figma marks Top + Horizontal as an unsupported combination.
157
+ - Statuses map to the Figma `.Artwork` states: `done` = inverse fill with a check mark (or the number when `type="number"`, nothing when `type="dot"`), `progress` = neutral fill with a strong 1px border, `disabled` = the same drawn at 40% opacity. Connectors are `border-strong` after a `done` / `progress` step and `border-default` otherwise.
158
+ - `type="icon"` shows the check mark on every step regardless of status; `type="dot"` shrinks the indicator to 8px and fills it for `done` and `progress`.
159
+ - Connector length doubles as the gap between steps and depends on `size` and `orientation`: large = `--rel-stepper-connector-length-lg` (86px) in both orientations; small horizontal = `--rel-stepper-connector-length-md` (60px); small vertical = `--rel-stepper-connector-length-sm` (42px). Note the `md` / `sm` suffixes refer to those defaults, not to a size value.
160
+ - Labels never wrap (`white-space: nowrap`); keep titles short or widen the connector hooks.
161
+ - `steps` is compared by reference: mutate-in-place changes are not picked up — assign a new array.
162
+ - Steps are `<div role="button">` elements, not native buttons, so they do not submit forms and do not get native disabled styling.
@@ -1,102 +1,117 @@
1
- # rel-switch
2
-
3
- ## Overview
4
- A toggle switch component for binary on/off states. Features smooth animations, focus halo effects, pressed states, and respects user motion preferences. Built with custom visual styling while maintaining native checkbox accessibility.
5
-
6
- ## Usage
7
- ```html
8
- <!-- Basic switch -->
9
- <rel-switch></rel-switch>
10
-
11
- <!-- Pre-checked switch -->
12
- <rel-switch checked></rel-switch>
13
-
14
- <!-- Disabled switch -->
15
- <rel-switch disabled></rel-switch>
16
- <rel-switch checked disabled></rel-switch>
17
-
18
- <!-- With event listener -->
19
- <rel-switch id="notifications"></rel-switch>
20
- <script>
21
- const toggle = document.getElementById('notifications');
22
- toggle.addEventListener('changed', (e) => {
23
- console.log('Switch state:', e.detail); // boolean: true/false
24
- });
25
- </script>
26
-
27
- <!-- Programmatically control -->
28
- <rel-switch id="darkMode"></rel-switch>
29
- <script>
30
- const darkModeSwitch = document.getElementById('darkMode');
31
- darkModeSwitch.checked = true;
32
- darkModeSwitch.disabled = false;
33
- </script>
34
- ```
35
-
36
- ## API
37
-
38
- ### Attributes/Properties
39
- | Name | Type | Default | Description |
40
- |------|------|---------|-------------|
41
- | `checked` | `boolean` | `false` | Whether the switch is in the ON position |
42
- | `disabled` | `boolean` | `false` | Disables the switch and reduces opacity |
43
-
44
- ## Events
45
- | Event | Detail | Description |
46
- |-------|--------|-------------|
47
- | `changed` | `boolean` (the new checked state) | Fired when the switch is toggled by user interaction |
48
-
49
- ## Slots
50
- Default slot not used.
51
-
52
- ## CSS Parts
53
- None exposed.
54
-
55
- ## Tokens / CSS Variables
56
-
57
- ### Component-Level Custom Properties
58
- The component defines these CSS custom properties on `:host` which can be overridden:
59
-
60
- - `--switch-track` - Track color when unchecked (default: `--rel-text-placeholder`)
61
- - `--switch-knob-on` - Knob color when checked (default: `--rel-text-default`)
62
- - `--switch-knob-off` - Knob color when unchecked (default: `--rel-bg-surface`)
63
- - `--switch-halo` - Focus halo color (default: `--rel-border-default`)
64
- - `--switch-disabled` - Color for disabled state (default: `--rel-text-disabled`)
65
-
66
- ### Design Token References
67
- - `--rel-text-placeholder` - Default track color
68
- - `--rel-text-dark-placeholder` - Hover track color
69
- - `--rel-text-default` - ON knob color
70
- - `--rel-bg-surface` - OFF knob color
71
- - `--rel-border-default` - Focus halo
72
- - `--rel-text-disabled` - Disabled state color
73
- - `--rel-bg-neutral-elevated-1` - Used in disabled styling
74
-
75
- ## Accessibility
76
- - Uses native checkbox input with `role="switch"` and `aria-checked`
77
- - Visually hidden checkbox maintains keyboard and screen reader support
78
- - Focus visible state shows prominent halo around knob
79
- - Disabled state prevents interaction and reduces opacity to 0.5
80
- - Smooth transitions respect `prefers-reduced-motion` media query
81
-
82
- ### Keyboard Interactions
83
- - `Space` - Toggle switch on/off
84
- - `Tab` - Move focus to/from switch
85
-
86
- ### Focus Behavior
87
- - Focus ring appears as a 6px halo around the knob using `--switch-halo` color
88
- - Halo uses box-shadow for smooth visual feedback
89
-
90
- ### Motion Support
91
- - Animations disabled when `prefers-reduced-motion: reduce` is set
92
- - Transitions apply to track color, knob position, knob color, and box-shadow
93
-
94
- ## Notes / Edge Cases
95
- - The switch is 42px wide × 22px tall with a 12px tall track and 18px diameter knob
96
- - Visual knob animates 22px horizontally when toggling from OFF to ON
97
- - Hover effect only applies when not disabled (track darkens slightly)
98
- - Pressed state applies a `scale(0.9)` transform to the knob for tactile feedback
99
- - The knob includes a subtle drop shadow (`0 1px 3px rgba(0,0,0,0.3)`) for depth
100
- - When disabled, both track and knob use `--switch-disabled` color
101
- - The component is `display: inline-block` by default
102
- - Focus halo appears on keyboard focus only (`:focus-visible`), not on mouse click
1
+ # rel-switch
2
+
3
+ `<rel-switch>` implements the Figma **Switch** component (5917:1174, Type=Line): a 44×20 toggle with a thin 10px track and a 20px handle that slides 20px, with hover, focused, pressed and disabled states from the Figma State axis. It wraps a native checkbox with `role="switch"`, so keyboard and screen-reader behaviour are native. Use it for settings that take effect immediately (notifications on/off, dark mode); for choices that are collected and submitted with a form use `<rel-checkbox>`.
4
+
5
+ ## Usage
6
+
7
+ ```html
8
+ <!-- Basic -->
9
+ <rel-switch field-aria-label="Email notifications"></rel-switch>
10
+
11
+ <!-- On by default -->
12
+ <rel-switch checked field-aria-label="Dark mode"></rel-switch>
13
+
14
+ <!-- Disabled, off and on -->
15
+ <rel-switch disabled field-aria-label="Locked setting"></rel-switch>
16
+ <rel-switch checked disabled field-aria-label="Locked setting"></rel-switch>
17
+
18
+ <!-- One handler for several switches: `value` is echoed in the event detail -->
19
+ <rel-switch value="marketing" field-aria-label="Marketing emails"></rel-switch>
20
+ <rel-switch value="product" field-aria-label="Product updates"></rel-switch>
21
+ <script>
22
+ document.addEventListener('rel-change', (e) => {
23
+ if (e.target.tagName !== 'REL-SWITCH') return;
24
+ console.log(e.detail.value, e.detail.checked); // "marketing", true
25
+ });
26
+ </script>
27
+
28
+ <!-- Programmatic control (no event is fired) -->
29
+ <rel-switch id="wifi" field-aria-label="Wi-Fi"></rel-switch>
30
+ <script>
31
+ const wifi = document.getElementById('wifi');
32
+ wifi.checked = true;
33
+ </script>
34
+ ```
35
+
36
+ <!-- readme-gen:start -->
37
+
38
+ <!-- Generated from rel-switch.ts — do not edit; run readme-generate.mjs -->
39
+
40
+ ## API
41
+
42
+ ### Attributes / Properties
43
+
44
+ | Property | Attribute | Type | Default | Description |
45
+ | --- | --- | --- | --- | --- |
46
+ | `checked` | `checked` | `boolean` | `false` | Whether the switch is on. Set it to control the state programmatically; user toggles update it and fire `rel-change`. |
47
+ | `type` | `type` | `"line" \| "pill"` | `"line"` | Shape (Figma "Type"): `"line"` (default) is the 44x20 control with a thin 40x10 rail and the handle riding on it; `"pill"` is the 44x24 filled track with the handle inside it. NOTE: Figma's own default is Pill. We default to `line` deliberately — flipping it would restyle every existing switch — so this is a recorded divergence from the design default, not an oversight. Reflected. |
48
+ | `disabled` | `disabled` | `boolean` | `false` | Disables interaction and swaps in the dulled rail / handle fills; the handle shadow is removed. |
49
+ | `value` | `value` | `string` | `""` | Arbitrary string echoed in the `rel-change` event detail — useful when one handler serves several switches. |
50
+ | `fieldAriaLabel` | `field-aria-label` | `string` | `undefined` | 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. |
51
+ | `label` | `label` | `string` | `undefined` | 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. |
52
+ | `labelPlacement` | `label-placement` | `"end" \| "start"` | `"end"` | Which side the label sits on: `end` (default) puts it after the control, `start` before it. Note this is a PHYSICAL side, not a writing-direction one, matching every other placement prop in the library. Reflected. |
53
+ | `size` | `size` | `"small" \| "medium" \| "large" \| "sm" \| "md" \| "lg"` | `"medium"` | Control scale: `small`, `medium` (default) or `large`. `sm` / `md` / `lg` are alternative spellings and stay supported — normalised to the full words, not deprecated. Figma's Switch has axes Type / State / Enabled only, with no Size, so this ramp is ours. `medium` is exactly the control that shipped before it existed, so an existing switch is unchanged. It does NOT respond to viewport width. Picking a size per breakpoint is the consuming application's decision — a control that silently resized itself at some width would be undocumentable behaviour, and the breakpoint that matters belongs to the layout around it, not to the switch. Reflected. |
54
+
55
+ ## Events
56
+
57
+ | Event | Detail | Bubbles / composed | Description |
58
+ | --- | --- | --- | --- |
59
+ | `rel-change` | `{ checked: this.checked, value: this.value, }` | bubbles / composed | Fired when the user toggles the switch (not on programmatic `checked` changes). Detail: `{ checked: boolean, value: string }`. |
60
+
61
+ ## Slots
62
+
63
+ | Slot | Description |
64
+ | --- | --- |
65
+ | (default) | Rich label content beside the control, as an alternative to the `label` property. Clicking it toggles the switch. |
66
+
67
+ ## CSS Parts
68
+
69
+ | Part | Description |
70
+ | --- | --- |
71
+ | `root` | The `<label>` holding the control and its optional text; carries the `label-placement` order |
72
+ | `base` | The control box around the hidden input and the track |
73
+ | `track` | The thin rail; the handle is its `::before` pseudo-element |
74
+ | `label` | The text beside the control; not rendered when there is neither a `label` nor slotted content |
75
+
76
+ ## Tokens / CSS Variables
77
+
78
+ ### Component hooks
79
+
80
+ Set these on the host (or an ancestor) to customise the element without touching design tokens.
81
+
82
+ | Custom property | Default | Description |
83
+ | --- | --- | --- |
84
+ | `--rel-switch-disabled` | `var(--switch-disabled)` | Handle colour while disabled; default `var(--rel-bg-neutral-elevated-1)` |
85
+ | `--rel-switch-knob-off` | `var(--switch-knob-off)` | Handle colour when unchecked; default `var(--rel-text-static-light)` |
86
+ | `--rel-switch-knob-on` | `var(--switch-knob-on)` | Handle colour when checked; default `var(--rel-bg-neutral-inverse-2)` |
87
+ | `--rel-switch-thumb-size` | `var(--_rel-sw-thumb)` | 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 |
88
+ | `--rel-switch-track` | `var(--switch-track)` | Rail colour; default `var(--rel-bg-neutral-elevated-2)` |
89
+ | `--rel-switch-track-height` | `var(--_rel-sw-h)` | Overall control height; defaults to the `size` ramp — 16 / 20 / 24px for `type="line"`, 20 / 24 / 28px for `type="pill"` |
90
+ | `--rel-switch-track-width` | `var(--_rel-sw-w)` | Overall control width; defaults to the `size` ramp (36 / 44 / 52px) |
91
+ | `--switch-halo` | `var(--rel-action-overlay-hover-strong)` | — |
92
+
93
+ <!-- readme-gen:end -->
94
+
95
+ ## Accessibility
96
+
97
+ - Renders a native `<input type="checkbox" role="switch">` with `aria-checked` mirroring `checked`; the input is visually hidden (opacity 0, zero size) inside a `<label part="base">`, so clicking anywhere on the control toggles it.
98
+ - `field-aria-label` becomes the input's `aria-label`. The input lives in the shadow root, so an external `<label for>` cannot reference it — set `field-aria-label` for the accessible name and place any visible text beside the switch.
99
+ - Focus: the wrapper shows a 1.5px focus outline whenever the input is focused (mouse or keyboard).
100
+ - `disabled` disables the native input (removed from the tab order), dims the control to 50% and drops the handle shadow.
101
+ - `prefers-reduced-motion: reduce` removes the track and handle transitions.
102
+
103
+ | Key | Action |
104
+ | --- | --- |
105
+ | `Space` | Toggles the switch (native checkbox behaviour). |
106
+ | `Tab` / `Shift+Tab` | Moves focus to / from the switch. |
107
+
108
+ ## Notes / Edge Cases
109
+
110
+ - `rel-change` fires for user toggles only; setting `checked` from JavaScript updates the visuals but emits nothing.
111
+ - Geometry follows the `size` ramp, derived from the Figma Line switch at `medium`: 44×20 control, 40×10 track inset 2px, 20px handle travelling 20px. `small` is 36×16 and `large` 52×24, holding the same invariants (handle = control height, width = 2× height + 4, rail = height ÷ 2, travel = width − 4 − handle). `type="pill"` shares the width, handle and travel but is 4px taller at every size — 20 / 24 / 28 — because its height is the handle plus its 2px inset, where Line's height *is* the handle. Changing `--rel-switch-track-width` widens the control without moving the "on" position.
112
+ - **`size` does not respond to viewport width.** Figma has no Size axis on Switch, so the ramp is ours, and choosing a size per breakpoint is the application's decision — set it from your own media queries or container queries. A control that resized itself at some width would be behaviour a design system cannot document, and the breakpoint that matters belongs to the layout around the switch, not to the switch.
113
+ - A `label` (or slotted content) becomes the switch's accessible name and is clickable, because the root element is the `<label>` that owns the input. Use `field-aria-label` instead when there is no visible text; when both are set, `label` wins.
114
+ - Figma's default Type=Pill switch (44×24 filled track with the handle inside) is not implemented.
115
+ - Hover tints the track with the action overlay and raises the handle shadow; pressed shows a 28px overlay disc behind the handle and squashes it to 20×16.
116
+ - Legacy hook aliases still honoured: `--switch-track`, `--switch-knob-on`, `--switch-knob-off` and `--switch-disabled` are read as fallbacks of the `--rel-switch-*` hooks. The old `--switch-halo` name was never read by any rule and is not a hook.
117
+ - `registerOnChange`, `registerOnTouched` and `writeValue` exist for the Angular `ControlValueAccessor` wrapper and are not public API.