@reliability-design/web 1.3.0 → 1.4.0

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 (123) hide show
  1. package/dist/components/rel-accordion/README.md +13 -13
  2. package/dist/components/rel-accordion/rel-accordion.ts +80 -45
  3. package/dist/components/rel-alert/README.md +15 -15
  4. package/dist/components/rel-alert/rel-alert.ts +161 -159
  5. package/dist/components/rel-avatar/README.md +15 -15
  6. package/dist/components/rel-avatar/rel-avatar.ts +42 -38
  7. package/dist/components/rel-badge/README.md +5 -5
  8. package/dist/components/rel-badge/rel-badge.ts +69 -17
  9. package/dist/components/rel-breadcrumb/README.md +7 -7
  10. package/dist/components/rel-breadcrumb/rel-breadcrumb.ts +16 -13
  11. package/dist/components/rel-button/README.md +20 -20
  12. package/dist/components/rel-button/rel-button.ts +327 -105
  13. package/dist/components/rel-calendar/README.md +5 -5
  14. package/dist/components/rel-calendar/rel-calendar.ts +61 -45
  15. package/dist/components/rel-card/README.md +6 -6
  16. package/dist/components/rel-card/rel-card.ts +88 -35
  17. package/dist/components/rel-cardmedia/README.md +9 -9
  18. package/dist/components/rel-cardmedia/rel-cardmedia.ts +27 -20
  19. package/dist/components/rel-carousel/README.md +3 -3
  20. package/dist/components/rel-carousel/rel-carousel.ts +174 -90
  21. package/dist/components/rel-chart/README.md +8 -8
  22. package/dist/components/rel-chart/rel-chart.ts +59 -41
  23. package/dist/components/rel-checkbox/README.md +7 -7
  24. package/dist/components/rel-checkbox/rel-checkbox.ts +61 -61
  25. package/dist/components/rel-chip/README.md +16 -16
  26. package/dist/components/rel-chip/rel-chip.ts +143 -111
  27. package/dist/components/rel-container/README.md +11 -11
  28. package/dist/components/rel-container/rel-container.ts +35 -9
  29. package/dist/components/rel-date-picker/README.md +43 -7
  30. package/dist/components/rel-date-picker/date-format.test.ts +43 -0
  31. package/dist/components/rel-date-picker/date-format.ts +47 -0
  32. package/dist/components/rel-date-picker/rel-date-picker.ts +257 -83
  33. package/dist/components/rel-divider/README.md +9 -9
  34. package/dist/components/rel-divider/rel-divider.ts +10 -9
  35. package/dist/components/rel-drawer/README.md +1 -1
  36. package/dist/components/rel-drawer/rel-drawer-footer.ts +1 -1
  37. package/dist/components/rel-drawer/rel-drawer.ts +56 -28
  38. package/dist/components/rel-kbd/README.md +20 -20
  39. package/dist/components/rel-kbd/rel-kbd.ts +119 -25
  40. package/dist/components/rel-layout/README.md +1 -1
  41. package/dist/components/rel-layout/rel-layout.ts +2 -2
  42. package/dist/components/rel-menu/README.md +15 -15
  43. package/dist/components/rel-menu/rel-menu.ts +155 -88
  44. package/dist/components/rel-menubar/README.md +20 -20
  45. package/dist/components/rel-menubar/rel-menubar.ts +47 -31
  46. package/dist/components/rel-nav-menu/README.md +21 -21
  47. package/dist/components/rel-nav-menu/rel-nav-menu-item.ts +17 -14
  48. package/dist/components/rel-nav-menu/rel-nav-menu-panel.ts +33 -14
  49. package/dist/components/rel-nav-menu/rel-nav-menu.ts +14 -7
  50. package/dist/components/rel-navbar/README.md +7 -7
  51. package/dist/components/rel-navbar/rel-navbar.ts +12 -11
  52. package/dist/components/rel-otp/README.md +3 -3
  53. package/dist/components/rel-otp/rel-otp.ts +46 -30
  54. package/dist/components/rel-pagination/README.md +18 -18
  55. package/dist/components/rel-pagination/rel-pagination.ts +141 -90
  56. package/dist/components/rel-popover/README.md +13 -13
  57. package/dist/components/rel-popover/rel-popover-content.ts +39 -14
  58. package/dist/components/rel-popover/rel-popover.ts +18 -7
  59. package/dist/components/rel-progress/README.md +5 -5
  60. package/dist/components/rel-progress/rel-progress.ts +16 -12
  61. package/dist/components/rel-radio/README.md +9 -9
  62. package/dist/components/rel-radio/rel-radio.ts +64 -50
  63. package/dist/components/rel-range-picker/rel-range-picker.ts +120 -117
  64. package/dist/components/rel-resizable/README.md +4 -4
  65. package/dist/components/rel-resizable/rel-resizable-group.ts +15 -4
  66. package/dist/components/rel-resizable/rel-resizable-handle.ts +13 -9
  67. package/dist/components/rel-resizable/rel-resizable-panel.ts +11 -0
  68. package/dist/components/rel-segmented-button/README.md +10 -10
  69. package/dist/components/rel-segmented-button/rel-segmented-button.ts +39 -40
  70. package/dist/components/rel-select/README.md +14 -14
  71. package/dist/components/rel-select/rel-select.ts +120 -188
  72. package/dist/components/rel-sheet/README.md +10 -10
  73. package/dist/components/rel-sheet/rel-sheet.ts +82 -23
  74. package/dist/components/rel-sidebar/README.md +5 -5
  75. package/dist/components/rel-sidebar/rel-sidebar.ts +75 -60
  76. package/dist/components/rel-skeleton/rel-skeleton.ts +8 -13
  77. package/dist/components/rel-slider/README.md +10 -10
  78. package/dist/components/rel-slider/rel-slider.ts +230 -57
  79. package/dist/components/rel-snackbar/README.md +9 -9
  80. package/dist/components/rel-snackbar/rel-snackbar.ts +44 -32
  81. package/dist/components/rel-stepper/README.md +10 -10
  82. package/dist/components/rel-stepper/rel-stepper.ts +110 -46
  83. package/dist/components/rel-switch/README.md +5 -5
  84. package/dist/components/rel-switch/rel-switch.ts +23 -12
  85. package/dist/components/rel-tab/README.md +13 -13
  86. package/dist/components/rel-tab/rel-tab.ts +73 -23
  87. package/dist/components/rel-table/README.md +7 -7
  88. package/dist/components/rel-table/rel-table-body.ts +2 -1
  89. package/dist/components/rel-table/rel-table-caption.ts +10 -7
  90. package/dist/components/rel-table/rel-table-cell.ts +7 -6
  91. package/dist/components/rel-table/rel-table-footer.ts +4 -3
  92. package/dist/components/rel-table/rel-table-head.ts +11 -9
  93. package/dist/components/rel-table/rel-table-header.ts +2 -1
  94. package/dist/components/rel-table/rel-table-row.ts +5 -4
  95. package/dist/components/rel-table/rel-table.ts +16 -7
  96. package/dist/components/rel-textfield/README.md +11 -11
  97. package/dist/components/rel-textfield/rel-textfield.ts +718 -397
  98. package/dist/components/rel-toast/README.md +11 -11
  99. package/dist/components/rel-toast/rel-toast.ts +33 -30
  100. package/dist/components/rel-tooltip/README.md +7 -7
  101. package/dist/components/rel-tooltip/rel-tooltip.ts +160 -123
  102. package/dist/components/rel-topbar/rel-topbar-actions.ts +5 -2
  103. package/dist/components/rel-topbar/rel-topbar-brand.ts +12 -5
  104. package/dist/components/rel-topbar/rel-topbar-dropdown-item.ts +14 -8
  105. package/dist/components/rel-topbar/rel-topbar-dropdown.ts +8 -5
  106. package/dist/components/rel-topbar/rel-topbar-item.ts +27 -22
  107. package/dist/components/rel-topbar/rel-topbar-mobile-panel.ts +4 -1
  108. package/dist/components/rel-topbar/rel-topbar-profile.ts +71 -50
  109. package/dist/components/rel-topbar/rel-topbar-utilities.ts +5 -2
  110. package/dist/components/rel-topbar/rel-topbar.ts +37 -29
  111. package/dist/index.d.mts +227 -40
  112. package/dist/index.d.ts +227 -40
  113. package/dist/index.js +3930 -2247
  114. package/dist/index.js.map +1 -1
  115. package/dist/index.mjs +4311 -2629
  116. package/dist/index.mjs.map +1 -1
  117. package/dist/styles/vars.css +653 -346
  118. package/dist/vars-WY2XCF5G.css +653 -0
  119. package/package.json +3 -2
  120. package/dist/components/rel-range-picker/rel-range-picker.css +0 -349
  121. package/dist/styles/vars-v2.css +0 -525
  122. package/dist/vars-I2PC74XD.css +0 -346
  123. package/dist/vars-v2-VMGWTFJZ.css +0 -525
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Token-based date formatting for rel-date-picker's selected-value display.
3
+ *
4
+ * Pure (Date + Intl only, no DOM) so it is unit-testable in Node. Tokens are
5
+ * matched longest-first; anything else (separators like `-` `/` `.` and spaces)
6
+ * passes through literally. Wrap literal text that contains token letters in
7
+ * square brackets, e.g. `[Week of] YYYY-MM-DD`.
8
+ *
9
+ * | Token | Meaning | Example (2026-07-21) |
10
+ * |-------|------------------------|----------------------|
11
+ * | YYYY | 4-digit year | 2026 |
12
+ * | YY | 2-digit year | 26 |
13
+ * | MMMM | full month (locale) | July |
14
+ * | MMM | short month (locale) | Jul |
15
+ * | MM | 2-digit month | 07 |
16
+ * | M | month number | 7 |
17
+ * | DD | 2-digit day | 21 |
18
+ * | D | day number | 21 |
19
+ */
20
+ export function formatDatePattern(
21
+ date: Date,
22
+ pattern: string,
23
+ locale = "en-US"
24
+ ): string {
25
+ const pad = (n: number) => String(n).padStart(2, "0");
26
+ const year = date.getFullYear();
27
+ const month = date.getMonth() + 1;
28
+ const day = date.getDate();
29
+
30
+ const tokens: Record<string, string> = {
31
+ YYYY: String(year),
32
+ YY: String(year).slice(-2),
33
+ MMMM: new Intl.DateTimeFormat(locale, { month: "long" }).format(date),
34
+ MMM: new Intl.DateTimeFormat(locale, { month: "short" }).format(date),
35
+ MM: pad(month),
36
+ M: String(month),
37
+ DD: pad(day),
38
+ D: String(day),
39
+ };
40
+
41
+ // `\[([^\]]*)\]` captures a bracketed literal (emitted without the brackets);
42
+ // otherwise the matched token is substituted from the map.
43
+ return pattern.replace(
44
+ /\[([^\]]*)\]|YYYY|YY|MMMM|MMM|MM|M|DD|D/g,
45
+ (match, literal) => (literal !== undefined ? literal : tokens[match])
46
+ );
47
+ }
@@ -1,5 +1,6 @@
1
1
  import { LitElement, html, css, nothing } from "lit";
2
2
  import { customElement, property, state, query } from "lit/decorators.js";
3
+ import { formatDatePattern } from "./date-format";
3
4
 
4
5
  interface CalendarDay {
5
6
  date: Date;
@@ -17,17 +18,57 @@ export class RelDatePicker extends LitElement {
17
18
  @property({ type: String }) label: string | null = null;
18
19
  @property({ type: Boolean, reflect: true }) disabled = false;
19
20
  @property({ type: Boolean }) required = false;
21
+ @property({ type: Boolean, reflect: true }) readonly = false;
22
+
23
+ // Date bounds. minDate/maxDate are the standard names; min/max are kept as
24
+ // deprecated aliases (minDate/maxDate win when both are set).
25
+ @property({ type: String, attribute: "min-date" }) minDate: string | null = null;
26
+ @property({ type: String, attribute: "max-date" }) maxDate: string | null = null;
27
+ /** @deprecated use minDate */
20
28
  @property({ type: String }) min: string | null = null;
29
+ /** @deprecated use maxDate */
21
30
  @property({ type: String }) max: string | null = null;
31
+
22
32
  @property({ type: String, attribute: "week-start" }) weekStart: "sun" | "mon" = "sun";
23
33
  @property({ type: String }) locale = "en-US";
34
+
35
+ // ── Display formatting of the SELECTED value ──
36
+ // `displayFormat` is a token pattern (YYYY-MM-DD, MM/DD/YYYY, DD/MM/YYYY, …);
37
+ // see date-format.ts for the token table. `formatDate` is a full callback and
38
+ // takes precedence. When neither is set the locale long date is used, so
39
+ // existing consumers are unaffected. Neither affects parsing, the popup
40
+ // calendar, or accessibility — only the visible trigger text.
41
+ @property({ type: String, attribute: "display-format" }) displayFormat: string | null = null;
42
+ /**
43
+ * Full control over the selected-value display; wins over `displayFormat`.
44
+ * Reactive (attribute:false) so assigning it after render re-renders the
45
+ * trigger — frameworks set callbacks as properties post-creation.
46
+ */
47
+ @property({ attribute: false }) formatDate?: (date: Date) => string;
24
48
  @property({ type: Boolean, reflect: true }) open = false;
25
49
  @property({ type: Boolean, attribute: "close-on-select" }) closeOnSelect = true;
26
50
  @property({ type: Boolean }) clearable = false;
27
51
 
52
+ // ── Standardized input API (mirrors rel-textfield) ──
53
+ @property({ type: Boolean }) error = false;
54
+ @property({ type: String, attribute: "error-message" }) errorMessage?: string;
55
+ @property({ type: String, attribute: "helper-text" }) helperText?: string;
56
+ @property({ type: String }) name?: string;
57
+ @property({ type: String }) size: "small" | "medium" | "large" = "medium";
58
+ @property({ type: String, reflect: true }) variant: "outlined" = "outlined";
59
+ @property({ type: Boolean, reflect: true }) fullWidth = false;
60
+
28
61
  // Custom disabled predicate (not settable via attribute)
29
62
  public isDateDisabled?: (date: Date) => boolean;
30
63
 
64
+ // minDate/maxDate supersede the deprecated min/max aliases
65
+ private get minBound(): string | null {
66
+ return this.minDate ?? this.min;
67
+ }
68
+ private get maxBound(): string | null {
69
+ return this.maxDate ?? this.max;
70
+ }
71
+
31
72
  @state() private viewYear: number = new Date().getFullYear();
32
73
  @state() private viewMonth: number = new Date().getMonth();
33
74
  @state() private focusedDate: Date | null = null;
@@ -40,11 +81,18 @@ export class RelDatePicker extends LitElement {
40
81
 
41
82
  static styles = css`
42
83
  :host {
43
- display: inline-block;
44
- font-family: var(--rel-fontFamilies-primary);
84
+ color: var(--rel-text-default);
85
+ display: block;
86
+ font-family: var(--rel-fonts-font-family);
45
87
  position: relative;
46
88
  }
47
89
 
90
+ /* Intrinsic width by default; set fullWidth to fill the parent. */
91
+ :host(:not([fullwidth])) {
92
+ display: inline-block;
93
+ width: auto;
94
+ }
95
+
48
96
  :host([disabled]) {
49
97
  opacity: 0.6;
50
98
  cursor: not-allowed;
@@ -54,16 +102,54 @@ export class RelDatePicker extends LitElement {
54
102
  pointer-events: none;
55
103
  }
56
104
 
105
+ :host([readonly]) .trigger-button {
106
+ cursor: default;
107
+ }
108
+
57
109
  .date-picker-wrapper {
58
110
  display: flex;
59
111
  flex-direction: column;
60
- gap: var(--rel-spacing-space-xs);
112
+ gap: var(--rel-spacing-xs);
61
113
  }
62
114
 
63
115
  .label {
64
- font-size: var(--rel-fontSizes-sm);
65
- font-weight: var(--rel-fontWeights-medium);
66
- color: var(--rel-text-heading);
116
+ color: var(--rel-text-label);
117
+
118
+ font-family: var(--rel-body-sm-medium-font-family);
119
+
120
+ font-weight: var(--rel-body-sm-medium-font-weight);
121
+
122
+ font-size: var(--rel-body-sm-medium-font-size);
123
+
124
+ line-height: var(--rel-body-sm-medium-line-height);
125
+
126
+ letter-spacing: var(--rel-body-sm-medium-letter-spacing);
127
+
128
+ }
129
+
130
+ .required-mark {
131
+ color: var(--rel-text-danger);
132
+ margin-left: 2px;
133
+ }
134
+
135
+ /* Support text (helperText / errorMessage) */
136
+ .date-picker-support {
137
+ color: var(--rel-text-placeholder);
138
+
139
+ font-family: var(--rel-body-xs-regular-font-family);
140
+
141
+ font-weight: var(--rel-body-xs-regular-font-weight);
142
+
143
+ font-size: var(--rel-body-xs-regular-font-size);
144
+
145
+ line-height: var(--rel-body-xs-regular-line-height);
146
+
147
+ letter-spacing: var(--rel-body-xs-regular-letter-spacing);
148
+
149
+ }
150
+
151
+ .date-picker-support.error {
152
+ color: var(--rel-text-danger);
67
153
  }
68
154
 
69
155
  .trigger-button {
@@ -72,65 +158,98 @@ export class RelDatePicker extends LitElement {
72
158
  display: flex;
73
159
  align-items: center;
74
160
  justify-content: space-between;
75
- gap: var(--rel-spacing-space-xs);
76
- padding: var(--rel-spacing-space-sm) var(--rel-spacing-space-md);
77
- background: var(--rel-color-bg-primary);
161
+ gap: var(--rel-spacing-xs);
162
+ padding: var(--rel-spacing-sm) var(--rel-spacing-md);
163
+ background: var(--rel-bg-surface);
78
164
  border: 1px solid var(--rel-border-default);
79
- border-radius: var(--rel-borders-radius-border-radius-200);
80
- font-family: var(--rel-fontFamilies-primary);
81
- font-size: var(--rel-fontSizes-md);
82
- color: var(--rel-text-primary);
165
+ border-radius: var(--rel-border-radius-sm);
166
+ color: var(--rel-text-default);
83
167
  cursor: pointer;
84
168
  transition: all 0.15s ease;
85
169
  text-align: left;
170
+ font-family: var(--rel-body-md-regular-font-family);
171
+ font-weight: var(--rel-body-md-regular-font-weight);
172
+ font-size: var(--rel-body-md-regular-font-size);
173
+ line-height: var(--rel-body-md-regular-line-height);
174
+ letter-spacing: var(--rel-body-md-regular-letter-spacing);
86
175
  }
87
176
 
88
177
  .trigger-button:hover:not(:disabled) {
89
- border-color: var(--rel-color-border-strong);
178
+ border-color: var(--rel-border-strong);
90
179
  }
91
180
 
92
181
  .trigger-button:focus-visible {
93
- outline: 2px solid var(--rel-color-border-focus);
182
+ outline: 2px solid var(--rel-border-focus);
94
183
  outline-offset: 2px;
95
- border-color: var(--rel-color-border-focus);
184
+ border-color: var(--rel-border-focus);
96
185
  }
97
186
 
98
187
  .trigger-button:disabled {
99
188
  cursor: not-allowed;
100
- background: var(--rel-surface-secondary);
189
+ background: var(--rel-bg-surface-elevated);
190
+ }
191
+
192
+ /* Error state */
193
+ .trigger-button.error,
194
+ .trigger-button.error:hover:not(:disabled) {
195
+ border-color: var(--rel-border-danger);
196
+ }
197
+
198
+ .trigger-button.error:focus-visible {
199
+ outline-color: var(--rel-border-danger);
200
+ border-color: var(--rel-border-danger);
201
+ }
202
+
203
+ /* Size variants (medium is the base .trigger-button style) */
204
+ .date-picker-wrapper.small .trigger-button {
205
+ padding: var(--rel-spacing-xs) var(--rel-spacing-sm);
206
+ font-family: var(--rel-body-sm-regular-font-family);
207
+ font-weight: var(--rel-body-sm-regular-font-weight);
208
+ font-size: var(--rel-body-sm-regular-font-size);
209
+ line-height: var(--rel-body-sm-regular-line-height);
210
+ letter-spacing: var(--rel-body-sm-regular-letter-spacing);
211
+ }
212
+
213
+ .date-picker-wrapper.large .trigger-button {
214
+ padding: var(--rel-spacing-md) var(--rel-spacing-md);
215
+ font-family: var(--rel-body-lg-regular-font-family);
216
+ font-weight: var(--rel-body-lg-regular-font-weight);
217
+ font-size: var(--rel-body-lg-regular-font-size);
218
+ line-height: var(--rel-body-lg-regular-line-height);
219
+ letter-spacing: var(--rel-body-lg-regular-letter-spacing);
101
220
  }
102
221
 
103
222
  .trigger-content {
104
223
  flex: 1;
105
- color: var(--rel-text-primary);
224
+ color: var(--rel-text-default);
106
225
  }
107
226
 
108
227
  .trigger-content.placeholder {
109
- color: var(--rel-colors-gray-placeholder);
228
+ color: var(--rel-text-placeholder);
110
229
  }
111
230
 
112
231
  .trigger-icons {
113
232
  display: flex;
114
233
  align-items: center;
115
- gap: var(--rel-spacing-space-2xs);
234
+ gap: var(--rel-spacing-2xs);
116
235
  }
117
236
 
118
237
  .icon-button {
119
238
  background: none;
120
239
  border: none;
121
- padding: var(--rel-spacing-space-3xs);
240
+ padding: var(--rel-spacing-3xs);
122
241
  cursor: pointer;
123
- color: var(--rel-color-text-placeholder);
242
+ color: var(--rel-text-placeholder);
124
243
  display: flex;
125
244
  align-items: center;
126
245
  justify-content: center;
127
- border-radius: var(--rel-borders-radius-border-radius-100);
246
+ border-radius: var(--rel-border-radius-xs);
128
247
  transition: all 0.15s ease;
129
248
  }
130
249
 
131
250
  .icon-button:hover {
132
- background: var(--rel-surface-secondary);
133
- color: var(--rel-color-text-primary);
251
+ background: var(--rel-bg-surface-elevated);
252
+ color: var(--rel-text-default);
134
253
  }
135
254
 
136
255
  .icon-button svg {
@@ -143,11 +262,11 @@ export class RelDatePicker extends LitElement {
143
262
  top: calc(100% + 4px);
144
263
  left: 0;
145
264
  z-index: 1000;
146
- background: var(--rel-color-bg-primary);
265
+ background: var(--rel-bg-surface);
147
266
  border: 1px solid var(--rel-border-default);
148
- border-radius: var(--rel-borders-radius-border-radius-200);
267
+ border-radius: var(--rel-border-radius-sm);
149
268
  box-shadow: var(--rel-shadows-shadow-300);
150
- padding: var(--rel-spacing-space-md);
269
+ padding: var(--rel-spacing-md);
151
270
  min-width: 280px;
152
271
  display: none;
153
272
  }
@@ -160,8 +279,8 @@ export class RelDatePicker extends LitElement {
160
279
  display: flex;
161
280
  align-items: center;
162
281
  justify-content: space-between;
163
- margin-bottom: var(--rel-spacing-space-md);
164
- gap: var(--rel-spacing-space-xs);
282
+ margin-bottom: var(--rel-spacing-md);
283
+ gap: var(--rel-spacing-xs);
165
284
  }
166
285
 
167
286
  .nav-button {
@@ -169,21 +288,21 @@ export class RelDatePicker extends LitElement {
169
288
  border: none;
170
289
  width: 32px;
171
290
  height: 32px;
172
- border-radius: var(--rel-borders-radius-border-radius-200);
291
+ border-radius: var(--rel-border-radius-sm);
173
292
  display: flex;
174
293
  align-items: center;
175
294
  justify-content: center;
176
295
  cursor: pointer;
177
- color: var(--rel-color-text-primary);
296
+ color: var(--rel-text-default);
178
297
  transition: background-color 0.15s ease;
179
298
  }
180
299
 
181
300
  .nav-button:hover {
182
- background: var(--rel-surface-secondary);
301
+ background: var(--rel-bg-surface-elevated);
183
302
  }
184
303
 
185
304
  .nav-button:focus-visible {
186
- outline: 2px solid var(--rel-color-border-focus);
305
+ outline: 2px solid var(--rel-border-focus);
187
306
  outline-offset: 2px;
188
307
  }
189
308
 
@@ -194,25 +313,27 @@ export class RelDatePicker extends LitElement {
194
313
 
195
314
  .dropdown-trigger {
196
315
  width: 100%;
197
- padding: var(--rel-spacing-space-xs) var(--rel-spacing-space-sm);
198
- background: var(--rel-color-bg-primary);
316
+ padding: var(--rel-spacing-xs) var(--rel-spacing-sm);
317
+ background: var(--rel-bg-surface);
199
318
  border: 1px solid var(--rel-border-default);
200
- border-radius: var(--rel-borders-radius-border-radius-200);
201
- font-family: var(--rel-fontFamilies-primary);
202
- font-size: var(--rel-fontSizes-sm);
203
- font-weight: var(--rel-fontWeights-medium);
204
- color: var(--rel-text-heading);
319
+ border-radius: var(--rel-border-radius-sm);
320
+ color: var(--rel-text-label);
205
321
  cursor: pointer;
206
322
  text-align: center;
207
323
  transition: all 0.15s ease;
324
+ font-family: var(--rel-body-sm-medium-font-family);
325
+ font-weight: var(--rel-body-sm-medium-font-weight);
326
+ font-size: var(--rel-body-sm-medium-font-size);
327
+ line-height: var(--rel-body-sm-medium-line-height);
328
+ letter-spacing: var(--rel-body-sm-medium-letter-spacing);
208
329
  }
209
330
 
210
331
  .dropdown-trigger:hover {
211
- background: var(--rel-surface-secondary);
332
+ background: var(--rel-bg-surface-elevated);
212
333
  }
213
334
 
214
335
  .dropdown-trigger:focus-visible {
215
- outline: 2px solid var(--rel-color-border-focus);
336
+ outline: 2px solid var(--rel-border-focus);
216
337
  outline-offset: 2px;
217
338
  }
218
339
 
@@ -222,9 +343,9 @@ export class RelDatePicker extends LitElement {
222
343
  left: 0;
223
344
  right: 0;
224
345
  z-index: 10;
225
- background: var(--rel-color-bg-primary);
346
+ background: var(--rel-bg-surface);
226
347
  border: 1px solid var(--rel-border-default);
227
- border-radius: var(--rel-borders-radius-border-radius-200);
348
+ border-radius: var(--rel-border-radius-sm);
228
349
  box-shadow: var(--rel-shadows-shadow-200);
229
350
  max-height: 200px;
230
351
  overflow-y: auto;
@@ -236,81 +357,90 @@ export class RelDatePicker extends LitElement {
236
357
  }
237
358
 
238
359
  .dropdown-item {
239
- padding: var(--rel-spacing-space-xs) var(--rel-spacing-space-sm);
360
+ padding: var(--rel-spacing-xs) var(--rel-spacing-sm);
240
361
  cursor: pointer;
241
- font-size: var(--rel-fontSizes-sm);
242
- color: var(--rel-text-primary);
362
+ color: var(--rel-text-default);
243
363
  transition: background-color 0.15s ease;
364
+ font-family: var(--rel-body-sm-regular-font-family);
365
+ font-weight: var(--rel-body-sm-regular-font-weight);
366
+ font-size: var(--rel-body-sm-regular-font-size);
367
+ line-height: var(--rel-body-sm-regular-line-height);
368
+ letter-spacing: var(--rel-body-sm-regular-letter-spacing);
244
369
  }
245
370
 
246
371
  .dropdown-item:hover {
247
- background: var(--rel-surface-secondary);
372
+ background: var(--rel-bg-surface-elevated);
248
373
  }
249
374
 
250
375
  .dropdown-item.selected {
251
- background: var(--rel-background-info);
376
+ background: var(--rel-bg-info-light);
252
377
  color: var(--rel-text-info);
253
- font-weight: var(--rel-fontWeights-semibold);
378
+ font-weight: var(--rel-body-md-semibold-font-weight);
254
379
  }
255
380
 
256
381
  .weekdays {
257
382
  display: grid;
258
383
  grid-template-columns: repeat(7, 1fr);
259
- gap: var(--rel-spacing-space-3xs);
260
- margin-bottom: var(--rel-spacing-space-xs);
384
+ gap: var(--rel-spacing-3xs);
385
+ margin-bottom: var(--rel-spacing-xs);
261
386
  }
262
387
 
263
388
  .weekday {
264
389
  text-align: center;
265
- font-size: var(--rel-fontSizes-xs);
266
- font-weight: var(--rel-fontWeights-semibold);
267
- color: var(--rel-color-text-placeholder);
268
- padding: var(--rel-spacing-space-xs);
390
+ color: var(--rel-text-placeholder);
391
+ padding: var(--rel-spacing-xs);
269
392
  text-transform: uppercase;
393
+ font-family: var(--rel-body-xs-semibold-font-family);
394
+ font-weight: var(--rel-body-xs-semibold-font-weight);
395
+ font-size: var(--rel-body-xs-semibold-font-size);
396
+ line-height: var(--rel-body-xs-semibold-line-height);
397
+ letter-spacing: var(--rel-body-xs-semibold-letter-spacing);
270
398
  }
271
399
 
272
400
  .calendar-grid {
273
401
  display: grid;
274
402
  grid-template-columns: repeat(7, 1fr);
275
- gap: var(--rel-spacing-space-3xs);
403
+ gap: var(--rel-spacing-3xs);
276
404
  }
277
405
 
278
406
  .day-button {
279
407
  aspect-ratio: 1;
280
408
  border: none;
281
409
  background: transparent;
282
- border-radius: var(--rel-borders-radius-border-radius-200);
283
- font-size: var(--rel-fontSizes-sm);
284
- font-weight: var(--rel-fontWeights-medium);
285
- color: var(--rel-text-primary);
410
+ border-radius: var(--rel-border-radius-sm);
411
+ color: var(--rel-text-default);
286
412
  cursor: pointer;
287
413
  position: relative;
288
414
  transition: all 0.15s ease;
289
- font-family: var(--rel-fontFamilies-primary);
415
+ font-family: var(--rel-body-sm-medium-font-family);
416
+ font-weight: var(--rel-body-sm-medium-font-weight);
417
+ font-size: var(--rel-body-sm-medium-font-size);
418
+ line-height: var(--rel-body-sm-medium-line-height);
419
+ letter-spacing: var(--rel-body-sm-medium-letter-spacing);
290
420
  }
291
421
 
292
422
  .day-button:hover:not(:disabled):not(.selected) {
293
- background: var(--rel-surface-secondary);
423
+ background: var(--rel-bg-surface-elevated);
294
424
  }
295
425
 
296
426
  .day-button:focus-visible {
297
- outline: 2px solid var(--rel-color-border-focus);
427
+ outline: 2px solid var(--rel-border-focus);
298
428
  outline-offset: -2px;
299
429
  z-index: 1;
300
430
  }
301
431
 
302
432
  .day-button.today:not(.selected) {
303
- border: 2px solid var(--rel-colors-green-primary);
433
+ border: 2px solid var(--rel-border-brand);
304
434
  }
305
435
 
306
436
  .day-button.selected {
307
- background: var(--rel-colors-green-primary);
308
- color: var(--rel-color-text-inverse);
309
- font-weight: var(--rel-fontWeights-semibold);
437
+ background: var(--rel-action-brand-fill);
438
+ color: var(--rel-action-brand-label-icons-primary);
439
+ font-weight: var(--rel-body-md-semibold-font-weight);
310
440
  }
311
441
 
312
442
  .day-button.other-month {
313
- color: var(--rel-colors-gray-placeholder);
443
+ color: var(--rel-text-placeholder);
314
444
  opacity: 0.5;
315
445
  }
316
446
 
@@ -410,6 +540,21 @@ export class RelDatePicker extends LitElement {
410
540
  const date = this.parseISODate(iso);
411
541
  if (!date) return iso;
412
542
 
543
+ // 1) explicit callback wins (fall back on error so a bad callback can't
544
+ // blank the field)
545
+ if (typeof this.formatDate === "function") {
546
+ try {
547
+ const out = this.formatDate(date);
548
+ if (typeof out === "string") return out;
549
+ } catch {
550
+ /* fall through to the default */
551
+ }
552
+ }
553
+ // 2) token pattern (e.g. "YYYY-MM-DD")
554
+ if (this.displayFormat) {
555
+ return formatDatePattern(date, this.displayFormat, this.locale);
556
+ }
557
+ // 3) backward-compatible locale long date
413
558
  return new Intl.DateTimeFormat(this.locale, {
414
559
  year: "numeric",
415
560
  month: "long",
@@ -428,13 +573,13 @@ export class RelDatePicker extends LitElement {
428
573
  private isDateDisabledInternal(date: Date): boolean {
429
574
  if (this.disabled) return true;
430
575
 
431
- if (this.min) {
432
- const minDate = this.parseISODate(this.min);
576
+ if (this.minBound) {
577
+ const minDate = this.parseISODate(this.minBound);
433
578
  if (minDate && date < minDate) return true;
434
579
  }
435
580
 
436
- if (this.max) {
437
- const maxDate = this.parseISODate(this.max);
581
+ if (this.maxBound) {
582
+ const maxDate = this.parseISODate(this.maxBound);
438
583
  if (maxDate && date > maxDate) return true;
439
584
  }
440
585
 
@@ -535,13 +680,13 @@ export class RelDatePicker extends LitElement {
535
680
  let startYear = currentYear - 100;
536
681
  let endYear = currentYear + 10;
537
682
 
538
- if (this.min) {
539
- const minDate = this.parseISODate(this.min);
683
+ if (this.minBound) {
684
+ const minDate = this.parseISODate(this.minBound);
540
685
  if (minDate) startYear = Math.max(startYear, minDate.getFullYear() - 5);
541
686
  }
542
687
 
543
- if (this.max) {
544
- const maxDate = this.parseISODate(this.max);
688
+ if (this.maxBound) {
689
+ const maxDate = this.parseISODate(this.maxBound);
545
690
  if (maxDate) endYear = Math.min(endYear, maxDate.getFullYear() + 5);
546
691
  }
547
692
 
@@ -553,7 +698,7 @@ export class RelDatePicker extends LitElement {
553
698
  }
554
699
 
555
700
  private toggleCalendarPopover() {
556
- if (this.disabled) return;
701
+ if (this.disabled || this.readonly) return;
557
702
  this.open = !this.open;
558
703
  this.dispatchEvent(
559
704
  new CustomEvent("rel-open-change", {
@@ -597,7 +742,7 @@ export class RelDatePicker extends LitElement {
597
742
  }
598
743
 
599
744
  private selectDay(day: CalendarDay) {
600
- if (day.isDisabled || this.disabled) return;
745
+ if (day.isDisabled || this.disabled || this.readonly) return;
601
746
 
602
747
  const newValue = this.formatISODate(day.date);
603
748
  this.value = newValue;
@@ -746,14 +891,34 @@ export class RelDatePicker extends LitElement {
746
891
  const months = this.getMonthsList();
747
892
  const years = this.getYearsList();
748
893
 
894
+ const isError = this.error;
895
+ const belowText = isError && this.errorMessage ? this.errorMessage : this.helperText;
896
+ const describedById = belowText ? "date-picker-desc" : undefined;
897
+ const wrapperClasses = [
898
+ "date-picker-wrapper",
899
+ this.size,
900
+ isError ? "error" : "",
901
+ this.readonly ? "readonly" : "",
902
+ ]
903
+ .filter(Boolean)
904
+ .join(" ");
905
+
749
906
  return html`
750
- <div class="date-picker-wrapper">
907
+ <div part="base" class="${wrapperClasses}">
751
908
  ${this.label
752
- ? html`<label class="label" id="date-picker-label">${this.label}</label>`
909
+ ? html`<label part="label" class="label" id="date-picker-label"
910
+ >${this.label}${this.required
911
+ ? html`<span class="required-mark" aria-hidden="true">*</span>`
912
+ : nothing}</label
913
+ >`
914
+ : nothing}
915
+
916
+ ${this.name
917
+ ? html`<input type="hidden" name=${this.name} .value=${this.value ?? ""} />`
753
918
  : nothing}
754
919
 
755
920
  <button
756
- class="trigger-button"
921
+ class="trigger-button ${isError ? "error" : ""}"
757
922
  type="button"
758
923
  ?disabled=${this.disabled}
759
924
  @click=${this.toggleCalendarPopover}
@@ -762,6 +927,9 @@ export class RelDatePicker extends LitElement {
762
927
  aria-haspopup="dialog"
763
928
  aria-expanded=${this.open}
764
929
  aria-required=${this.required}
930
+ aria-invalid=${isError}
931
+ aria-readonly=${this.readonly}
932
+ aria-describedby=${describedById ?? nothing}
765
933
  aria-labelledby=${this.label ? "date-picker-label" : nothing}
766
934
  >
767
935
  <span class="trigger-content ${this.value ? "" : "placeholder"}">
@@ -914,6 +1082,12 @@ export class RelDatePicker extends LitElement {
914
1082
  )}
915
1083
  </div>
916
1084
  </div>
1085
+
1086
+ ${belowText
1087
+ ? html`<div class="date-picker-support ${isError ? "error" : ""}" id=${describedById}>
1088
+ ${belowText}
1089
+ </div>`
1090
+ : nothing}
917
1091
  </div>
918
1092
  `;
919
1093
  }