@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,224 +1,168 @@
1
- # rel-sheet
2
-
3
- ## Overview
4
- A slide-in panel component system with multiple helper components. Features four slide directions (top, right, bottom, left), configurable sizes, modal/non-modal modes, focus trapping, scroll locking, and optional overlay. Built with composition: `rel-sheet` (controller), `rel-sheet-trigger` (opener), `rel-sheet-content` (panel), and layout helpers (`rel-sheet-header`, `rel-sheet-footer`, `rel-sheet-title`, `rel-sheet-description`, `rel-sheet-close`).
5
-
6
- ## Components
7
- - **`<rel-sheet>`**: Root wrapper that manages open state
8
- - **`<rel-sheet-trigger>`**: Button to open the sheet
9
- - **`<rel-sheet-content>`**: Slide-in panel with all visual elements
10
- - **`<rel-sheet-header>`**: Header layout helper with bottom border
11
- - **`<rel-sheet-footer>`**: Footer layout helper with top border
12
- - **`<rel-sheet-title>`**: Title text (auto-linked to `aria-labelledby`)
13
- - **`<rel-sheet-description>`**: Description text (auto-linked to `aria-describedby`)
14
- - **`<rel-sheet-close>`**: Custom close button wrapper
15
-
16
- ## Usage
17
- ```html
18
- <!-- Basic right-side sheet -->
19
- <rel-sheet>
20
- <rel-sheet-trigger>
21
- <button>Open Settings</button>
22
- </rel-sheet-trigger>
23
-
24
- <rel-sheet-content>
25
- <rel-sheet-header>
26
- <rel-sheet-title>Settings</rel-sheet-title>
27
- <rel-sheet-description>Configure your preferences</rel-sheet-description>
28
- </rel-sheet-header>
29
-
30
- <p>Sheet content goes here...</p>
31
-
32
- <rel-sheet-footer>
33
- <rel-sheet-close>
34
- <button>Cancel</button>
35
- </rel-sheet-close>
36
- <button>Save</button>
37
- </rel-sheet-footer>
38
- </rel-sheet-content>
39
- </rel-sheet>
40
-
41
- <!-- Left-side sheet, large size -->
42
- <rel-sheet>
43
- <rel-sheet-trigger>
44
- <button>Open Menu</button>
45
- </rel-sheet-trigger>
46
-
47
- <rel-sheet-content side="left" size="lg">
48
- <nav>
49
- <a href="#home">Home</a>
50
- <a href="#about">About</a>
51
- <a href="#contact">Contact</a>
52
- </nav>
53
- </rel-sheet-content>
54
- </rel-sheet>
55
-
56
- <!-- Bottom sheet without overlay -->
57
- <rel-sheet defaultopen>
58
- <rel-sheet-trigger>
59
- <button>Toggle Bottom Sheet</button>
60
- </rel-sheet-trigger>
61
-
62
- <rel-sheet-content side="bottom" hide-overlay>
63
- <p>Bottom sheet content</p>
64
- </rel-sheet-content>
65
- </rel-sheet>
66
-
67
- <!-- Controlled state -->
68
- <rel-sheet id="controlledSheet" open>
69
- <rel-sheet-trigger>
70
- <button>Open</button>
71
- </rel-sheet-trigger>
72
-
73
- <rel-sheet-content>
74
- <p>This sheet is controlled externally</p>
75
- </rel-sheet-content>
76
- </rel-sheet>
77
- <script>
78
- const sheet = document.getElementById('controlledSheet');
79
- sheet.addEventListener('rel-open-change', (e) => {
80
- console.log('Sheet open:', e.detail.open);
81
- console.log('Reason:', e.detail.reason);
82
- });
83
- </script>
84
-
85
- <!-- Without built-in close button -->
86
- <rel-sheet>
87
- <rel-sheet-trigger>
88
- <button>Open</button>
89
- </rel-sheet-trigger>
90
-
91
- <rel-sheet-content show-close="false">
92
- <p>Custom close handling</p>
93
- <rel-sheet-close>
94
- <button>Close Me</button>
95
- </rel-sheet-close>
96
- </rel-sheet-content>
97
- </rel-sheet>
98
- ```
99
-
100
- ## API
101
-
102
- ### rel-sheet Properties
103
- | Name | Type | Default | Description |
104
- |------|------|---------|-------------|
105
- | `open` (reflected) | `boolean?` | `undefined` | Controlled open state |
106
- | `defaultopen` | `boolean` | `false` | Default open state (uncontrolled) |
107
- | `disabled` | `boolean` | `false` | Disable trigger |
108
- | `modal` | `boolean` | `true` | Enable modal behavior (focus trap) |
109
-
110
- ### rel-sheet-content Properties
111
- | Name | Type | Default | Description |
112
- |------|------|---------|-------------|
113
- | `side` | `"top" \| "right" \| "bottom" \| "left"` | `"right"` | Which side the sheet slides from |
114
- | `size` | `"sm" \| "md" \| "lg" \| "xl"` | `"md"` | Sheet width/height (320/420/540/640px) |
115
- | `hide-overlay` | `boolean` | `false` | Hide the backdrop overlay |
116
- | `close-on-overlay` | `boolean` | `true` | Allow closing by clicking overlay |
117
- | `show-close` | `boolean` | `true` | Show built-in close button (✕) |
118
-
119
- ## Events
120
- | Event | Detail | Description |
121
- |-------|--------|-------------|
122
- | `rel-open-change` | `{ open, reason }` | Fired when sheet opens/closes |
123
-
124
- ### Close Reasons
125
- - `"trigger"`: Toggled via trigger button
126
- - `"close"`: Closed via close button
127
- - `"overlay"`: Closed by clicking overlay
128
- - `"escape"`: Closed via Escape key
129
- - `"api"`: Changed via `setOpen()` method
130
-
131
- ## Methods
132
-
133
- ### Sheet Methods
134
- ```typescript
135
- sheet.setOpen(nextOpen: boolean, reason?: string): void
136
- ```
137
-
138
- ## Slots
139
- | Slot | Component | Description |
140
- |------|-----------|-------------|
141
- | (default) | `rel-sheet` | Trigger and content elements |
142
- | (default) | `rel-sheet-trigger` | Trigger button/element |
143
- | (default) | `rel-sheet-content` | Panel content |
144
- | (default) | Layout helpers | Content within headers/footers/titles |
145
-
146
- ## CSS Parts
147
- | Part | Description |
148
- |------|-------------|
149
- | `overlay` | Backdrop overlay element |
150
- | `panel` | Main panel container |
151
-
152
- ## Tokens / CSS Variables
153
-
154
- ### Size Tokens (defined on `<rel-sheet-content>`)
155
- - `--sheet-size-sm`: `320px`
156
- - `--sheet-size-md`: `420px`
157
- - `--sheet-size-lg`: `540px`
158
- - `--sheet-size-xl`: `640px`
159
-
160
- ### Transition Tokens
161
- - `--sheet-transition-duration`: `0.3s`
162
- - `--sheet-transition-timing`: `cubic-bezier(0.32, 0.72, 0, 1)`
163
-
164
- ### Color Tokens
165
- - `--rel-bg-surface` - Panel background
166
- - `--rel-text-label` - Title and close button color
167
- - `--rel-text-body` - Description color
168
- - `--rel-bg-surface-elevated` - Close button hover background
169
- - `--rel-border-default` - Header/footer borders
170
- - `--rel-overlay-scrim` - Overlay background
171
- - `--rel-bg-info` - Close button focus outline
172
-
173
- ### Shadow Tokens
174
- - `--rel-shadows-shadow-500` - Panel shadow
175
-
176
- ### Border Tokens
177
- - `--rel-border-radius-md` - Panel border radius (top corners for bottom, etc.)
178
-
179
- ### Spacing Tokens
180
- - `--rel-spacing-lg` - Panel content padding
181
- - `--rel-spacing-md` - Header/footer padding, close button positioning
182
- - `--rel-spacing-xs` - Close button padding
183
-
184
- ## Accessibility
185
- - Panel uses `role="dialog"` and `aria-modal`
186
- - Auto-links `aria-labelledby` to `<rel-sheet-title>`
187
- - Auto-links `aria-describedby` to `<rel-sheet-description>`
188
- - Focus trap in modal mode
189
- - Escape key closes sheet
190
- - Restores focus to trigger on close
191
-
192
- ### Keyboard Interactions
193
- | Key | Action |
194
- |-----|--------|
195
- | `Escape` | Close sheet |
196
- | `Tab` | Focus trap within sheet (modal mode only) |
197
-
198
- ### Focus Behavior
199
- - On open, focuses first focusable element (or panel if none found)
200
- - Focus trapped within panel in modal mode
201
- - On close, restores focus to trigger button
202
-
203
- ## Notes / Edge Cases
204
- - **Side positioning and transforms**:
205
- - **right**: Slides in from right edge, `translateX(100%)` to `translateX(0)`
206
- - **left**: Slides in from left edge, `translateX(-100%)` to `translateX(0)`
207
- - **bottom**: Slides in from bottom, `translateY(100%)` to `translateY(0)`
208
- - **top**: Slides in from top, `translateY(-100%)` to `translateY(0)`
209
-
210
- - **Size constraints**:
211
- - Left/right: `max-width: 90vw`
212
- - Top/bottom: `max-height: 80vh`
213
-
214
- - **Scroll locking**: Body scroll is locked when sheet is open (with scrollbar width compensation)
215
- - **Close button position**: Top-right by default, but top-left for `side="left"`
216
- - **Reduced motion**: Transitions disabled, panel hidden via `display: none` instead of transform
217
- - The sheet root uses `display: contents`
218
- - Panel is overlaid with `z-index: 1000`, fixed position
219
- - Overlay detection for `aria-labelledby`/`aria-describedby` happens automatically based on slotted `<rel-sheet-title>` and `<rel-sheet-description>` elements
220
- - Built-in close button is "✕" character at font-size 20px
221
-
222
- ### Controlled vs Uncontrolled
223
- - If `open` prop is set, the sheet is controlled and won't manage its own state
224
- - Use `rel-open-change` event to sync controlled state
1
+ # rel-modal
2
+
3
+ `<rel-modal>` is the design system's modal dialog and implements the Figma **Modal Sheet** component (8171:3412): a panel anchored to the bottom, top or centre of the viewport over a scrim, with a title/description header, a scrollable body, an optional right-aligned footer and a built-in loading state. It lives in the `rel-sheet` folder and is exported as the `RelModal` class — there is no separate sheet element; the "sheet" is the `side="bottom"` / `side="top"` presentation of this one element. Use it for confirmations, short forms and messages that must interrupt the page. For edge-anchored panels that hold navigation or longer editing flows use `<rel-drawer>`; for lightweight contextual content anchored to a trigger use `<rel-popover>`.
4
+
5
+ ## Usage
6
+
7
+ ```html
8
+ <!-- Bottom sheet (default) with header, body and footer actions -->
9
+ <rel-button id="open-delete">Delete project</rel-button>
10
+
11
+ <rel-modal id="delete-modal" title="Delete project?" description="This action cannot be undone.">
12
+ <p>All files and history for <strong>Acme Rollout</strong> will be removed.</p>
13
+ <rel-button slot="footer" variant="secondary" id="cancel">Cancel</rel-button>
14
+ <rel-button slot="footer" tone="destructive" id="confirm">Delete</rel-button>
15
+ </rel-modal>
16
+
17
+ <script>
18
+ const modal = document.getElementById('delete-modal');
19
+ document.getElementById('open-delete').addEventListener('click', () => { modal.open = true; });
20
+ document.getElementById('cancel').addEventListener('click', () => { modal.open = false; });
21
+ modal.addEventListener('rel-open-change', (e) => {
22
+ console.log('open =', e.detail); // true | false
23
+ });
24
+ </script>
25
+
26
+ <!-- Centred dialog, small width -->
27
+ <rel-modal side="center" size="small" title="Session expired" description="Sign in again to continue.">
28
+ <rel-button slot="footer" variant="primary">Sign in</rel-button>
29
+ </rel-modal>
30
+
31
+ <!-- Top-anchored sheet with extra header content -->
32
+ <rel-modal side="top" size="large" title="New release">
33
+ <rel-tab slot="header" size="small">
34
+ <rel-tab-item slot="items" label="Summary"></rel-tab-item>
35
+ <rel-tab-item slot="items" label="Details"></rel-tab-item>
36
+ </rel-tab>
37
+ <p>Release notes go here.</p>
38
+ </rel-modal>
39
+
40
+ <!-- Message-only body (Figma "Text" slot) -->
41
+ <rel-modal variant="text" side="center" title="Saved" open>
42
+ <p>Your changes were saved.</p>
43
+ <p>You can close this window.</p>
44
+ </rel-modal>
45
+
46
+ <!-- Fullscreen, fixed desktop layout at every viewport width -->
47
+ <rel-modal size="fullscreen" breakpoint="web" title="Editor">
48
+ <p>Edge-to-edge content.</p>
49
+ </rel-modal>
50
+
51
+ <!-- Loading and locked states -->
52
+ <rel-modal title="Publishing…" loading open>
53
+ <p>Please wait.</p>
54
+ </rel-modal>
55
+ <rel-modal title="Processing" disabled open>
56
+ <p>Closing is blocked until the job finishes.</p>
57
+ <rel-button slot="footer">Done</rel-button>
58
+ </rel-modal>
59
+
60
+ <!-- Non-dismissible: no close button, backdrop and Escape ignored -->
61
+ <rel-modal id="locked" title="Accept terms">
62
+ <p>You must accept to continue.</p>
63
+ <rel-button slot="footer" variant="primary">Accept</rel-button>
64
+ </rel-modal>
65
+ <script>
66
+ // Boolean props that default to true can only be switched off from JS —
67
+ // an absent attribute leaves the default in place.
68
+ document.getElementById('locked').dismissible = false;
69
+ </script>
70
+ ```
71
+
72
+ <!-- readme-gen:start -->
73
+
74
+ <!-- Generated from rel-sheet.ts — do not edit; run readme-generate.mjs -->
75
+
76
+ ## API
77
+
78
+ ### Attributes / Properties
79
+
80
+ | Property | Attribute | Type | Default | Description |
81
+ | --- | --- | --- | --- | --- |
82
+ | `open` | `open` | `boolean` | `false` | Whether the modal is shown. Toggle it to open/close; every change fires `rel-open-change`. Reflected. |
83
+ | `side` | `side` | `"top" \| "bottom" \| "center"` | `"bottom"` | Where the panel is anchored: `bottom` (48px above the bottom edge, slides up), `top` (48px below the top edge, slides down) or `center` (fades and scales in). Below 480px with `breakpoint="responsive"` every side collapses to a full-width bottom sheet. |
84
+ | `title` | `title` | `string` | `""` | Heading text (rendered as an `<h2>`, linked via `aria-labelledby`). The `title` attribute is stripped from the host after it is read so no native tooltip appears. |
85
+ | `description` | `description` | `string` | `""` | Supporting text under the title (linked via `aria-describedby`). |
86
+ | `subtitle` | `subtitle` | `string` | `""` | Alias for `description`; rendered only when `description` is empty. |
87
+ | `dismissible` | `dismissible` | `boolean` | `true` | Coarse dismissal switch. When false the close button is hidden and backdrop clicks / Escape are ignored regardless of the granular `close-on-*` / `show-close-button` props. |
88
+ | `modal` | `modal` | `boolean` | `true` | Modal mode: renders the scrim, sets `role="dialog"` + `aria-modal`, traps Tab inside the panel and moves focus in on open. When false the panel is a `region` with no scrim and no focus trap. |
89
+ | `size` | `size` | `"small" \| "medium" \| "large" \| "extra-large" \| "fullscreen" \| "sm" \| "md" \| "lg" \| "xl"` | `"medium"` | Panel width scale. `medium` (default) matches the Figma Modal Sheet width; `fullscreen` covers the viewport. `sm` / `md` / `lg` / `xl` are alternative spellings of `small` / `medium` / `large` / `extra-large` and stay supported — they are normalised to the full words, not deprecated. Reflected. |
90
+ | `variant` | `variant` | `"default" \| "text"` | `"default"` | Body treatment — see `ModalVariant`. `"text"` styles the default slot as the Figma "Text" message slot; the header (and its divider) render as usual above it. Default rendering is unchanged. Reflected. |
91
+ | `closeOnBackdropClick` | `close-on-backdrop-click` | `boolean` | `true` | Close when the scrim is clicked. Only effective while `dismissible` is true; ignored while `disabled` or `loading`. |
92
+ | `closeOnEscape` | `close-on-escape` | `boolean` | `true` | Close on the Escape key. Only effective while `dismissible` is true; ignored while `disabled`. |
93
+ | `showCloseButton` | `show-close-button` | `boolean` | `true` | Render the "X" close button in the header. Only effective while `dismissible` is true. |
94
+ | `autoFocus` | `auto-focus` | `boolean` | `true` | Move focus into the panel when opened (modal mode only). |
95
+ | `restoreFocus` | `restore-focus` | `boolean` | `true` | Return focus to the opener when closed. |
96
+ | `scrollable` | `scrollable` | `boolean` | `true` | When false, the body no longer scrolls internally — tall content clips at the panel's max height. |
97
+ | `loading` | `loading` | `boolean` | `false` | Shows a spinner overlay and blocks all interaction inside the panel. Reflected. |
98
+ | `disabled` | `disabled` | `boolean` | `false` | Blocks user-initiated closes (X / backdrop / Escape) and footer actions. Reflected. |
99
+ | `zIndex` | `z-index` | `number` | `1000` | Stacking order of the whole modal (backdrop + panel). |
100
+ | `breakpoint` | `breakpoint` | `"responsive" \| "web"` | `"responsive"` | Figma "Breakpoint" axis. responsive (default) collapses to a full-width bottom sheet below 480px; web keeps the fixed desktop panel at every size. Reflected. |
101
+
102
+ ## Events
103
+
104
+ | Event | Detail | Bubbles / composed | Description |
105
+ | --- | --- | --- | --- |
106
+ | `rel-open-change` | `true / false` | bubbles / composed | Fired whenever `open` changes, from any cause (prop set, close button, backdrop, Escape). Detail: the new `open` boolean. |
107
+
108
+ ## Slots
109
+
110
+ | Slot | Description |
111
+ | --- | --- |
112
+ | (default) | Body content of the modal |
113
+ | `header` | Extra header content rendered under the title/description (e.g. a stepper or tabs) |
114
+ | `footer` | Actions row (right-aligned, 8px gap); the footer only renders when this slot has content |
115
+
116
+ ## CSS Parts
117
+
118
+ | Part | Description |
119
+ | --- | --- |
120
+ | `overlay` | The full-viewport scrim behind the panel |
121
+ | `panel` | The dialog panel (role="dialog") |
122
+ | `header` | Header row holding title, description, header slot and close button |
123
+ | `close-button` | The "X" close button |
124
+ | `content` | Scrollable body wrapper around the default slot |
125
+ | `footer` | Footer wrapper around the `footer` slot |
126
+ | `loading` | Spinner overlay shown while `loading` is set |
127
+
128
+ ## Tokens / CSS Variables
129
+
130
+ ### Component hooks
131
+
132
+ Set these on the host (or an ancestor) to customise the element without touching design tokens.
133
+
134
+ | Custom property | Default | Description |
135
+ | --- | --- | --- |
136
+ | `--rel-modal-transition-duration` | `var(--modal-transition-duration)` | Open/close animation duration; default `0.3s` |
137
+ | `--rel-modal-transition-timing` | `var(--modal-transition-timing)` | Open/close animation easing; default `cubic-bezier(0.32, 0.72, 0, 1)` |
138
+ | `--rel-modal-width` | `var(--modal-width)` | Panel width; overrides the `size` scale (md default `clamp(360px, 49vw, 708px)`, sm `min(400px, 100vw - 32px)`, lg `min(900px, 100vw - 48px)`, xl `min(1140px, 100vw - 48px)`). Ignored by `fullscreen` and by the responsive collapse below 480px |
139
+
140
+ <!-- readme-gen:end -->
141
+
142
+ ## Accessibility
143
+
144
+ - The panel is `role="dialog"` with `aria-modal="true"` while `modal` is on; with `modal` off it becomes a `role="region"` without scrim or focus trap.
145
+ - `aria-labelledby` points at the rendered `<h2>` when `title` is set, otherwise the panel carries `aria-label="Dialog"`; `aria-describedby` points at the description paragraph when `description` (or `subtitle`) is set.
146
+ - While `loading` the panel has `aria-busy="true"` and the header, body and footer are made `inert`; while `disabled` only the footer is `inert` and the close button is disabled.
147
+ - The close button is a real `<button>` with `aria-label="Close"`.
148
+ - Focus: on open (modal + `auto-focus`) focus moves to the first focusable element inside the panel, or to the panel itself (`tabindex="-1"`). On close (`restore-focus`) focus returns to the element that was active before opening — resolved through shadow roots, so a `<rel-button>` opener gets focus back.
149
+ - Body scroll is locked while open; the scrollbar width is added as body padding so the page does not shift.
150
+ - `prefers-reduced-motion: reduce` removes the slide/fade transitions and hides the closed panel outright.
151
+
152
+ | Key | Action |
153
+ | --- | --- |
154
+ | `Escape` | Closes the modal when `dismissible` and `close-on-escape` are on and the modal is not `disabled`. |
155
+ | `Tab` / `Shift+Tab` | Cycles focus inside the panel while `modal` is on (focus trap); wraps at either end. |
156
+
157
+ ## Notes / Edge Cases
158
+
159
+ - `title` is also HTML's global tooltip attribute. The component reads the attribute into the property and then removes it from the host, so no native tooltip appears over the modal or its scrim.
160
+ - `subtitle` is an alias for `description` and is only rendered when `description` is empty.
161
+ - The footer is only rendered when the `footer` slot has content; the host gains a `has-footer` attribute while it does.
162
+ - `dismissible` is the coarse switch and is ANDed with `close-on-backdrop-click`, `close-on-escape` and `show-close-button`; turning it off locks all three regardless of their own values.
163
+ - `loading` blocks backdrop clicks and the close button but does **not** block `Escape`; set `disabled` (or `close-on-escape` = false) if Escape must be blocked as well.
164
+ - Boolean props default to `true` (`dismissible`, `modal`, `close-on-*`, `show-close-button`, `auto-focus`, `restore-focus`, `scrollable`); an absent attribute keeps the default, so switch them off from JavaScript (`modal.closeOnEscape = false`).
165
+ - `size` picks the panel width; `--rel-modal-width` overrides it for every size except `fullscreen`. Below 480px with `breakpoint="responsive"` every side and size (except `fullscreen`) collapses to a full-width bottom sheet, ignoring the width hook.
166
+ - `z-index` is applied to the whole fixed container (scrim + panel); use it when the modal must sit above other stacked UI such as a toast region.
167
+ - Legacy hook aliases still honoured: `--modal-transition-duration`, `--modal-transition-timing` and `--modal-width` are read as fallbacks of the `--rel-modal-*` hooks. Note `--modal-width` is also set internally per `size`, so only the canonical `--rel-modal-width` reliably overrides the width from outside.
168
+ - The old `<rel-sheet>` / `<rel-sheet-*>` element family described in earlier docs was never shipped; this element is the only export of the folder.
@@ -0,0 +1,104 @@
1
+ # rel-skeleton
2
+
3
+ Loading placeholder that reserves the space of content still being fetched: a neutral `block`, a `circle` (avatars, icons) or a stack of text `line` bars, animated with a `pulse` or `shimmer` until you set `loaded`, at which point it hides itself. Use it when you know the shape of the incoming content (cards, list rows, avatars); use a spinner for indeterminate waits with no layout to preserve.
4
+
5
+ ## Usage
6
+
7
+ ```html
8
+ <!-- Block (default): fills its container, height set explicitly -->
9
+ <rel-skeleton height="160px"></rel-skeleton>
10
+
11
+ <!-- Circle: only a width is needed, the aspect ratio keeps it round -->
12
+ <rel-skeleton shape="circle" width="48px"></rel-skeleton>
13
+
14
+ <!-- Paragraph: three text bars, the last one shorter -->
15
+ <rel-skeleton shape="line" lines="3"></rel-skeleton>
16
+
17
+ <!-- Animation variants -->
18
+ <rel-skeleton shape="line" lines="2" animation="shimmer"></rel-skeleton>
19
+ <rel-skeleton shape="block" height="80px" animation="none"></rel-skeleton>
20
+
21
+ <!-- Custom colours -->
22
+ <rel-skeleton
23
+ shape="block"
24
+ height="120px"
25
+ animation="shimmer"
26
+ style="--rel-skeleton-color: #e5e7eb; --rel-skeleton-highlight: #f9fafb;"
27
+ ></rel-skeleton>
28
+
29
+ <!-- A list-row placeholder that swaps for real content -->
30
+ <div style="display: flex; gap: 12px; align-items: center;">
31
+ <rel-skeleton id="avatar" shape="circle" width="40px"></rel-skeleton>
32
+ <div style="flex: 1;">
33
+ <rel-skeleton id="text" shape="line" lines="2"></rel-skeleton>
34
+ </div>
35
+ </div>
36
+ <script>
37
+ fetchUser().then(() => {
38
+ document.getElementById('avatar').loaded = true;
39
+ document.getElementById('text').loaded = true;
40
+ });
41
+ </script>
42
+ ```
43
+
44
+ <!-- readme-gen:start -->
45
+
46
+ <!-- Generated from rel-skeleton.ts — do not edit; run readme-generate.mjs -->
47
+
48
+ ## API
49
+
50
+ ### Attributes / Properties
51
+
52
+ | Property | Attribute | Type | Default | Description |
53
+ | --- | --- | --- | --- | --- |
54
+ | `shape` | `shape` | `"line" \| "circle" \| "block"` | `"block"` | Placeholder shape: `block` fills the host (min 16px tall), `circle` is a square-aspect disc sized by the host width, `line` renders `lines` 16px text bars with the last one at 65% width. Reflected. |
55
+ | `lines` | `lines` | `number` | `1` | Number of text bars when `shape="line"` (minimum 1, --rel-spacing-xs apart); ignored by the other shapes. |
56
+ | `width` | `width` | `string` | `""` | Any CSS width (e.g. `"240px"`, `"50%"`) applied to the host's inline style. Empty leaves the host at its natural block width. |
57
+ | `height` | `height` | `string` | `""` | Any CSS height (e.g. `"120px"`) applied to the host's inline style. Not needed for `circle`, which derives its height from the width. |
58
+ | `animation` | `animation` | `"pulse" \| "shimmer" \| "none"` | `"pulse"` | Motion: `pulse` fades opacity between 20% and 40% (Figma's Start/End states), `shimmer` sweeps a highlight band left to right, `none` is static. Both animations are disabled under `prefers-reduced-motion: reduce`. Reflected. |
59
+ | `loaded` | `loaded` | `boolean` | `false` | Set once the real content is ready: hides the skeleton (`display: none`) and removes its `role="status"` / "Loading" label. Reflected. |
60
+
61
+ ## Events
62
+
63
+ | Event | Detail | Bubbles / composed | Description |
64
+ | --- | --- | --- | --- |
65
+ | — | — | — | This element emits no custom events. |
66
+
67
+ ## Slots
68
+
69
+ | Slot | Description |
70
+ | --- | --- |
71
+ | — | This element has no slots. |
72
+
73
+ ## CSS Parts
74
+
75
+ | Part | Description |
76
+ | --- | --- |
77
+ | `base` | Each placeholder surface: the single block/circle, or every bar when `shape="line"`. |
78
+
79
+ ## Tokens / CSS Variables
80
+
81
+ ### Component hooks
82
+
83
+ Set these on the host (or an ancestor) to customise the element without touching design tokens.
84
+
85
+ | Custom property | Default | Description |
86
+ | --- | --- | --- |
87
+ | `--rel-skeleton-color` | `var(--rel-bg-neutral-elevated-3)` | Fill colour of the placeholder (defaults to the elevated-3 neutral background token). |
88
+ | `--rel-skeleton-highlight` | `var(--rel-bg-neutral-elevated-2)` | Lighter band that sweeps across the placeholder when `animation="shimmer"` (defaults to the elevated-2 neutral background token). |
89
+ | `--rel-skeleton-line-height` | `16px` | Height of each text bar when `shape="line"`; default 16px (Figma's Line shape). |
90
+
91
+ <!-- readme-gen:end -->
92
+
93
+ ## Accessibility
94
+
95
+ - While not `loaded`, the host carries `role="status"` and `aria-label="Loading"`, so assistive technology announces that the region is loading; both are removed once `loaded` is set (the attributes are managed on each update, so they appear after the first render).
96
+ - The placeholder is not focusable and has no keyboard interaction.
97
+ - Under `prefers-reduced-motion: reduce` both `pulse` and `shimmer` are disabled and the placeholder renders static.
98
+
99
+ ## Notes / Edge Cases
100
+
101
+ - **Sizing.** `width` / `height` are written to the host's inline `style` and accept any CSS length. A `block` with no `height` is 16px tall (its `min-height`); a `circle` needs only a width (`aspect-ratio: 1 / 1`); `line` bars are 16px tall, `--rel-spacing-xs` apart, full width, with the last bar at 65% when there is more than one.
102
+ - **`loaded` hides, it does not remove.** The host switches to `display: none` and stays in the DOM; place the real content as a sibling rather than inside the skeleton (the element has no slot).
103
+ - **Multiple `base` parts.** With `shape="line"` every bar carries `part="base"`, so `::part(base)` styles all of them.
104
+ - **Shimmer overrides the fill.** In `shimmer` mode the background becomes a gradient built from `--rel-skeleton-color` and `--rel-skeleton-highlight`; set both when customising colours.