kanto-ds 1.6.0 → 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 (73) hide show
  1. package/README.md +56 -6
  2. package/dist/cdn/kanto.min.js +6619 -0
  3. package/dist/chunks/defaults-Cpeg13O5.js +68 -0
  4. package/dist/chunks/defaults-Cpeg13O5.js.map +1 -0
  5. package/dist/chunks/{floating-B33g-M_1.js → floating-C9RLa6cZ.js} +2 -2
  6. package/dist/chunks/floating-C9RLa6cZ.js.map +1 -0
  7. package/dist/components/core/kt-icon/kt-icon.js +1 -1
  8. package/dist/components/forms/kt-date-input/kt-date-input.js +1 -1
  9. package/dist/components/forms/kt-date-picker/kt-date-picker.js +1 -1
  10. package/dist/components/forms/kt-input/kt-input.js +1 -1
  11. package/dist/components/forms/kt-input-menu/kt-input-menu.js +1 -1
  12. package/dist/components/forms/kt-multi-select/kt-multi-select.js +1 -1
  13. package/dist/components/forms/kt-select/kt-select.js +1 -1
  14. package/dist/components/overlays/kt-dropdown/kt-dropdown.js +1 -1
  15. package/dist/docs/kt-alert.md +52 -0
  16. package/dist/docs/kt-avatar.md +46 -0
  17. package/dist/docs/kt-badge.md +104 -0
  18. package/dist/docs/kt-breadcrumb.md +53 -0
  19. package/dist/docs/kt-button.md +72 -0
  20. package/dist/docs/kt-calendar.md +98 -0
  21. package/dist/docs/kt-card.md +93 -0
  22. package/dist/docs/kt-chart.md +230 -0
  23. package/dist/docs/kt-checkbox.md +65 -0
  24. package/dist/docs/kt-code.md +88 -0
  25. package/dist/docs/kt-collapsible.md +50 -0
  26. package/dist/docs/kt-confirm-dialog.md +52 -0
  27. package/dist/docs/kt-date-input.md +97 -0
  28. package/dist/docs/kt-date-picker.md +116 -0
  29. package/dist/docs/kt-drag-drop.md +85 -0
  30. package/dist/docs/kt-dropdown.md +65 -0
  31. package/dist/docs/kt-empty-state.md +36 -0
  32. package/dist/docs/kt-form.md +93 -0
  33. package/dist/docs/kt-header.md +100 -0
  34. package/dist/docs/kt-icon.md +68 -0
  35. package/dist/docs/kt-input-menu.md +90 -0
  36. package/dist/docs/kt-input.md +137 -0
  37. package/dist/docs/kt-kbd.md +44 -0
  38. package/dist/docs/kt-label-input.md +54 -0
  39. package/dist/docs/kt-meter.md +79 -0
  40. package/dist/docs/kt-modal.md +62 -0
  41. package/dist/docs/kt-multi-select.md +115 -0
  42. package/dist/docs/kt-page-header.md +53 -0
  43. package/dist/docs/kt-pagination.md +36 -0
  44. package/dist/docs/kt-progress-bar.md +66 -0
  45. package/dist/docs/kt-radio-group.md +73 -0
  46. package/dist/docs/kt-segmented-control.md +70 -0
  47. package/dist/docs/kt-select.md +80 -0
  48. package/dist/docs/kt-side-panel.md +59 -0
  49. package/dist/docs/kt-skeleton.md +46 -0
  50. package/dist/docs/kt-split-button.md +105 -0
  51. package/dist/docs/kt-stat.md +57 -0
  52. package/dist/docs/kt-sub-menu-navigation.md +118 -0
  53. package/dist/docs/kt-table.md +179 -0
  54. package/dist/docs/kt-tabs.md +87 -0
  55. package/dist/docs/kt-textarea.md +59 -0
  56. package/dist/docs/kt-time-input.md +88 -0
  57. package/dist/docs/kt-timeline.md +63 -0
  58. package/dist/docs/kt-toast-container.md +69 -0
  59. package/dist/docs/kt-toast.md +41 -0
  60. package/dist/docs/kt-toggle-button-group.md +67 -0
  61. package/dist/docs/kt-toggle-button.md +52 -0
  62. package/dist/docs/kt-toggle.md +47 -0
  63. package/dist/docs/kt-tooltip.md +68 -0
  64. package/dist/icons/defaults.d.ts +9 -0
  65. package/dist/icons/index.js +1 -1
  66. package/dist/index.js +1 -1
  67. package/dist/internal/floating.d.ts +4 -0
  68. package/dist/tokens/colors.css +5 -2
  69. package/dist/web-types.json +1 -1
  70. package/package.json +29 -6
  71. package/dist/chunks/defaults-CFrjqae2.js +0 -59
  72. package/dist/chunks/defaults-CFrjqae2.js.map +0 -1
  73. package/dist/chunks/floating-B33g-M_1.js.map +0 -1
@@ -0,0 +1,137 @@
1
+ # `<kt-input>`
2
+
3
+ A single-line text field.
4
+
5
+ Kanto fields are borderless: a fill on `--color-dark-12` that gains a 2px
6
+ **outline** — grey on hover, primary on focus, danger on error. An outline sits
7
+ outside the box, so none of those states reflow the layout.
8
+
9
+ ```html
10
+ <kt-input placeholder="Search everything..." icon="search"></kt-input>
11
+ <kt-input type="password" name="password"></kt-input>
12
+ <kt-input error="Invalid email address" value="not-an-address"></kt-input>
13
+ <kt-input size="small" placeholder="Filtrer"></kt-input>
14
+ ```
15
+
16
+ ## Forms
17
+
18
+ `<kt-input>` is a form-associated custom element. It serialises into `FormData`
19
+ under its `name`, resets with the form, and reports validity like a native
20
+ input — no hidden mirror input, no manual wiring.
21
+
22
+ ```html
23
+ <form>
24
+ <kt-input name="email" type="email" required></kt-input>
25
+ <kt-button type="submit">Send</kt-button>
26
+ </form>
27
+ ```
28
+
29
+ ```js
30
+ new FormData(form).get('email'); // the field's value
31
+ ```
32
+
33
+ ## Reading the value
34
+
35
+ `value` is a property, and a `value` attribute seeds it and becomes the reset
36
+ value. Listn for `kt-change` for a committed value, `kt-input` for every
37
+ keystroke:
38
+
39
+ ```js
40
+ input.addEventListener('kt-change', (e) => console.log(e.detail.value));
41
+ ```
42
+
43
+ The inner `change` event is stopped at the boundary rather than allowed to leak
44
+ out retargeted and untyped.
45
+
46
+ ## Password and clear
47
+
48
+ `type="password"` adds a reveal toggle. Any non-empty field shows a clear
49
+ button, which rotates 90° on hover; set `clearable="false"` to suppress it.
50
+ Both are `tabindex="-1"` — they are conveniences for the mouse, and putting
51
+ them in the tab order would double the number of stops in a form.
52
+
53
+ ## Phone mode
54
+
55
+ `type="tel"` adds a country picker: flag, dial code, and a searchable panel.
56
+
57
+ ```html
58
+ <kt-input type="tel" name="phone" country="be"></kt-input>
59
+ ```
60
+
61
+ `value` holds the **national digits only** — `612345678` — and the field
62
+ displays them grouped the way the country's `format` groups them
63
+ (`1 23 45 67 89` for France gives `6 12 34 56 78`). What the _form_ receives is
64
+ the full international number, `+33612345678`: submitting national digits
65
+ without the country would throw away the thing the user just picked.
66
+
67
+ A number typed with the country's trunk prefix — `06 12 34 56 78` after +33 —
68
+ puts the field in its error state: `+330612345678` does not exist. The message
69
+ shows in a tooltip on the alert icon — on hover, and while the field has focus
70
+ so it reaches keyboard users too, until Escape puts it away — is read out
71
+ through the control's description, and the form sees a `patternMismatch`. Countries where a leading 0
72
+ belongs to the number, like Italy, have no `trunkPrefix` and are never flagged.
73
+ An `error` set by the application wins over this check.
74
+
75
+ About 95 countries ship by default, sorted by name. Pass your own list to
76
+ narrow or widen it:
77
+
78
+ ```js
79
+ input.countries = [
80
+ { id: 'fr', name: 'France', dialCode: '33', format: '1 23 45 67 89', trunkPrefix: '0' },
81
+ { id: 'it', name: 'Italy', dialCode: '39', format: '312 345 6789' },
82
+ ];
83
+ ```
84
+
85
+ Phone mode is reached **only** through `type="tel"`. Sniffing the placeholder,
86
+ the name or the icon for `/tel|phone/` instead would turn any field named
87
+ `telephone_verifie` into a country picker.
88
+
89
+ ## Labelling
90
+
91
+ Wrap the field in `<kt-label-input>`, or give it a `label` when there is no
92
+ visible one:
93
+
94
+ ```html
95
+ <kt-label-input label="Email address" required>
96
+ <kt-input name="email" type="email" required></kt-input>
97
+ </kt-label-input>
98
+
99
+ <kt-input label="Search" icon="search"></kt-input>
100
+ ```
101
+
102
+ ## API
103
+
104
+ | Property | Attribute | Type | Default |
105
+ | -------------- | -------------- | -------------------------------- | ---------- |
106
+ | `value` | `value` | `string` | `''` |
107
+ | `name` | `name` | `string` | `''` |
108
+ | `type` | `type` | `string` | `'text'` |
109
+ | `placeholder` | `placeholder` | `string` | `''` |
110
+ | `size` | `size` | `'small' \| 'medium' \| 'large'` | `'medium'` |
111
+ | `disabled` | `disabled` | `boolean` | `false` |
112
+ | `readonly` | `readonly` | `boolean` | `false` |
113
+ | `required` | `required` | `boolean` | `false` |
114
+ | `icon` | `icon` | `string` | `''` |
115
+ | `error` | `error` | `string` | `''` |
116
+ | `clearable` | `clearable` | `boolean` | `true` |
117
+ | `label` | `label` | `string` | `''` |
118
+ | `autocomplete` | `autocomplete` | `string` | `''` |
119
+ | `maxlength` | `maxlength` | `number` | — |
120
+
121
+ | Method | Description |
122
+ | ---------- | ---------------------------- |
123
+ | `focus()` | Focuses the inner input |
124
+ | `blur()` | Blurs it |
125
+ | `select()` | Selects the field's contents |
126
+
127
+ | Event | Detail | Fired when |
128
+ | ----------- | ------------------- | ---------------------------- |
129
+ | `kt-input` | `{ value: string }` | On every keystroke |
130
+ | `kt-change` | `{ value: string }` | On commit (blur or Enter) |
131
+ | `kt-clear` | — | The clear button was pressed |
132
+
133
+ | Part | Description |
134
+ | --------- | --------------------- |
135
+ | `base` | The field container |
136
+ | `control` | The native `<input>` |
137
+ | `actions` | The trailing icon row |
@@ -0,0 +1,44 @@
1
+ # `<kt-kbd>`
2
+
3
+ A keyboard shortcut, rendered per platform.
4
+
5
+ ```html
6
+ <kt-kbd keys="mod k"></kt-kbd>
7
+ <kt-kbd keys="ctrl shift p"></kt-kbd>
8
+ <kt-kbd>/</kt-kbd>
9
+ ```
10
+
11
+ `mod k` shows `⌘ K` on a Mac and `Ctrl K` everywhere else. A shortcut printed
12
+ with the wrong modifier is worse than no shortcut at all — the reader tries it,
13
+ nothing happens, and they stop trusting the hints.
14
+
15
+ ## `mod` versus `meta`
16
+
17
+ Reach for **`mod`**: it means "the primary shortcut modifier", which is Command
18
+ on a Mac and Control elsewhere.
19
+
20
+ `meta` stays honest — on Windows the Meta key really is the Windows key, so that
21
+ is what it prints. Most design systems quietly map `meta` to Ctrl and are wrong
22
+ about it on one platform.
23
+
24
+ | Key | Mac | Elsewhere |
25
+ | -------------------------------- | ----------- | ----------------------- |
26
+ | `mod` | ⌘ | Ctrl |
27
+ | `meta` | ⌘ | Win |
28
+ | `ctrl` | ⌃ | Ctrl |
29
+ | `alt` / `option` | ⌥ | Alt |
30
+ | `shift` | ⇧ | Shift |
31
+ | `enter` / `escape` / `backspace` | ↵ / esc / ⌫ | Enter / Esc / Backspace |
32
+
33
+ Anything else is passed through uppercased.
34
+
35
+ ## API
36
+
37
+ | Property | Attribute | Type | Default |
38
+ | ---------- | ---------- | ---------------------------- | -------- |
39
+ | `keys` | `keys` | `string` (space-separated) | `''` |
40
+ | `platform` | `platform` | `'auto' \| 'mac' \| 'other'` | `'auto'` |
41
+
42
+ | Getter | Returns |
43
+ | -------- | ---------------------------------- |
44
+ | `labels` | The keys as they will be displayed |
@@ -0,0 +1,54 @@
1
+ # `<kt-label-input>`
2
+
3
+ A label above a form control, with a red asterisk when it is required.
4
+
5
+ ```html
6
+ <kt-label-input label="Email address" required>
7
+ <kt-input name="email" type="email" required></kt-input>
8
+ </kt-label-input>
9
+ ```
10
+
11
+ ## Why it is not a `<label>`
12
+
13
+ Native label association does not cross a shadow boundary. `for="..."` needs the
14
+ control's id in the same tree, and wrapping a control in a `<label>` only works
15
+ when both are in that tree — a slotted `<kt-input>` is not.
16
+
17
+ So this element passes its text down to the slotted control's `label` property,
18
+ which the control turns into its own `aria-label`. Controls without that
19
+ property get `aria-label` set directly. Clicking the label focuses the control,
20
+ the way a real label does.
21
+
22
+ That means the asterisk is `aria-hidden`: screen readers get "required" from the
23
+ control's own `required` attribute, not from a decorative star in the label.
24
+
25
+ ## Works with anything
26
+
27
+ ```html
28
+ <kt-label-input label="Notes">
29
+ <kt-textarea maxlength="500"></kt-textarea>
30
+ </kt-label-input>
31
+
32
+ <kt-label-input label="Region">
33
+ <kt-select></kt-select>
34
+ </kt-label-input>
35
+
36
+ <kt-label-input label="File">
37
+ <input type="file" />
38
+ </kt-label-input>
39
+ ```
40
+
41
+ ## API
42
+
43
+ | Property | Attribute | Type | Default |
44
+ | ---------- | ---------- | --------- | ------- |
45
+ | `label` | `label` | `string` | `''` |
46
+ | `required` | `required` | `boolean` | `false` |
47
+
48
+ | Slot | Description |
49
+ | --------- | -------------------- |
50
+ | _default_ | The control to label |
51
+
52
+ | Part | Description |
53
+ | ------- | ------------- |
54
+ | `label` | The `<label>` |
@@ -0,0 +1,79 @@
1
+ # `<kt-meter>`
2
+
3
+ How a fixed total is spent: storage by file type, a budget by category, seats by
4
+ role.
5
+
6
+ ```html
7
+ <kt-meter label="Storage" used="25.8 GB used" total="of 983 GB" value="3"></kt-meter>
8
+ ```
9
+
10
+ ```js
11
+ meter.segments = [
12
+ { label: 'Documents', value: 16.14 },
13
+ { label: 'Photos', value: 3.94 },
14
+ { label: 'Other', value: 5.72 },
15
+ ];
16
+ meter.max = 983;
17
+ meter.showLegend = true;
18
+ meter.format = (n) => `${n} GB`;
19
+ ```
20
+
21
+ ## Against `<kt-progress-bar>`
22
+
23
+ A progress bar answers **how far along**: it fills toward completion, and
24
+ reaching the end is good news — which is why it turns green there. A meter
25
+ answers **how it is divided**: it is full from the moment it renders, and
26
+ reaching the end is bad news.
27
+
28
+ Different question, different component. A bar that shows one and means the
29
+ other is a common way to mislead a reader, so the two are not a variant of each
30
+ other here.
31
+
32
+ ## Segments and colour
33
+
34
+ Segments take `--chart-series-N` by position — the same fixed, never-cycled
35
+ order the charts use, so a category is the same colour in the meter and in the
36
+ chart beside it. Pass `color` to override one. Past eight, fold the tail into
37
+ an "Other" segment rather than starting the palette again under a new meaning.
38
+
39
+ Adjacent fills are separated by a 2px gap of the surface, so two segments of
40
+ similar hue still read as two.
41
+
42
+ Without `max`, segments are measured against their own sum and the bar is full.
43
+ With `max`, they are measured against it and the remainder stays empty — which
44
+ is what "25.8 GB of 983 GB" means.
45
+
46
+ ## API
47
+
48
+ | Property | Attribute | Type | Default |
49
+ | ------------ | ------------- | --------------------------- | --------------- |
50
+ | `label` | `label` | `string` | `''` |
51
+ | `used` | `used` | `string` | `''` |
52
+ | `total` | `total` | `string` | `''` |
53
+ | `value` | `value` | `number` (0–100) | `0` |
54
+ | `segments` | — | `KtMeterSegment[]` | `[]` |
55
+ | `max` | `max` | `number` | `0` (their sum) |
56
+ | `showLegend` | `show-legend` | `boolean` | `false` |
57
+ | `format` | — | `(value: number) => string` | `String` |
58
+
59
+ `used` and `total` are free strings rather than numbers, because the caption is
60
+ prose — "25.8 GB used", "of 983 GB", "7 of 20 seats" — and formatting it is the
61
+ application's job, not the meter's.
62
+
63
+ ```ts
64
+ interface KtMeterSegment {
65
+ label: string;
66
+ value: number;
67
+ color?: string;
68
+ }
69
+ ```
70
+
71
+ | CSS property | Description | Default |
72
+ | ------------------- | ---------------- | ------- |
73
+ | `--kt-meter-height` | The bar's height | `8px` |
74
+
75
+ | Part | Description |
76
+ | -------- | --------------- |
77
+ | `base` | The container |
78
+ | `track` | The bar |
79
+ | `legend` | The legend list |
@@ -0,0 +1,62 @@
1
+ # `<kt-modal>`
2
+
3
+ A centred dialog for anything that is not a yes/no question.
4
+
5
+ ```html
6
+ <kt-modal open heading="Compose" description="This goes to the whole team.">
7
+ <kt-textarea rows="6" placeholder="Write something..."></kt-textarea>
8
+ <kt-button slot="footer" variant="dark">Save draft</kt-button>
9
+ <kt-button slot="footer" icon="send">Send</kt-button>
10
+ </kt-modal>
11
+ ```
12
+
13
+ ## Against `<kt-confirm-dialog>`
14
+
15
+ The confirm dialog asks one thing and offers two answers, and its defaults are
16
+ tuned for that — Cancel takes focus, the confirm button is red. This one holds
17
+ _content_: a compose window, a command palette, a picker. You choose the footer.
18
+
19
+ Both are native `<dialog>`s, so the top layer, focus containment, page
20
+ inertness and Escape come from the platform rather than from a focus trap that
21
+ leaks.
22
+
23
+ ## Guarding unsaved work
24
+
25
+ `kt-close` is cancelable, and that covers Escape too:
26
+
27
+ ```js
28
+ modal.addEventListener('kt-close', (e) => {
29
+ if (!draft.dirty) return;
30
+ e.preventDefault();
31
+ confirmDiscard.open = true;
32
+ });
33
+ ```
34
+
35
+ `no-close-button` and `no-backdrop-close` narrow the ways out, but **Escape
36
+ always works**. A modal you cannot leave is a trap; cancelling `kt-close` is the
37
+ right way to guard one.
38
+
39
+ ## API
40
+
41
+ | Property | Attribute | Type | Default |
42
+ | ----------------- | ------------------- | ------------------------------------------ | ---------- |
43
+ | `open` | `open` | `boolean` | `false` |
44
+ | `heading` | `heading` | `string` | `''` |
45
+ | `description` | `description` | `string` | `''` |
46
+ | `size` | `size` | `'small' \| 'medium' \| 'large' \| 'full'` | `'medium'` |
47
+ | `noCloseButton` | `no-close-button` | `boolean` | `false` |
48
+ | `noBackdropClose` | `no-backdrop-close` | `boolean` | `false` |
49
+
50
+ | Method | Description |
51
+ | ---------------- | ----------------------------------------- |
52
+ | `requestClose()` | Fires `kt-close`, closes unless cancelled |
53
+
54
+ | Slot | Description |
55
+ | --------- | -------------------------------- |
56
+ | _default_ | The body |
57
+ | `header` | Replaces the `heading` attribute |
58
+ | `footer` | Actions. Hidden when empty. |
59
+
60
+ | Event | Description |
61
+ | ---------- | -------------------------------- |
62
+ | `kt-close` | Dismissal requested. Cancelable. |
@@ -0,0 +1,115 @@
1
+ # `<kt-multi-select>`
2
+
3
+ Several choices from a list: type to narrow it, pick as many as you need.
4
+
5
+ ```html
6
+ <kt-multi-select label="Regions" placeholder="Any region"></kt-multi-select>
7
+ ```
8
+
9
+ ```js
10
+ select.options = [
11
+ { id: 'ne', label: 'North East' },
12
+ { id: 'sw', label: 'South West' },
13
+ { id: 'nw', label: 'North West' },
14
+ ];
15
+ select.value = ['ne'];
16
+
17
+ select.addEventListener('kt-change', (e) => filter(e.detail.value));
18
+ ```
19
+
20
+ ## Against the alternatives
21
+
22
+ Reach for it when a filter takes several values from a list long enough to
23
+ search — regions, owners, tags. For two or three options that all fit on
24
+ screen, a `<kt-toggle-button-group multiple>` shows them without a click. For
25
+ one value, `<kt-input-menu>` or `<kt-select>`.
26
+
27
+ ## One line, whatever is chosen
28
+
29
+ Each choice shows in the field as a chip: a fill set into the field, the same
30
+ distance from its top, bottom and left edge, with its own remove button — the
31
+ family of a chosen option, not an outlined tag. The field keeps to one line:
32
+ the chips that do not fit collapse into a quiet **+N** beside the ones that do,
33
+ or into "3 selected" when not one fits, so the field sits in a row of filters
34
+ without changing its height.
35
+ The open list shows every option, the chosen ones ticked — which is where the
36
+ hidden ones can be seen and taken out. Widen the field and more chips come
37
+ back.
38
+
39
+ ## Choosing
40
+
41
+ The list stays open after each choice, since the point is to make several.
42
+ Clicking a chosen option, or pressing Enter on it, takes it out again; the
43
+ order of `value` is the order things were chosen. Disabled options can be seen
44
+ but not chosen.
45
+
46
+ `kt-change` fires with the whole new `value` and the matching `options`, not
47
+ the one that changed: a filter usually wants the full set.
48
+
49
+ ## Server-side filtering
50
+
51
+ `kt-filter` fires on every keystroke, as on `<kt-input-menu>`: assign the
52
+ results to `options` and the list follows. Chosen values stay chosen while
53
+ their options are filtered out — a chip whose option is no longer in `options`
54
+ shows its id.
55
+
56
+ ```js
57
+ select.addEventListener('kt-filter', async (e) => {
58
+ select.options = await search(e.detail.query);
59
+ });
60
+ ```
61
+
62
+ ## In a form
63
+
64
+ A form control, submitted like `<select multiple>`: one entry per chosen id
65
+ under `name`, and nothing at all while empty — read them with
66
+ `formData.getAll(name)`. `required` blocks submission until at least one value
67
+ is chosen, `form.reset()` puts back the values it started with, and a
68
+ `<fieldset disabled>` disables it.
69
+
70
+ ## Accessibility
71
+
72
+ The field is an ARIA combobox over a listbox marked `aria-multiselectable`,
73
+ each option `aria-selected` when chosen. The number chosen is part of the
74
+ field's description ("2 selected"), since the chips may not all be visible.
75
+ Each chip's remove button is named after it — "Remove North East".
76
+
77
+ ## Keyboard
78
+
79
+ | Key | Does |
80
+ | -------------- | -------------------------------------------------- |
81
+ | `↓` / `↑` | Opens the list, then moves the active option |
82
+ | `Home` / `End` | First / last match |
83
+ | `Enter` | Adds or removes the active option; the list stays |
84
+ | `Backspace` | In an empty field, removes the last chip |
85
+ | `Escape` | Closes and forgets the query, keeping every choice |
86
+ | `Tab` | Closes and moves on |
87
+
88
+ ## API
89
+
90
+ | Property | Attribute | Type | Default |
91
+ | ------------- | ------------- | ---------------------- | --------------- |
92
+ | `options` | — | `KtOption[]` | `[]` |
93
+ | `value` | — | `(string \| number)[]` | `[]` |
94
+ | `placeholder` | `placeholder` | `string` | `''` |
95
+ | `name` | `name` | `string` | `''` |
96
+ | `disabled` | `disabled` | `boolean` | `false` |
97
+ | `required` | `required` | `boolean` | `false` |
98
+ | `error` | `error` | `string` | `''` |
99
+ | `label` | `label` | `string` | `''` |
100
+ | `emptyText` | `empty-text` | `string` | `'No options…'` |
101
+
102
+ `selectedOptions` (read-only) is the chosen options, in the order chosen.
103
+
104
+ | Event | Detail |
105
+ | ----------- | -------------------- |
106
+ | `kt-change` | `{ value, options }` |
107
+ | `kt-filter` | `{ query: string }` |
108
+
109
+ | Part | Description |
110
+ | --------- | -------------------- |
111
+ | `control` | The field |
112
+ | `chips` | The row of chips |
113
+ | `input` | The native `<input>` |
114
+ | `popup` | The option list |
115
+ | `option` | An option row |
@@ -0,0 +1,53 @@
1
+ # `<kt-page-header>`
2
+
3
+ The block every screen opens with: an overline, a title, a sentence, and the
4
+ actions that belong to the page rather than to anything on it.
5
+
6
+ ```html
7
+ <kt-page-header
8
+ eyebrow="Dashboard"
9
+ heading="Good evening"
10
+ description="An overview of your workspace: recent files, integrations and activity."
11
+ >
12
+ <kt-button slot="actions" variant="dark" icon="upload">Upload a file</kt-button>
13
+ <kt-button slot="actions" icon="plus">New document</kt-button>
14
+ </kt-page-header>
15
+ ```
16
+
17
+ It exists because it was being rebuilt on every screen, and the copies drifted:
18
+ different heading levels, different gaps, actions sometimes above the title and
19
+ sometimes beside it. None of those are decisions worth making twice.
20
+
21
+ ## Two levels
22
+
23
+ `level="page"` (the default) renders an `<h1>` at display scale — one per
24
+ screen. `level="section"` renders an `<h2>` at body scale, for a region inside
25
+ a page, where the trailing action is usually a link onward rather than a button:
26
+
27
+ ```html
28
+ <kt-page-header level="section" heading="Recently opened">
29
+ <a slot="actions" href="#/files">See all files</a>
30
+ </kt-page-header>
31
+ ```
32
+
33
+ Both wrap on a narrow screen: the actions drop under the copy rather than
34
+ squeezing the title.
35
+
36
+ ## API
37
+
38
+ | Property | Attribute | Type | Default |
39
+ | ------------- | ------------- | --------------------- | -------- |
40
+ | `eyebrow` | `eyebrow` | `string` | `''` |
41
+ | `heading` | `heading` | `string` | `''` |
42
+ | `description` | `description` | `string` | `''` |
43
+ | `level` | `level` | `'page' \| 'section'` | `'page'` |
44
+
45
+ | Slot | Description |
46
+ | --------- | ------------------------------------------- |
47
+ | _default_ | Extra content under the description |
48
+ | `actions` | Buttons or a link, trailing on wide screens |
49
+
50
+ | Part | Description |
51
+ | --------- | ------------------- |
52
+ | `base` | The container |
53
+ | `heading` | The heading element |
@@ -0,0 +1,36 @@
1
+ # `<kt-pagination>`
2
+
3
+ Previous / next paging, with the position between them.
4
+
5
+ ```html
6
+ <kt-pagination page="2" total-pages="7"></kt-pagination>
7
+ ```
8
+
9
+ ```js
10
+ pager.addEventListener('kt-page-change', (e) => load(e.detail.page));
11
+ ```
12
+
13
+ `<kt-table>` uses it in its own footer; it is separate so a card list or a
14
+ gallery can page the same way a table does.
15
+
16
+ The page number is `aria-live="polite"`, so a change is announced without
17
+ interrupting whatever the reader is already working through. The element clamps
18
+ to the range: it will not emit a page below 1 or past `total-pages`.
19
+
20
+ ## API
21
+
22
+ | Property | Attribute | Type | Default |
23
+ | ------------ | ------------- | -------- | -------------- |
24
+ | `page` | `page` | `number` | `1` |
25
+ | `totalPages` | `total-pages` | `number` | `1` |
26
+ | `label` | `label` | `string` | `'Pagination'` |
27
+
28
+ | Event | Detail |
29
+ | ---------------- | ------------------ |
30
+ | `kt-page-change` | `{ page: number }` |
31
+
32
+ | Part | Description |
33
+ | ---------- | --------------------- |
34
+ | `previous` | The previous button |
35
+ | `next` | The next button |
36
+ | `info` | The "Page 2 / 7" text |
@@ -0,0 +1,66 @@
1
+ # `<kt-progress-bar>`
2
+
3
+ A determinate progress bar.
4
+
5
+ ```html
6
+ <kt-progress-bar value="64" show-value show-label label="Importing entities"></kt-progress-bar>
7
+ <kt-progress-bar value="90" variant="warning" striped animated></kt-progress-bar>
8
+ <kt-progress-bar value="4" max="7" size="small"></kt-progress-bar>
9
+ ```
10
+
11
+ ## The caption
12
+
13
+ `show-value` puts the percentage in a caption **above** the track, not inside
14
+ it. A label centred over an 8px bar is half on the fill and half off it, so it
15
+ is unreadable at one end of the range whatever colour it is given — and at
16
+ `size="small"` the 4px track clips it outright. Above the bar it reads in every
17
+ theme, at every size, at every value.
18
+
19
+ `show-label` puts `label` in the same row, on the left. Without it `label` stays
20
+ the accessible name and nothing visible changes, which is right when the copy
21
+ around the bar already says what is progressing.
22
+
23
+ Reaching `max` switches the fill to the success colour whatever the variant, so
24
+ "done" reads at a glance without the caller swapping anything.
25
+
26
+ Out-of-range values are clamped and a `max` of zero is treated as 100 — a
27
+ progress bar should never be the thing that puts `NaN%` on screen.
28
+
29
+ ## Stripes
30
+
31
+ The animated stripe is the one decorative animation in Kanto, and it earns its
32
+ place: it is how an operation says it is still working when the number is not
33
+ moving. It needs both `striped` and `animated`, and it stops under
34
+ `prefers-reduced-motion`.
35
+
36
+ ## Accessibility
37
+
38
+ `role="progressbar"` with `aria-valuenow`/`aria-valuemax`, plus an
39
+ `aria-valuetext` of `"64 %"` so it is read as a percentage rather than a bare
40
+ number. Give it a `label` saying _what_ is progressing.
41
+
42
+ ## API
43
+
44
+ | Property | Attribute | Type | Default |
45
+ | ----------- | ------------ | ------------------------------------------------------------------------ | ----------- |
46
+ | `value` | `value` | `number` | `0` |
47
+ | `max` | `max` | `number` | `100` |
48
+ | `size` | `size` | `'small' \| 'medium' \| 'large'` | `'medium'` |
49
+ | `showLabel` | `show-label` | `boolean` | `false` |
50
+ | `variant` | `variant` | `'primary' \| 'info' \| 'success' \| 'warning' \| 'danger' \| 'neutral'` | `'primary'` |
51
+ | `showValue` | `show-value` | `boolean` | `false` |
52
+ | `striped` | `striped` | `boolean` | `false` |
53
+ | `animated` | `animated` | `boolean` | `false` |
54
+ | `label` | `label` | `string` | `''` |
55
+
56
+ | Getter | Returns |
57
+ | --------- | ----------------------- |
58
+ | `percent` | Whole percentage, 0–100 |
59
+
60
+ | Part | Description |
61
+ | --------- | ----------------------- |
62
+ | `base` | The container |
63
+ | `caption` | The row above the track |
64
+ | `value` | The percentage |
65
+ | `track` | The groove |
66
+ | `fill` | The filled portion |