kanto-ds 1.6.1 → 1.7.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 (65) hide show
  1. package/README.md +52 -4
  2. package/dist/cdn/kanto.min.js +6619 -0
  3. package/dist/chunks/{floating-B33g-M_1.js → floating-C9RLa6cZ.js} +2 -2
  4. package/dist/chunks/floating-C9RLa6cZ.js.map +1 -0
  5. package/dist/components/forms/kt-date-input/kt-date-input.js +1 -1
  6. package/dist/components/forms/kt-date-picker/kt-date-picker.js +1 -1
  7. package/dist/components/forms/kt-input/kt-input.js +1 -1
  8. package/dist/components/forms/kt-input-menu/kt-input-menu.js +1 -1
  9. package/dist/components/forms/kt-multi-select/kt-multi-select.js +1 -1
  10. package/dist/components/forms/kt-select/kt-select.js +1 -1
  11. package/dist/components/overlays/kt-dropdown/kt-dropdown.js +1 -1
  12. package/dist/docs/kt-alert.md +52 -0
  13. package/dist/docs/kt-avatar.md +46 -0
  14. package/dist/docs/kt-badge.md +104 -0
  15. package/dist/docs/kt-breadcrumb.md +53 -0
  16. package/dist/docs/kt-button.md +72 -0
  17. package/dist/docs/kt-calendar.md +98 -0
  18. package/dist/docs/kt-card.md +93 -0
  19. package/dist/docs/kt-chart.md +230 -0
  20. package/dist/docs/kt-checkbox.md +65 -0
  21. package/dist/docs/kt-code.md +88 -0
  22. package/dist/docs/kt-collapsible.md +50 -0
  23. package/dist/docs/kt-confirm-dialog.md +52 -0
  24. package/dist/docs/kt-date-input.md +97 -0
  25. package/dist/docs/kt-date-picker.md +116 -0
  26. package/dist/docs/kt-drag-drop.md +85 -0
  27. package/dist/docs/kt-dropdown.md +65 -0
  28. package/dist/docs/kt-empty-state.md +36 -0
  29. package/dist/docs/kt-form.md +93 -0
  30. package/dist/docs/kt-header.md +100 -0
  31. package/dist/docs/kt-icon.md +68 -0
  32. package/dist/docs/kt-input-menu.md +90 -0
  33. package/dist/docs/kt-input.md +137 -0
  34. package/dist/docs/kt-kbd.md +44 -0
  35. package/dist/docs/kt-label-input.md +54 -0
  36. package/dist/docs/kt-meter.md +79 -0
  37. package/dist/docs/kt-modal.md +62 -0
  38. package/dist/docs/kt-multi-select.md +115 -0
  39. package/dist/docs/kt-page-header.md +53 -0
  40. package/dist/docs/kt-pagination.md +36 -0
  41. package/dist/docs/kt-progress-bar.md +66 -0
  42. package/dist/docs/kt-radio-group.md +73 -0
  43. package/dist/docs/kt-segmented-control.md +70 -0
  44. package/dist/docs/kt-select.md +80 -0
  45. package/dist/docs/kt-side-panel.md +59 -0
  46. package/dist/docs/kt-skeleton.md +46 -0
  47. package/dist/docs/kt-split-button.md +105 -0
  48. package/dist/docs/kt-stat.md +57 -0
  49. package/dist/docs/kt-sub-menu-navigation.md +118 -0
  50. package/dist/docs/kt-table.md +179 -0
  51. package/dist/docs/kt-tabs.md +87 -0
  52. package/dist/docs/kt-textarea.md +59 -0
  53. package/dist/docs/kt-time-input.md +88 -0
  54. package/dist/docs/kt-timeline.md +63 -0
  55. package/dist/docs/kt-toast-container.md +69 -0
  56. package/dist/docs/kt-toast.md +41 -0
  57. package/dist/docs/kt-toggle-button-group.md +67 -0
  58. package/dist/docs/kt-toggle-button.md +52 -0
  59. package/dist/docs/kt-toggle.md +47 -0
  60. package/dist/docs/kt-tooltip.md +68 -0
  61. package/dist/internal/floating.d.ts +4 -0
  62. package/dist/tokens/colors.css +5 -2
  63. package/dist/web-types.json +1 -1
  64. package/package.json +23 -5
  65. package/dist/chunks/floating-B33g-M_1.js.map +0 -1
@@ -0,0 +1,179 @@
1
+ # `<kt-table>`
2
+
3
+ A data table: tri-state sorting, single or multiple selection, paging.
4
+
5
+ ```js
6
+ table.columns = [
7
+ { key: 'name', label: 'Nom', sortable: true },
8
+ { key: 'amount', label: 'Amount', sortable: true, align: 'right' },
9
+ { key: 'status', label: 'Status' },
10
+ ];
11
+ table.data = rows;
12
+ table.pageSize = 20;
13
+ ```
14
+
15
+ ## Sorting
16
+
17
+ Sortable headers are **buttons**, not clickable `<th>`s. A header you can only
18
+ sort with a mouse is a table half the users cannot sort.
19
+
20
+ Each press cycles: unsorted → ascending → descending → unsorted. The third press
21
+ restoring the original order is what makes sorting undoable.
22
+
23
+ Text sorts through `Intl.Collator` with `numeric: true`, in the table's
24
+ `locale`, else the page's `<html lang>`, else the browser's language. Each
25
+ language files accents its own way — "Ångström" next to "Angstrom" in English,
26
+ after "Zeta" in Swedish — and "Entity 2" comes before "Entity 10". Empty values sort to the end whichever way the column points — a
27
+ descending sort should not open with a screen of blanks.
28
+
29
+ `aria-sort` on the header reflects the state.
30
+
31
+ The rows are sorted once per change of `data` or of the order, not on every
32
+ render, so hovering and selecting stay quick on a few thousand rows. That relies
33
+ on `data` being replaced, not edited: assign a new array
34
+ (`table.data = [...rows, added]`) rather than pushing into the old one.
35
+
36
+ ## Server-side data
37
+
38
+ With `manual`, the table sorts and pages nothing itself: `data` is the page the
39
+ server sent, shown as it is, and `total-rows` is how many rows the server holds,
40
+ which the pager counts from. Headers and the pager still update `sortKey`,
41
+ `sortDirection` and `page` and fire their events; the application loads what
42
+ they ask for.
43
+
44
+ ```js
45
+ table.manual = true;
46
+ table.pageSize = 25;
47
+
48
+ async function load() {
49
+ table.loading = true;
50
+ const { rows, total } = await api.customers({
51
+ page: table.page,
52
+ size: table.pageSize,
53
+ sort: table.sortKey,
54
+ direction: table.sortDirection,
55
+ });
56
+ table.data = rows;
57
+ table.totalRows = total;
58
+ table.loading = false;
59
+ }
60
+
61
+ table.addEventListener('kt-sort-change', load);
62
+ table.addEventListener('kt-page-change', load);
63
+ load();
64
+ ```
65
+
66
+ A new sort sends `page` back to 1 and fires only `kt-sort-change` — one event,
67
+ one reload. While `loading`, a table that already shows a page keeps it on
68
+ screen, dimmed and `aria-busy`, so the pager that asked for the next one keeps
69
+ its place and its focus; the loading message only replaces the table before
70
+ anything has loaded.
71
+
72
+ Selection is kept by row identity, so it survives paging. `selectedRows` can
73
+ only return the selected rows of the page on screen: keep the rows themselves
74
+ on your side if you need them across pages.
75
+
76
+ ## Thousands of rows
77
+
78
+ `virtual` renders only the rows in view — a few dozen, whatever the length of
79
+ `data` — so five thousand rows scroll as smoothly as fifty, without paging. The
80
+ table then scrolls inside its own box, under a header that stays on top: give
81
+ it a height.
82
+
83
+ ```html
84
+ <kt-table virtual style="height: 480px"></kt-table>
85
+ ```
86
+
87
+ Every row must be the same height. The first one rendered is measured, so
88
+ `compact` and your own padding are followed, but a custom cell taller than the
89
+ rest would put the scroll position out of step with the rows.
90
+
91
+ Sorting and selection work on the data, not on what is rendered: the header box
92
+ selects every row, not only those on screen. `aria-rowcount` and
93
+ `aria-rowindex` let a screen reader announce "row 1,204 of 5,000" with thirty
94
+ rows in the page. A `page-size` takes precedence over `virtual`.
95
+
96
+ ## Selection
97
+
98
+ ```html
99
+ <kt-table selectable selection-mode="multiple"></kt-table>
100
+ ```
101
+
102
+ Rows are identified by `id`, then `uid`, then the row object itself. `selected`
103
+ holds those identities; `selectedRows` gives you the rows.
104
+
105
+ Multiple mode adds a select-all box in the header, which goes indeterminate on
106
+ a partial selection.
107
+
108
+ ```js
109
+ table.addEventListener('kt-selection-change', (e) => {
110
+ console.log(e.detail.selected, e.detail.rows);
111
+ });
112
+ ```
113
+
114
+ ## Custom cells
115
+
116
+ `renderCell` returns anything Lit can render — or `undefined` to fall back to
117
+ the raw value, so it only has to handle the columns it cares about.
118
+
119
+ ```js
120
+ table.renderCell = (row, column) =>
121
+ column.key === 'status'
122
+ ? html`<kt-badge variant="category" color="var(--color-success-base)">${row.status}</kt-badge>`
123
+ : undefined;
124
+ ```
125
+
126
+ ## API
127
+
128
+ | Property | Attribute | Type | Default |
129
+ | --------------- | ---------------- | -------------------------- | ------------------------- |
130
+ | `columns` | — | `KtTableColumn[]` | `[]` |
131
+ | `data` | — | `KtTableRow[]` | `[]` |
132
+ | `selectable` | `selectable` | `boolean` | `false` |
133
+ | `selectionMode` | `selection-mode` | `'single' \| 'multiple'` | `'multiple'` |
134
+ | `selected` | — | `unknown[]` | `[]` |
135
+ | `pageSize` | `page-size` | `number` (0 = no paging) | `0` |
136
+ | `page` | `page` | `number`, from 1 | `1` |
137
+ | `manual` | `manual` | `boolean` | `false` |
138
+ | `totalRows` | `total-rows` | `number` (manual mode) | `0` |
139
+ | `virtual` | `virtual` | `boolean` | `false` |
140
+ | `loading` | `loading` | `boolean` | `false` |
141
+ | `compact` | `compact` | `boolean` | `false` |
142
+ | `sortKey` | `sort-key` | `string \| null` | `null` |
143
+ | `sortDirection` | `sort-direction` | `'asc' \| 'desc' \| null` | `null` |
144
+ | `renderCell` | — | `(row, column) => unknown` | — |
145
+ | `emptyText` | `empty-text` | `string` | `'No data to display'` |
146
+ | `loadingText` | `loading-text` | `string` | `'Loading…'` |
147
+ | `label` | `label` | `string` | `''` |
148
+ | `locale` | `locale` | `string` (BCP 47) | `''` (page, then browser) |
149
+
150
+ ```ts
151
+ interface KtTableColumn {
152
+ key: string;
153
+ label?: string; // falls back to key
154
+ sortable?: boolean;
155
+ align?: 'left' | 'center' | 'right';
156
+ width?: string;
157
+ }
158
+ ```
159
+
160
+ | Getter | Returns |
161
+ | -------------- | ------------------------------- |
162
+ | `sortedRows` | All rows, sorted, before paging |
163
+ | `visibleRows` | The rows currently on screen |
164
+ | `selectedRows` | The selected rows themselves |
165
+ | `totalPages` | Page count |
166
+
167
+ | Event | Detail |
168
+ | --------------------- | -------------------- |
169
+ | `kt-sort-change` | `{ key, direction }` |
170
+ | `kt-selection-change` | `{ selected, rows }` |
171
+ | `kt-row-click` | `{ row, index }` |
172
+ | `kt-page-change` | `{ page }` |
173
+
174
+ | Part | Description |
175
+ | ------------- | ------------- |
176
+ | `table` | The `<table>` |
177
+ | `header-cell` | A `<th>` |
178
+ | `row` | A `<tr>` |
179
+ | `cell` | A `<td>` |
@@ -0,0 +1,87 @@
1
+ # `<kt-tabs>`
2
+
3
+ An underlined tab bar.
4
+
5
+ ```js
6
+ tabs.tabs = [
7
+ { value: 'usage', label: 'Usage' },
8
+ { value: 'api', label: 'API', icon: 'code' },
9
+ { value: 'theme', label: 'Theme', disabled: true },
10
+ ];
11
+ tabs.value = 'usage';
12
+ tabs.addEventListener('kt-change', (e) => show(e.detail.value));
13
+ ```
14
+
15
+ ## Against `<kt-segmented-control>`
16
+
17
+ They look similar in a screenshot and mean different things.
18
+
19
+ A **segmented control** picks a _value_ — a date range, a unit, a sort order —
20
+ and belongs inside a form or a toolbar. **Tabs** switch which _region of the
21
+ page_ you are looking at; they read as part of the page structure rather than
22
+ as a control sitting on it.
23
+
24
+ The shapes say so: an inset track with a raised pill, versus a rule with one
25
+ segment underlined. Using the wrong one is not a styling mistake, it is telling
26
+ the reader the wrong thing about what will happen.
27
+
28
+ ## Panels
29
+
30
+ `<kt-tabs>` manages selection only — swapping the panel is yours, the same
31
+ contract as the segmented control. Give each tab a `panel` id and the tab
32
+ carries `aria-controls`, so the pairing is announced:
33
+
34
+ ```html
35
+ <kt-tabs></kt-tabs>
36
+ <section id="usage-panel" role="tabpanel">…</section>
37
+ ```
38
+
39
+ ```js
40
+ tabs.tabs = [{ value: 'usage', label: 'Usage', panel: 'usage-panel' }];
41
+ ```
42
+
43
+ ## Loading
44
+
45
+ `loading` renders three placeholder tabs instead of the real ones, so a bar
46
+ waiting on a fetch reserves its height rather than appearing under the content
47
+ already on screen.
48
+
49
+ ## Keyboard
50
+
51
+ One tab stop for the whole bar, as a tablist should have.
52
+
53
+ | Key | Does |
54
+ | -------------- | ----------------------------------- |
55
+ | `←` / `→` | Previous / next tab, and selects it |
56
+ | `Home` / `End` | First / last selectable tab |
57
+
58
+ Disabled tabs are skipped, and selection wraps at both ends.
59
+
60
+ ## API
61
+
62
+ | Property | Attribute | Type | Default |
63
+ | ---------- | ---------- | -------------------------- | ------- |
64
+ | `tabs` | — | `KtTab[]` | `[]` |
65
+ | `value` | `value` | `string \| number \| null` | `null` |
66
+ | `disabled` | `disabled` | `boolean` | `false` |
67
+ | `loading` | `loading` | `boolean` | `false` |
68
+ | `label` | `label` | `string` | `''` |
69
+
70
+ ```ts
71
+ interface KtTab {
72
+ value: string | number;
73
+ label?: string; // falls back to String(value)
74
+ icon?: string; // Lucide name, kebab-case
75
+ disabled?: boolean;
76
+ panel?: string; // id of the region it controls
77
+ }
78
+ ```
79
+
80
+ | Event | Detail |
81
+ | ----------- | ----------------------------------------------------------------- |
82
+ | `kt-change` | `{ value, tab }` — not fired when the active tab is clicked again |
83
+
84
+ | Part | Description |
85
+ | ------ | ----------- |
86
+ | `base` | The tablist |
87
+ | `tab` | One tab |
@@ -0,0 +1,59 @@
1
+ # `<kt-textarea>`
2
+
3
+ A multi-line field, with the same borderless fill and outline states as
4
+ `<kt-input>`.
5
+
6
+ ```html
7
+ <kt-textarea placeholder="Describe the incident..." rows="5"></kt-textarea>
8
+ <kt-textarea maxlength="280" name="bio"></kt-textarea>
9
+ <kt-textarea error="280 characters maximum"></kt-textarea>
10
+ <kt-textarea resize="vertical" placeholder="As long as it needs to be"></kt-textarea>
11
+ ```
12
+
13
+ ## Resizing
14
+
15
+ Off by default. The grip is drawn by the operating system, lands on the field's
16
+ rounded corner and belongs to no design system — and a field the layout sized is
17
+ not usually the reader's to resize. Set `resize="vertical"` where the content
18
+ genuinely varies and the extra height will not break the screen around it.
19
+
20
+ ## The counter
21
+
22
+ Setting `maxlength` shows a `0 / 280` counter, which turns red at the limit.
23
+ It exists because a `maxlength` alone is a silent failure: the browser simply
24
+ stops accepting keystrokes, with nothing on screen to explain why.
25
+
26
+ The counter is `aria-live="polite"`, so it is announced as the user approaches
27
+ the limit rather than on every character.
28
+
29
+ ## Form participation
30
+
31
+ Form-associated, like `<kt-input>`: it serialises under its `name`, resets with
32
+ the form, and reports `valueMissing` when `required` and empty.
33
+
34
+ ## API
35
+
36
+ | Property | Attribute | Type | Default |
37
+ | ------------- | ------------- | ---------------------- | -------- |
38
+ | `value` | `value` | `string` | `''` |
39
+ | `name` | `name` | `string` | `''` |
40
+ | `placeholder` | `placeholder` | `string` | `''` |
41
+ | `rows` | `rows` | `number` | `3` |
42
+ | `maxlength` | `maxlength` | `number` | — |
43
+ | `disabled` | `disabled` | `boolean` | `false` |
44
+ | `readonly` | `readonly` | `boolean` | `false` |
45
+ | `required` | `required` | `boolean` | `false` |
46
+ | `error` | `error` | `string` | `''` |
47
+ | `label` | `label` | `string` | `''` |
48
+ | `resize` | `resize` | `'vertical' \| 'none'` | `'none'` |
49
+
50
+ | Event | Detail |
51
+ | ----------- | ------------------- |
52
+ | `kt-input` | `{ value: string }` |
53
+ | `kt-change` | `{ value: string }` |
54
+
55
+ | Part | Description |
56
+ | --------- | ----------------------- |
57
+ | `base` | The field container |
58
+ | `control` | The native `<textarea>` |
59
+ | `counter` | The character counter |
@@ -0,0 +1,88 @@
1
+ # `<kt-time-input>`
2
+
3
+ A time of day typed into its parts — hours, minutes and, if asked, seconds.
4
+
5
+ ```html
6
+ <kt-time-input label="Starts at" name="start" step="15" min="08:00" max="20:00"></kt-time-input>
7
+ <kt-time-input label="Logged at" seconds value="09:15:30"></kt-time-input>
8
+ ```
9
+
10
+ It shares its keyboard with [`<kt-date-input>`](./kt-date-input.md):
11
+ each part is a segment, typed digit by digit, stepped with the arrows, the
12
+ focus moving on as each fills — `1430` is half past two in the afternoon.
13
+
14
+ ## The reader's clock
15
+
16
+ The clock is the one the reader's language reads: `14:30` in London and
17
+ Paris, `02:30 PM` in New York, `14.30` in Copenhagen. `hour-cycle` sets it
18
+ instead — `h12` for hours from 1 to 12 with AM/PM, `h23` for 0 to 23. The
19
+ locale is `locale` if set, else the page's `lang`, else the browser's
20
+ language.
21
+
22
+ On a 12-hour clock the AM/PM segment takes the first letter of either — `a`
23
+ or `p` — or the arrows. The hour reads with it: 12 AM is midnight, 12 PM noon.
24
+
25
+ ## The value
26
+
27
+ Whatever the clock shows, the value is the 24-hour time `<input type="time">`
28
+ and a server speak: `14:30`, or `14:30:05` with `seconds`. It is `null` until
29
+ every segment is filled; a segment left at one digit is written out when the
30
+ focus leaves it, `9` becoming `09`. `kt-change` fires once per time, not per
31
+ keystroke.
32
+
33
+ `step` is how many minutes the arrows move by: with `step="15"`, `↑` from
34
+ `09:07` goes to `09:15`. Typed minutes are not rounded.
35
+
36
+ A time before `min` or after `max` puts the field in its error state, the
37
+ message in a tooltip on the alert icon. The value is `null` and the form sees
38
+ a `badInput`.
39
+
40
+ | Key | Does |
41
+ | ------------- | ----------------------------------------------------- |
42
+ | digits | fill the segment in focus |
43
+ | `a` / `p` | AM or PM, on a 12-hour clock |
44
+ | `↑` / `↓` | step the segment, wrapping round the clock |
45
+ | `Home`/`End` | the segment's first or last value |
46
+ | `←` / `→` | the segment before or after |
47
+ | `Backspace` | erases a digit, then steps back to the segment before |
48
+ | `:` `.` space | the next segment |
49
+
50
+ ## In a form
51
+
52
+ A form control: it submits the value under `name`, and nothing while the time
53
+ is incomplete. `required` blocks submission while it is empty, `error` shows a
54
+ message, and `form.reset()` puts back the initial value.
55
+
56
+ ## API
57
+
58
+ | Property | Attribute | Type | Default |
59
+ | ----------- | ------------ | -------------------------------- | ------------------ |
60
+ | `value` | `value` | `string \| null` | `null` |
61
+ | `seconds` | `seconds` | `boolean` | `false` |
62
+ | `hourCycle` | `hour-cycle` | `'h12' \| 'h23' \| ''` | `''`, the locale's |
63
+ | `step` | `step` | `number` | `1` |
64
+ | `min` | `min` | `string` | `''` |
65
+ | `max` | `max` | `string` | `''` |
66
+ | `name` | `name` | `string` | `''` |
67
+ | `label` | `label` | `string` | `''` |
68
+ | `locale` | `locale` | `string` | page, then browser |
69
+ | `size` | `size` | `'small' \| 'medium' \| 'large'` | `'medium'` |
70
+ | `disabled` | `disabled` | `boolean` | `false` |
71
+ | `required` | `required` | `boolean` | `false` |
72
+ | `error` | `error` | `string` | `''` |
73
+
74
+ | Event | Detail |
75
+ | ----------- | --------------------------------------------------------- |
76
+ | `kt-change` | `{ value: string \| null }` — `HH:MM`, `HH:MM:SS`, `null` |
77
+
78
+ | Part | Description |
79
+ | --------- | ---------------------------------------- |
80
+ | `field` | The field |
81
+ | `segment` | An hour, minute, second or AM/PM segment |
82
+
83
+ ## Accessibility
84
+
85
+ The field is a `group` named by `label`. Each segment is a `spinbutton` named
86
+ "Hour", "Minute", "Second" or "AM or PM", with its range and value. `Tab`
87
+ walks the segments. On a phone, the number segments bring up the numeric
88
+ keypad.
@@ -0,0 +1,63 @@
1
+ # `<kt-timeline>`
2
+
3
+ An ordered run of events on a rail.
4
+
5
+ ```html
6
+ <kt-timeline>
7
+ <kt-timeline-item heading="Deployed" time="09:24" variant="success" icon="rocket">
8
+ Build 4210 went to production.
9
+ </kt-timeline-item>
10
+ <kt-timeline-item heading="Review requested" time="09:02" icon="eye">
11
+ Two files, forty lines.
12
+ </kt-timeline-item>
13
+ <kt-timeline-item heading="Branch opened" time="08:41"></kt-timeline-item>
14
+ </kt-timeline>
15
+ ```
16
+
17
+ The same shape keeps reappearing — an activity feed, an audit log, a changelog,
18
+ the stages of a delivery, the roles on a résumé — and every time it gets rebuilt
19
+ out of a list, a pseudo-element line and a hand-placed dot.
20
+
21
+ The fiddly part is the rail. It must start at the first dot and stop at the
22
+ last, which a border on the list cannot do: a border runs the full height, so
23
+ the line overshoots below the final event. Here each item draws its own segment
24
+ hanging below its marker, and the last item drops it. The item cannot work out
25
+ on its own that it is last — `:last-child` matches in the light tree, which the
26
+ item's own shadow styles cannot reach — so `<kt-timeline>` reads its slot and
27
+ sets `last` on the final child, again whenever the list changes.
28
+
29
+ Without an `icon` the marker is a 9px dot; with one it is a 24px tinted circle.
30
+ Mixing the two in one list is fine and reads as emphasis — give the events that
31
+ matter an icon and leave the rest as dots.
32
+
33
+ `variant` only colours the marker. As with every status colour in Kanto, it
34
+ never carries the meaning alone: the heading says what happened.
35
+
36
+ ## API
37
+
38
+ ### `<kt-timeline>`
39
+
40
+ | Property | Attribute | Type | Default |
41
+ | --------- | --------- | --------- | ------- |
42
+ | `compact` | `compact` | `boolean` | `false` |
43
+
44
+ `compact` is passed down to every item, so set it once on the list.
45
+
46
+ ### `<kt-timeline-item>`
47
+
48
+ | Property | Attribute | Type | Default |
49
+ | --------- | --------- | ------------------------------------------------------------ | ----------- |
50
+ | `heading` | `heading` | `string` | `''` |
51
+ | `time` | `time` | `string` | `''` |
52
+ | `icon` | `icon` | `string` | `''` |
53
+ | `variant` | `variant` | `neutral \| primary \| success \| warning \| danger \| info` | `'neutral'` |
54
+ | `last` | `last` | `boolean` | `false` |
55
+
56
+ `time` is a free string — `09:24`, `2 days ago`, `June 2026` — because a
57
+ timeline is as often a résumé as it is a log. `last` is managed by the parent;
58
+ set it yourself only if you are laying items out without `<kt-timeline>`.
59
+
60
+ | Slot | Description |
61
+ | --------- | ------------------------------------------- |
62
+ | _default_ | The body of the event |
63
+ | `actions` | Trailing controls, aligned with the heading |
@@ -0,0 +1,69 @@
1
+ # `<kt-toast-container>` / `toaster`
2
+
3
+ The stack that owns the toasts on screen, and the imperative shortcut to it.
4
+
5
+ ## `toaster` — the usual way
6
+
7
+ ```js
8
+ import { toaster } from 'kanto-ds';
9
+
10
+ toaster.success('Entity created');
11
+ toaster.error('Could not save', { description: 'Try again in a moment.' });
12
+ ```
13
+
14
+ It creates one `<kt-toast-container>` on `<body>` the first time it is called
15
+ and reuses it — including one you already put in your own markup, which it
16
+ adopts rather than duplicating.
17
+
18
+ Defaults that encode the intent: success and info dismiss after 4s, warnings
19
+ after 6s, and **errors never dismiss themselves**. An error the user did not
20
+ happen to be looking at is an error they never saw.
21
+
22
+ ## `<kt-toast-container>` — when placement matters
23
+
24
+ ```html
25
+ <kt-toast-container position="top-right" limit="3"></kt-toast-container>
26
+ ```
27
+
28
+ ```js
29
+ container.show({ variant: 'success', heading: 'Entity created', duration: 4000 });
30
+ ```
31
+
32
+ The container owns the toasts: it appends them, and removes them when they ask
33
+ to go. A toast that removes itself from a list it does not own is how you end up
34
+ with two on screen and neither leaving.
35
+
36
+ Newest toasts stack towards the screen edge, so the newest is always nearest the
37
+ corner. Past `limit`, the oldest is dropped.
38
+
39
+ **Hovering the stack pauses every countdown**, and leaving it resumes them —
40
+ otherwise a toast expires under the pointer of someone reading it.
41
+
42
+ The container itself does not intercept pointer events; only the toasts inside
43
+ it do, so the rest of the corner stays clickable.
44
+
45
+ ## API — `<kt-toast-container>`
46
+
47
+ | Property | Attribute | Type | Default |
48
+ | ---------- | ---------- | -------------------------------------------------------------- | ---------------- |
49
+ | `position` | `position` | `'top-left' \| 'top-right' \| 'bottom-left' \| 'bottom-right'` | `'bottom-right'` |
50
+ | `limit` | `limit` | `number` | `5` |
51
+
52
+ | Method / getter | Description |
53
+ | ---------------- | ----------------------------- |
54
+ | `show(options)` | Raises a toast, returns it |
55
+ | `dismiss(toast)` | Removes one |
56
+ | `clear()` | Removes all |
57
+ | `toasts` | Those on screen, oldest first |
58
+
59
+ ## API — `toaster`
60
+
61
+ | Member | Description |
62
+ | ----------------------------------------------- | ------------------------------------ |
63
+ | `success/info/warning/error(heading, options?)` | Raises a toast, returns it |
64
+ | `show(options)` | The general form |
65
+ | `getContainer()` | The container, creating it if needed |
66
+ | `clear()` | Clears the stack |
67
+ | `position` | Where an auto-created container sits |
68
+
69
+ See [`<kt-toast>`](./kt-toast.md) for a single notification.
@@ -0,0 +1,41 @@
1
+ # `<kt-toast>`
2
+
3
+ A transient notification.
4
+
5
+ For raising one, reach for [`toaster`](./kt-toast-container.md)
6
+ — this page is the element itself.
7
+
8
+ ## Usage
9
+
10
+ ```html
11
+ <kt-toast variant="warning" heading="Approaching your quota" dismissible duration="6000"></kt-toast>
12
+ ```
13
+
14
+ `heading`, not `title`: every element already has a `title`, and shadowing it
15
+ would turn the toast into its own tooltip.
16
+
17
+ Errors are `role="alert"` / `aria-live="assertive"` and interrupt the screen
18
+ reader; everything else is `role="status"` / `polite` and waits its turn.
19
+
20
+ This is the only element in Kanto with a real shadow, and the only one that
21
+ blurs what is behind it. A toast floats above the page, so it says so.
22
+
23
+ ## API
24
+
25
+ | Property | Attribute | Type | Default |
26
+ | ------------- | ------------- | ---------------------------------------------------- | --------------- |
27
+ | `variant` | `variant` | `'success' \| 'information' \| 'warning' \| 'error'` | `'information'` |
28
+ | `heading` | `heading` | `string` | `''` |
29
+ | `description` | `description` | `string` | `''` |
30
+ | `dismissible` | `dismissible` | `boolean` | `false` |
31
+ | `duration` | `duration` | `number` (ms, 0 = stays) | `0` |
32
+
33
+ | Method | Description |
34
+ | ---------- | --------------------------- |
35
+ | `close()` | Fires `kt-toast-close` |
36
+ | `pause()` | Freezes the countdown |
37
+ | `resume()` | Resumes it where it stopped |
38
+
39
+ | Event | Fired when |
40
+ | ---------------- | ---------------------------- |
41
+ | `kt-toast-close` | The toast asks to be removed |
@@ -0,0 +1,67 @@
1
+ # `<kt-toggle-button-group>`
2
+
3
+ Joins `<kt-toggle-button>`s into one bar and owns their selection.
4
+
5
+ ```html
6
+ <kt-toggle-button-group label="View">
7
+ <kt-toggle-button value="list">List</kt-toggle-button>
8
+ <kt-toggle-button value="grid">Grid</kt-toggle-button>
9
+ <kt-toggle-button value="map">Map</kt-toggle-button>
10
+ </kt-toggle-button-group>
11
+ ```
12
+
13
+ ```js
14
+ group.addEventListener('kt-change', (e) => console.log(e.detail.value));
15
+ ```
16
+
17
+ ## The group owns the selection
18
+
19
+ Buttons are slotted rather than described by an options array, so a label can be
20
+ anything — an icon, a count, a badge.
21
+
22
+ Inside a group a button becomes **controlled**: it no longer flips itself and no
23
+ longer emits. The group reads the click, works out what the selection now is,
24
+ and writes `selected` back down. That is what keeps one press from producing two
25
+ contradictory `kt-change` events.
26
+
27
+ So: listen on the group, never on its buttons.
28
+
29
+ ## Single and multiple
30
+
31
+ Single mode behaves like a radio group with a deselect — pressing the active
32
+ button clears it, and `value` is a string or `null`.
33
+
34
+ `multiple` makes it a set of checkboxes drawn together, and `value` becomes an
35
+ array.
36
+
37
+ ```html
38
+ <kt-toggle-button-group multiple orientation="vertical"></kt-toggle-button-group>
39
+ ```
40
+
41
+ Corner rounding belongs to the group, which is the only thing that knows which
42
+ button is first and which is last; it drives `--kt-toggle-button-radius` on each
43
+ child through CSS.
44
+
45
+ ## API
46
+
47
+ | Property | Attribute | Type | Default |
48
+ | ------------- | ------------- | ---------------------------- | -------------- |
49
+ | `value` | — | `string \| string[] \| null` | `null` |
50
+ | `multiple` | `multiple` | `boolean` | `false` |
51
+ | `orientation` | `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` |
52
+ | `label` | `label` | `string` | `''` |
53
+
54
+ | Event | Detail |
55
+ | ----------- | ----------- |
56
+ | `kt-change` | `{ value }` |
57
+
58
+ | Slot | Description |
59
+ | --------- | ----------------------------- |
60
+ | _default_ | `<kt-toggle-button>` children |
61
+
62
+ | Part | Description |
63
+ | ------ | ------------------- |
64
+ | `base` | The group container |
65
+
66
+ See [`<kt-toggle-button>`](./kt-toggle-button.md) for the
67
+ button itself.