@lekoala/date-picker 0.5.1 → 0.5.3

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.
package/README.md CHANGED
@@ -1,294 +1,296 @@
1
- # @lekoala/date-picker
2
-
3
- Small, native-first date picker and inline calendar primitives.
4
-
5
- > The interaction contract, event names, public styling tokens and the component API are frozen — see [docs/DECISIONS.md](docs/DECISIONS.md). Internal DOM and class names are `.dp-*` implementation details and stay private.
6
-
7
- This project fills the gap between a normal editable date field and a full scheduling calendar. A plain date input remains the right answer for simple CMS/editing forms. This component becomes useful when a date has context: availability, disabled days, annotations, remote state, start/end relationships, or a mini calendar that navigates another view.
8
-
9
- ## The two primitives
10
-
11
- ### `<date-calendar>`
12
-
13
- An inline calendar with real month/year navigation, keyboard support and three distinct pieces of state:
14
-
15
- - `display` — the month being rendered (`YYYY-MM`);
16
- - `focusedDate` — the roving keyboard target (`YYYY-MM-DD`);
17
- - `value` — the selected date (`YYYY-MM-DD`, optional).
18
-
19
- `display` and `focusedDate` are coupled during navigation/display: changing the displayed month re-clamps the focused day into that month, because the roving-tabbable grid cell must stay visible. `value` and activation stay independent of both.
20
-
21
- User activation is exposed separately as `dateactivate`. This is what makes the same calendar useful as a selector **and** as a mini navigator.
22
-
23
- ```html
24
- <date-calendar value="2026-09-10" fixed-weeks></date-calendar>
25
- ```
26
-
27
- For a navigator that should not own a selection:
28
-
29
- ```html
30
- <date-calendar id="mini" selection="none" fixed-weeks show-week-numbers></date-calendar>
31
- <script type="module">
32
- const mini = document.querySelector("#mini");
33
- mini.addEventListener("dateactivate", (event) => {
34
- agenda.gotoDate(event.detail.date);
35
- });
36
- </script>
37
- ```
38
-
39
- ### `<date-picker>`
40
-
41
- A composition around a real editable text input. The user sees and edits a short locale-aware numeric date while form submission stays canonical ISO.
42
-
43
- **Use `<date-picker>` when choosing a date benefits from calendar context.** For a simple editable date field, a normal input with formatting and validation is the right tool instead.
44
-
45
- ```html
46
- <date-picker value="2026-09-10" locale="fr-BE">
47
- <input name="appointment_date" required>
48
- </date-picker>
49
- ```
50
-
51
- With JavaScript enabled the input displays `10/09/2026`; the hidden submitted value remains `2026-09-10`. The visible input keeps the label, focus, required state and validation UI.
52
-
53
- By default the calendar opens when the field receives focus — without stealing the keyboard focus, so typing works immediately. `open-on-focus="false"` makes the calendar opt-in (trigger button or `ArrowDown`). Under `readonly` the value stays editable-by-calendar-off but is still submitted; under `disabled` the field is also unsubmitted.
54
-
55
- ## Start/end ranges
56
-
57
- Two product shapes cover ranges.
58
-
59
- **One shared calendar surface** — two editable fields wired to a single picker popup:
60
-
61
- ```html
62
- <date-picker range>
63
- <input data-range-start name="arrival" aria-label="Arrival">
64
- <input data-range-end name="departure" aria-label="Departure">
65
- </date-picker>
66
- ```
67
-
68
- Opening from a field targets that bound; picking the first date moves the active endpoint to the other bound without closing, and the second pick completes and closes. `picker.range` is the atomic `{ start, end }` API (single `rangechange` event).
69
-
70
- **Two independent pickers** — a small external link for visually separate fields:
71
-
72
- ```js
73
- import { linkDateRange } from "@lekoala/date-picker";
74
-
75
- const cleanup = linkDateRange(startPicker, endPicker);
76
- ```
77
-
78
- The relationship applies `end >= start` and `start <= end` as effective bounds. Neither picker knows about its sibling.
79
-
80
- ## Date + time
81
-
82
- A date pairs with one or two native time companions. The picker owns the civil date; the time inputs own their civil time values:
83
-
84
- ```html
85
- <date-picker>
86
- <input name="appointment[date]">
87
- <input type="time" data-time-start name="appointment[time]">
88
- </date-picker>
89
- ```
90
-
91
- ```text
92
- appointment[date] = 2026-09-10
93
- appointment[time] = 09:30
94
- ```
95
-
96
- The application/server combines these civil values and applies the relevant timezone when a datetime/instant is needed. The picker never constructs a combined datetime value.
97
-
98
- ## Availability and per-day metadata
99
-
100
- The calendar accepts a source function or `{ load() }` object with the same abortable shape used elsewhere in LeKoala components:
101
-
102
- ```js
103
- calendar.source = async ({ start, end }, { signal }) => {
104
- const response = await fetch(`/availability?start=${start}&end=${end}`, { signal });
105
- return response.json();
106
- };
107
- ```
108
-
109
- Accepted payloads are deliberately simple:
110
-
111
- ```js
112
- {
113
- dates: {
114
- "2026-09-10": {
115
- enabled: true,
116
- description: "Available morning and afternoon",
117
- morning: true,
118
- afternoon: true
119
- }
120
- }
121
- }
122
- ```
123
-
124
- Business metadata is not interpreted by the core. Consumers can add visual content without replacing the grid cell or its accessibility semantics:
125
-
126
- ```js
127
- calendar.renderDay = (_date, state) => {
128
- if (!state.morning && !state.afternoon) return "";
129
- return `${state.morning ? "●" : "○"}${state.afternoon ? "●" : "○"}`;
130
- };
131
- ```
132
-
133
- ## Date math
134
-
135
- The public value model is intentionally boring: canonical `YYYY-MM-DD` strings. There is no public `Date`, timestamp, timezone or `Temporal` dependency.
136
-
137
- Pure civil-date helpers are exported under `dates`:
138
-
139
- ```js
140
- import { dates } from "@lekoala/date-picker";
141
-
142
- const weeks = dates.getMonthWeeks("2026-09-03", { firstDay: 1 });
143
- ```
144
-
145
- `getMonthWeeks()` returns the true 4–6 full civil weeks covering the month. It never pads to six rows. `fixed-weeks` is a rendering choice made by `<date-calendar>`.
146
-
147
- ## Accessibility baseline
148
-
149
- The calendar follows the WAI-ARIA date-picker/grid interaction model:
150
-
151
- - semantic `table[role="grid"]`;
152
- - one roving `tabindex="0"` grid cell;
153
- - arrow keys move by day/week;
154
- - Home/End move to week boundaries;
155
- - PageUp/PageDown move by month;
156
- - Shift+PageUp/PageDown move by year;
157
- - Enter/Space activate a date;
158
- - keyboard movement never implicitly selects;
159
- - `aria-selected` identifies the selected date;
160
- - `aria-current="date"` identifies today;
161
- - abbreviated weekday headers keep full `abbr` names.
162
-
163
- The picker popup uses native Popover for the top layer and `@lekoala/floating` for geometry. It has `role="dialog"`, but it is intentionally **not** marked `aria-modal` because the current implementation does not inert the rest of the document.
164
-
165
- The calendar trigger is a real button painted inside the field's own box (like the native `input[type=time]` indicator): the field reserves its place with `padding-inline-end`, keeps its own border and background, and its focus ring wraps the trigger too. The trigger draws no chrome of its own and shows a small inner ring when focused.
166
-
167
- Fields, header controls and days share an inner outline that survives forced colors. Customize its color with `--dp-focus` (default `var(--dp-accent)`) and thickness with `--dp-focus-width` (default `2px`) on `date-picker` and `date-calendar`. The trigger keeps a `-5px` inset around its icon; selected days and range endpoints use `--dp-accent-fg` for contrast. Fields and header controls now show this solid outline instead of the previous soft halo.
168
-
169
- When the popup opens on the field's focus, grid navigation is reachable via `ArrowDown` (or the trigger button) so the focus-restore/Escape flow stays predictable. Closing returns focus to the control that opened the popover: the field when it opened by focus, the trigger when it was activated. In range mode focus follows the active bound instead, because a first pick moves the workflow to the other bound.
170
-
171
- See [docs/ACCESSIBILITY.md](docs/ACCESSIBILITY.md).
172
-
173
- ## Install / develop
174
-
175
- ```bash
176
- bun install
177
- bun run dev
178
- ```
179
-
180
- If a previous `bun run dev` is still holding port `4859`, clear it with `bun run dev:kill` (or restart in one go with `bun run dev:restart`).
181
-
182
- Tooling is aligned with the current LeKoala component repos:
183
-
184
- - Bun 1.4.3
185
- - TypeScript 7.0.2 (`checkJs` + declaration emit)
186
- - Biome 2.5.15
187
- - Playwright 1.64.0
188
- - `@lekoala/floating ^0.2.0`
189
-
190
- Useful commands:
191
-
192
- ```bash
193
- bun run test
194
- bun run typecheck
195
- bun run lint
196
- bun run build
197
- bun run test:browser
198
- bun run test:browser:all
199
- bun run check:all
200
- ```
201
-
202
- ## Build outputs
203
-
204
- `bun run build` produces:
205
-
206
- ```text
207
- dist/date-picker.js
208
- dist/date-picker.min.js
209
- dist/date-picker.css
210
- dist/date-picker.min.css
211
- dist/date-picker.standalone.min.js
212
- dist/types/**
213
- ```
214
-
215
- The default ESM exports point at `src/`, matching the native-first development style of `@lekoala/combobox`. The classic build is file:// friendly; the standalone build additionally injects the component CSS.
216
-
217
- ## Scope
218
-
219
- Included:
220
-
221
- - inline single-date selection;
222
- - mini-calendar/navigation-only use;
223
- - month and year selection;
224
- - previous/next navigation;
225
- - editable localized input + canonical submitted value;
226
- - min/max and custom disabled rules;
227
- - presentation-only range band (`highlightedRange`);
228
- - shared-calendar two-field range picker (`<date-picker range>`);
229
- - linked start/end pickers (`linkDateRange`);
230
- - native time companions (one or two `input[type=time]` beside the date);
231
- - `Intl` month/year control order + `month-format="short"`;
232
- - RTL through the inherited `dir` (no `isRTL` option);
233
- - readable keyboard focus on disabled/selected days;
234
- - compact-consumer CSS tokens (`--dp-header-control-size`, `--dp-day-radius`, …);
235
- - async per-day state with cancellation;
236
- - custom day decorations that cannot replace the accessible cell;
237
- - fixed six-row presentation as an opt-in;
238
- - ISO week numbers;
239
- - keyboard and focus contracts.
240
-
241
- Deliberately out of scope:
242
-
243
- - custom time-picker UI and combined datetime values (only native `input[type=time]` companions are supported);
244
- - timezones;
245
- - in-place single-calendar range *selection interaction* (use the two-field picker);
246
- - multi-date selection;
247
- - recurrence;
248
- - natural-language parsing;
249
- - plugin registry;
250
- - virtualized months;
251
- - scheduling/time slots.
252
-
253
- ## Docs
254
-
255
- - [Changelog](CHANGELOG.md)
256
- - [Architecture](docs/ARCHITECTURE.md)
257
- - [Decisions](docs/DECISIONS.md)
258
- - [API](docs/API.md)
259
- - [Accessibility checklist](docs/AT-CHECKLIST.md)
260
- - [Use cases](docs/USE_CASES.md)
261
- - [Accessibility](docs/ACCESSIBILITY.md)
262
- - [Testing](docs/TESTING.md)
263
- - [Roadmap](docs/ROADMAP.md)
264
- - [Calendar integration](docs/CALENDAR-INTEGRATION.md)
265
- - [Flatpickr/Formidable migration](docs/MIGRATION-FLATPICKR.md)
266
-
267
- ### Host control classes
268
-
269
- Set application-wide header styles, including for calendars and pickers already in the page:
270
-
271
- ```js
272
- import { setDefaultControlClasses } from "@lekoala/date-picker";
273
-
274
- setDefaultControlClasses({
275
- month: "form-select form-select-sm",
276
- year: "form-control form-control-sm",
277
- nav: "btn btn-outline-secondary btn-sm",
278
- });
279
- ```
280
-
281
- Each role with author classes uses the host design system's skin. Override individual roles through `controlClasses`: omitted roles follow current global defaults, `""` restores the library skin for a role, and `null` follows all defaults. Day cells and the field trigger are unchanged.
282
-
283
- For a classic script (including the CDN build), use the registered constructor after the script has loaded:
284
-
285
- ```js
286
- customElements.get("date-picker").setDefaultControlClasses({
287
- month: "select sm",
288
- year: "input sm",
289
- nav: "btn outline sm icon-only",
290
- });
291
- customElements.get("date-picker").setDefaultMessages({ chooseDate: "Choose a date" });
292
- ```
293
-
294
- Both element constructors expose `setDefaultControlClasses`, `getDefaultControlClasses`, `setDefaultMessages` and `getDefaultMessages`, sharing the same defaults. Class defaults update inherited roles on connected elements, including instances with partial overrides. Message defaults update elements without a messages override. Properties assigned before definition are reapplied through their setters when the element connects, including `controlClasses`, `messages`, `source` and `range`.
1
+ # @lekoala/date-picker
2
+
3
+ Small, native-first date picker and inline calendar primitives.
4
+
5
+ > The interaction contract, event names, public styling tokens and the component API are frozen — see [docs/DECISIONS.md](docs/DECISIONS.md). Internal DOM and class names are `.dp-*` implementation details and stay private.
6
+
7
+ This project fills the gap between a normal editable date field and a full scheduling calendar. A plain date input remains the right answer for simple CMS/editing forms. This component becomes useful when a date has context: availability, disabled days, annotations, remote state, start/end relationships, or a mini calendar that navigates another view.
8
+
9
+ ## The two primitives
10
+
11
+ ### `<date-calendar>`
12
+
13
+ An inline calendar with real month/year navigation, keyboard support and three distinct pieces of state:
14
+
15
+ - `display` — the month being rendered (`YYYY-MM`);
16
+ - `focusedDate` — the roving keyboard target (`YYYY-MM-DD`);
17
+ - `value` — the selected date (`YYYY-MM-DD`, optional).
18
+
19
+ `display` and `focusedDate` are coupled during navigation/display: changing the displayed month re-clamps the focused day into that month, because the roving-tabbable grid cell must stay visible. `value` and activation stay independent of both.
20
+
21
+ User activation is exposed separately as `dateactivate`. This is what makes the same calendar useful as a selector **and** as a mini navigator.
22
+
23
+ ```html
24
+ <date-calendar value="2026-09-10" fixed-weeks></date-calendar>
25
+ ```
26
+
27
+ For a navigator that should not own a selection:
28
+
29
+ ```html
30
+ <date-calendar id="mini" selection="none" fixed-weeks show-week-numbers></date-calendar>
31
+ <script type="module">
32
+ const mini = document.querySelector("#mini");
33
+ mini.addEventListener("dateactivate", (event) => {
34
+ agenda.gotoDate(event.detail.date);
35
+ });
36
+ </script>
37
+ ```
38
+
39
+ ### `<date-picker>`
40
+
41
+ A composition around a real editable text input. The user sees and edits a short locale-aware numeric date while form submission stays canonical ISO.
42
+
43
+ **Use `<date-picker>` when choosing a date benefits from calendar context.** For a simple editable date field, a normal input with formatting and validation is the right tool instead.
44
+
45
+ ```html
46
+ <date-picker value="2026-09-10" locale="fr-BE">
47
+ <input name="appointment_date" required>
48
+ </date-picker>
49
+ ```
50
+
51
+ With JavaScript enabled the input displays `10/09/2026`; the hidden submitted value remains `2026-09-10`. The visible input keeps the label, focus, required state and validation UI.
52
+
53
+ By default the calendar opens when the field receives focus — without stealing the keyboard focus, so typing works immediately. `open-on-focus="false"` makes the calendar opt-in (trigger button or `ArrowDown`). Under `readonly` the value stays editable-by-calendar-off but is still submitted; under `disabled` the field is also unsubmitted.
54
+
55
+ ## Start/end ranges
56
+
57
+ Two product shapes cover ranges.
58
+
59
+ **One shared calendar surface** — two editable fields wired to a single picker popup:
60
+
61
+ ```html
62
+ <date-picker range>
63
+ <input data-range-start name="arrival" aria-label="Arrival">
64
+ <input data-range-end name="departure" aria-label="Departure">
65
+ </date-picker>
66
+ ```
67
+
68
+ Opening from a field targets that bound; picking the first date moves the active endpoint to the other bound without closing, and the second pick completes and closes. `picker.range` is the atomic `{ start, end }` API (single `rangechange` event).
69
+
70
+ **Two independent pickers** — a small external link for visually separate fields:
71
+
72
+ ```js
73
+ import { linkDateRange } from "@lekoala/date-picker";
74
+
75
+ const cleanup = linkDateRange(startPicker, endPicker);
76
+ ```
77
+
78
+ The relationship applies `end >= start` and `start <= end` as effective bounds. Neither picker knows about its sibling.
79
+
80
+ ## Date + time
81
+
82
+ A date pairs with one or two native time companions. The picker owns the civil date; the time inputs own their civil time values:
83
+
84
+ ```html
85
+ <date-picker>
86
+ <input name="appointment[date]">
87
+ <input type="time" data-time-start name="appointment[time]">
88
+ </date-picker>
89
+ ```
90
+
91
+ ```text
92
+ appointment[date] = 2026-09-10
93
+ appointment[time] = 09:30
94
+ ```
95
+
96
+ The application/server combines these civil values and applies the relevant timezone when a datetime/instant is needed. The picker never constructs a combined datetime value.
97
+
98
+ ## Availability and per-day metadata
99
+
100
+ The calendar accepts a source function or `{ load() }` object with the same abortable shape used elsewhere in LeKoala components:
101
+
102
+ ```js
103
+ calendar.source = async ({ start, end }, { signal }) => {
104
+ const response = await fetch(`/availability?start=${start}&end=${end}`, { signal });
105
+ return response.json();
106
+ };
107
+ ```
108
+
109
+ Accepted payloads are deliberately simple:
110
+
111
+ ```js
112
+ {
113
+ dates: {
114
+ "2026-09-10": {
115
+ enabled: true,
116
+ description: "Available morning and afternoon",
117
+ morning: true,
118
+ afternoon: true
119
+ }
120
+ }
121
+ }
122
+ ```
123
+
124
+ Business metadata is not interpreted by the core. Consumers can add visual content without replacing the grid cell or its accessibility semantics:
125
+
126
+ ```js
127
+ calendar.renderDay = (_date, state) => {
128
+ if (!state.morning && !state.afternoon) return "";
129
+ return `${state.morning ? "●" : "○"}${state.afternoon ? "●" : "○"}`;
130
+ };
131
+ ```
132
+
133
+ ## Date math
134
+
135
+ The public value model is intentionally boring: canonical `YYYY-MM-DD` strings. There is no public `Date`, timestamp, timezone or `Temporal` dependency.
136
+
137
+ Pure civil-date helpers are exported under `dates`:
138
+
139
+ ```js
140
+ import { dates } from "@lekoala/date-picker";
141
+
142
+ const weeks = dates.getMonthWeeks("2026-09-03", { firstDay: 1 });
143
+ ```
144
+
145
+ `getMonthWeeks()` returns the true 4–6 full civil weeks covering the month. It never pads to six rows. `fixed-weeks` is a rendering choice made by `<date-calendar>`.
146
+
147
+ ## Accessibility baseline
148
+
149
+ The calendar follows the WAI-ARIA date-picker/grid interaction model:
150
+
151
+ - semantic `table[role="grid"]`;
152
+ - one roving `tabindex="0"` grid cell;
153
+ - arrow keys move by day/week;
154
+ - Home/End move to week boundaries;
155
+ - PageUp/PageDown move by month;
156
+ - Shift+PageUp/PageDown move by year;
157
+ - Enter/Space activate a date;
158
+ - keyboard movement never implicitly selects;
159
+ - `aria-selected` identifies the selected date;
160
+ - `aria-current="date"` identifies today;
161
+ - abbreviated weekday headers keep full `abbr` names.
162
+
163
+ The picker popup uses native Popover for the top layer and `@lekoala/floating` for geometry. It has `role="dialog"`, but it is intentionally **not** marked `aria-modal` because the current implementation does not inert the rest of the document.
164
+
165
+ The calendar trigger is a real button painted inside the field's own box (like the native `input[type=time]` indicator): the field reserves its place with `padding-inline-end`, keeps its own border and background, and its focus ring wraps the trigger too. The trigger draws no chrome of its own and shows a small inner ring when focused.
166
+
167
+ Fields, header controls and days share an inner outline that survives forced colors. Customize its color with `--dp-focus` (default `var(--dp-accent)`) and thickness with `--dp-focus-width` (default `2px`) on `date-picker` and `date-calendar`. The trigger keeps a `-5px` inset around its icon; selected days and range endpoints use `--dp-accent-fg` for contrast. Fields and header controls now show this solid outline instead of the previous soft halo.
168
+
169
+ When the popup opens on the field's focus, grid navigation is reachable via `ArrowDown` (or the trigger button) so the focus-restore/Escape flow stays predictable. Closing returns focus to the control that opened the popover: the field when it opened by focus, the trigger when it was activated. In range mode focus follows the active bound instead, because a first pick moves the workflow to the other bound.
170
+
171
+ See [docs/ACCESSIBILITY.md](docs/ACCESSIBILITY.md).
172
+
173
+ ## Install / develop
174
+
175
+ ```bash
176
+ bun install
177
+ bun run dev
178
+ ```
179
+
180
+ If a previous `bun run dev` is still holding port `4859`, clear it with `bun run dev:kill` (or restart in one go with `bun run dev:restart`).
181
+
182
+ Tooling is aligned with the current LeKoala component repos:
183
+
184
+ - Bun 1.4.3
185
+ - TypeScript 7.0.2 (`checkJs` + declaration emit)
186
+ - Biome 2.5.15
187
+ - Playwright 1.64.0
188
+ - `@lekoala/floating ^0.3.1`
189
+
190
+ Useful commands:
191
+
192
+ ```bash
193
+ bun run test
194
+ bun run typecheck
195
+ bun run lint
196
+ bun run build
197
+ bun run test:browser
198
+ bun run test:browser:all
199
+ bun run check:all
200
+ ```
201
+
202
+ ## Build outputs
203
+
204
+ `bun run build` produces:
205
+
206
+ ```text
207
+ dist/date-picker.js
208
+ dist/date-picker.min.js
209
+ dist/date-picker.css
210
+ dist/date-picker.min.css
211
+ dist/date-picker.standalone.min.js
212
+ dist/types/**
213
+ ```
214
+
215
+ The default ESM exports point at `src/`, matching the native-first development style of `@lekoala/combobox`. The classic build is file:// friendly; the standalone build additionally injects the component CSS.
216
+
217
+ When loaded as a classic script, the standalone build copies the script's `nonce` to its injected stylesheet for CSP policies that require a style nonce. Set a nonce generated by your server on the `<script>` tag and allow it in `style-src`. Module scripts do not expose `document.currentScript`; when using modules with a strict style CSP, load the CSS separately.
218
+
219
+ ## Scope
220
+
221
+ Included:
222
+
223
+ - inline single-date selection;
224
+ - mini-calendar/navigation-only use;
225
+ - month and year selection;
226
+ - previous/next navigation;
227
+ - editable localized input + canonical submitted value;
228
+ - min/max and custom disabled rules;
229
+ - presentation-only range band (`highlightedRange`);
230
+ - shared-calendar two-field range picker (`<date-picker range>`);
231
+ - linked start/end pickers (`linkDateRange`);
232
+ - native time companions (one or two `input[type=time]` beside the date);
233
+ - `Intl` month/year control order + `month-format="short"`;
234
+ - RTL through the inherited `dir` (no `isRTL` option);
235
+ - readable keyboard focus on disabled/selected days;
236
+ - compact-consumer CSS tokens (`--dp-header-control-size`, `--dp-day-radius`, …);
237
+ - async per-day state with cancellation;
238
+ - custom day decorations that cannot replace the accessible cell;
239
+ - fixed six-row presentation as an opt-in;
240
+ - ISO week numbers;
241
+ - keyboard and focus contracts.
242
+
243
+ Deliberately out of scope:
244
+
245
+ - custom time-picker UI and combined datetime values (only native `input[type=time]` companions are supported);
246
+ - timezones;
247
+ - in-place single-calendar range *selection interaction* (use the two-field picker);
248
+ - multi-date selection;
249
+ - recurrence;
250
+ - natural-language parsing;
251
+ - plugin registry;
252
+ - virtualized months;
253
+ - scheduling/time slots.
254
+
255
+ ## Docs
256
+
257
+ - [Changelog](CHANGELOG.md)
258
+ - [Architecture](docs/ARCHITECTURE.md)
259
+ - [Decisions](docs/DECISIONS.md)
260
+ - [API](docs/API.md)
261
+ - [Accessibility checklist](docs/AT-CHECKLIST.md)
262
+ - [Use cases](docs/USE_CASES.md)
263
+ - [Accessibility](docs/ACCESSIBILITY.md)
264
+ - [Testing](docs/TESTING.md)
265
+ - [Roadmap](docs/ROADMAP.md)
266
+ - [Calendar integration](docs/CALENDAR-INTEGRATION.md)
267
+ - [Flatpickr/Formidable migration](docs/MIGRATION-FLATPICKR.md)
268
+
269
+ ### Host control classes
270
+
271
+ Set application-wide header styles, including for calendars and pickers already in the page:
272
+
273
+ ```js
274
+ import { setDefaultControlClasses } from "@lekoala/date-picker";
275
+
276
+ setDefaultControlClasses({
277
+ month: "form-select form-select-sm",
278
+ year: "form-control form-control-sm",
279
+ nav: "btn btn-outline-secondary btn-sm",
280
+ });
281
+ ```
282
+
283
+ Each role with author classes uses the host design system's skin. Override individual roles through `controlClasses`: omitted roles follow current global defaults, `""` restores the library skin for a role, and `null` follows all defaults. Day cells and the field trigger are unchanged.
284
+
285
+ For a classic script (including the CDN build), use the registered constructor after the script has loaded:
286
+
287
+ ```js
288
+ customElements.get("date-picker").setDefaultControlClasses({
289
+ month: "select sm",
290
+ year: "input sm",
291
+ nav: "btn outline sm icon-only",
292
+ });
293
+ customElements.get("date-picker").setDefaultMessages({ chooseDate: "Choose a date" });
294
+ ```
295
+
296
+ Both element constructors expose `setDefaultControlClasses`, `getDefaultControlClasses`, `setDefaultMessages` and `getDefaultMessages`, sharing the same defaults. Class defaults update inherited roles on connected elements, including instances with partial overrides. Message defaults update elements without a messages override. Properties assigned before definition are reapplied through their setters when the element connects, including `controlClasses`, `messages`, `source` and `range`.
@@ -295,6 +295,9 @@
295
295
  },
296
296
  {
297
297
  "name": "month-format"
298
+ },
299
+ {
300
+ "name": "range"
298
301
  }
299
302
  ],
300
303
  "members": [