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,68 @@
1
+ import { X as s, User as a, Upload as i, TriangleAlert as c, Trash2 as l, Settings as f, Search as g, RefreshCw as p, Plus as u, Pencil as C, Minus as y, Menu as A, LoaderCircle as w, Info as d, Filter as h, FileUp as I, File as $, EyeOff as b, Eye as U, ExternalLink as k, Ellipsis as v, Download as E, Copy as L, CircleCheck as Z, CircleAlert as m, ChevronUp as z, ChevronRight as D, ChevronLeft as F, ChevronDown as M, Check as j, Calendar as x, ArrowUp as O, ArrowDown as P } from "lucide";
2
+ const n = /* @__PURE__ */ new Map();
3
+ function t(e) {
4
+ return e.replace(/([A-Z]+)([A-Z][a-z])/g, "$1-$2").replace(/([a-zA-Z])(\d)/g, "$1-$2").replace(/([a-z])([A-Z])/g, "$1-$2").replace(/[\s_]+/g, "-").toLowerCase();
5
+ }
6
+ function R(e, r) {
7
+ n.set(t(e), r);
8
+ }
9
+ function S(e) {
10
+ for (const [r, o] of Object.entries(e))
11
+ T(o) && R(r, o);
12
+ }
13
+ function X(e) {
14
+ return n.get(t(e));
15
+ }
16
+ function _() {
17
+ return [...n.keys()].sort();
18
+ }
19
+ function T(e) {
20
+ return Array.isArray(e) && e.every(
21
+ (r) => Array.isArray(r) && typeof r[0] == "string" && typeof r[1] == "object" && r[1] !== null
22
+ );
23
+ }
24
+ const K = {
25
+ ArrowDown: P,
26
+ ArrowUp: O,
27
+ Calendar: x,
28
+ Check: j,
29
+ ChevronDown: M,
30
+ ChevronLeft: F,
31
+ ChevronRight: D,
32
+ ChevronUp: z,
33
+ CircleAlert: m,
34
+ CircleCheck: Z,
35
+ Copy: L,
36
+ Download: E,
37
+ Ellipsis: v,
38
+ ExternalLink: k,
39
+ Eye: U,
40
+ EyeOff: b,
41
+ File: $,
42
+ FileUp: I,
43
+ Filter: h,
44
+ Info: d,
45
+ LoaderCircle: w,
46
+ Menu: A,
47
+ Minus: y,
48
+ Pencil: C,
49
+ Plus: u,
50
+ RefreshCw: p,
51
+ Search: g,
52
+ Settings: f,
53
+ Trash2: l,
54
+ TriangleAlert: c,
55
+ Upload: i,
56
+ User: a,
57
+ X: s
58
+ };
59
+ S(K);
60
+ export {
61
+ S as a,
62
+ _ as b,
63
+ K as d,
64
+ X as g,
65
+ R as r,
66
+ t
67
+ };
68
+ //# sourceMappingURL=defaults-Cpeg13O5.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defaults-Cpeg13O5.js","sources":["../../src/icons/registry.ts","../../src/icons/defaults.ts"],"sourcesContent":["/**\n * The icon registry.\n *\n * `<kt-icon>` looks icons up by name at render time, which means the set of\n * icons cannot be statically analysed and a bundler cannot tree-shake it. So\n * Kanto does not bundle all two thousand Lucide icons: the elements register\n * the handful they need themselves, and an application registers whatever else\n * it uses.\n *\n * import { Rocket, Wallet } from 'lucide';\n * import { registerIcons } from 'kanto-ds/icons';\n *\n * registerIcons({ Rocket, Wallet });\n *\n * Names are normalised to kebab-case, so `Rocket`, `rocket` and\n * `ChevronDown` / `chevron-down` all resolve.\n */\n\n/** A Lucide icon: a flat list of SVG children, each `[tag, attributes]`. */\nexport type IconNode = [tag: string, attrs: Record<string, string | number | undefined>][];\n\nconst registry = new Map<string, IconNode>();\n\n/**\n * `ChevronDown` → `chevron-down`, `Trash2` → `trash-2`, `AArrowDown` →\n * `a-arrow-down`.\n *\n * The digit boundary matters: Lucide names that icon `trash-2`, so a rule that\n * only splits on case produces `trash2` and the lookup quietly misses.\n */\nexport function toKebabCase(name: string): string {\n return name\n .replace(/([A-Z]+)([A-Z][a-z])/g, '$1-$2') // acronym run before a word\n .replace(/([a-zA-Z])(\\d)/g, '$1-$2') // letter before a digit\n .replace(/([a-z])([A-Z])/g, '$1-$2') // ordinary camel boundary\n .replace(/[\\s_]+/g, '-')\n .toLowerCase();\n}\n\n/** Registers a single icon under `name`. */\nexport function registerIcon(name: string, node: IconNode): void {\n registry.set(toKebabCase(name), node);\n}\n\n/**\n * Registers a map of icons. Accepts a Lucide namespace import directly:\n * every non-icon export (`createElement`, `createIcons`, …) is skipped.\n */\nexport function registerIcons(icons: Record<string, unknown>): void {\n for (const [name, node] of Object.entries(icons)) {\n if (isIconNode(node)) registerIcon(name, node);\n }\n}\n\n/** Returns the icon registered under `name`, or `undefined`. */\nexport function getIcon(name: string): IconNode | undefined {\n return registry.get(toKebabCase(name));\n}\n\n/** Every registered name, kebab-cased and sorted. Useful for docs and tests. */\nexport function registeredIcons(): string[] {\n return [...registry.keys()].sort();\n}\n\nfunction isIconNode(value: unknown): value is IconNode {\n return (\n Array.isArray(value) &&\n value.every(\n (child) =>\n Array.isArray(child) &&\n typeof child[0] === 'string' &&\n typeof child[1] === 'object' &&\n child[1] !== null,\n )\n );\n}\n","/**\n * The icons the design system uses in its own elements, and the few a first\n * screen reaches for — plus, pencil, settings, download, upload, an external\n * link, a filter, an ellipsis, a refresh — so a button copied from the docs\n * draws its icon without a registration step.\n *\n * Importing any Kanto element pulls this set in — roughly 3 kB of path data,\n * the price of `<kt-select>` drawing its own chevron and a first `icon=\"plus\"`\n * working. Other application icons are registered through `registerIcons`.\n */\nimport {\n ArrowDown,\n ArrowUp,\n Calendar,\n Check,\n ChevronDown,\n ChevronLeft,\n ChevronRight,\n ChevronUp,\n CircleAlert,\n CircleCheck,\n Copy,\n Download,\n Ellipsis,\n ExternalLink,\n Eye,\n EyeOff,\n File,\n FileUp,\n Filter,\n Info,\n LoaderCircle,\n Menu,\n Minus,\n Pencil,\n Plus,\n RefreshCw,\n Search,\n Settings,\n Trash2,\n TriangleAlert,\n Upload,\n User,\n X,\n} from 'lucide';\n// The sibling registry, not the `kanto-ds` barrel: the barrel pulls in every\n// element, and one of them pulls in this file — a cycle that leaves\n// `registerIcons` undefined by the time the call below runs.\nimport { registerIcons } from './registry.js';\n\n/** Icons referenced by Kanto elements, keyed by their Lucide name. */\nexport const defaultIcons = {\n ArrowDown,\n ArrowUp,\n Calendar,\n Check,\n ChevronDown,\n ChevronLeft,\n ChevronRight,\n ChevronUp,\n CircleAlert,\n CircleCheck,\n Copy,\n Download,\n Ellipsis,\n ExternalLink,\n Eye,\n EyeOff,\n File,\n FileUp,\n Filter,\n Info,\n LoaderCircle,\n Menu,\n Minus,\n Pencil,\n Plus,\n RefreshCw,\n Search,\n Settings,\n Trash2,\n TriangleAlert,\n Upload,\n User,\n X,\n};\n\nregisterIcons(defaultIcons);\n"],"names":["registry","toKebabCase","name","registerIcon","node","registerIcons","icons","isIconNode","getIcon","registeredIcons","value","child","defaultIcons","ArrowDown","ArrowUp","Calendar","Check","ChevronDown","ChevronLeft","ChevronRight","ChevronUp","CircleAlert","CircleCheck","Copy","Download","Ellipsis","ExternalLink","Eye","EyeOff","File","FileUp","Filter","Info","LoaderCircle","Menu","Minus","Pencil","Plus","RefreshCw","Search","Settings","Trash2","TriangleAlert","Upload","User","X"],"mappings":";AAqBA,MAAMA,wBAAe,IAAA;AASd,SAASC,EAAYC,GAAsB;AAChD,SAAOA,EACJ,QAAQ,yBAAyB,OAAO,EACxC,QAAQ,mBAAmB,OAAO,EAClC,QAAQ,mBAAmB,OAAO,EAClC,QAAQ,WAAW,GAAG,EACtB,YAAA;AACL;AAGO,SAASC,EAAaD,GAAcE,GAAsB;AAC/D,EAAAJ,EAAS,IAAIC,EAAYC,CAAI,GAAGE,CAAI;AACtC;AAMO,SAASC,EAAcC,GAAsC;AAClE,aAAW,CAACJ,GAAME,CAAI,KAAK,OAAO,QAAQE,CAAK;AAC7C,IAAIC,EAAWH,CAAI,KAAGD,EAAaD,GAAME,CAAI;AAEjD;AAGO,SAASI,EAAQN,GAAoC;AAC1D,SAAOF,EAAS,IAAIC,EAAYC,CAAI,CAAC;AACvC;AAGO,SAASO,IAA4B;AAC1C,SAAO,CAAC,GAAGT,EAAS,KAAA,CAAM,EAAE,KAAA;AAC9B;AAEA,SAASO,EAAWG,GAAmC;AACrD,SACE,MAAM,QAAQA,CAAK,KACnBA,EAAM;AAAA,IACJ,CAACC,MACC,MAAM,QAAQA,CAAK,KACnB,OAAOA,EAAM,CAAC,KAAM,YACpB,OAAOA,EAAM,CAAC,KAAM,YACpBA,EAAM,CAAC,MAAM;AAAA,EAAA;AAGrB;ACxBO,MAAMC,IAAe;AAAA,EAC1B,WAAAC;AAAA,EACA,SAAAC;AAAA,EACA,UAAAC;AAAA,EACA,OAAAC;AAAA,EACA,aAAAC;AAAA,EACA,aAAAC;AAAA,EACA,cAAAC;AAAA,EACA,WAAAC;AAAA,EACA,aAAAC;AAAA,EACA,aAAAC;AAAA,EACA,MAAAC;AAAA,EACA,UAAAC;AAAA,EACA,UAAAC;AAAA,EACA,cAAAC;AAAA,EACA,KAAAC;AAAA,EACA,QAAAC;AAAA,EACA,MAAAC;AAAA,EACA,QAAAC;AAAA,EACA,QAAAC;AAAA,EACA,MAAAC;AAAA,EACA,cAAAC;AAAA,EACA,MAAAC;AAAA,EACA,OAAAC;AAAA,EACA,QAAAC;AAAA,EACA,MAAAC;AAAA,EACA,WAAAC;AAAA,EACA,QAAAC;AAAA,EACA,UAAAC;AAAA,EACA,QAAAC;AAAA,EACA,eAAAC;AAAA,EACA,QAAAC;AAAA,EACA,MAAAC;AAAA,EACA,GAAAC;AACF;AAEAxC,EAAcO,CAAY;"}
@@ -3,7 +3,7 @@ import { p as r } from "./position-Cx_ZH5zh.js";
3
3
  const p = n`
4
4
  .floating {
5
5
  position: fixed;
6
- inset: auto;
6
+ inset: 0 auto auto 0;
7
7
  display: block;
8
8
  /* As wide as what it holds, wherever it is placed: a box sized from its
9
9
  position would change size when moved, and be moved again. */
@@ -60,4 +60,4 @@ export {
60
60
  d as F,
61
61
  p as f
62
62
  };
63
- //# sourceMappingURL=floating-B33g-M_1.js.map
63
+ //# sourceMappingURL=floating-C9RLa6cZ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"floating-C9RLa6cZ.js","sources":["../../src/internal/floating.ts"],"sourcesContent":["import { css, type ReactiveController, type ReactiveControllerHost } from 'lit';\nimport { placeFloating, type Align, type Side } from './position.js';\n\n/**\n * A panel that opens from a trigger — a select's list, a date picker's\n * calendar, a menu — drawn in the top layer and placed from the trigger.\n *\n * A panel positioned inside its element was cut off by any ancestor with\n * `overflow: hidden`: a table cell, a side panel, an app's scrolling main\n * area. In the top layer, nothing clips it. It opens on the side asked for, or\n * the other one when that has no room, and follows the trigger as the page\n * scrolls.\n */\n\nexport interface FloatingOptions {\n /** The panel, which carries `popover=\"manual\"` and the `floating` class. */\n readonly panel: () => HTMLElement | null | undefined;\n /** What the panel opens from. */\n readonly anchor: () => Element | null | undefined;\n /** The side it prefers. */\n readonly side?: () => Side;\n /** Which edge of the anchor it lines up with; its start by default. */\n readonly align?: () => Align;\n /** Sizes the panel to the anchor's width — a list under its field. */\n readonly matchWidth?: boolean;\n /** Told the side it opened on, for the direction of its entrance. */\n readonly onPlace?: (side: Side) => void;\n}\n\n/**\n * The panel's own styles. Closed, it keeps the display and the hidden,\n * animatable state its element gives it, so it can fade out after it leaves\n * the top layer: `display` here only undoes the popover's `display: none`, and\n * a panel's own `display` still wins.\n *\n * Until it is first placed it sits top left. With auto insets a closed panel\n * sat where the flow put it — beside a right-hand trigger, past the edge of\n * the page, which then scrolled sideways.\n */\nexport const floatingStyles = css`\n .floating {\n position: fixed;\n inset: 0 auto auto 0;\n display: block;\n /* As wide as what it holds, wherever it is placed: a box sized from its\n position would change size when moved, and be moved again. */\n width: max-content;\n margin: 0;\n color: inherit;\n /* Set apart from the page by the theme: a lighter surface in the dark,\n an edge and a soft shadow in the light. */\n border: var(--border-width) solid var(--border-popover);\n box-shadow: var(--shadow-popover);\n }\n`;\n\nexport class FloatingController implements ReactiveController {\n private open = false;\n private frame = 0;\n /** Places the panel again when its size changes — content drawn after it opened. */\n private resizeObserver: ResizeObserver | undefined;\n\n constructor(\n host: ReactiveControllerHost,\n private readonly options: FloatingOptions,\n ) {\n host.addController(this);\n }\n\n /**\n * Call from the host's `updated()` with whether the panel is open. While it\n * is, every update re-places it: its content — a filtered list — can change\n * its size.\n */\n sync(open: boolean): void {\n // First, and whatever the panel: one rendered only while open is already\n // gone when it closes, and its listeners must go with it.\n if (open !== this.open) {\n this.open = open;\n const method = open ? 'addEventListener' : 'removeEventListener';\n window[method]('scroll', this.follow, { capture: true, passive: true });\n window[method]('resize', this.follow);\n }\n\n const panel = this.options.panel();\n // Where popovers are missing — a test DOM — the panel stays where it is.\n if (!panel || typeof panel.showPopover !== 'function') return;\n\n if (open) {\n if (!panel.matches(':popover-open')) panel.showPopover();\n this.place();\n // A panel measured at opening may grow once what it holds has drawn —\n // a calendar is an element of its own — and must be placed again.\n if (!this.resizeObserver && typeof ResizeObserver !== 'undefined') {\n this.resizeObserver = new ResizeObserver(this.follow);\n this.resizeObserver.observe(panel);\n }\n } else {\n this.unobserve();\n if (panel.matches(':popover-open')) panel.hidePopover();\n }\n }\n\n private unobserve(): void {\n this.resizeObserver?.disconnect();\n this.resizeObserver = undefined;\n }\n\n hostDisconnected(): void {\n this.sync(false);\n this.unobserve();\n cancelAnimationFrame(this.frame);\n }\n\n private follow = (): void => {\n cancelAnimationFrame(this.frame);\n this.frame = requestAnimationFrame(() => this.place());\n };\n\n private place(): void {\n const panel = this.options.panel();\n const anchor = this.options.anchor()?.getBoundingClientRect();\n if (!panel || !anchor) return;\n\n if (this.options.matchWidth) panel.style.width = `${anchor.width}px`;\n // Untransformed size: the entrance animation scales and slides the panel.\n const { x, y, placement } = placeFloating(\n anchor,\n { width: panel.offsetWidth, height: panel.offsetHeight },\n { width: window.innerWidth, height: window.innerHeight },\n this.options.side?.() ?? 'bottom',\n this.options.align?.() ?? 'start',\n );\n panel.style.left = `${x}px`;\n panel.style.top = `${y}px`;\n this.options.onPlace?.(placement);\n }\n}\n"],"names":["floatingStyles","css","FloatingController","host","options","open","method","panel","anchor","x","y","placement","placeFloating"],"mappings":";;AAuCO,MAAMA,IAAiBC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAiBvB,MAAMC,EAAiD;AAAA,EAM5D,YACEC,GACiBC,GACjB;AADiB,SAAA,UAAAA,GAPnB,KAAQ,OAAO,IACf,KAAQ,QAAQ,GAwDhB,KAAQ,SAAS,MAAY;AAC3B,2BAAqB,KAAK,KAAK,GAC/B,KAAK,QAAQ,sBAAsB,MAAM,KAAK,OAAO;AAAA,IACvD,GAnDED,EAAK,cAAc,IAAI;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,KAAKE,GAAqB;AAGxB,QAAIA,MAAS,KAAK,MAAM;AACtB,WAAK,OAAOA;AACZ,YAAMC,IAASD,IAAO,qBAAqB;AAC3C,aAAOC,CAAM,EAAE,UAAU,KAAK,QAAQ,EAAE,SAAS,IAAM,SAAS,IAAM,GACtE,OAAOA,CAAM,EAAE,UAAU,KAAK,MAAM;AAAA,IACtC;AAEA,UAAMC,IAAQ,KAAK,QAAQ,MAAA;AAE3B,IAAI,CAACA,KAAS,OAAOA,EAAM,eAAgB,eAEvCF,KACGE,EAAM,QAAQ,eAAe,OAAS,YAAA,GAC3C,KAAK,MAAA,GAGD,CAAC,KAAK,kBAAkB,OAAO,iBAAmB,QACpD,KAAK,iBAAiB,IAAI,eAAe,KAAK,MAAM,GACpD,KAAK,eAAe,QAAQA,CAAK,OAGnC,KAAK,UAAA,GACDA,EAAM,QAAQ,eAAe,OAAS,YAAA;AAAA,EAE9C;AAAA,EAEQ,YAAkB;AACxB,SAAK,gBAAgB,WAAA,GACrB,KAAK,iBAAiB;AAAA,EACxB;AAAA,EAEA,mBAAyB;AACvB,SAAK,KAAK,EAAK,GACf,KAAK,UAAA,GACL,qBAAqB,KAAK,KAAK;AAAA,EACjC;AAAA,EAOQ,QAAc;AACpB,UAAMA,IAAQ,KAAK,QAAQ,MAAA,GACrBC,IAAS,KAAK,QAAQ,OAAA,GAAU,sBAAA;AACtC,QAAI,CAACD,KAAS,CAACC,EAAQ;AAEvB,IAAI,KAAK,QAAQ,eAAYD,EAAM,MAAM,QAAQ,GAAGC,EAAO,KAAK;AAEhE,UAAM,EAAE,GAAAC,GAAG,GAAAC,GAAG,WAAAC,EAAA,IAAcC;AAAA,MAC1BJ;AAAA,MACA,EAAE,OAAOD,EAAM,aAAa,QAAQA,EAAM,aAAA;AAAA,MAC1C,EAAE,OAAO,OAAO,YAAY,QAAQ,OAAO,YAAA;AAAA,MAC3C,KAAK,QAAQ,OAAA,KAAY;AAAA,MACzB,KAAK,QAAQ,aAAa;AAAA,IAAA;AAE5B,IAAAA,EAAM,MAAM,OAAO,GAAGE,CAAC,MACvBF,EAAM,MAAM,MAAM,GAAGG,CAAC,MACtB,KAAK,QAAQ,UAAUC,CAAS;AAAA,EAClC;AACF;"}
@@ -1,7 +1,7 @@
1
1
  import { css as f, html as c, nothing as l } from "lit";
2
2
  import { property as n } from "lit/decorators.js";
3
3
  import { K as m, d as u } from "../../../chunks/kt-element-T0VbIBNp.js";
4
- import { g as v } from "../../../chunks/defaults-CFrjqae2.js";
4
+ import { g as v } from "../../../chunks/defaults-Cpeg13O5.js";
5
5
  var w = Object.defineProperty, o = (i, e, r, h) => {
6
6
  for (var t = void 0, a = i.length - 1, d; a >= 0; a--)
7
7
  (d = i[a]) && (t = d(e, r, t) || t);
@@ -3,7 +3,7 @@ import { property as w, state as x, query as $ } from "lit/decorators.js";
3
3
  import { classMap as C } from "lit/directives/class-map.js";
4
4
  import { d as P } from "../../../chunks/kt-element-T0VbIBNp.js";
5
5
  import { u as S, t as I } from "../../../chunks/events-r51msXNf.js";
6
- import { F as q, f as E } from "../../../chunks/floating-B33g-M_1.js";
6
+ import { F as q, f as E } from "../../../chunks/floating-C9RLa6cZ.js";
7
7
  import { K as f } from "../../../chunks/segmented-field-BBXR4XFL.js";
8
8
  import { d as y, p as u, t as v, c as b, f as Y } from "../../../chunks/kt-calendar-CtwOby1R.js";
9
9
  import { d as K, e as L, t as M } from "../../../chunks/typed-date-CPAUNHzr.js";
@@ -5,7 +5,7 @@ import { live as O } from "lit/directives/live.js";
5
5
  import { K as D, d as R } from "../../../chunks/kt-element-T0VbIBNp.js";
6
6
  import { u as q, t as E, e as v } from "../../../chunks/events-r51msXNf.js";
7
7
  import { a as B, s as F, b as L } from "../../../chunks/form-control-B74_EHtG.js";
8
- import { F as z, f as K } from "../../../chunks/floating-B33g-M_1.js";
8
+ import { F as z, f as K } from "../../../chunks/floating-C9RLa6cZ.js";
9
9
  import { r as M, d as _ } from "../../../chunks/locale-C5xLkGx5.js";
10
10
  import { s as f } from "../../../chunks/index-TWJzttLX.js";
11
11
  import { a as P, p as u, c as g, b as w, t as m, e as k, f as C, g as V, h as I, d as W } from "../../../chunks/kt-calendar-CtwOby1R.js";
@@ -5,7 +5,7 @@ import { live as w } from "lit/directives/live.js";
5
5
  import { K as g, d as $ } from "../../../chunks/kt-element-T0VbIBNp.js";
6
6
  import { t as C, e as c } from "../../../chunks/events-r51msXNf.js";
7
7
  import { a as S, s as b, b as E } from "../../../chunks/form-control-B74_EHtG.js";
8
- import { F as O, f as z } from "../../../chunks/floating-B33g-M_1.js";
8
+ import { F as O, f as z } from "../../../chunks/floating-C9RLa6cZ.js";
9
9
  import { s as u } from "../../../chunks/index-TWJzttLX.js";
10
10
  import "../../core/kt-icon/kt-icon.js";
11
11
  import "../../feedback/kt-tooltip/kt-tooltip.js";
@@ -6,7 +6,7 @@ import { K as m, d as $ } from "../../../chunks/kt-element-T0VbIBNp.js";
6
6
  import { u as I, t as y, e as f } from "../../../chunks/events-r51msXNf.js";
7
7
  import { a as D, s as q, b as O } from "../../../chunks/form-control-B74_EHtG.js";
8
8
  import { a as S, b as C, o as g, f as v, l as P, n as E } from "../../../chunks/listbox-LsUEyGhQ.js";
9
- import { F as L, f as A } from "../../../chunks/floating-B33g-M_1.js";
9
+ import { F as L, f as A } from "../../../chunks/floating-C9RLa6cZ.js";
10
10
  import { s as l } from "../../../chunks/index-TWJzttLX.js";
11
11
  import "../../core/kt-icon/kt-icon.js";
12
12
  var V = Object.defineProperty, i = (u, t, o, r) => {
@@ -6,7 +6,7 @@ import { K as $, d as P } from "../../../chunks/kt-element-T0VbIBNp.js";
6
6
  import { u as C, t as I, e as D } from "../../../chunks/events-r51msXNf.js";
7
7
  import { a as L, s as z, b as B } from "../../../chunks/form-control-B74_EHtG.js";
8
8
  import { a as T, b as F, f as y, l as V, n as _, o as w } from "../../../chunks/listbox-LsUEyGhQ.js";
9
- import { F as A, f as W } from "../../../chunks/floating-B33g-M_1.js";
9
+ import { F as A, f as W } from "../../../chunks/floating-C9RLa6cZ.js";
10
10
  import { s as p } from "../../../chunks/index-TWJzttLX.js";
11
11
  import "../../core/kt-icon/kt-icon.js";
12
12
  var K = Object.defineProperty, r = (m, t, i, e) => {
@@ -5,7 +5,7 @@ import { K as v, d as w } from "../../../chunks/kt-element-T0VbIBNp.js";
5
5
  import { u as k, t as g, e as m } from "../../../chunks/events-r51msXNf.js";
6
6
  import { a as $, s as I, b as D } from "../../../chunks/form-control-B74_EHtG.js";
7
7
  import { a as S, f as b, l as L, n as C, o as y } from "../../../chunks/listbox-LsUEyGhQ.js";
8
- import { F as E, f as P } from "../../../chunks/floating-B33g-M_1.js";
8
+ import { F as E, f as P } from "../../../chunks/floating-C9RLa6cZ.js";
9
9
  import { s as l } from "../../../chunks/index-TWJzttLX.js";
10
10
  import "../../core/kt-icon/kt-icon.js";
11
11
  var q = Object.defineProperty, i = (c, e, o, a) => {
@@ -4,7 +4,7 @@ import { classMap as u } from "lit/directives/class-map.js";
4
4
  import { K as c, d as b } from "../../../chunks/kt-element-T0VbIBNp.js";
5
5
  import { t as m, e as d } from "../../../chunks/events-r51msXNf.js";
6
6
  import { o as x } from "../../../chunks/listbox-LsUEyGhQ.js";
7
- import { F as w, f as k } from "../../../chunks/floating-B33g-M_1.js";
7
+ import { F as w, f as k } from "../../../chunks/floating-C9RLa6cZ.js";
8
8
  import { s as $ } from "../../../chunks/index-TWJzttLX.js";
9
9
  var P = Object.defineProperty, r = (i, t, s, a) => {
10
10
  for (var o = void 0, l = i.length - 1, g; l >= 0; l--)
@@ -0,0 +1,52 @@
1
+ # `<kt-alert>`
2
+
3
+ A message that stays on the page.
4
+
5
+ ```html
6
+ <kt-alert
7
+ variant="warning"
8
+ heading="Storage almost full"
9
+ description="You are using 88% of your quota."
10
+ dismissible
11
+ >
12
+ <kt-button slot="actions" size="small" variant="warning">Upgrade</kt-button>
13
+ </kt-alert>
14
+ ```
15
+
16
+ ## Against a toast
17
+
18
+ A **toast** is transient and interrupts; it belongs to the moment. An **alert**
19
+ sits in the layout and waits; it belongs to the page.
20
+
21
+ A form that failed to save, a trial about to expire, a consent notice — all
22
+ alerts. "Saved", "Copied", "3 rows deleted" — all toasts. Getting this backwards
23
+ is how important messages disappear after four seconds.
24
+
25
+ ## Colour and urgency
26
+
27
+ The whole block takes the variant's tint and border, so the message's
28
+ temperature is legible before a word is read. `danger` is `aria-live="assertive"`
29
+ and interrupts the screen reader; everything else is polite and waits its turn.
30
+
31
+ `dismissible` adds the button and fires `kt-close` — **removing the alert is
32
+ yours**, because only the caller knows whether it should come back.
33
+
34
+ ## API
35
+
36
+ | Property | Attribute | Type | Default |
37
+ | ------------- | ------------- | ----------------------------------------------------------- | -------- |
38
+ | `variant` | `variant` | `'info' \| 'success' \| 'warning' \| 'danger' \| 'neutral'` | `'info'` |
39
+ | `heading` | `heading` | `string` | `''` |
40
+ | `description` | `description` | `string` | `''` |
41
+ | `dismissible` | `dismissible` | `boolean` | `false` |
42
+ | `noIcon` | `no-icon` | `boolean` | `false` |
43
+ | `icon` | `icon` | `string` | `''` |
44
+
45
+ | Slot | Description |
46
+ | --------- | ------------------------------------ |
47
+ | _default_ | Body, when `description` is not used |
48
+ | `actions` | Buttons, at the end |
49
+
50
+ | Event | Description |
51
+ | ---------- | ------------------------------ |
52
+ | `kt-close` | The dismiss button was pressed |
@@ -0,0 +1,46 @@
1
+ # `<kt-avatar>`
2
+
3
+ A person or an organisation, as a picture or as their initials.
4
+
5
+ ```html
6
+ <kt-avatar name="Benjamin Canac"></kt-avatar>
7
+ <kt-avatar name="Emma Davis" src="/emma.jpg" status="online"></kt-avatar>
8
+ <kt-avatar name="Kanto Studio" square size="large"></kt-avatar>
9
+ ```
10
+
11
+ ## The fallback is the main path
12
+
13
+ Most rows in most products have no photo, so the initials are what actually
14
+ renders. They are derived from the name — first and last word, or the first two
15
+ letters of a single word — and an email address is read as one, so
16
+ `emma.davis@example.com` becomes `ED`.
17
+
18
+ The colour is picked from the name, which means **the same person is the same
19
+ colour on every screen**. That is not decoration: it lets the eye find a row
20
+ before it has read it. Override it with `color` when the entity has a brand of
21
+ its own.
22
+
23
+ A photo that fails to load falls back to the initials rather than to a broken
24
+ image. Changing `src` clears that failure, so a retry is a property assignment.
25
+
26
+ ## API
27
+
28
+ | Property | Attribute | Type | Default |
29
+ | -------- | --------- | ------------------------------------------- | ---------- |
30
+ | `name` | `name` | `string` | `''` |
31
+ | `src` | `src` | `string` | `''` |
32
+ | `size` | `size` | `'small' \| 'medium' \| 'large'` | `'medium'` |
33
+ | `square` | `square` | `boolean` | `false` |
34
+ | `status` | `status` | `'online' \| 'away' \| 'busy' \| 'offline'` | — |
35
+ | `color` | `color` | `string` | `''` |
36
+
37
+ Sizes are 24 / 32 / 48px, and everything inside scales from
38
+ `--kt-avatar-size`, so overriding that one property resizes the whole thing.
39
+
40
+ | Part | Description |
41
+ | ------- | ----------- |
42
+ | `base` | The circle |
43
+ | `image` | The `<img>` |
44
+
45
+ `initialsOf(name)` is exported too, for the cases where you need the same
46
+ letters somewhere else.
@@ -0,0 +1,104 @@
1
+ # `<kt-badge>`
2
+
3
+ A compact label: a pill `tag`, a monospaced `code` token, a `category` tinted
4
+ with a colour of your choosing, or a `count`.
5
+
6
+ ```html
7
+ <kt-badge>Design systems</kt-badge>
8
+ <kt-badge tone="success">Active</kt-badge>
9
+ <kt-badge variant="code">--surface-card</kt-badge>
10
+ <kt-badge variant="category" color="#3987e5">Infrastructure</kt-badge>
11
+ <kt-badge variant="count" tone="danger" max="99">128</kt-badge>
12
+ <kt-badge icon="user" removable>Assigned to me</kt-badge>
13
+ ```
14
+
15
+ ## Shape and colour are separate
16
+
17
+ `variant` is the **shape** — what kind of thing this is. `tone` is the
18
+ **colour** — what it means. They are separate properties because they answer
19
+ different questions: a tag can be neutral or dangerous, and so can a count.
20
+ Folding them into one list would give you nine variants and still no way to say
21
+ "a tag, but this one is a warning".
22
+
23
+ | `variant` | What it is |
24
+ | ---------- | ---------------------------------------------------- |
25
+ | `tag` | The default. A pill: a label, a filter, a topic. |
26
+ | `code` | A monospaced token — a property name, a flag, a key. |
27
+ | `category` | Tinted with `color`, for a palette the data owns. |
28
+ | `count` | A number pinned to something else. |
29
+
30
+ `count` is the one shape with no border. A number on a nav item is a mark on
31
+ the thing it counts, not a label with an outline of its own — and at that size
32
+ a border is most of what you see.
33
+
34
+ `tone` uses the system's tinted pair throughout: the colour behind at low
35
+ opacity, the colour itself as the ink. The same pair a secondary button, a
36
+ danger button and an avatar wear, so a badge beside any of them reads as one
37
+ family.
38
+
39
+ ## Categories
40
+
41
+ `category` takes any CSS colour and fills at 20% of it:
42
+
43
+ ```html
44
+ <kt-badge variant="category" color="var(--color-success-base)">Delivered</kt-badge>
45
+ <kt-badge variant="category" color="rgb(57, 135, 229)">Infrastructure</kt-badge>
46
+ ```
47
+
48
+ The mixing is `color-mix`, which is honest for every notation. The build this
49
+ replaced concatenated a `"33"` onto the string and produced garbage for
50
+ `rgb()` and for named colours.
51
+
52
+ Reach for `tone` when the meaning is one of the system's six states, and
53
+ `category` when the palette belongs to the data — a project colour, a label the
54
+ user picked.
55
+
56
+ ## Counting
57
+
58
+ `max` caps a numeric label: `max="99"` on `128` shows `99+`. A non-numeric
59
+ label is left alone, so `max` is safe to set from a template that does not know
60
+ what it will be given.
61
+
62
+ ## Interaction
63
+
64
+ Neither `clickable` nor `removable` is on by default, because most badges are
65
+ labels and a label that looks pressable is a lie.
66
+
67
+ `clickable` makes it a real control — `role="button"`, a tab stop, Enter and
68
+ Space — and fires `kt-badge-click`. `removable` adds a remove button that fires
69
+ a cancelable `kt-remove`; cancel it and remove the badge yourself once the
70
+ server agrees. A badge that is both does not report the click when the remove
71
+ button is what was pressed.
72
+
73
+ ## API
74
+
75
+ | Property | Attribute | Type | Default |
76
+ | ----------- | ----------- | ------------------------------------------------------------------------ | ----------- |
77
+ | `variant` | `variant` | `'tag' \| 'code' \| 'category' \| 'count'` | `'tag'` |
78
+ | `tone` | `tone` | `'neutral' \| 'primary' \| 'success' \| 'warning' \| 'danger' \| 'info'` | `'neutral'` |
79
+ | `size` | `size` | `'small' \| 'medium'` | `'medium'` |
80
+ | `label` | `label` | `string` | `''` |
81
+ | `color` | `color` | `string` — any CSS colour, for `category` | `''` |
82
+ | `icon` | `icon` | `string` — lucide name, before the text | `''` |
83
+ | `clickable` | `clickable` | `boolean` | `false` |
84
+ | `removable` | `removable` | `boolean` | `false` |
85
+ | `max` | `max` | `number` | — |
86
+ | `error` | `error` | `boolean` — shorthand for `tone="danger"` | `false` |
87
+
88
+ | Event | Detail |
89
+ | ---------------- | ------------ |
90
+ | `kt-badge-click` | — cancelable |
91
+ | `kt-remove` | — cancelable |
92
+
93
+ | Slot | Description |
94
+ | --------- | -------------------------------- |
95
+ | _default_ | The text. Falls back to `label`. |
96
+
97
+ | CSS property | Description |
98
+ | ------------------ | ------------------------------------ |
99
+ | `--kt-badge-color` | Category hue; the fill is it at 20%. |
100
+
101
+ | Part | Description |
102
+ | -------- | ----------------------------------- |
103
+ | `base` | The container |
104
+ | `remove` | The remove button, when `removable` |
@@ -0,0 +1,53 @@
1
+ # `<kt-breadcrumb>`
2
+
3
+ A trail showing where the current page sits.
4
+
5
+ ```js
6
+ trail.items = [
7
+ { label: 'Home', href: '/' },
8
+ { label: 'Entities', href: '/entites' },
9
+ { label: 'Entity 4812' },
10
+ ];
11
+ ```
12
+
13
+ The last entry is the current page: rendered as text with `aria-current="page"`,
14
+ not as a link. A breadcrumb whose final item links to the page you are already
15
+ on is a dead control.
16
+
17
+ ## Client-side routing
18
+
19
+ `kt-navigate` is cancelable. Cancel it and the browser will not follow the
20
+ href; leave it alone and the link behaves like a link.
21
+
22
+ ```js
23
+ trail.addEventListener('kt-navigate', (e) => {
24
+ e.preventDefault();
25
+ router.push(e.detail.item.href);
26
+ });
27
+ ```
28
+
29
+ Modified clicks — ⌘, Ctrl, Shift, middle button — are never intercepted. The
30
+ user is asking the browser for a new tab, and the element stays out of it.
31
+
32
+ ## API
33
+
34
+ | Property | Attribute | Type | Default |
35
+ | -------- | --------- | -------------------- | -------------- |
36
+ | `items` | — | `KtBreadcrumbItem[]` | `[]` |
37
+ | `label` | `label` | `string` | `"Breadcrumb"` |
38
+
39
+ ```ts
40
+ interface KtBreadcrumbItem {
41
+ label: string;
42
+ href?: string;
43
+ }
44
+ ```
45
+
46
+ | Event | Detail |
47
+ | ------------- | ------------------------------ |
48
+ | `kt-navigate` | `{ item, index }` — cancelable |
49
+
50
+ | Part | Description |
51
+ | ------ | ------------- |
52
+ | `nav` | The `<nav>` |
53
+ | `item` | A trail entry |
@@ -0,0 +1,72 @@
1
+ # `<kt-button>`
2
+
3
+ The Kanto action button. Ten variants, three sizes, an optional Lucide icon.
4
+
5
+ ```html
6
+ <kt-button icon="plus">New entity</kt-button>
7
+ <kt-button variant="secondary" icon="refresh-cw">Refresh</kt-button>
8
+ <kt-button variant="danger">Delete</kt-button>
9
+ <kt-button variant="text">View full history</kt-button>
10
+ ```
11
+
12
+ ## Choosing a variant
13
+
14
+ | Variant | Use for |
15
+ | -------------------------- | --------------------------------------------------------------- |
16
+ | `primary` | The one action a screen is about. At most one per view. |
17
+ | `secondary` | Supporting actions. Soft indigo tint. |
18
+ | `secondary-no-bg` | The same, with no resting background — for toolbars. |
19
+ | `dark` | Neutral actions that must not compete for attention. |
20
+ | `danger` | Destructive, but recoverable. Tinted red. |
21
+ | `delete` | Irreversible. The only solid red in the system — use sparingly. |
22
+ | `warning` `info` `success` | Status-carrying actions inside a matching context. |
23
+ | `text` | Inline, link-like. No padding, no height. |
24
+
25
+ ## Icon-only
26
+
27
+ An `icon` with no slotted text collapses the button to a square. It then has no
28
+ accessible name of its own, so `label` is required:
29
+
30
+ ```html
31
+ <kt-button icon="x" label="Close" variant="secondary-no-bg"></kt-button>
32
+ ```
33
+
34
+ ## Forms
35
+
36
+ A button inside a shadow root is invisible to the enclosing form, so
37
+ `type="submit"` would normally do nothing. `<kt-button>` bridges that: it runs
38
+ the form's constraint validation, fires a real `submit` event, and carries
39
+ `name` / `value` into the submission.
40
+
41
+ ```html
42
+ <form>
43
+ <kt-input name="email" type="email" required></kt-input>
44
+ <kt-button type="submit">Send</kt-button>
45
+ </form>
46
+ ```
47
+
48
+ ## API
49
+
50
+ | Property | Attribute | Type | Default |
51
+ | -------------- | --------------- | --------------------------------- | ----------- |
52
+ | `variant` | `variant` | see table above | `'primary'` |
53
+ | `size` | `size` | `'small' \| 'medium' \| 'large'` | `'medium'` |
54
+ | `disabled` | `disabled` | `boolean` | `false` |
55
+ | `type` | `type` | `'button' \| 'submit' \| 'reset'` | `'button'` |
56
+ | `icon` | `icon` | `string` | `''` |
57
+ | `iconPosition` | `icon-position` | `'left' \| 'right'` | `'left'` |
58
+ | `label` | `label` | `string` | `''` |
59
+ | `fullWidth` | `full-width` | `boolean` | `false` |
60
+ | `name` | `name` | `string` | `''` |
61
+ | `value` | `value` | `string` | `''` |
62
+
63
+ | Slot | Description |
64
+ | --------- | ---------------- |
65
+ | _default_ | The button label |
66
+
67
+ | Part | Description |
68
+ | -------- | --------------------- |
69
+ | `button` | The native `<button>` |
70
+ | `icon` | The icon, when set |
71
+
72
+ Emits no custom events — listen for `click` as on any button.
@@ -0,0 +1,98 @@
1
+ # `<kt-calendar>`
2
+
3
+ A month calendar to pick a day or a period from, shown in the page.
4
+
5
+ ```html
6
+ <kt-calendar value="2026-09-25"></kt-calendar>
7
+ <kt-calendar range value="2026-09-07/2026-09-18" min="2026-01-01"></kt-calendar>
8
+ ```
9
+
10
+ It is the calendar `<kt-date-picker>` opens in its popup. Use it on its own
11
+ when the calendar _is_ the interface — a booking page, a dashboard's date
12
+ filter, a planning view — and `<kt-date-picker>` when it is only a way to fill
13
+ a field.
14
+
15
+ ## A month and a year in two clicks
16
+
17
+ The title — "September 2026" — opens one panel: a scrolling list of years
18
+ beside the twelve months of the one chosen. Pick the year, then the month, and
19
+ the days are back, the day of the month kept: from 25 September, choosing March
20
+ 1987 lands on 25 March 1987. The list opens on the chosen year, centred, and
21
+ reaches from `min` to `max` — or, without them, 120 years back and 50 ahead,
22
+ birthdays to plans. Typing four digits in the list jumps to that year. Months
23
+ wholly outside `min`/`max` are disabled.
24
+
25
+ The arrows beside the title page a month at a time; they are the only thing
26
+ that does.
27
+
28
+ ## Two months
29
+
30
+ `months="2"` shows two months side by side — what `<kt-date-picker range>`
31
+ does, so a period across a month's end is two clicks. Each grid holds its own
32
+ days only, so none shows twice, and the keyboard moves across both, paging
33
+ only once it leaves the second.
34
+
35
+ ## Keyboard
36
+
37
+ In the days, the WAI-ARIA date grid pattern:
38
+
39
+ | Key | Does |
40
+ | ---------------------------- | ------------------------------- |
41
+ | `←` / `→` | Previous / next day |
42
+ | `↑` / `↓` | Same day, previous / next week |
43
+ | `Home` / `End` | First / last day of the week |
44
+ | `Page Up` / `Page Down` | Same day, previous / next month |
45
+ | `Shift` + `Page Up` / `Down` | Same day, previous / next year |
46
+ | `Enter` / `Space` | Choose the day in focus |
47
+
48
+ In the year list:
49
+
50
+ | Key | Does |
51
+ | ----------------------- | -------------------------------------------------------------------- |
52
+ | `↑` / `↓` | Previous / next year |
53
+ | `Page Up` / `Page Down` | Ten years back / ahead |
54
+ | `Home` / `End` | First / last year offered |
55
+ | Four digits | That year |
56
+ | `Enter`, `Tab` | On to the months |
57
+ | `Escape` | Back to the days — and no further, so an enclosing dialog stays open |
58
+
59
+ In the months: the arrows, three to a row; `Page Up`/`Page Down` by year;
60
+ `Enter` to choose; `Escape` back to the days.
61
+
62
+ Focus follows every switch, so the keys keep working without a click in
63
+ between.
64
+
65
+ ## ISO values and locales
66
+
67
+ As for `<kt-date-picker>`: the value is ISO 8601 text — `2026-09-25`, or
68
+ `2026-09-07/2026-09-18` with `range` — and names, the first day of the week
69
+ and every accessible label follow `locale`, else the page's `lang`, else the
70
+ browser's language. A period takes two picks in either order and fires
71
+ `kt-change` once, with the whole period.
72
+
73
+ `<kt-calendar>` is not a form control; inside a form, use `<kt-date-picker>`.
74
+
75
+ ## API
76
+
77
+ | Property | Attribute | Type | Default |
78
+ | -------- | --------- | ---------------- | ------------------ |
79
+ | `value` | `value` | `string \| null` | `null` |
80
+ | `range` | `range` | `boolean` | `false` |
81
+ | `min` | `min` | `string` | `''` |
82
+ | `max` | `max` | `string` | `''` |
83
+ | `months` | `months` | `1 \| 2` | `1` |
84
+ | `locale` | `locale` | `string` | page, then browser |
85
+ | `label` | `label` | `string` | from `setStrings` |
86
+
87
+ | Method | Description |
88
+ | --------- | ------------------------------------------ |
89
+ | `focus()` | Focuses the current day — chosen, or today |
90
+
91
+ | Event | Detail |
92
+ | ----------- | ------------------------------------------ |
93
+ | `kt-change` | `{ value: string }` — ISO date or interval |
94
+
95
+ | Part | Description |
96
+ | ------ | ------------ |
97
+ | `base` | The calendar |
98
+ | `day` | A day cell |