@lo-tek/interfaces 0.39.0 → 0.40.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 (148) hide show
  1. package/CHANGELOG.md +100 -0
  2. package/README.md +1 -1
  3. package/agent/astro.md +9 -9
  4. package/agent/bundles.md +11 -11
  5. package/agent/datastar.md +65 -65
  6. package/agent/html.md +59 -59
  7. package/agent/react.md +11 -11
  8. package/agent/templates.md +65 -58
  9. package/core/lotek-core.wasm +0 -0
  10. package/core/lotek.wasm +0 -0
  11. package/dist/components/css/accordion/accordion.d.ts +1 -1
  12. package/dist/components/css/accordion/accordion.js +1 -1
  13. package/dist/components/css/alert/alert.d.ts +1 -1
  14. package/dist/components/css/alert/alert.js +1 -1
  15. package/dist/components/css/avatar/avatar.d.ts +1 -1
  16. package/dist/components/css/avatar/avatar.js +1 -1
  17. package/dist/components/css/badge/badge.d.ts +1 -1
  18. package/dist/components/css/badge/badge.js +1 -1
  19. package/dist/components/css/breadcrumbs/breadcrumbs.d.ts +1 -1
  20. package/dist/components/css/breadcrumbs/breadcrumbs.js +1 -1
  21. package/dist/components/css/card/card.d.ts +1 -1
  22. package/dist/components/css/card/card.js +1 -1
  23. package/dist/components/css/file-upload/file-upload.d.ts +1 -1
  24. package/dist/components/css/file-upload/file-upload.js +1 -1
  25. package/dist/components/css/icon/icon.d.ts +1 -1
  26. package/dist/components/css/icon/icon.js +1 -1
  27. package/dist/components/css/label/label.d.ts +1 -1
  28. package/dist/components/css/label/label.js +1 -1
  29. package/dist/components/css/link/link.d.ts +1 -1
  30. package/dist/components/css/link/link.js +1 -1
  31. package/dist/components/css/spinner/spinner.d.ts +7 -5
  32. package/dist/components/css/spinner/spinner.d.ts.map +1 -1
  33. package/dist/components/css/spinner/spinner.js +7 -5
  34. package/dist/components/css/spinner/spinner.js.map +1 -1
  35. package/dist/components/css/tag/tag.d.ts +1 -1
  36. package/dist/components/css/tag/tag.js +1 -1
  37. package/dist/components/css/toggle-group/toggle-group.d.ts +1 -1
  38. package/dist/components/css/toggle-group/toggle-group.js +1 -1
  39. package/dist/components/css/tooltip/tooltip.d.ts +2 -2
  40. package/dist/components/css/tooltip/tooltip.js +2 -2
  41. package/dist/components/frame/dialog/dialog.d.ts +11 -6
  42. package/dist/components/frame/dialog/dialog.d.ts.map +1 -1
  43. package/dist/components/frame/dialog/dialog.js +4 -4
  44. package/dist/components/frame/dialog/dialog.js.map +1 -1
  45. package/dist/components/frame/dialog/lt-dialog.d.ts +1 -1
  46. package/dist/components/frame/dialog/lt-dialog.js +7 -7
  47. package/dist/components/frame/error-summary/error-summary.d.ts +6 -2
  48. package/dist/components/frame/error-summary/error-summary.d.ts.map +1 -1
  49. package/dist/components/frame/error-summary/error-summary.js +1 -1
  50. package/dist/components/frame/error-summary/error-summary.js.map +1 -1
  51. package/dist/components/frame/error-summary/lt-error-summary.d.ts +1 -1
  52. package/dist/components/frame/error-summary/lt-error-summary.js +6 -6
  53. package/dist/components/frame/field/field-core.d.ts +4 -2
  54. package/dist/components/frame/field/field-core.d.ts.map +1 -1
  55. package/dist/components/frame/field/field-core.js.map +1 -1
  56. package/dist/components/frame/field/lt-field.d.ts +5 -4
  57. package/dist/components/frame/field/lt-field.d.ts.map +1 -1
  58. package/dist/components/frame/field/lt-field.js +5 -4
  59. package/dist/components/frame/field/lt-field.js.map +1 -1
  60. package/dist/components/frame/popover/lt-popover.d.ts +3 -3
  61. package/dist/components/frame/popover/lt-popover.js +3 -3
  62. package/dist/components/frame/popover/popover.d.ts +7 -3
  63. package/dist/components/frame/popover/popover.d.ts.map +1 -1
  64. package/dist/components/frame/popover/popover.js +2 -2
  65. package/dist/components/frame/popover/popover.js.map +1 -1
  66. package/dist/components/frame/session-timeout/lt-session-timeout.d.ts +8 -8
  67. package/dist/components/frame/session-timeout/lt-session-timeout.js +7 -7
  68. package/dist/components/frame/session-timeout/session-timeout.d.ts +10 -5
  69. package/dist/components/frame/session-timeout/session-timeout.d.ts.map +1 -1
  70. package/dist/components/frame/session-timeout/session-timeout.js +4 -4
  71. package/dist/components/frame/session-timeout/session-timeout.js.map +1 -1
  72. package/dist/components/frame/suggestion/lt-suggestion.d.ts +12 -11
  73. package/dist/components/frame/suggestion/lt-suggestion.d.ts.map +1 -1
  74. package/dist/components/frame/suggestion/lt-suggestion.js +11 -10
  75. package/dist/components/frame/suggestion/lt-suggestion.js.map +1 -1
  76. package/dist/components/frame/suggestion/suggestion.d.ts +3 -1
  77. package/dist/components/frame/suggestion/suggestion.d.ts.map +1 -1
  78. package/dist/components/frame/suggestion/suggestion.js.map +1 -1
  79. package/dist/components/frame/tabs/lt-tabs.d.ts +3 -3
  80. package/dist/components/frame/tabs/lt-tabs.js +5 -5
  81. package/dist/components/frame/tabs/lt-tabs.js.map +1 -1
  82. package/dist/components/frame/tabs/tabs.d.ts +8 -6
  83. package/dist/components/frame/tabs/tabs.d.ts.map +1 -1
  84. package/dist/components/frame/tabs/tabs.js +4 -4
  85. package/dist/components/frame/tabs/tabs.js.map +1 -1
  86. package/dist/components/standalone/connection-status/lt-connection-status.d.ts +1 -1
  87. package/dist/components/standalone/connection-status/lt-connection-status.js +1 -1
  88. package/dist/components/standalone/toast/lt-toast.d.ts +1 -1
  89. package/dist/components/standalone/toast/lt-toast.js +1 -1
  90. package/dist/diagnostics/index.d.ts +2 -2
  91. package/dist/diagnostics/index.js +2 -2
  92. package/dist/dom.d.ts +2 -1
  93. package/dist/dom.d.ts.map +1 -1
  94. package/dist/dom.js +2 -1
  95. package/dist/dom.js.map +1 -1
  96. package/dist/lotek.css +20 -1
  97. package/dist/lt.d.ts +5 -5
  98. package/dist/lt.js +5 -5
  99. package/dist/react.d.ts.map +1 -1
  100. package/dist/vocabulary/elements.js +1 -1
  101. package/dist/vocabulary/elements.js.map +1 -1
  102. package/dist/vocabulary/types.d.ts +20 -0
  103. package/dist/vocabulary/types.d.ts.map +1 -1
  104. package/live-templates-kotlin.xml +64 -0
  105. package/live-templates.xml +23 -23
  106. package/manifest/manifest.json +368 -80
  107. package/manifest/manifest.schema.json +54 -7
  108. package/package.json +2 -1
  109. package/src/components/css/accordion/accordion.ts +1 -1
  110. package/src/components/css/alert/alert.ts +1 -1
  111. package/src/components/css/avatar/avatar.ts +1 -1
  112. package/src/components/css/badge/badge.ts +1 -1
  113. package/src/components/css/breadcrumbs/breadcrumbs.ts +1 -1
  114. package/src/components/css/card/card.ts +1 -1
  115. package/src/components/css/file-upload/file-upload.ts +1 -1
  116. package/src/components/css/icon/icon.ts +1 -1
  117. package/src/components/css/input/input.css +21 -0
  118. package/src/components/css/label/label.ts +1 -1
  119. package/src/components/css/link/link.ts +1 -1
  120. package/src/components/css/spinner/spinner.ts +8 -5
  121. package/src/components/css/tag/tag.ts +1 -1
  122. package/src/components/css/toggle-group/toggle-group.ts +1 -1
  123. package/src/components/css/tooltip/tooltip.ts +2 -2
  124. package/src/components/frame/dialog/dialog.ts +11 -6
  125. package/src/components/frame/dialog/lt-dialog.ts +7 -7
  126. package/src/components/frame/error-summary/error-summary.ts +6 -2
  127. package/src/components/frame/error-summary/lt-error-summary.ts +6 -6
  128. package/src/components/frame/field/field-core.ts +4 -2
  129. package/src/components/frame/field/lt-field.ts +5 -4
  130. package/src/components/frame/popover/lt-popover.ts +3 -3
  131. package/src/components/frame/popover/popover.ts +7 -3
  132. package/src/components/frame/session-timeout/lt-session-timeout.ts +8 -8
  133. package/src/components/frame/session-timeout/session-timeout.ts +10 -5
  134. package/src/components/frame/suggestion/lt-suggestion.ts +13 -12
  135. package/src/components/frame/suggestion/suggestion.css +1 -1
  136. package/src/components/frame/suggestion/suggestion.ts +3 -1
  137. package/src/components/frame/tabs/lt-tabs.ts +5 -5
  138. package/src/components/frame/tabs/tabs.css +1 -1
  139. package/src/components/frame/tabs/tabs.ts +8 -6
  140. package/src/components/standalone/connection-status/lt-connection-status.ts +1 -1
  141. package/src/components/standalone/toast/lt-toast.ts +1 -1
  142. package/src/diagnostics/index.ts +2 -2
  143. package/src/dom.ts +2 -1
  144. package/src/lt.ts +5 -5
  145. package/src/tokens/boundary.css +4 -0
  146. package/src/vocabulary/elements.ts +1 -1
  147. package/src/vocabulary/types.ts +17 -0
  148. package/web-types.json +2 -2
@@ -65,11 +65,15 @@
65
65
  "type": "object",
66
66
  "required": ["text", "replacedBy"],
67
67
  "properties": {
68
- "text": { "type": "string" },
68
+ "text": {
69
+ "type": "string"
70
+ },
69
71
  "replacedBy": {
70
72
  "type": "array",
71
73
  "description": "The attributes that replace the text. Any one of them is enough.",
72
- "items": { "type": "string" }
74
+ "items": {
75
+ "type": "string"
76
+ }
73
77
  }
74
78
  },
75
79
  "additionalProperties": false
@@ -138,14 +142,24 @@
138
142
  "type": "object",
139
143
  "required": ["variable", "text", "attribute"],
140
144
  "properties": {
141
- "variable": { "type": "string" },
142
- "text": { "type": "string" },
143
- "attribute": { "type": "string" },
144
- "value": { "type": "string" },
145
+ "variable": {
146
+ "type": "string"
147
+ },
148
+ "text": {
149
+ "type": "string"
150
+ },
151
+ "attribute": {
152
+ "type": "string"
153
+ },
154
+ "value": {
155
+ "type": "string"
156
+ },
145
157
  "unless": {
146
158
  "type": "array",
147
159
  "description": "Attributes that keep the text from showing.",
148
- "items": { "type": "string" }
160
+ "items": {
161
+ "type": "string"
162
+ }
149
163
  }
150
164
  },
151
165
  "additionalProperties": false
@@ -214,6 +228,35 @@
214
228
  "name": {
215
229
  "type": "string"
216
230
  },
231
+ "description": {
232
+ "type": "string",
233
+ "description": "The comment on the builder function, without the code examples and without advice for React: what it is for and what the template has to know. Paragraphs are separated by an empty line. Added after schemaVersion 1 was set, so an older manifest does not have it."
234
+ },
235
+ "example": {
236
+ "type": "string",
237
+ "description": "The code examples in the comment, in TypeScript."
238
+ },
239
+ "docs": {
240
+ "type": "string",
241
+ "format": "uri",
242
+ "description": "The page the builder function is documented on."
243
+ },
244
+ "parts": {
245
+ "type": "object",
246
+ "description": "Which part of a frame component each set in the result fills, as field.control fills the control of lt-field. The part's selector in elements is what the set may go on. A set that fills no part is left out.",
247
+ "additionalProperties": {
248
+ "type": "object",
249
+ "required": ["element", "part"],
250
+ "properties": {
251
+ "element": {
252
+ "type": "string"
253
+ },
254
+ "part": {
255
+ "type": "string"
256
+ }
257
+ }
258
+ }
259
+ },
217
260
  "options": {
218
261
  "type": "array",
219
262
  "items": {
@@ -232,6 +275,10 @@
232
275
  "description": {
233
276
  "type": "string",
234
277
  "description": "What the option does, taken from the comment on the option in the type."
278
+ },
279
+ "default": {
280
+ "type": "string",
281
+ "description": "For an option with a closed list: the value that gives the same answer as leaving the option out, read from the answers. The description says the same."
235
282
  }
236
283
  }
237
284
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lo-tek/interfaces",
3
- "version": "0.39.0",
3
+ "version": "0.40.0",
4
4
  "description": "Framework-agnostic design system: tokens, CSS components and web components.",
5
5
  "license": "MIT",
6
6
  "author": "August Søberg-Klyver",
@@ -335,6 +335,7 @@
335
335
  "dist",
336
336
  "web-types.json",
337
337
  "live-templates.xml",
338
+ "live-templates-kotlin.xml",
338
339
  "manifest/manifest.json",
339
340
  "manifest/files.json",
340
341
  "core/lotek-core.wasm",
@@ -30,7 +30,7 @@ export type AccordionAttributes = {
30
30
  *
31
31
  * ```ts
32
32
  * <details {...accordion()}>
33
- * <summary>Who can apply?</summary>
33
+ * <summary>Who can take out a policy?</summary>
34
34
  * <div class={accordion.content}>…</div>
35
35
  * </details>
36
36
  * ```
@@ -29,7 +29,7 @@ export type AlertAttributes = {
29
29
  *
30
30
  * ```ts
31
31
  * <div {...alert({ color: "warning" })}>
32
- * <p class={alert.title}>The application has not been sent</p>
32
+ * <p class={alert.title}>Your routes have not been saved</p>
33
33
  * </div>
34
34
  * ```
35
35
  */
@@ -33,7 +33,7 @@ export type AvatarAttributes = {
33
33
  * picture stands alone, it must have a text that says who it is.
34
34
  *
35
35
  * ```ts
36
- * <span {...avatar({ size: "small" })} aria-hidden="true">ON</span>
36
+ * <span {...avatar({ size: "small" })} aria-hidden="true">DP</span>
37
37
  * ```
38
38
  */
39
39
  export const avatar = Object.assign(
@@ -27,7 +27,7 @@ export type BadgeAttributes = {
27
27
  * The attributes for a status badge.
28
28
  *
29
29
  * ```ts
30
- * <span {...badge({ color: "success" })}>Approved</span>
30
+ * <span {...badge({ color: "success" })}>Paid</span>
31
31
  * ```
32
32
  */
33
33
  export const badge = Object.assign(
@@ -21,7 +21,7 @@ export type BreadcrumbsAttributes = {
21
21
  * <nav aria-label="You are here">
22
22
  * <ol {...breadcrumbs()}>
23
23
  * <li><a href="/">Home</a></li>
24
- * <li><a href="/cases" aria-current="page">My cases</a></li>
24
+ * <li><a href="/claims" aria-current="page">My claims</a></li>
25
25
  * </ol>
26
26
  * </nav>
27
27
  * ```
@@ -31,7 +31,7 @@ export type CardAttributes = {
31
31
  * reached with the keyboard nor be announced as something to press.
32
32
  *
33
33
  * ```ts
34
- * <a {...card({ interactive: true })} href="/case/481">…</a>
34
+ * <a {...card({ interactive: true })} href="/claims/AV-2079-0481">…</a>
35
35
  * ```
36
36
  */
37
37
  export const card = Object.assign(
@@ -39,7 +39,7 @@ export type FileUploadAttributes = {
39
39
  * "all files", so the type must be checked on the server anyway.
40
40
  *
41
41
  * ```ts
42
- * <input {...fileUpload({ multiple: true, accept: ".pdf" })} />
42
+ * <input {...fileUpload({ multiple: true, accept: ".bin" })} />
43
43
  * ```
44
44
  */
45
45
  export const fileUpload = Object.assign(
@@ -30,7 +30,7 @@ export type IconAttributes = {
30
30
  * reports a button that lacks one.
31
31
  *
32
32
  * ```ts
33
- * <button {...button()} aria-label="Delete the application">
33
+ * <button {...button()} aria-label="Delete ticket #0331">
34
34
  * <svg {...icon()} viewBox="0 0 24 24">…</svg>
35
35
  * </button>
36
36
  * ```
@@ -126,6 +126,27 @@
126
126
  cursor: pointer;
127
127
  }
128
128
 
129
+ /* iOS Safari: with the native appearance, a date or time field stood wider
130
+ than its container. Without it, the field fits.
131
+ Undocumented: on 2026-11-01, CSS.supports("-webkit-touch-callout", "none")
132
+ was false in Playwright's Chromium, Firefox and WebKit on macOS, also with
133
+ iPhone emulation, and this rule applied in Safari on an iPhone. A WebKit
134
+ build with touch events elsewhere may match it too. */
135
+ @supports (-webkit-touch-callout: none) {
136
+ .lt-input[data-variant="date"],
137
+ .lt-input[data-variant="time"],
138
+ .lt-input[data-variant="datetime-local"] {
139
+ -webkit-appearance: none;
140
+ appearance: none;
141
+ }
142
+
143
+ .lt-input[data-variant="date"]::-webkit-date-and-time-value,
144
+ .lt-input[data-variant="time"]::-webkit-date-and-time-value,
145
+ .lt-input[data-variant="datetime-local"]::-webkit-date-and-time-value {
146
+ text-align: start;
147
+ }
148
+ }
149
+
129
150
  /* High contrast: the icon is painted into the image with a fixed colour,
130
151
  and can become invisible in a dark theme. The browser's own button is
131
152
  hidden behind our icon, and is brought back here. */
@@ -33,7 +33,7 @@ export type LabelAttributes = {
33
33
  * or the screen reader is not told that it must be filled in.
34
34
  *
35
35
  * ```ts
36
- * <label {...label({ required: "symbol" })} htmlFor="email">Email address</label>
36
+ * <label {...label({ required: "symbol" })} htmlFor="fixer">Fixer's address</label>
37
37
  * ```
38
38
  */
39
39
  export const label = Object.assign(
@@ -22,7 +22,7 @@ export type LinkAttributes = {
22
22
  * The attributes for a link.
23
23
  *
24
24
  * ```ts
25
- * <a {...link()} href="/receipt">See receipt</a>
25
+ * <a {...link()} href="/small-print">Read the small print</a>
26
26
  * <a {...link({ disabled: true })}>See annual statement</a>
27
27
  * ```
28
28
  *
@@ -19,18 +19,20 @@ export type SpinnerAttributes = {
19
19
  "data-size"?: NonDefaultSpinnerSize
20
20
  role?: "status"
21
21
  "aria-label"?: string
22
+ "aria-hidden"?: "true"
22
23
  }
23
24
 
24
25
  /**
25
26
  * The attributes for a loading indicator.
26
27
  *
27
- * Without `label` the ring is only a shape, and screen readers announce
28
- * nothing. With `label` it becomes a `role="status"` that says what is going
29
- * on. If you already have a visible text next to it, let that carry the
30
- * message and drop `label`, so the same thing is not announced twice.
28
+ * Without `label` the ring is only a shape, and is hidden from screen readers
29
+ * with `aria-hidden`, as an icon without a label is. With `label` it becomes a
30
+ * `role="status"` that says what is going on. If you already have a visible
31
+ * text next to it, let that carry the message and drop `label`, so the same
32
+ * thing is not announced twice.
31
33
  *
32
34
  * ```ts
33
- * <span {...spinner({ label: "Loading applications" })} />
35
+ * <span {...spinner({ label: "Loading sectors" })} />
34
36
  * ```
35
37
  */
36
38
  export const spinner = Object.assign(
@@ -40,6 +42,7 @@ export const spinner = Object.assign(
40
42
  "data-size": size === "medium" ? undefined : size,
41
43
  role: label ? ("status" as const) : undefined,
42
44
  "aria-label": label,
45
+ "aria-hidden": label ? undefined : ("true" as const),
43
46
  }),
44
47
  {
45
48
  sizes: spinnerSizes,
@@ -32,7 +32,7 @@ export type TagAttributes = {
32
32
  * since only you know whether the tag is selected.
33
33
  *
34
34
  * ```ts
35
- * <button {...tag({ selectable: true })} aria-pressed="true">Housing benefit</button>
35
+ * <button {...tag({ selectable: true })} aria-pressed="true">Cutter</button>
36
36
  * ```
37
37
  */
38
38
  export const tag = Object.assign(
@@ -23,7 +23,7 @@ export type ToggleGroupAttributes = {
23
23
  * <fieldset {...toggleGroup()}>
24
24
  * <legend class="lt-sr-only">View</legend>
25
25
  * <label class={toggleGroup.option}>
26
- * <input type="radio" name="view" value="list" checked /> List
26
+ * <input type="radio" name="view" value="dry" checked /> Dry
27
27
  * </label>
28
28
  * </fieldset>
29
29
  * ```
@@ -23,8 +23,8 @@ export type TooltipAttributes = {
23
23
  *
24
24
  * ```ts
25
25
  * <span {...tooltip()}>
26
- * <button aria-describedby="hint">Archive</button>
27
- * <span class={tooltip.bubble} role="tooltip" id="hint">Moves it to the archive</span>
26
+ * <button aria-describedby="hint">Appraise</button>
27
+ * <span class={tooltip.bubble} role="tooltip" id="hint">At street value</span>
28
28
  * </span>
29
29
  * ```
30
30
  */
@@ -25,7 +25,12 @@ export const dialogColors = [
25
25
  export type DialogColor = (typeof dialogColors)[number]
26
26
 
27
27
  export type DialogOptions = {
28
- /** Id of the heading. The dialog takes its name from it with `aria-labelledby`. */
28
+ /**
29
+ * Id of the heading. The dialog takes its name from it with
30
+ * `aria-labelledby`. Make it from a key in the data, never from the loop
31
+ * index or a counter, so it is unique on the page and the same on every
32
+ * render.
33
+ */
29
34
  titleId: string
30
35
  /**
31
36
  * The dialog is open. Default: closed.
@@ -36,7 +41,7 @@ export type DialogOptions = {
36
41
  */
37
42
  open?: boolean
38
43
  /**
39
- * Colour of the header. Default: none.
44
+ * Colour of the header. Default: `neutral`, which gives none.
40
45
  *
41
46
  * The colour applies to `header`, so the header must be in the markup for
42
47
  * it to show: `<div {...box.header}>` around the heading. Without a header
@@ -94,15 +99,15 @@ export type DialogAttributes = {
94
99
  * turns it into a modal.
95
100
  *
96
101
  * ```ts
97
- * const box = lt.dialog({ titleId: "delete-title", open: true })
102
+ * const box = lt.dialog({ titleId: "cancel-title", open: true })
98
103
  * ```
99
104
  * ```html
100
105
  * <lt-dialog {...box.host}>
101
106
  * <dialog {...box.dialog}>
102
- * <h2 {...box.title}>Delete the application?</h2>
103
- * <div {...box.body}>The application and its attachments will be gone.</div>
107
+ * <h2 {...box.title}>Cancel job 222-0481?</h2>
108
+ * <div {...box.body}>The crew keeps the advance. This cannot be undone.</div>
104
109
  * <form method="dialog" {...box.footer}>
105
- * <button class="lt-button" value="cancel">Cancel</button>
110
+ * <button class="lt-button" value="keep">Keep the job</button>
106
111
  * </form>
107
112
  * </dialog>
108
113
  * </lt-dialog>
@@ -65,8 +65,8 @@ export const LT_DIALOG_PARTS = {
65
65
  * ```html
66
66
  * <lt-dialog open>
67
67
  * <dialog class="lt-dialog" aria-labelledby="title" open>
68
- * <h2 class="lt-dialog__title" id="title">The decision is registered</h2>
69
- * <div class="lt-dialog__body">The case has been processed.</div>
68
+ * <h2 class="lt-dialog__title" id="title">The order is placed</h2>
69
+ * <div class="lt-dialog__body">Job 222-0481 is open to crews.</div>
70
70
  * <form method="dialog" class="lt-dialog__footer">
71
71
  * <button class="lt-button" value="close">Close</button>
72
72
  * </form>
@@ -237,7 +237,7 @@ export class LtDialog extends HostElement {
237
237
  }
238
238
 
239
239
  /**
240
- * Fills in what a template without JavaScript did not write: the class on
240
+ * Fills in what a template without a builder function did not write: the class on
241
241
  * the dialog, and the title. The title is what `aria-labelledby` points to
242
242
  * when the server wrote it, otherwise the first heading outside the body,
243
243
  * and only when the dialog does not already have a name from `aria-label`.
@@ -449,7 +449,7 @@ export class LtDialog extends HostElement {
449
449
  * run. When something wrote `open` on the host in the meantime, such as a
450
450
  * framework setting the same value again, the component opened the dialog
451
451
  * again and cleared `returnValue`, and a listener on `close` read an empty
452
- * value instead of "delete". `returnValue` is cleared every time the
452
+ * value instead of "cancel". `returnValue` is cleared every time the
453
453
  * dialog opens, so a value here comes from a button in this round.
454
454
  */
455
455
  if (
@@ -478,9 +478,9 @@ export class LtDialog extends HostElement {
478
478
  /*
479
479
  * Escape and `close()` without an argument leave the previous
480
480
  * `returnValue` in place. A dialog the server opened again then
481
- * reported "delete" from last time when it was closed from the server,
482
- * and the app that tells "Cancel" from "Delete" by the value deleted
483
- * something the user never asked for.
481
+ * reported "cancel" from last time when it was closed from the server,
482
+ * and the app that tells "Keep the job" from "Cancel the job" by the
483
+ * value cancelled something the user never asked for.
484
484
  */
485
485
  dialog.returnValue = ""
486
486
  dialog.showModal()
@@ -6,7 +6,11 @@ export const ERROR_SUMMARY_TITLE_CLASS = "lt-error-summary__title" as const
6
6
  export type ErrorSummaryOptions = {
7
7
  /** The number of errors the server found. When it is zero, the box is hidden. */
8
8
  count?: number
9
- /** The id of the box, so the form can move focus here after submission. */
9
+ /**
10
+ * The id of the box, so the form can move focus here after submission.
11
+ * Make it from a key in the data, never from the loop index or a counter,
12
+ * so it is unique on the page and the same on every render.
13
+ */
10
14
  id?: string
11
15
  /**
12
16
  * The id of the heading. The box takes its name from it with
@@ -50,7 +54,7 @@ export type ErrorSummaryAttributes = {
50
54
  * ```html
51
55
  * <lt-error-summary {...summary.host}>
52
56
  * <h2 {...summary.title}>You need to fix 2 errors</h2>
53
- * <ul><li><a href="#email">The email address is missing an @</a></li></ul>
57
+ * <ul><li><a href="#fixer">The fixer's address is missing an @</a></li></ul>
54
58
  * </lt-error-summary>
55
59
  * ```
56
60
  */
@@ -26,10 +26,10 @@ export const LT_ERROR_SUMMARY_PARTS = {
26
26
  * Takes the user from an error in the summary to the field that failed.
27
27
  *
28
28
  * The server writes the content: the heading and the list of links. When the
29
- * markup is written with JavaScript, `lt.errorSummary()` also writes the
30
- * class, `role="alert"` and `tabindex="-1"` on the box, and the component
31
- * leaves them alone. When the markup comes from a template without
32
- * JavaScript, the component fills in those three and the class on the
29
+ * markup is written with a builder function, `lt.errorSummary()` also writes
30
+ * the class, `role="alert"` and `tabindex="-1"` on the box, and the component
31
+ * leaves them alone. When the markup comes from a template without one, the
32
+ * component fills in those three and the class on the
33
33
  * heading, and sets them back when a patch removes them. Either way, a box
34
34
  * without a name gets `aria-labelledby` pointing at its heading. The component
35
35
  * creates no nodes: it used to, and then the heading disappeared on the
@@ -49,7 +49,7 @@ export const LT_ERROR_SUMMARY_PARTS = {
49
49
  * <lt-error-summary>
50
50
  * <h2>The form has two errors</h2>
51
51
  * <ul class="lt-list">
52
- * <li><a href="#email">Enter a valid email address</a></li>
52
+ * <li><a href="#fixer">Enter a valid address</a></li>
53
53
  * </ul>
54
54
  * </lt-error-summary>
55
55
  * ```
@@ -185,7 +185,7 @@ export class LtErrorSummary extends HostElement {
185
185
  }
186
186
 
187
187
  /**
188
- * Fills in what a template without JavaScript did not write. Only what is
188
+ * Fills in what a template without a builder function did not write. Only what is
189
189
  * missing: a role the server chose stays, and so does a `tabindex`.
190
190
  */
191
191
  private wire(): void {
@@ -17,10 +17,12 @@ export { createFieldId }
17
17
 
18
18
  export type FieldOptions = {
19
19
  /**
20
- * The id of the control. Required.
20
+ * The id of the control. Required. Make it from a key in the data, never
21
+ * from the loop index or a counter, so it is unique on the page and the
22
+ * same on every render.
21
23
  *
22
24
  * In React it comes from `useId()`. Otherwise the field's name is usually
23
- * the obvious choice. If you know for certain that the markup is rendered
25
+ * the obvious choice, and in a loop the name with the row's key. If you know for certain that the markup is rendered
24
26
  * once, call `createFieldId()` yourself.
25
27
  *
26
28
  * It used to be optional, and the function created one when it was
@@ -124,10 +124,11 @@ function readControlWord(control: Element): ControlWord {
124
124
  /**
125
125
  * Connects label, control, help text and error text in plain DOM.
126
126
  *
127
- * The component is for markup produced without JavaScript: a Go template, a
128
- * PHP file, a Razor view or handwritten HTML. When the markup is written with
129
- * JavaScript, wherever the code runs, `lt.field()` should write the
130
- * attributes instead, and this element is not needed.
127
+ * The component is for markup from code that cannot call a builder function:
128
+ * a Go template, a PHP file, a Razor view or handwritten HTML. When the code
129
+ * can, in TypeScript wherever it runs, or in Kotlin or Java, `lt.field()` or
130
+ * `Lt.field()` should write the attributes instead, and this element is not
131
+ * needed.
131
132
  *
132
133
  * The component renders nothing. It used to insert a `<slot>` element into
133
134
  * plain DOM, and since the server did not know about it, Datastar's morph
@@ -40,9 +40,9 @@ export const LT_POPOVER_PARTS = {
40
40
  * What the top layer does not do is place the panel. `position-anchor` is not
41
41
  * yet in every browser, so the position is worked out here.
42
42
  *
43
- * When the markup is made with JavaScript, `lt.popover()` writes the wiring:
44
- * `aria-controls` on the button, and the class, the id and `popover` on the
45
- * panel. When the markup comes from a template without JavaScript, the
43
+ * When the markup is made with a builder function, `lt.popover()` writes the
44
+ * wiring: `aria-controls` on the button, and the class, the id and `popover`
45
+ * on the panel. When the markup comes from a template without one, the
46
46
  * template writes a button and a panel with the class `lt-popover` and an id:
47
47
  * the component sets `popover="manual"` and writes `aria-controls` on the
48
48
  * button from the id. Only what is missing, so what the server wrote stays.
@@ -16,7 +16,11 @@ export function isPopoverPlacement(value: unknown): value is PopoverPlacement {
16
16
  }
17
17
 
18
18
  export type PopoverOptions = {
19
- /** Id of the panel. Connects the button to the panel with `aria-controls`. */
19
+ /**
20
+ * Id of the panel. Connects the button to the panel with `aria-controls`.
21
+ * Make it from a key in the data, never from the loop index or a counter,
22
+ * so it is unique on the page and the same on every render.
23
+ */
20
24
  id: string
21
25
  /** The panel is open. Default: closed. */
22
26
  open?: boolean
@@ -52,8 +56,8 @@ export type PopoverAttributes = {
52
56
  * ```
53
57
  * ```html
54
58
  * <lt-popover>
55
- * <button {...box.trigger}>What does this mean?</button>
56
- * <div {...box.panel}>We use the date of birth to find the right rate.</div>
59
+ * <button {...box.trigger}>What is the late fee?</button>
60
+ * <div {...box.panel}>EB 100 a night past the return date. No exceptions.</div>
57
61
  * </lt-popover>
58
62
  * ```
59
63
  */
@@ -141,9 +141,9 @@ function deepActiveElement(): HTMLElement | null {
141
141
  * Warns before a signed-in session expires.
142
142
  *
143
143
  * This is the thing every large organisation builds itself and gets wrong.
144
- * Either no warning comes, and the user loses a half-filled application
145
- * form, or the dialog steals focus in the middle of a sentence, or the
146
- * screen reader reads the countdown every second.
144
+ * Either no warning comes, and the user loses a half-filled form, or the
145
+ * dialog steals focus in the middle of a sentence, or the screen reader
146
+ * reads the countdown every second.
147
147
  *
148
148
  * The renderer writes the dialog and the text, so the warning is in the
149
149
  * language the app uses, with the translation tool the app already has. The
@@ -163,14 +163,14 @@ function deepActiveElement(): HTMLElement | null {
163
163
  * ```html
164
164
  * <lt-session-timeout warn-at="1500" expires-at="1800" data-ignore-morph>
165
165
  * <dialog class="lt-session-timeout__dialog" role="alertdialog" aria-labelledby="session">
166
- * <h2 class="lt-session-timeout__title" id="session">You will soon be signed out</h2>
166
+ * <h2 class="lt-session-timeout__title" id="session">Your session is ending</h2>
167
167
  * <p class="lt-session-timeout__text">
168
- * We will sign you out in <span class="lt-session-timeout__count" aria-hidden="true"></span>.
168
+ * Your session on the Lattice ends in <span class="lt-session-timeout__count" aria-hidden="true"></span>. Unsaved routes will be lost.
169
169
  * </p>
170
170
  * <span class="lt-sr-only" role="status"></span>
171
171
  * <form method="dialog" class="lt-session-timeout__actions">
172
- * <button class="lt-button" value="extend">Stay signed in</button>
173
- * <button class="lt-button" data-variant="secondary" value="logout">Sign out now</button>
172
+ * <button class="lt-button" value="extend">Stay connected</button>
173
+ * <button class="lt-button" data-variant="secondary" value="logout">Log out</button>
174
174
  * </form>
175
175
  * </dialog>
176
176
  * </lt-session-timeout>
@@ -224,7 +224,7 @@ export class LtSessionTimeout extends HostElement {
224
224
  * `close` arrives in a later task than the click that closed the dialog.
225
225
  * When the clock ticked in between, `tick()` opened the dialog again and
226
226
  * cleared `returnValue`, and the queued event then read `""` and extended
227
- * the session: the user pressed "Sign out now", and the app got
227
+ * the session: the user pressed "Log out", and the app got
228
228
  * `session-extend`.
229
229
  */
230
230
  private shownDialog?: HTMLDialogElement
@@ -15,7 +15,12 @@ export const SESSION_EXTEND = "extend" as const
15
15
  export const SESSION_LOGOUT = "logout" as const
16
16
 
17
17
  export type SessionTimeoutOptions = {
18
- /** Id of the heading. The dialog takes its name from it with `aria-labelledby`. */
18
+ /**
19
+ * Id of the heading. The dialog takes its name from it with
20
+ * `aria-labelledby`. Make it from a key in the data, never from the loop
21
+ * index or a counter, so it is unique on the page and the same on every
22
+ * render.
23
+ */
19
24
  titleId: string
20
25
  /** Seconds without activity before the warning appears. Default: 25 minutes. */
21
26
  warnAt?: number
@@ -76,14 +81,14 @@ export type SessionTimeoutAttributes = {
76
81
  * ```html
77
82
  * <lt-session-timeout {...session.host}>
78
83
  * <dialog {...session.dialog}>
79
- * <h2 {...session.title}>You will soon be signed out</h2>
84
+ * <h2 {...session.title}>Your session is ending</h2>
80
85
  * <p {...session.text}>
81
- * We will sign you out in <span {...session.count}></span>.
86
+ * Your session on the Lattice ends in <span {...session.count}></span>. Unsaved routes will be lost.
82
87
  * </p>
83
88
  * <span {...session.live}></span>
84
89
  * <form {...session.actions}>
85
- * <button {...session.extend}>Stay signed in</button>
86
- * <button {...session.logout}>Sign out now</button>
90
+ * <button {...session.extend}>Stay connected</button>
91
+ * <button {...session.logout}>Log out</button>
87
92
  * </form>
88
93
  * </dialog>
89
94
  * </lt-session-timeout>