@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,130 +1,190 @@
1
- # rel-button
2
-
3
- ## Overview
4
- A versatile button component with six variants (default, primary, secondary, text, link, icon), three tone options, three sizes, and configurable border radius. Features hover, active, and focus states with smooth transitions. Slots support custom content including text, icons, or combinations.
5
-
6
- ## Usage
7
- ```html
8
- <!-- Basic variants -->
9
- <rel-button variant="default">Default Button</rel-button>
10
- <rel-button variant="primary">Primary Button</rel-button>
11
- <rel-button variant="secondary">Secondary Button</rel-button>
12
- <rel-button variant="text">Text Button</rel-button>
13
- <rel-button variant="link">Link Style</rel-button>
14
-
15
- <!-- Icon button -->
16
- <rel-button variant="icon">⚙️</rel-button>
17
-
18
- <!-- Tones (affect color scheme) -->
19
- <rel-button variant="primary" tone="default">Default Tone</rel-button>
20
- <rel-button variant="primary" tone="critical">Critical Action</rel-button>
21
- <rel-button variant="primary" tone="success">Success Action</rel-button>
22
-
23
- <!-- Sizes -->
24
- <rel-button size="small">Small</rel-button>
25
- <rel-button size="medium">Medium</rel-button>
26
- <rel-button size="large">Large</rel-button>
27
-
28
- <!-- Border radius options -->
29
- <rel-button border="corner-radius">Rounded</rel-button>
30
- <rel-button border="small-corner-radius">Small Rounded</rel-button>
31
- <rel-button border="no-corner-radius">Sharp</rel-button>
32
-
33
- <!-- Disabled -->
34
- <rel-button disabled>Disabled Button</rel-button>
35
-
36
- <!-- With icon and text (using slot) -->
37
- <rel-button variant="primary">
38
- <svg width="16" height="16" viewBox="0 0 16 16"><path d="M8 0l3 6h6l-5 4 2 6-6-4-6 4 2-6-5-4h6z"/></svg>
39
- Save
40
- </rel-button>
41
-
42
- <!-- Listen for click -->
43
- <rel-button id="actionBtn" variant="primary">Click Me</rel-button>
44
- <script>
45
- document.getElementById('actionBtn').addEventListener('click', () => {
46
- console.log('Button clicked');
47
- });
48
- </script>
49
- ```
50
-
51
- ## API
52
-
53
- ### Attributes/Properties
54
- | Name | Type | Default | Description |
55
- |------|------|---------|-------------|
56
- | `variant` | `"default" \| "primary" \| "secondary" \| "text" \| "link" \| "icon"` | `"default"` | Visual style variant |
57
- | `tone` | `"default" \| "critical" \| "success"` | `"default"` | Color tone (affects default/primary variants) |
58
- | `size` | `"small" \| "medium" \| "large"` | `"medium"` | Button size |
59
- | `border` | `"corner-radius" \| "small-corner-radius" \| "no-corner-radius"` | `"corner-radius"` | Border radius style |
60
- | `disabled` | `boolean` (reflected) | `false` | Disables interaction and applies disabled styling |
61
-
62
- ## Events
63
- | Event | Detail | Description |
64
- |-------|--------|-------------|
65
- | `click` | `MouseEvent` | Re-dispatched from the internal button (for framework compatibility) |
66
-
67
- ## Slots
68
- | Slot | Description |
69
- |------|-------------|
70
- | (default) | Button content (text, icons, or combination) |
71
-
72
- ## CSS Parts
73
- None exposed.
74
-
75
- ## Tokens / CSS Variables
76
-
77
- ### Semantic Tokens
78
- - `--rel-fonts-font-family` - Font family
79
- - `--rel-fonts-weight-body-semibold` - Font weight
80
- - `--rel-body-sm-regular-font-size`, `--rel-body-md-regular-font-size`, `--rel-body-xl-regular-font-size` - Font sizes for sizes
81
-
82
- ### Spacing Tokens
83
- - `--rel-spacing-2xs` - Internal gap between elements
84
- - `--rel-spacing-md`, `--rel-spacing-lg`, `--rel-spacing-xl` - Horizontal padding by size
85
-
86
- ### Color Tokens (by variant/tone)
87
- - **default + default tone**: `--rel-text-default` (bg), `--rel-bg-surface` (text)
88
- - **default + critical tone**: `--rel-bg-danger` (bg), `--rel-bg-surface` (text)
89
- - **default + success tone**: `--rel-text-success` (bg), `--rel-bg-surface` (text)
90
- - **primary + default tone**: `--rel-bg-brand` (bg), `--rel-text-default` (text)
91
- - **primary + critical tone**: `--rel-bg-danger` (bg), `--rel-bg-surface` (text)
92
- - **primary + success tone**: `--rel-text-success` (bg), `--rel-bg-surface` (text)
93
- - **secondary**: `--rel-bg-surface` (bg), `--rel-text-default` (text + border)
94
- - **text**: transparent (bg), `--rel-text-default` (text)
95
- - **link**: transparent (bg), `--rel-bg-info` (text), underlined
96
- - **icon**: `--rel-bg-surface-elevated` (bg), `--rel-text-default` (text), circular (40px × 40px)
97
- - **disabled**: `--rel-border-default` (bg), `--rel-text-disabled` (text)
98
-
99
- ### Border Tokens
100
- - `--rel-border-radius-full-rounded` - Large corner radius
101
- - `--rel-border-radius-sm` - Small corner radius
102
- - `--rel-border-radius-square` - No corner radius
103
-
104
- ## Accessibility
105
- - Uses native `<button>` element for proper semantics
106
- - `aria-disabled` attribute set on disabled buttons
107
- - Focus visible state with blue outline
108
- - Keyboard accessible (Space/Enter activation)
109
-
110
- ### Keyboard Interactions
111
- - `Tab` - Focus the button
112
- - `Space/Enter` - Activate the button
113
-
114
- ### Focus Behavior
115
- - Blue 2px outline with 2px offset on focus-visible
116
- - Focus outline works across all variants
117
-
118
- ## Notes / Edge Cases
119
- - **Icon variant** is always circular (40px × 40px), ignores size property, and has no padding
120
- - **Link variant** has `text-decoration: underline`
121
- - **Secondary variant** includes a 1px border matching the text color
122
- - Hover effect applies `filter: brightness(0.92)` to darken
123
- - Active (pressed) effect applies `transform: scale(0.97)` for tactile feedback
124
- - Disabled buttons have `pointer-events: none` and use `!important` to override theming
125
- - The component re-dispatches click events to ensure framework compatibility (e.g., Angular)
126
- - Clicking a disabled button prevents default and stops propagation
127
- - Size variants affect height (32px/40px/48px) and horizontal padding
128
- - The button has `display: inline-flex` with `align-items: center` and `justify-content: center`
129
- - Internal gap of `var(--rel-spacing-2xs)` allows icons + text combinations
130
- - Transitions are `0.15s ease` for all visual changes
1
+ # rel-button
2
+
3
+ Action button implementing the Figma "Button" component: a native `<button>` with the Figma **Style** (`variant`), **Tone** (`tone`) and **Size** axes, an independent **Icon Only** mode, a **Loading** state and an optional corner badge. Use it for every clickable action (submit, confirm, open, delete). Do not use it for navigation to another page — use a link instead.
4
+
5
+ ## Usage
6
+
7
+ ```html
8
+ <!-- Styles (Figma "Style") -->
9
+ <rel-button variant="primary">Primary</rel-button>
10
+ <rel-button variant="secondary">Secondary</rel-button>
11
+ <rel-button variant="ghost">Ghost</rel-button>
12
+ <rel-button variant="link">Link style</rel-button>
13
+ <rel-button variant="default">Neutral fill</rel-button>
14
+ <div style="background: #1f2937; padding: 8px;">
15
+ <rel-button variant="static">Static (on dark surfaces)</rel-button>
16
+ </div>
17
+
18
+ <!-- Tones (Figma "Tone") -->
19
+ <rel-button variant="primary" tone="brand">Save</rel-button>
20
+ <rel-button variant="primary" tone="neutral">Cancel</rel-button>
21
+ <rel-button variant="primary" tone="destructive">Delete</rel-button>
22
+ <rel-button variant="secondary" tone="destructive">Remove</rel-button>
23
+ <rel-button variant="ghost" tone="destructive">Discard</rel-button>
24
+
25
+ <!-- Sizes: 32 / 40 / 48px -->
26
+ <rel-button size="small">Small</rel-button>
27
+ <rel-button size="medium">Medium</rel-button>
28
+ <rel-button size="large">Large</rel-button>
29
+
30
+ <!-- Corner radius -->
31
+ <rel-button border="corner-radius">Pill</rel-button>
32
+ <rel-button border="small-corner-radius">Small radius</rel-button>
33
+ <rel-button border="no-corner-radius">Square</rel-button>
34
+
35
+ <!-- Icon + label, and icon-only (needs an accessible name) -->
36
+ <rel-button variant="primary">
37
+ <rel-icon name="plus"></rel-icon>
38
+ Add item
39
+ </rel-button>
40
+ <rel-button variant="secondary" icon-only aria-label="Settings">
41
+ <rel-icon name="settings"></rel-icon>
42
+ </rel-button>
43
+ <rel-button variant="ghost" icon-only size="small" aria-label="Close">
44
+ <rel-icon name="x"></rel-icon>
45
+ </rel-button>
46
+
47
+ <!-- States -->
48
+ <rel-button variant="primary" loading>Saving</rel-button>
49
+ <rel-button variant="primary" disabled>Unavailable</rel-button>
50
+
51
+ <!-- Corner badges -->
52
+ <rel-button variant="secondary" badge="3">Inbox</rel-button>
53
+ <rel-button variant="ghost" icon-only badge-dot aria-label="Notifications">
54
+ <rel-icon name="bell"></rel-icon>
55
+ </rel-button>
56
+
57
+ <!-- Listen for clicks (native click bubbles through the shadow root) -->
58
+ <rel-button id="save" variant="primary">Save</rel-button>
59
+ <script>
60
+ document.getElementById('save').addEventListener('click', () => {
61
+ console.log('saved');
62
+ });
63
+ </script>
64
+
65
+ <!-- Per-instance minimum width -->
66
+ <rel-button variant="primary" style="--rel-button-min-width: 160px;">Wide</rel-button>
67
+ ```
68
+
69
+ ### Deprecated aliases
70
+
71
+ ```html
72
+ <!-- Legacy Style spellings — still rendered identically, prefer the canonical names -->
73
+ <rel-button variant="outline">Outline</rel-button> <!-- → variant="secondary" -->
74
+ <rel-button variant="text">Text</rel-button> <!-- → variant="ghost" -->
75
+ <rel-button variant="icon" aria-label="Settings"> <!-- → icon-only + a variant -->
76
+ <rel-icon name="settings"></rel-icon>
77
+ </rel-button>
78
+
79
+ <!-- Legacy Tone spellings — each logs once per page:
80
+ [rel-button] "tone="critical"" is deprecated and will be removed in 3.0 — use "tone="destructive"" instead. -->
81
+ <rel-button variant="primary" tone="critical">Delete</rel-button> <!-- → tone="destructive" -->
82
+ <rel-button variant="primary" tone="danger">Delete</rel-button> <!-- → tone="destructive" -->
83
+ ```
84
+
85
+ <!-- readme-gen:start -->
86
+
87
+ <!-- Generated from rel-button.ts — do not edit; run readme-generate.mjs -->
88
+
89
+ ## API
90
+
91
+ ### Attributes / Properties
92
+
93
+ | Property | Attribute | Type | Default | Description |
94
+ | --- | --- | --- | --- | --- |
95
+ | `variant` | `variant` | `"default" \| "primary" \| "secondary" \| "text" \| "link" \| "icon" \| "static" \| "outline" \| "ghost"` | `"default"` | Style axis (Figma "Style"). Canonical: `default` (legacy neutral fill), `primary`, `secondary` (outlined), `ghost` (text-only), `link` (underlined inline text), `static` (transparent, static-light label for dark/colored surfaces). Deprecated aliases still accepted: `outline` = secondary, `text` = ghost, and `icon` (pre-2.0 40px circular icon button — use `icon-only` with a style instead). |
96
+ | `tone` | `tone` | `"default" \| "primary" \| "secondary" \| "critical" \| "success" \| "neutral" \| "brand" \| "danger" \| "destructive"` | `undefined` | Tone axis (Figma "Tone"). Canonical: `neutral` (= `default`), `brand` (= `primary`), `destructive`. Deprecated aliases `critical` and `danger` map to destructive and log a one-time console notice. `secondary` and `success` are legacy extras with no Figma counterpart (secondary only affects the legacy `icon` variant). Left UNSET it means `neutral`, except on `variant="primary"`, where it still means `brand` for one release — see `effectiveTone`. Write the tone explicitly and you always get what you wrote. |
97
+ | `iconOnly` | `icon-only` | `boolean` | `false` | Figma "Type=Icon Only": renders a square button (32/40/48px per `size`) of the current style and tone with no padding or min-width. Composes with every `variant`; slot a single `rel-icon` and set `aria-label`. Reflected. |
98
+ | `loading` | `loading` | `boolean` | `false` | Figma "State=Loading": replaces the label with a spinner (`indicator` part), sets `aria-busy`, and blocks pointer and programmatic clicks. The button keeps its width. Reflected. |
99
+ | `size` | `size` | `"small" \| "medium" \| "large"` | `"medium"` | Figma "Size": `small` = 32px, `medium` = 40px, `large` = 48px height, with matching padding, typography and minimum width. |
100
+ | `border` | `border` | `"corner-radius" \| "small-corner-radius" \| "no-corner-radius"` | `"corner-radius"` | Corner treatment: `corner-radius` = fully rounded pill, `small-corner-radius` = small radius token, `no-corner-radius` = square corners. |
101
+ | `disabled` | `disabled` | `boolean` | `false` | Figma "State=Disabled": dims the button to 50% opacity, flattens filled styles to the disabled fill, shows a `not-allowed` cursor and swallows every click (including programmatic `.click()`). Reflected. |
102
+ | `ariaLabel` | `aria-label` | `string \| null` | `null` | Accessible name forwarded to the inner native `<button>`. Required for `icon-only` buttons, whose slotted icon has no visible text. |
103
+ | `badge` | `badge` | `string` | `""` | Text for a small `rel-badge` anchored to the top-right corner (e.g. "3", "999+"). Empty string renders no badge. Ignored when `badge-dot` is set. |
104
+ | `badgeDot` | `badge-dot` | `boolean` | `false` | Renders a small dot badge in the top-right corner instead of a numbered badge; takes precedence over `badge`. The dot is inset 2px from the corner so it lands on a circular icon button's edge. |
105
+
106
+ ## Events
107
+
108
+ | Event | Detail | Bubbles / composed | Description |
109
+ | --- | --- | --- | --- |
110
+ | — | — | — | This element emits no custom events. |
111
+
112
+ ## Slots
113
+
114
+ | Slot | Description |
115
+ | --- | --- |
116
+ | (default) | Button content: label text, a `rel-icon`, or icon + text (kept on one line with the size's gap). |
117
+
118
+ ## CSS Parts
119
+
120
+ | Part | Description |
121
+ | --- | --- |
122
+ | `base` | The inner native `<button>` element (padding, min-width, fill, border, focus ring). |
123
+ | `indicator` | The loading spinner, rendered only while `loading` is set. |
124
+
125
+ ## Tokens / CSS Variables
126
+
127
+ ### Component hooks
128
+
129
+ Set these on the host (or an ancestor) to customise the element without touching design tokens.
130
+
131
+ | Custom property | Default | Description |
132
+ | --- | --- | --- |
133
+ | `--rel-button-min-width` | `80px` | Overrides the per-size minimum width (Figma small/medium/large = 80/100/120px, ghost small = 56px). Ignored by `icon-only` and `link`. |
134
+
135
+ <!-- readme-gen:end -->
136
+
137
+ ## Accessibility
138
+
139
+ - Renders a native `<button>` (the `base` part), so it is focusable, announced as a button and activated with the keyboard without extra work.
140
+ - `aria-disabled` mirrors `disabled`; `aria-busy` mirrors `loading`. The spinner is `aria-hidden`, and the label stays in the DOM (visually hidden) while loading, so the accessible name does not change.
141
+ - `aria-label` is forwarded to the inner button. Always set it on `icon-only` buttons and on the legacy `icon` variant — a slotted icon has no text.
142
+ - Disabled and loading buttons swallow clicks in the capture phase on the host, so `el.click()` and delegated listeners never fire.
143
+
144
+ | Key | Action |
145
+ | --- | --- |
146
+ | `Tab` / `Shift+Tab` | Move focus to / from the button |
147
+ | `Enter` / `Space` | Activate (native button behaviour) |
148
+
149
+ Focus: `:focus-visible` shows the shared 2px focus outline plus inner/outer halo on every style except `link`, which shows a 2px focus-coloured underline bar and the hover label colour instead (still a visible indicator per WCAG 2.4.7). Pressing scales the button to 97%; the spinner slows to 2s under `prefers-reduced-motion: reduce`.
150
+
151
+ ## Notes / Edge Cases
152
+
153
+ ### The `variant="primary"` tone transition
154
+
155
+ Figma's `Style=Primary` is the **filled** style, and `Tone` picks its colour — so Primary + Neutral is the neutral fill and Primary + Brand is the brand fill. This component's `variant="primary"` historically meant *both* "filled" and "brand-coloured", which left the Figma-canonical Primary/Neutral cell unreachable by its own names.
156
+
157
+ That cell now works: **`variant="primary" tone="neutral"` renders the neutral fill.**
158
+
159
+ For one release there is a deliberate wrinkle, and it is worth knowing before you debug it:
160
+
161
+ | Markup | Renders | Why |
162
+ | --- | --- | --- |
163
+ | `<rel-button>` | neutral fill | Unchanged. `variant` defaults to `default`, which is Figma's Primary/Neutral. |
164
+ | `<rel-button variant="primary">` | **brand** fill | Unset tone still means brand *here only*, so existing brand buttons do not silently turn neutral. Logs a one-time console notice. |
165
+ | `<rel-button variant="primary" tone="brand">` | brand fill | The explicit form. **Write this.** |
166
+ | `<rel-button variant="primary" tone="neutral">` | neutral fill | The Figma-canonical cell, now reachable. |
167
+
168
+ So for this release, writing the default value explicitly changes the result: `variant="primary"` alone and `variant="primary" tone="neutral"` render differently. Setting `tone` explicitly always gives you exactly what you wrote. A future major will make an unset tone mean `neutral` everywhere, matching every other style.
169
+
170
+ ### Tone × Style matrix
171
+
172
+ Figma populates only part of the grid. Combinations outside it have no design-system definition and fall back to the style's neutral treatment:
173
+
174
+ | Tone | Primary | Secondary | Ghost | Link | Static |
175
+ | --- | --- | --- | --- | --- | --- |
176
+ | Neutral | yes | yes | yes | yes | yes |
177
+ | Brand | yes | — | — | — | — |
178
+ | Destructive | yes | yes | yes | yes | — |
179
+
180
+ `Link` additionally has no icon-only and no loading state in the design.
181
+
182
+ - **Events.** The element dispatches no custom events. The native `click` is `composed`, so a listener on `<rel-button>` (or an ancestor) receives exactly one event per activation — do not re-dispatch it.
183
+ - **Minimum widths** come from the Figma master: small/medium/large = 80/100/120px, static = 40/50/60px. `link` has no minimum, `icon-only` is a fixed square (32/40/48px) and the legacy `icon` variant is always a 40px circle regardless of `size`. Override per instance with `--rel-button-min-width`.
184
+ - **`border`** exposes `small-corner-radius` and `no-corner-radius`, which have no design-system backing — every one of Figma's 624 button nodes is fully rounded. They are a code-only extension, kept for existing layouts.
185
+ - **Legacy `icon` variant vs `icon-only`.** `variant="icon"` keeps its pre-2.0 look (40px circle, tone-driven fill, `tone="secondary"` gives it an outline) and ignores `size` and `border`. `icon-only` is an axis that composes with any style and size — prefer it.
186
+ - **`tone` on `secondary` / `ghost` / `link`** changes the label, border or text colour only; the fill stays on the surface. The destructive outline border is deliberately subtle at rest and only goes solid on hover/pressed, matching Figma.
187
+ - **`static`** is a neutral-only style meant for coloured or dark backgrounds; other tones have no static treatment.
188
+ - **`success`** is a legacy tone without a Figma counterpart; it renders a green fill/outline/ghost tint but is not part of the current spec.
189
+ - **Badges** render a small `rel-badge` positioned outside the button's box (numbered) or 2px inside the corner (dot); give the button room in tight layouts. `badge-dot` wins when both are set.
190
+ - **Disabled styling**: only filled styles (`default`, `primary`, `icon`) flatten to the neutral disabled fill; `secondary`, `ghost`, `link` and `static` keep their shape at 50% opacity so they still read as their own style.
@@ -1,109 +1,109 @@
1
- # rel-calendar
2
-
3
- A fully accessible calendar component built with Lit for the Reliability Design System.
4
-
5
- ## Features
6
-
7
- - Single date selection
8
- - Month navigation
9
- - Keyboard navigation support (arrow keys, Home/End, PageUp/PageDown)
10
- - Min/max date constraints
11
- - Custom disabled date predicate
12
- - Configurable week start (Sunday or Monday)
13
- - Locale-aware month names
14
- - Today indicator
15
- - Full ARIA support
16
-
17
- ## Usage
18
-
19
- ```html
20
- <rel-calendar></rel-calendar>
21
- ```
22
-
23
- ### With value
24
-
25
- ```html
26
- <rel-calendar value="2026-01-16"></rel-calendar>
27
- ```
28
-
29
- ### With min/max
30
-
31
- ```html
32
- <rel-calendar
33
- min="2026-01-01"
34
- max="2026-12-31"
35
- ></rel-calendar>
36
- ```
37
-
38
- ### Week starting Monday
39
-
40
- ```html
41
- <rel-calendar week-start="mon"></rel-calendar>
42
- ```
43
-
44
- ### Disabled
45
-
46
- ```html
47
- <rel-calendar disabled></rel-calendar>
48
- ```
49
-
50
- ### Custom disabled dates (JavaScript)
51
-
52
- ```javascript
53
- const calendar = document.querySelector('rel-calendar');
54
-
55
- // Disable weekends
56
- calendar.isDateDisabled = (date) => {
57
- const day = date.getDay();
58
- return day === 0 || day === 6; // Sunday or Saturday
59
- };
60
-
61
- // Listen to changes
62
- calendar.addEventListener('rel-change', (e) => {
63
- console.log('Selected date:', e.detail.value); // "2026-01-16"
64
- console.log('Selected date object:', e.detail.date); // Date object
65
- });
66
- ```
67
-
68
- ## Attributes
69
-
70
- | Attribute | Type | Default | Description |
71
- |-----------|------|---------|-------------|
72
- | `value` | `string \| null` | `null` | Selected date in ISO format (YYYY-MM-DD) |
73
- | `min` | `string \| null` | `null` | Minimum selectable date (ISO format) |
74
- | `max` | `string \| null` | `null` | Maximum selectable date (ISO format) |
75
- | `disabled` | `boolean` | `false` | Disables all interaction |
76
- | `week-start` | `"sun" \| "mon"` | `"sun"` | First day of the week |
77
- | `locale` | `string` | `"en-US"` | Locale for month names (uses Intl.DateTimeFormat) |
78
-
79
- ## Properties
80
-
81
- | Property | Type | Description |
82
- |----------|------|-------------|
83
- | `isDateDisabled` | `(date: Date) => boolean` | Custom function to disable specific dates |
84
-
85
- ## Events
86
-
87
- | Event | Detail | Description |
88
- |-------|--------|-------------|
89
- | `rel-change` | `{ value: string \| null, date: Date \| null }` | Fired when selection changes |
90
-
91
- ## Keyboard Navigation
92
-
93
- - **Arrow keys**: Move focus by day (left/right) and week (up/down)
94
- - **Home/End**: Jump to start/end of current week
95
- - **PageUp/PageDown**: Change month
96
- - **Shift + PageUp/PageDown**: Change year
97
- - **Enter/Space**: Select focused day
98
-
99
- ## Styling
100
-
101
- The component uses design tokens from the Reliability Design System. All colors, spacing, and typography are inherited from CSS variables.
102
-
103
- Key tokens used:
104
- - `--rel-bg-*` / `--rel-text-*` / `--rel-border-*` semantic colors
105
- - `--rel-spacing-*` for spacing
106
- - `--rel-body-*` / `--rel-headings-*` composite typography roles
107
- - `--rel-border-radius-*` for border radius
108
- - Focus outline uses `--rel-bg-info`
109
- - Selected state uses `--rel-bg-brand`
1
+ # rel-calendar
2
+
3
+ `<rel-calendar>` is the inline, always-visible month calendar of the Reliability Design System (Figma "Calendar"). It renders one month as a 7-column grid with previous/next navigation, a today ring, dimmed weekends and adjacent-month days, `min`/`max` bounds and a custom disabled-day predicate, and lets the user select a single date. Use it when the calendar itself is the interface — a scheduling sidebar, an inline availability view. For a form field that opens a calendar in a popover use `<rel-date-picker>`; for a from/to range use `<rel-range-picker>`.
4
+
5
+ ## Usage
6
+
7
+ ```html
8
+ <!-- Default: today's month, nothing selected -->
9
+ <rel-calendar></rel-calendar>
10
+
11
+ <!-- Preselected date (ISO YYYY-MM-DD); the grid opens on that month -->
12
+ <rel-calendar value="2026-01-16"></rel-calendar>
13
+
14
+ <!-- Bounded: days outside min/max render disabled -->
15
+ <rel-calendar min="2026-01-01" max="2026-12-31"></rel-calendar>
16
+
17
+ <!-- Monday-first week with German month and weekday names -->
18
+ <rel-calendar week-start="mon" locale="de-DE"></rel-calendar>
19
+
20
+ <!-- Disabled: every cell and both navigation buttons are inert -->
21
+ <rel-calendar value="2026-01-16" disabled></rel-calendar>
22
+ ```
23
+
24
+ ```js
25
+ const calendar = document.querySelector("rel-calendar");
26
+
27
+ // Disable individual days on top of min/max (property only — there is no attribute)
28
+ calendar.isDateDisabled = (date) => date.getDay() === 0 || date.getDay() === 6;
29
+
30
+ calendar.addEventListener("rel-change", (e) => {
31
+ e.detail.value; // "2026-01-16"
32
+ e.detail.date; // Date at local midnight
33
+ });
34
+ ```
35
+
36
+ <!-- readme-gen:start -->
37
+
38
+ <!-- Generated from rel-calendar.ts — do not edit; run readme-generate.mjs -->
39
+
40
+ ## API
41
+
42
+ ### Attributes / Properties
43
+
44
+ | Property | Attribute | Type | Default | Description |
45
+ | --- | --- | --- | --- | --- |
46
+ | `value` | `value` | `string \| null` | `null` | Selected date as an ISO `YYYY-MM-DD` string, or `null` for no selection; setting it also moves the visible month. Reflected. |
47
+ | `min` | `min` | `string \| null` | `null` | Earliest selectable date (ISO `YYYY-MM-DD`); earlier days render disabled. |
48
+ | `max` | `max` | `string \| null` | `null` | Latest selectable date (ISO `YYYY-MM-DD`); later days render disabled. |
49
+ | `disabled` | `disabled` | `boolean` | `false` | Disables every day cell and the month navigation; the host dims to 40% opacity (Figma State=Disabled). Reflected. |
50
+ | `weekStart` | `week-start` | `"sun" \| "mon"` | `"sun"` | First column of the week grid: `"sun"` (default) or `"mon"`. |
51
+ | `locale` | `locale` | `string` | `"en-US"` | BCP 47 locale for the month name, weekday headers and day cell labels (via `Intl.DateTimeFormat`). |
52
+
53
+ ### Public fields / accessors
54
+
55
+ | Name | Type | Description |
56
+ | --- | --- | --- |
57
+ | `isDateDisabled` | `(date: Date) => boolean` | Custom predicate that disables individual days (return `true` to disable); applied on top of `min`/`max`. Property only — assign it from JavaScript. |
58
+
59
+ ## Events
60
+
61
+ | Event | Detail | Bubbles / composed | Description |
62
+ | --- | --- | --- | --- |
63
+ | `rel-change` | `{ value: newValue, date: new Date(day.date), }` | bubbles / composed | Fired when the user selects a day. Detail: `{ value: string, date: Date }` — the ISO `YYYY-MM-DD` string and a local-midnight Date. |
64
+
65
+ ## Slots
66
+
67
+ | Slot | Description |
68
+ | --- | --- |
69
+ | — | This element has no slots. |
70
+
71
+ ## CSS Parts
72
+
73
+ | Part | Description |
74
+ | --- | --- |
75
+ | `header` | Month navigation row (previous button, month/year label, next button) |
76
+ | `action` | The previous-month and next-month buttons |
77
+ | `label` | The month/year heading and each weekday column header |
78
+ | `list` | The weekday-header row and the day grid |
79
+ | `item` | Each day cell button |
80
+
81
+ <!-- readme-gen:end -->
82
+
83
+ ## Accessibility
84
+
85
+ - The day area is a `role="grid"` named "<Month> <Year>" (e.g. "January 2026"); the weekday header row and each week are `role="row"`, weekday names are `role="columnheader"`.
86
+ - Every day is a native `<button role="gridcell">` whose `aria-label` is the full long date in `locale` ("Friday, January 16, 2026"), with `aria-selected` for the selected day, `aria-current="date"` on today, and the native `disabled` attribute for days outside `min`/`max`, rejected by `isDateDisabled`, or when the host is `disabled`.
87
+ - The navigation buttons are labelled "Previous month" / "Next month".
88
+ - Roving tabindex: exactly one cell is a Tab stop — the last cell the user arrowed to, else the selected day, else today, else the first enabled day of the month — so Tab can always re-enter the grid.
89
+ - After every month or year change (buttons, PageUp/PageDown, or arrowing past the edge of the month) the new "<Month> <Year>" is read to screen readers through the shared `announce()` live region (`utils/announce.ts`).
90
+
91
+ | Key | Action |
92
+ | --- | --- |
93
+ | ArrowLeft / ArrowRight | Focus the previous / next day (crossing into the adjacent month switches the view) |
94
+ | ArrowUp / ArrowDown | Focus the same weekday one week earlier / later |
95
+ | Home / End | Focus the first / last cell of the current row |
96
+ | PageUp / PageDown | Show the previous / next month |
97
+ | Shift + PageUp / Shift + PageDown | Show the previous / next year |
98
+ | Enter / Space | Select the focused day (fires `rel-change`) |
99
+
100
+ ## Notes / Edge Cases
101
+
102
+ - `value`, `min` and `max` must be `YYYY-MM-DD`; any other string is ignored (no selection, view unchanged). Setting `value` moves the visible month; clearing it to `null` leaves the view where it is.
103
+ - The grid always renders six weeks (42 cells). The leading/trailing cells belong to the adjacent months and, unlike in `rel-date-picker`, they are selectable (subject to `min`/`max`/`isDateDisabled`); picking one fires `rel-change` and the view jumps to that month.
104
+ - Comparisons happen at local midnight: the `Date` passed to `isDateDisabled` and returned in `rel-change` is `new Date(y, m, d)`, so read it with local getters, not `toISOString()`.
105
+ - `disabled` makes every day fail the disabled check and disables both navigation buttons; pointer events stay on the host so the `not-allowed` cursor shows.
106
+ - Each cell follows Figma ".Date Days": a 36 px full-rounded halo around a 28 px disc. The halo carries hover (`action/overlay/hover`), the focus ring and the selected day's `elevated-1` backing; the disc carries today's 1 px brand ring and the selected day's `inverse-2` fill with an inverse semibold label.
107
+ - Figma treats the cell tone as one axis, so exactly one applies per day: today beats weekend beats adjacent-month. Adjacent-month labels render at 40% and weekend labels at 70% — on the label only, so a *selected* weekend or adjacent-month day still reads at full contrast on the dark disc. A disabled day switches to the disabled text colour, and a disabled today swaps its brand ring for a neutral one.
108
+ - The month-navigation buttons are hand-rolled ghost buttons that bind the same action tokens as `<rel-button variant="ghost">` (audit 8b) — they are intentionally not `<rel-button>` instances.
109
+ - The host has a fixed `max-width: 320px`, its own radius, shadow and padding and no border (Figma draws the card with `shadow/xl`, approximated here by `--rel-shadows-shadow-400`, the nearest shipped step); wrap it or override on the host if you need a different footprint.