@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
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "$schema": "./manifest.schema.json",
3
3
  "schemaVersion": 1,
4
- "version": "0.39.0",
4
+ "version": "0.40.0",
5
5
  "textLanguage": "en",
6
6
  "elements": {
7
7
  "lt-field": {
8
8
  "link": "https://lo-tek.sobernetics.no/components/field/",
9
- "description": "Wires label, field, help text and error text together: sets `aria-describedby` and `aria-invalid` on the control and an id on the help text and the error text. Never writes an id on the control: the template writes `for` and `id`, or wraps the field in the label. The field is invalid when the error text is there without `hidden`. Exists for markup produced without JavaScript. When the markup is produced with JavaScript, use `lt.field()` instead.",
9
+ "description": "Wires label, field, help text and error text together: sets `aria-describedby` and `aria-invalid` on the control and an id on the help text and the error text. Never writes an id on the control: the template writes `for` and `id`, or wraps the field in the label. The field is invalid when the error text is there without `hidden`. Exists for markup from code that cannot call a builder function. When it can, in TypeScript, Kotlin or Java, use `lt.field()` or `Lt.field()` instead.",
10
10
  "attributes": {
11
11
  "disabled": {
12
12
  "type": "flag",
@@ -1528,7 +1528,7 @@
1528
1528
  "danger"
1529
1529
  ],
1530
1530
  "default": "neutral",
1531
- "description": "Colour of the header. Default: none."
1531
+ "description": "Colour of the header. Default: `neutral`, which gives none."
1532
1532
  }
1533
1533
  }
1534
1534
  },
@@ -1879,6 +1879,8 @@
1879
1879
  "builders": [
1880
1880
  {
1881
1881
  "name": "accordion",
1882
+ "description": "The attributes for a panel that can be expanded.\n\nThe class goes on a `<details>` with a `<summary>` first. The browser handles opening, closing, the keyboard and screen readers announcing \"expanded\" or \"collapsed\". Build the same thing from buttons and `aria-expanded`, and all of that must be written again, and the browser's find in page no longer finds text inside a closed panel.",
1883
+ "example": "<details {...accordion()}>\n <summary>Who can take out a policy?</summary>\n <div class={accordion.content}>…</div>\n</details>",
1882
1884
  "options": [
1883
1885
  {
1884
1886
  "name": "variant",
@@ -1890,7 +1892,8 @@
1890
1892
  "plain"
1891
1893
  ]
1892
1894
  },
1893
- "description": "Its own frame, or just a line between the panels. Default: `default`."
1895
+ "description": "Its own frame, or just a line between the panels. Default: `default`.",
1896
+ "default": "default"
1894
1897
  }
1895
1898
  ],
1896
1899
  "returns": {
@@ -1916,10 +1919,13 @@
1916
1919
  },
1917
1920
  "classes": {
1918
1921
  "content": "lt-accordion__content"
1919
- }
1922
+ },
1923
+ "docs": "https://lo-tek.sobernetics.no/components/accordion/"
1920
1924
  },
1921
1925
  {
1922
1926
  "name": "alert",
1927
+ "description": "The attributes for a message about the whole page or a whole form.\n\nThe role is not set here, because it depends on when the message comes. If it is in the markup from the start, it should have no role. If it appears while the page is idle, it needs `role=\"status\"`, or `role=\"alert\"` if the user must stop.",
1928
+ "example": "<div {...alert({ color: \"warning\" })}>\n <p class={alert.title}>Your routes have not been saved</p>\n</div>",
1923
1929
  "options": [
1924
1930
  {
1925
1931
  "name": "color",
@@ -1967,10 +1973,13 @@
1967
1973
  },
1968
1974
  "classes": {
1969
1975
  "title": "lt-alert__title"
1970
- }
1976
+ },
1977
+ "docs": "https://lo-tek.sobernetics.no/components/alert/"
1971
1978
  },
1972
1979
  {
1973
1980
  "name": "avatar",
1981
+ "description": "The attributes for a profile picture or initials.\n\nIf the name is already next to it, the picture is decoration: set `aria-hidden=\"true\"` on it, or the name is announced twice. If the picture stands alone, it must have a text that says who it is.",
1982
+ "example": "<span {...avatar({ size: \"small\" })} aria-hidden=\"true\">DP</span>",
1974
1983
  "options": [
1975
1984
  {
1976
1985
  "name": "size",
@@ -1983,7 +1992,8 @@
1983
1992
  "large"
1984
1993
  ]
1985
1994
  },
1986
- "description": "Size. Default: `medium`."
1995
+ "description": "Size. Default: `medium`.",
1996
+ "default": "medium"
1987
1997
  },
1988
1998
  {
1989
1999
  "name": "variant",
@@ -1995,7 +2005,8 @@
1995
2005
  "square"
1996
2006
  ]
1997
2007
  },
1998
- "description": "Round or rounded square. Default: `circle`."
2008
+ "description": "Round or rounded square. Default: `circle`.",
2009
+ "default": "circle"
1999
2010
  }
2000
2011
  ],
2001
2012
  "returns": {
@@ -2032,10 +2043,13 @@
2032
2043
  },
2033
2044
  "classes": {
2034
2045
  "stack": "lt-avatar-stack"
2035
- }
2046
+ },
2047
+ "docs": "https://lo-tek.sobernetics.no/components/avatar/"
2036
2048
  },
2037
2049
  {
2038
2050
  "name": "badge",
2051
+ "description": "The attributes for a status badge.",
2052
+ "example": "<span {...badge({ color: \"success\" })}>Paid</span>",
2039
2053
  "options": [
2040
2054
  {
2041
2055
  "name": "color",
@@ -2050,7 +2064,8 @@
2050
2064
  "neutral"
2051
2065
  ]
2052
2066
  },
2053
- "description": "What the status means. Default: `info`."
2067
+ "description": "What the status means. Default: `info`.",
2068
+ "default": "info"
2054
2069
  }
2055
2070
  ],
2056
2071
  "returns": {
@@ -2082,10 +2097,13 @@
2082
2097
  }
2083
2098
  }
2084
2099
  }
2085
- }
2100
+ },
2101
+ "docs": "https://lo-tek.sobernetics.no/components/badge/"
2086
2102
  },
2087
2103
  {
2088
2104
  "name": "breadcrumbs",
2105
+ "description": "The attributes for a breadcrumb trail.\n\nThe class goes on an `<ol>` inside a `<nav>` with a text that says what the navigation is, for example `aria-label=\"You are here\"`. The order matters, so the list must be numbered even though the numbers are not shown.\n\nThe separator between the steps is added with CSS. Had it been in the markup, screen readers would have read \"slash\" between every step.",
2106
+ "example": "<nav aria-label=\"You are here\">\n <ol {...breadcrumbs()}>\n <li><a href=\"/\">Home</a></li>\n <li><a href=\"/claims\" aria-current=\"page\">My claims</a></li>\n </ol>\n</nav>",
2089
2107
  "options": [],
2090
2108
  "returns": {
2091
2109
  "kind": "attributes"
@@ -2098,10 +2116,13 @@
2098
2116
  },
2099
2117
  "options": {}
2100
2118
  }
2101
- }
2119
+ },
2120
+ "docs": "https://lo-tek.sobernetics.no/components/breadcrumbs/"
2102
2121
  },
2103
2122
  {
2104
2123
  "name": "button",
2124
+ "description": "The attributes for a button.",
2125
+ "example": "<button {...button({ variant: \"secondary\" })}>Save draft</button>",
2105
2126
  "options": [
2106
2127
  {
2107
2128
  "name": "variant",
@@ -2115,7 +2136,8 @@
2115
2136
  "danger"
2116
2137
  ]
2117
2138
  },
2118
- "description": "How weighty the action is. Default: `primary`."
2139
+ "description": "How weighty the action is. Default: `primary`.",
2140
+ "default": "primary"
2119
2141
  }
2120
2142
  ],
2121
2143
  "returns": {
@@ -2144,10 +2166,13 @@
2144
2166
  }
2145
2167
  }
2146
2168
  }
2147
- }
2169
+ },
2170
+ "docs": "https://lo-tek.sobernetics.no/components/button/"
2148
2171
  },
2149
2172
  {
2150
2173
  "name": "card",
2174
+ "description": "The attributes for a card.\n\n`interactive` gives hover and a focus ring, but makes nothing clickable by itself. If the whole card should be pressable, the element must be an `<a>` or a `<button>`. A `<div>` with a click listener can neither be reached with the keyboard nor be announced as something to press.",
2175
+ "example": "<a {...card({ interactive: true })} href=\"/claims/AV-2079-0481\">…</a>",
2151
2176
  "options": [
2152
2177
  {
2153
2178
  "name": "variant",
@@ -2159,7 +2184,8 @@
2159
2184
  "filled"
2160
2185
  ]
2161
2186
  },
2162
- "description": "Surface or outline. Default: `outline`."
2187
+ "description": "Surface or outline. Default: `outline`.",
2188
+ "default": "outline"
2163
2189
  },
2164
2190
  {
2165
2191
  "name": "interactive",
@@ -2201,10 +2227,13 @@
2201
2227
  },
2202
2228
  "classes": {
2203
2229
  "title": "lt-card__title"
2204
- }
2230
+ },
2231
+ "docs": "https://lo-tek.sobernetics.no/components/card/"
2205
2232
  },
2206
2233
  {
2207
2234
  "name": "checkbox",
2235
+ "description": "The attributes for a checkbox.\n\n`type` is set here, so the box cannot become a text field with a box's styling. The indeterminate state is set with `element.indeterminate = true` in JavaScript, not as an attribute, because HTML has no such attribute.",
2236
+ "example": "<input {...checkbox({ state: \"invalid\" })} />",
2208
2237
  "options": [
2209
2238
  {
2210
2239
  "name": "state",
@@ -2217,7 +2246,8 @@
2217
2246
  "success"
2218
2247
  ]
2219
2248
  },
2220
- "description": "Validation state. Default: `default`."
2249
+ "description": "Validation state. Default: `default`.",
2250
+ "default": "default"
2221
2251
  }
2222
2252
  ],
2223
2253
  "returns": {
@@ -2248,10 +2278,13 @@
2248
2278
  },
2249
2279
  "classes": {
2250
2280
  "row": "lt-checkbox-row"
2251
- }
2281
+ },
2282
+ "docs": "https://lo-tek.sobernetics.no/components/checkbox/"
2252
2283
  },
2253
2284
  {
2254
2285
  "name": "connectionStatus",
2286
+ "description": "The attributes for the bar that says the connection is lost.\n\nBy definition the server cannot tell you it is unreachable. When it is down, nothing comes from it to render. So the component creates all the content itself, and `data-ignore-morph` stops a later patch from tearing it away the moment the connection is back. The host is the live region, `role=\"status\"` with `aria-live=\"polite\"`, so the region exists before the bar and its text arrive.",
2287
+ "example": "<lt-connection-status {...lt.connectionStatus()} />",
2255
2288
  "options": [
2256
2289
  {
2257
2290
  "name": "offlineText",
@@ -2303,10 +2336,13 @@
2303
2336
  }
2304
2337
  }
2305
2338
  }
2306
- }
2339
+ },
2340
+ "docs": "https://lo-tek.sobernetics.no/components/connection-status/"
2307
2341
  },
2308
2342
  {
2309
2343
  "name": "dialog",
2344
+ "description": "The attributes for a dialog.\n\nOpening a dialog is a call, not an attribute: only `showModal()` moves focus in, keeps focus inside the dialog, closes on Escape and makes the rest of the page inert. A `<dialog open>` in the markup is a box on the page.\n\nThat is a problem for a server that only sends HTML, since it cannot call anything in the browser. So the server says the dialog is open, and `<lt-dialog>` makes the call.\n\nThe two `open`s are not the same, and it is worth keeping them apart:\n\n- `open` on the **host** is the server saying the dialog should be shown. It is not preserved. If it were, the server could never open the dialog again after the user had closed it once. If the server sends the region again with `open` still set, the dialog opens again. For a dismissal to last, the app must tell the server.\n- `open` on the **`<dialog>`** is set by the browser itself when `showModal()` is called. It is not in the server's HTML, so a morph removes it, and the component puts it back. The server also writes it when it knows the dialog should be shown: without JavaScript a `<dialog>` without `open` is hidden, and its content does not exist for the reader. With the attribute it stands as a box on the page until the component turns it into a modal.",
2345
+ "example": "const box = lt.dialog({ titleId: \"cancel-title\", open: true })\n\n<lt-dialog {...box.host}>\n <dialog {...box.dialog}>\n <h2 {...box.title}>Cancel job 222-0481?</h2>\n <div {...box.body}>The crew keeps the advance. This cannot be undone.</div>\n <form method=\"dialog\" {...box.footer}>\n <button class=\"lt-button\" value=\"keep\">Keep the job</button>\n </form>\n </dialog>\n</lt-dialog>",
2310
2346
  "options": [
2311
2347
  {
2312
2348
  "name": "titleId",
@@ -2314,7 +2350,7 @@
2314
2350
  "type": {
2315
2351
  "kind": "text"
2316
2352
  },
2317
- "description": "Id of the heading. The dialog takes its name from it with `aria-labelledby`."
2353
+ "description": "Id of the heading. The dialog takes its name from it with `aria-labelledby`. Make it from a key in the data, never from the loop index or a counter, so it is unique on the page and the same on every render."
2318
2354
  },
2319
2355
  {
2320
2356
  "name": "open",
@@ -2338,7 +2374,8 @@
2338
2374
  "danger"
2339
2375
  ]
2340
2376
  },
2341
- "description": "Colour of the header. Default: none."
2377
+ "description": "Colour of the header. Default: `neutral`, which gives none.",
2378
+ "default": "neutral"
2342
2379
  }
2343
2380
  ],
2344
2381
  "returns": {
@@ -2546,10 +2583,27 @@
2546
2583
  }
2547
2584
  }
2548
2585
  }
2586
+ },
2587
+ "docs": "https://lo-tek.sobernetics.no/components/dialog/",
2588
+ "parts": {
2589
+ "dialog": {
2590
+ "element": "lt-dialog",
2591
+ "part": "dialog"
2592
+ },
2593
+ "title": {
2594
+ "element": "lt-dialog",
2595
+ "part": "title"
2596
+ },
2597
+ "body": {
2598
+ "element": "lt-dialog",
2599
+ "part": "body"
2600
+ }
2549
2601
  }
2550
2602
  },
2551
2603
  {
2552
2604
  "name": "divider",
2605
+ "description": "The attributes for a divider.\n\nThe class goes on an `<hr>`. If the line separates two parts of the content, it is information screen readers should get. If it is only decoration between two boxes that are already apart, set `aria-hidden=\"true\"` on it.",
2606
+ "example": "<hr {...divider({ variant: \"subtle\" })} />",
2553
2607
  "options": [
2554
2608
  {
2555
2609
  "name": "variant",
@@ -2562,7 +2616,8 @@
2562
2616
  "strong"
2563
2617
  ]
2564
2618
  },
2565
- "description": "How visible the line is. Default: `default`."
2619
+ "description": "How visible the line is. Default: `default`.",
2620
+ "default": "default"
2566
2621
  }
2567
2622
  ],
2568
2623
  "returns": {
@@ -2588,10 +2643,13 @@
2588
2643
  }
2589
2644
  }
2590
2645
  }
2591
- }
2646
+ },
2647
+ "docs": "https://lo-tek.sobernetics.no/components/divider/"
2592
2648
  },
2593
2649
  {
2594
2650
  "name": "errorSummary",
2651
+ "description": "The attributes for an error summary.\n\nThe server writes the whole box, including the heading and the list. `<lt-error-summary>` only moves focus and handles clicks on the links. The component used to create the heading itself, and then it disappeared on every morph in Datastar.",
2652
+ "example": "const summary = lt.errorSummary({\n count: errors.length,\n id: \"errors\",\n titleId: \"errors-title\",\n})\n\n<lt-error-summary {...summary.host}>\n <h2 {...summary.title}>You need to fix 2 errors</h2>\n <ul><li><a href=\"#fixer\">The fixer's address is missing an @</a></li></ul>\n</lt-error-summary>",
2595
2653
  "options": [
2596
2654
  {
2597
2655
  "name": "count",
@@ -2607,7 +2665,7 @@
2607
2665
  "type": {
2608
2666
  "kind": "text"
2609
2667
  },
2610
- "description": "The id of the box, so the form can move focus here after submission."
2668
+ "description": "The id of the box, so the form can move focus here after submission. Make it from a key in the data, never from the loop index or a counter, so it is unique on the page and the same on every render."
2611
2669
  },
2612
2670
  {
2613
2671
  "name": "titleId",
@@ -2628,10 +2686,19 @@
2628
2686
  "kind": "attributes"
2629
2687
  }
2630
2688
  }
2689
+ },
2690
+ "docs": "https://lo-tek.sobernetics.no/components/error-summary/",
2691
+ "parts": {
2692
+ "title": {
2693
+ "element": "lt-error-summary",
2694
+ "part": "title"
2695
+ }
2631
2696
  }
2632
2697
  },
2633
2698
  {
2634
2699
  "name": "errorText",
2700
+ "description": "The attributes for an error text below a field.",
2701
+ "example": "<p {...errorText({ variant: \"warning\" })}>…</p>",
2635
2702
  "options": [
2636
2703
  {
2637
2704
  "name": "variant",
@@ -2643,7 +2710,8 @@
2643
2710
  "warning"
2644
2711
  ]
2645
2712
  },
2646
- "description": "Whether the error stops submission or only warns. Default: `error`."
2713
+ "description": "Whether the error stops submission or only warns. Default: `error`.",
2714
+ "default": "error"
2647
2715
  }
2648
2716
  ],
2649
2717
  "returns": {
@@ -2666,10 +2734,13 @@
2666
2734
  }
2667
2735
  }
2668
2736
  }
2669
- }
2737
+ },
2738
+ "docs": "https://lo-tek.sobernetics.no/components/error-text/"
2670
2739
  },
2671
2740
  {
2672
2741
  "name": "field",
2742
+ "description": "Connects the label, field, help text and error text.\n\nThe same calculation `<lt-field>` does in the browser, but as data, for the code that renders the markup. Then no web component is needed.",
2743
+ "example": "const field = lt.field({ id: \"fixer\", required: \"text\", help: true, error: true, invalid })\n\n<label {...field.label}>Fixer's address</label>\n<input {...lt.input({ type: \"email\", state: field.state })} {...field.control} />\n<p {...lt.helpText()} {...field.help}>The board writes to the fixer, never to the crew.</p>\n<p {...lt.errorText()} {...field.error}>Enter a valid address.</p>",
2673
2744
  "options": [
2674
2745
  {
2675
2746
  "name": "id",
@@ -2677,7 +2748,7 @@
2677
2748
  "type": {
2678
2749
  "kind": "text"
2679
2750
  },
2680
- "description": "The id of the control. Required."
2751
+ "description": "The id of the control. Required. Make it from a key in the data, never from the loop index or a counter, so it is unique on the page and the same on every render."
2681
2752
  },
2682
2753
  {
2683
2754
  "name": "help",
@@ -2780,10 +2851,31 @@
2780
2851
  ]
2781
2852
  }
2782
2853
  }
2854
+ },
2855
+ "docs": "https://lo-tek.sobernetics.no/components/field/",
2856
+ "parts": {
2857
+ "label": {
2858
+ "element": "lt-field",
2859
+ "part": "label"
2860
+ },
2861
+ "control": {
2862
+ "element": "lt-field",
2863
+ "part": "control"
2864
+ },
2865
+ "help": {
2866
+ "element": "lt-field",
2867
+ "part": "help"
2868
+ },
2869
+ "error": {
2870
+ "element": "lt-field",
2871
+ "part": "error"
2872
+ }
2783
2873
  }
2784
2874
  },
2785
2875
  {
2786
2876
  "name": "fieldset",
2877
+ "description": "The attributes for a group of controls that belong together.\n\n`<fieldset>` with `<legend>` is the one way to give a set of radio buttons or checkboxes a shared label that screen readers actually announce before each choice.",
2878
+ "example": "<fieldset {...fieldset({ state: \"invalid\" })}>\n <legend {...legend({ required: \"symbol\" })}>Delivery method</legend>\n</fieldset>",
2787
2879
  "options": [
2788
2880
  {
2789
2881
  "name": "state",
@@ -2796,7 +2888,8 @@
2796
2888
  "success"
2797
2889
  ]
2798
2890
  },
2799
- "description": "Validation state for the whole group. Default: `default`."
2891
+ "description": "Validation state for the whole group. Default: `default`.",
2892
+ "default": "default"
2800
2893
  },
2801
2894
  {
2802
2895
  "name": "disabled",
@@ -2838,10 +2931,13 @@
2838
2931
  }
2839
2932
  }
2840
2933
  }
2841
- }
2934
+ },
2935
+ "docs": "https://lo-tek.sobernetics.no/components/fieldset/"
2842
2936
  },
2843
2937
  {
2844
2938
  "name": "fileUpload",
2939
+ "description": "The attributes for a file upload field.\n\nThe field is a plain `<input type=\"file\">`. The button inside is the browser's own, and its text comes from the operating system's language. It cannot be translated, so the label above the field must say what to upload.\n\n`accept` is a help in the file picker, not a barrier. The user can choose \"all files\", so the type must be checked on the server anyway.",
2940
+ "example": "<input {...fileUpload({ multiple: true, accept: \".bin\" })} />",
2845
2941
  "options": [
2846
2942
  {
2847
2943
  "name": "state",
@@ -2854,7 +2950,8 @@
2854
2950
  "success"
2855
2951
  ]
2856
2952
  },
2857
- "description": "Validation state. Default: `default`."
2953
+ "description": "Validation state. Default: `default`.",
2954
+ "default": "default"
2858
2955
  },
2859
2956
  {
2860
2957
  "name": "multiple",
@@ -2918,10 +3015,13 @@
2918
3015
  },
2919
3016
  "classes": {
2920
3017
  "list": "lt-file-upload-list"
2921
- }
3018
+ },
3019
+ "docs": "https://lo-tek.sobernetics.no/components/file-upload/"
2922
3020
  },
2923
3021
  {
2924
3022
  "name": "heading",
3023
+ "description": "The attributes for a heading.\n\nSize and level are two different things. The level is `<h1>` to `<h6>` and sets the structure screen readers navigate by; `size` only sets how large the text looks. An `<h2>` that should look small is correct; an `<h4>` chosen because it was small enough skips a level in the structure.",
3024
+ "example": "<h2 {...heading({ size: \"m\" })}>Attachments</h2>",
2925
3025
  "options": [
2926
3026
  {
2927
3027
  "name": "size",
@@ -2937,7 +3037,8 @@
2937
3037
  "mega"
2938
3038
  ]
2939
3039
  },
2940
- "description": "Visual size. Default: `l`."
3040
+ "description": "Visual size. Default: `l`.",
3041
+ "default": "l"
2941
3042
  }
2942
3043
  ],
2943
3044
  "returns": {
@@ -2972,10 +3073,13 @@
2972
3073
  }
2973
3074
  }
2974
3075
  }
2975
- }
3076
+ },
3077
+ "docs": "https://lo-tek.sobernetics.no/components/heading/"
2976
3078
  },
2977
3079
  {
2978
3080
  "name": "helpText",
3081
+ "description": "The attributes for help text below a field.",
3082
+ "example": "<p {...helpText({ variant: \"warning\" })}>…</p>",
2979
3083
  "options": [
2980
3084
  {
2981
3085
  "name": "variant",
@@ -2989,7 +3093,8 @@
2989
3093
  "warning"
2990
3094
  ]
2991
3095
  },
2992
- "description": "How loudly the text should speak. Default: `muted`."
3096
+ "description": "How loudly the text should speak. Default: `muted`.",
3097
+ "default": "muted"
2993
3098
  }
2994
3099
  ],
2995
3100
  "returns": {
@@ -3018,10 +3123,13 @@
3018
3123
  }
3019
3124
  }
3020
3125
  }
3021
- }
3126
+ },
3127
+ "docs": "https://lo-tek.sobernetics.no/components/help-text/"
3022
3128
  },
3023
3129
  {
3024
3130
  "name": "icon",
3131
+ "description": "The attributes for an icon. The class goes on an `<svg>`, and the svg comes from the app's icon set: LO-TEK ships no icons.\n\nThe icon is as large as the text around it, and is drawn in the colour the svg says, which should be `currentColor`. Without `label` it is decoration, and if it stands alone in a button, the button must get its name somewhere else: `aria-label` on the button, or a text with `lt-sr-only`. The core reports a button that lacks one.",
3132
+ "example": "<button {...button()} aria-label=\"Delete ticket #0331\">\n <svg {...icon()} viewBox=\"0 0 24 24\">…</svg>\n</button>",
3025
3133
  "options": [
3026
3134
  {
3027
3135
  "name": "label",
@@ -3054,10 +3162,13 @@
3054
3162
  }
3055
3163
  }
3056
3164
  }
3057
- }
3165
+ },
3166
+ "docs": "https://lo-tek.sobernetics.no/components/icon/"
3058
3167
  },
3059
3168
  {
3060
3169
  "name": "input",
3170
+ "description": "The attributes for a text field.\n\n`type` sets both the HTML type and the icon variant, so the two cannot get out of step. Write them by hand, and it is easy to get a date field that looks like a date field, but where nothing happens when the user presses the icon.",
3171
+ "example": "<input {...input({ type: \"date\", state: \"invalid\" })} />",
3061
3172
  "options": [
3062
3173
  {
3063
3174
  "name": "type",
@@ -3079,7 +3190,8 @@
3079
3190
  "time"
3080
3191
  ]
3081
3192
  },
3082
- "description": "The HTML type. Set as `type`, and decides the icon. Default: `text`."
3193
+ "description": "The HTML type. Set as `type`, and decides the icon. Default: `text`.",
3194
+ "default": "text"
3083
3195
  },
3084
3196
  {
3085
3197
  "name": "state",
@@ -3092,7 +3204,8 @@
3092
3204
  "success"
3093
3205
  ]
3094
3206
  },
3095
- "description": "Validation state. Default: `default`."
3207
+ "description": "Validation state. Default: `default`.",
3208
+ "default": "default"
3096
3209
  }
3097
3210
  ],
3098
3211
  "returns": {
@@ -3161,10 +3274,13 @@
3161
3274
  }
3162
3275
  }
3163
3276
  }
3164
- }
3277
+ },
3278
+ "docs": "https://lo-tek.sobernetics.no/components/input/"
3165
3279
  },
3166
3280
  {
3167
3281
  "name": "label",
3282
+ "description": "The attributes for a label.\n\nThe marker is only visual, so set `required` on the field itself as well, or the screen reader is not told that it must be filled in.",
3283
+ "example": "<label {...label({ required: \"symbol\" })} htmlFor=\"fixer\">Fixer's address</label>",
3168
3284
  "options": [
3169
3285
  {
3170
3286
  "name": "required",
@@ -3197,10 +3313,12 @@
3197
3313
  ],
3198
3314
  "returns": {
3199
3315
  "kind": "attributes"
3200
- }
3316
+ },
3317
+ "docs": "https://lo-tek.sobernetics.no/components/label/"
3201
3318
  },
3202
3319
  {
3203
3320
  "name": "legend",
3321
+ "description": "The attributes for the label of a group.\n\nThe same marker as `label()`, but on a `<legend>`.",
3204
3322
  "options": [
3205
3323
  {
3206
3324
  "name": "required",
@@ -3233,10 +3351,13 @@
3233
3351
  ],
3234
3352
  "returns": {
3235
3353
  "kind": "attributes"
3236
- }
3354
+ },
3355
+ "docs": "https://lo-tek.sobernetics.no/components/fieldset/"
3237
3356
  },
3238
3357
  {
3239
3358
  "name": "link",
3359
+ "description": "The attributes for a link.\n\nAn `<a>` without `href` is no link to a screen reader and is not in the tab order. `role` and `tabindex` give it both back while it is disabled.",
3360
+ "example": "<a {...link()} href=\"/small-print\">Read the small print</a>\n<a {...link({ disabled: true })}>See annual statement</a>",
3240
3361
  "options": [
3241
3362
  {
3242
3363
  "name": "disabled",
@@ -3269,10 +3390,13 @@
3269
3390
  }
3270
3391
  }
3271
3392
  }
3272
- }
3393
+ },
3394
+ "docs": "https://lo-tek.sobernetics.no/components/link/"
3273
3395
  },
3274
3396
  {
3275
3397
  "name": "list",
3398
+ "description": "The attributes for a list.\n\nThe class goes on `<ul>` or `<ol>`. `plain` and `divided` remove the bullets visually, but the list stays a list, so the screen reader still says how many items it has. That is why the variant exists: without it people would use `<div>` to get rid of the bullets. The exception is Safari, where a `<ul>` without visible bullets can need `role=\"list\"`. See the component page.",
3399
+ "example": "<ul {...list({ variant: \"divided\" })}>…</ul>",
3276
3400
  "options": [
3277
3401
  {
3278
3402
  "name": "variant",
@@ -3285,7 +3409,8 @@
3285
3409
  "divided"
3286
3410
  ]
3287
3411
  },
3288
- "description": "Bullets, plain rows, or rows with a line between them. Default: `default`."
3412
+ "description": "Bullets, plain rows, or rows with a line between them. Default: `default`.",
3413
+ "default": "default"
3289
3414
  }
3290
3415
  ],
3291
3416
  "returns": {
@@ -3311,10 +3436,13 @@
3311
3436
  }
3312
3437
  }
3313
3438
  }
3314
- }
3439
+ },
3440
+ "docs": "https://lo-tek.sobernetics.no/components/list/"
3315
3441
  },
3316
3442
  {
3317
3443
  "name": "pagination",
3444
+ "description": "The attributes for pagination.\n\nThe class goes on a `<ul>` inside a `<nav aria-label=\"Pagination\">`. Each page is a link with its own address, not a button: the user can then open page three in a new tab, and come back with the back button.\n\nThe current page is marked with `aria-current=\"page\"`, which both gives the colour and is what the screen reader announces.",
3445
+ "example": "<nav aria-label=\"Pagination\">\n <ul {...pagination()}>…</ul>\n</nav>",
3318
3446
  "options": [],
3319
3447
  "returns": {
3320
3448
  "kind": "attributes"
@@ -3330,10 +3458,13 @@
3330
3458
  },
3331
3459
  "classes": {
3332
3460
  "gap": "lt-pagination__gap"
3333
- }
3461
+ },
3462
+ "docs": "https://lo-tek.sobernetics.no/components/pagination/"
3334
3463
  },
3335
3464
  {
3336
3465
  "name": "paragraph",
3466
+ "description": "The attributes for a paragraph.\n\nThe width is limited to 70 characters. Longer lines make it hard to find the start of the next one, and that is the most common reason a text is hard to read even when the type is large enough.",
3467
+ "example": "<p {...paragraph({ variant: \"lead\" })}>A short summary of what the page is about.</p>",
3337
3468
  "options": [
3338
3469
  {
3339
3470
  "name": "size",
@@ -3346,7 +3477,8 @@
3346
3477
  "large"
3347
3478
  ]
3348
3479
  },
3349
- "description": "Text size. Default: `medium`."
3480
+ "description": "Text size. Default: `medium`.",
3481
+ "default": "medium"
3350
3482
  },
3351
3483
  {
3352
3484
  "name": "variant",
@@ -3358,7 +3490,8 @@
3358
3490
  "lead"
3359
3491
  ]
3360
3492
  },
3361
- "description": "`lead` is the introduction right below the heading. Default: `default`."
3493
+ "description": "`lead` is the introduction right below the heading. Default: `default`.",
3494
+ "default": "default"
3362
3495
  }
3363
3496
  ],
3364
3497
  "returns": {
@@ -3392,10 +3525,13 @@
3392
3525
  }
3393
3526
  }
3394
3527
  }
3395
- }
3528
+ },
3529
+ "docs": "https://lo-tek.sobernetics.no/components/paragraph/"
3396
3530
  },
3397
3531
  {
3398
3532
  "name": "popover",
3533
+ "description": "The attributes for a popover.\n\nThe server writes both the button and the panel, with the wiring between them. `<lt-popover>` works out the position and closes on a click outside.",
3534
+ "example": "const box = lt.popover({ id: \"help\" })\n\n<lt-popover>\n <button {...box.trigger}>What is the late fee?</button>\n <div {...box.panel}>EB 100 a night past the return date. No exceptions.</div>\n</lt-popover>",
3399
3535
  "options": [
3400
3536
  {
3401
3537
  "name": "id",
@@ -3403,7 +3539,7 @@
3403
3539
  "type": {
3404
3540
  "kind": "text"
3405
3541
  },
3406
- "description": "Id of the panel. Connects the button to the panel with `aria-controls`."
3542
+ "description": "Id of the panel. Connects the button to the panel with `aria-controls`. Make it from a key in the data, never from the loop index or a counter, so it is unique on the page and the same on every render."
3407
3543
  },
3408
3544
  {
3409
3545
  "name": "open",
@@ -3425,7 +3561,8 @@
3425
3561
  "top-end"
3426
3562
  ]
3427
3563
  },
3428
- "description": "Which edge the panel hangs from. Default: `bottom-start`."
3564
+ "description": "Which edge the panel hangs from. Default: `bottom-start`.",
3565
+ "default": "bottom-start"
3429
3566
  }
3430
3567
  ],
3431
3568
  "returns": {
@@ -3521,10 +3658,23 @@
3521
3658
  }
3522
3659
  }
3523
3660
  }
3661
+ },
3662
+ "docs": "https://lo-tek.sobernetics.no/components/popover/",
3663
+ "parts": {
3664
+ "trigger": {
3665
+ "element": "lt-popover",
3666
+ "part": "trigger"
3667
+ },
3668
+ "panel": {
3669
+ "element": "lt-popover",
3670
+ "part": "panel"
3671
+ }
3524
3672
  }
3525
3673
  },
3526
3674
  {
3527
3675
  "name": "progress",
3676
+ "description": "The attributes for a progress bar on a `<progress>`.\n\nThe element has the semantics built in: the browser exposes it as `role=\"progressbar\"` and announces how far it has come. You need neither a role, `aria-valuenow` nor `aria-valuemax`.\n\nLeave out `value`, and the bar becomes indeterminate: \"something is going on, we do not know for how long\". That is the same as `lt.spinner()` says, so choose one of them.\n\n`max` defaults to 100 and is always written out, against the rule that a default gives no attribute. The reason is that the rule is about appearance, where the CSS already has the answer. `max` is semantics, and HTML's own default is `1`: a `<progress value=\"40\">` without `max` shows a full bar, without anything saying so.",
3677
+ "example": "<progress {...progress({ value: 3, max: 7, label: \"Step 3 of 7\" })} />",
3528
3678
  "options": [
3529
3679
  {
3530
3680
  "name": "value",
@@ -3628,10 +3778,13 @@
3628
3778
  }
3629
3779
  }
3630
3780
  }
3631
- }
3781
+ },
3782
+ "docs": "https://lo-tek.sobernetics.no/components/progress/"
3632
3783
  },
3633
3784
  {
3634
3785
  "name": "radio",
3786
+ "description": "The attributes for a radio button.\n\nAll the buttons in a group must have the same `name`. That connection is what lets only one be selected, and lets the arrow keys move between them. You set `name` yourself, since only you know what the group is called.",
3787
+ "example": "<input {...radio()} name=\"delivery\" value=\"post\" />",
3635
3788
  "options": [
3636
3789
  {
3637
3790
  "name": "state",
@@ -3644,7 +3797,8 @@
3644
3797
  "success"
3645
3798
  ]
3646
3799
  },
3647
- "description": "Validation state. Default: `default`."
3800
+ "description": "Validation state. Default: `default`.",
3801
+ "default": "default"
3648
3802
  }
3649
3803
  ],
3650
3804
  "returns": {
@@ -3675,10 +3829,13 @@
3675
3829
  },
3676
3830
  "classes": {
3677
3831
  "row": "lt-radio-row"
3678
- }
3832
+ },
3833
+ "docs": "https://lo-tek.sobernetics.no/components/radio/"
3679
3834
  },
3680
3835
  {
3681
3836
  "name": "search",
3837
+ "description": "The attributes for a search field.\n\nThe field is an `lt-input` with room for the magnifying glass, so both classes are set. `type=\"search\"` gives the browser's own cross for clearing the field, and on mobile a keyboard with \"Search\" instead of \"Enter\".",
3838
+ "example": "<input {...search()} name=\"q\" />",
3682
3839
  "options": [
3683
3840
  {
3684
3841
  "name": "state",
@@ -3691,7 +3848,8 @@
3691
3848
  "success"
3692
3849
  ]
3693
3850
  },
3694
- "description": "Validation state. Default: `default`."
3851
+ "description": "Validation state. Default: `default`.",
3852
+ "default": "default"
3695
3853
  }
3696
3854
  ],
3697
3855
  "returns": {
@@ -3722,10 +3880,13 @@
3722
3880
  },
3723
3881
  "classes": {
3724
3882
  "row": "lt-search-row"
3725
- }
3883
+ },
3884
+ "docs": "https://lo-tek.sobernetics.no/components/search/"
3726
3885
  },
3727
3886
  {
3728
3887
  "name": "select",
3888
+ "description": "The attributes for a dropdown list.",
3889
+ "example": "<select {...select({ state: \"invalid\" })} />\n<select {...select({ picker: \"styled\" })} />",
3729
3890
  "options": [
3730
3891
  {
3731
3892
  "name": "state",
@@ -3738,7 +3899,8 @@
3738
3899
  "success"
3739
3900
  ]
3740
3901
  },
3741
- "description": "Validation state. Default: `default`."
3902
+ "description": "Validation state. Default: `default`.",
3903
+ "default": "default"
3742
3904
  },
3743
3905
  {
3744
3906
  "name": "picker",
@@ -3750,7 +3912,8 @@
3750
3912
  "styled"
3751
3913
  ]
3752
3914
  },
3753
- "description": "Who draws the dropdown list. Default: `auto`."
3915
+ "description": "Who draws the dropdown list. Default: `auto`.",
3916
+ "default": "auto"
3754
3917
  }
3755
3918
  ],
3756
3919
  "returns": {
@@ -3785,10 +3948,13 @@
3785
3948
  }
3786
3949
  }
3787
3950
  }
3788
- }
3951
+ },
3952
+ "docs": "https://lo-tek.sobernetics.no/components/select/"
3789
3953
  },
3790
3954
  {
3791
3955
  "name": "sessionTimeout",
3956
+ "description": "The attributes for the warning that a signed-in session is about to expire.\n\nThe renderer writes the dialog and all of the text, in its own language and with its own translation tool. The component only keeps time: it opens and closes the dialog, fills in the number and announces the countdown.\n\nThe buttons are plain buttons in a `<form method=\"dialog\">`. The browser closes the dialog and sets `returnValue` to the button's `value`, and the component reads it. `data-ignore-morph` asks Datastar's morph to leave the warning alone. Without it the morph takes `open` and the number while the dialog is in the top layer. The component puts `open` back, and the number comes back on the next tick. With the attribute the number stays the whole time.",
3957
+ "example": "const session = lt.sessionTimeout({ titleId: \"session-title\" })\n\n<lt-session-timeout {...session.host}>\n <dialog {...session.dialog}>\n <h2 {...session.title}>Your session is ending</h2>\n <p {...session.text}>\n Your session on the Lattice ends in <span {...session.count}></span>. Unsaved routes will be lost.\n </p>\n <span {...session.live}></span>\n <form {...session.actions}>\n <button {...session.extend}>Stay connected</button>\n <button {...session.logout}>Log out</button>\n </form>\n </dialog>\n</lt-session-timeout>",
3792
3958
  "options": [
3793
3959
  {
3794
3960
  "name": "titleId",
@@ -3796,7 +3962,7 @@
3796
3962
  "type": {
3797
3963
  "kind": "text"
3798
3964
  },
3799
- "description": "Id of the heading. The dialog takes its name from it with `aria-labelledby`."
3965
+ "description": "Id of the heading. The dialog takes its name from it with `aria-labelledby`. Make it from a key in the data, never from the loop index or a counter, so it is unique on the page and the same on every render."
3800
3966
  },
3801
3967
  {
3802
3968
  "name": "warnAt",
@@ -4046,10 +4212,43 @@
4046
4212
  }
4047
4213
  }
4048
4214
  }
4215
+ },
4216
+ "docs": "https://lo-tek.sobernetics.no/components/session-timeout/",
4217
+ "parts": {
4218
+ "dialog": {
4219
+ "element": "lt-session-timeout",
4220
+ "part": "dialog"
4221
+ },
4222
+ "title": {
4223
+ "element": "lt-session-timeout",
4224
+ "part": "title"
4225
+ },
4226
+ "text": {
4227
+ "element": "lt-session-timeout",
4228
+ "part": "text"
4229
+ },
4230
+ "count": {
4231
+ "element": "lt-session-timeout",
4232
+ "part": "count"
4233
+ },
4234
+ "live": {
4235
+ "element": "lt-session-timeout",
4236
+ "part": "status"
4237
+ },
4238
+ "extend": {
4239
+ "element": "lt-session-timeout",
4240
+ "part": "button"
4241
+ },
4242
+ "logout": {
4243
+ "element": "lt-session-timeout",
4244
+ "part": "button"
4245
+ }
4049
4246
  }
4050
4247
  },
4051
4248
  {
4052
4249
  "name": "skeleton",
4250
+ "description": "The attributes for a placeholder while the content loads.\n\nThe shapes are always hidden from screen readers. They say nothing about what is coming, and a row of empty boxes being announced is worse than silence. Announce the wait in one place instead, with `role=\"status\"`.",
4251
+ "example": "<div {...skeleton({ variant: \"text\" })} />",
4053
4252
  "options": [
4054
4253
  {
4055
4254
  "name": "variant",
@@ -4062,7 +4261,8 @@
4062
4261
  "circle"
4063
4262
  ]
4064
4263
  },
4065
- "description": "Shape. Default: `block`."
4264
+ "description": "Shape. Default: `block`.",
4265
+ "default": "block"
4066
4266
  }
4067
4267
  ],
4068
4268
  "returns": {
@@ -4089,10 +4289,13 @@
4089
4289
  }
4090
4290
  }
4091
4291
  }
4092
- }
4292
+ },
4293
+ "docs": "https://lo-tek.sobernetics.no/components/skeleton/"
4093
4294
  },
4094
4295
  {
4095
4296
  "name": "skipLink",
4297
+ "description": "The attributes for a skip-to-content link.\n\nThe link must be the first element in `<body>`, and point to the id of the main content. It sits off screen until it gets focus, and therefore cannot be hidden with `display: none`: the keyboard could not reach it either, and the whole point is lost.",
4298
+ "example": "<a {...skipLink()} href=\"#main-content\">Skip to main content</a>",
4096
4299
  "options": [],
4097
4300
  "returns": {
4098
4301
  "kind": "attributes"
@@ -4105,10 +4308,13 @@
4105
4308
  },
4106
4309
  "options": {}
4107
4310
  }
4108
- }
4311
+ },
4312
+ "docs": "https://lo-tek.sobernetics.no/components/skip-link/"
4109
4313
  },
4110
4314
  {
4111
4315
  "name": "spinner",
4316
+ "description": "The attributes for a loading indicator.\n\nWithout `label` the ring is only a shape, and is hidden from screen readers with `aria-hidden`, as an icon without a label is. With `label` it becomes a `role=\"status\"` that says what is going on. If you already have a visible text next to it, let that carry the message and drop `label`, so the same thing is not announced twice.",
4317
+ "example": "<span {...spinner({ label: \"Loading sectors\" })} />",
4112
4318
  "options": [
4113
4319
  {
4114
4320
  "name": "size",
@@ -4121,7 +4327,8 @@
4121
4327
  "large"
4122
4328
  ]
4123
4329
  },
4124
- "description": "Size. Default: `medium`."
4330
+ "description": "Size. Default: `medium`.",
4331
+ "default": "medium"
4125
4332
  },
4126
4333
  {
4127
4334
  "name": "label",
@@ -4139,7 +4346,8 @@
4139
4346
  "kind": "attributes",
4140
4347
  "table": {
4141
4348
  "base": {
4142
- "class": "lt-spinner"
4349
+ "class": "lt-spinner",
4350
+ "aria-hidden": "true"
4143
4351
  },
4144
4352
  "options": {
4145
4353
  "size": {
@@ -4156,6 +4364,7 @@
4156
4364
  "label": {
4157
4365
  "cases": {},
4158
4366
  "template": {
4367
+ "aria-hidden": null,
4159
4368
  "role": "status",
4160
4369
  "aria-label": "{label}"
4161
4370
  },
@@ -4165,10 +4374,13 @@
4165
4374
  }
4166
4375
  }
4167
4376
  }
4168
- }
4377
+ },
4378
+ "docs": "https://lo-tek.sobernetics.no/components/spinner/"
4169
4379
  },
4170
4380
  {
4171
4381
  "name": "srOnly",
4382
+ "description": "The attributes for text that only screen readers should get.\n\nUse it when a label or explanation is needed by someone listening to the page, but would be noise to someone looking at it. If the content should be gone for everyone, use `hidden` instead.",
4383
+ "example": "<span {...srOnly()}>Opens in a new tab</span>",
4172
4384
  "options": [],
4173
4385
  "returns": {
4174
4386
  "kind": "attributes"
@@ -4181,10 +4393,12 @@
4181
4393
  },
4182
4394
  "options": {}
4183
4395
  }
4184
- }
4396
+ },
4397
+ "docs": "https://lo-tek.sobernetics.no/components/sr-only/"
4185
4398
  },
4186
4399
  {
4187
4400
  "name": "suggestion",
4401
+ "description": "The attributes for a field with a suggestion list.\n\nThe server writes both the field and the list. The web component filters, handles the arrow keys and keeps `aria-activedescendant` in sync. It renders nothing: it used to create the whole field itself, and then there was nothing to fill in before the script had run.\n\nNo preserve list. The attributes that change while the user types are set by the web component, and it sets them back when a patch strips them. If something else has already filtered, set `prefiltered` on `<lt-suggestion>`, and the web component leaves both the options and the empty message alone. If the server should also decide whether the list is expanded and which option is highlighted, set `server-controlled` as well.",
4188
4402
  "options": [
4189
4403
  {
4190
4404
  "name": "invalid",
@@ -4268,7 +4482,7 @@
4268
4482
  "type": {
4269
4483
  "kind": "text"
4270
4484
  },
4271
- "description": "Shared id stem. The control becomes `${id}`, the list `${id}-list`, and the options `${id}-option-${key}`."
4485
+ "description": "Shared id stem. The control becomes `${id}`, the list `${id}-list`, and the options `${id}-option-${key}`. Make it from a key in the data, never from the loop index or a counter, so it is unique on the page and the same on every render."
4272
4486
  },
4273
4487
  {
4274
4488
  "name": "keys",
@@ -4345,10 +4559,43 @@
4345
4559
  ]
4346
4560
  }
4347
4561
  }
4562
+ },
4563
+ "docs": "https://lo-tek.sobernetics.no/components/suggestion/",
4564
+ "parts": {
4565
+ "label": {
4566
+ "element": "lt-suggestion",
4567
+ "part": "label"
4568
+ },
4569
+ "control": {
4570
+ "element": "lt-suggestion",
4571
+ "part": "control"
4572
+ },
4573
+ "list": {
4574
+ "element": "lt-suggestion",
4575
+ "part": "list"
4576
+ },
4577
+ "options": {
4578
+ "element": "lt-suggestion",
4579
+ "part": "option"
4580
+ },
4581
+ "empty": {
4582
+ "element": "lt-suggestion",
4583
+ "part": "empty"
4584
+ },
4585
+ "status": {
4586
+ "element": "lt-suggestion",
4587
+ "part": "status"
4588
+ },
4589
+ "value": {
4590
+ "element": "lt-suggestion",
4591
+ "part": "value"
4592
+ }
4348
4593
  }
4349
4594
  },
4350
4595
  {
4351
4596
  "name": "switch",
4597
+ "description": "The attributes for an on/off switch.\n\nThe switch is a checkbox with `role=\"switch\"`. Screen readers then announce it as \"on\" and \"off\" rather than \"checked\", while the keyboard and `FormData` work as for a plain checkbox.\n\nThe switch has no validation state. A choice that is on or off cannot be invalid in itself; if turning it on is required, the requirement belongs in a checkbox with `required`.",
4598
+ "example": "<input {...switchControl()} />",
4352
4599
  "options": [
4353
4600
  {
4354
4601
  "name": "disabled",
@@ -4384,10 +4631,13 @@
4384
4631
  },
4385
4632
  "classes": {
4386
4633
  "row": "lt-switch-row"
4387
- }
4634
+ },
4635
+ "docs": "https://lo-tek.sobernetics.no/components/switch/"
4388
4636
  },
4389
4637
  {
4390
4638
  "name": "table",
4639
+ "description": "The attributes for a table.\n\nThe class goes on a real `<table>` with `<thead>` and `<th>`. The header cells are what let screen readers say \"Amount, 1,240 kroner\" when the user is in a cell. Build the table from `<div>` elements, and that connection does not exist.",
4640
+ "example": "<table {...table({ variant: \"striped\" })}>…</table>",
4391
4641
  "options": [
4392
4642
  {
4393
4643
  "name": "variant",
@@ -4399,7 +4649,8 @@
4399
4649
  "striped"
4400
4650
  ]
4401
4651
  },
4402
- "description": "Every other row on a muted surface. Default: `default`."
4652
+ "description": "Every other row on a muted surface. Default: `default`.",
4653
+ "default": "default"
4403
4654
  },
4404
4655
  {
4405
4656
  "name": "hoverable",
@@ -4441,10 +4692,13 @@
4441
4692
  },
4442
4693
  "classes": {
4443
4694
  "scroll": "lt-table-scroll"
4444
- }
4695
+ },
4696
+ "docs": "https://lo-tek.sobernetics.no/components/table/"
4445
4697
  },
4446
4698
  {
4447
4699
  "name": "tableSort",
4700
+ "description": "The attributes for a sortable column header.\n\n`aria-sort` is on the `<th>`, not on the button, because the cell is what describes the column. Only the sorted column has the attribute. The arrow is drawn from the same attribute, so what the screen reader says and what is shown cannot drift apart. The sorting itself is the app's job.",
4701
+ "example": "const date = lt.tableSort({ direction: \"descending\" })\n\n<th scope=\"col\" {...date.header}>\n <button {...date.button}>Date</button>\n</th>",
4448
4702
  "options": [
4449
4703
  {
4450
4704
  "name": "direction",
@@ -4503,10 +4757,13 @@
4503
4757
  }
4504
4758
  }
4505
4759
  }
4506
- }
4760
+ },
4761
+ "docs": "https://lo-tek.sobernetics.no/components/table/"
4507
4762
  },
4508
4763
  {
4509
4764
  "name": "tabs",
4765
+ "description": "The attributes for a tab list where the tabs are links to the panels.\n\nOnly links, ids and classes. Without JavaScript the tab list is then a plain list of links, and the panels follow one another: a table of contents that works for everyone. The roles, which tab is selected and which panels are hidden are written by `<lt-tabs>`. Had they been here, the markup without JavaScript would have said \"tab\" about a link that does not behave like one, and hidden the panels.\n\nThe first tab is selected when the page loads. Another one comes from the address, such as `#file-attachments`, or from `selected` and `select()` on `<lt-tabs>`.",
4766
+ "example": "const tabs = lt.tabs({ id: \"file\", keys: [\"claim\", \"attachments\"], label: \"Parts of the claim\" })",
4510
4767
  "options": [
4511
4768
  {
4512
4769
  "name": "id",
@@ -4514,7 +4771,7 @@
4514
4771
  "type": {
4515
4772
  "kind": "text"
4516
4773
  },
4517
- "description": "The shared id stem. The panels become `${id}-${key}`, and the headings in them `${id}-${key}-heading`."
4774
+ "description": "The shared id stem. The panels become `${id}-${key}`, and the headings in them `${id}-${key}-heading`. Make it from a key in the data, never from the loop index or a counter, so it is unique on the page and the same on every render."
4518
4775
  },
4519
4776
  {
4520
4777
  "name": "keys",
@@ -4522,7 +4779,7 @@
4522
4779
  "type": {
4523
4780
  "kind": "textList"
4524
4781
  },
4525
- "description": "One key per tab, from the data, such as `[\"application\", \"attachments\"]`. The key gives the panel its id, and the link points to it. An index is not a key: if the server inserts a tab first, the same index points to another panel. An empty key, whitespace and two identical keys throw."
4782
+ "description": "One key per tab, from the data, such as `[\"claim\", \"attachments\"]`. The key gives the panel its id, and the link points to it. An index is not a key: if the server inserts a tab first, the same index points to another panel. An empty key, whitespace and two identical keys throw."
4526
4783
  },
4527
4784
  {
4528
4785
  "name": "label",
@@ -4558,10 +4815,27 @@
4558
4815
  }
4559
4816
  }
4560
4817
  }
4818
+ },
4819
+ "docs": "https://lo-tek.sobernetics.no/components/tabs/",
4820
+ "parts": {
4821
+ "list": {
4822
+ "element": "lt-tabs",
4823
+ "part": "list"
4824
+ },
4825
+ "tabs": {
4826
+ "element": "lt-tabs",
4827
+ "part": "tab"
4828
+ },
4829
+ "panels": {
4830
+ "element": "lt-tabs",
4831
+ "part": "panel"
4832
+ }
4561
4833
  }
4562
4834
  },
4563
4835
  {
4564
4836
  "name": "tag",
4837
+ "description": "The attributes for a tag.\n\nA tag labels content: keywords on an article, selected filters above a list. If it says what state something is in, Badge is right instead.\n\n`selectable` gives hover and a focus ring, but makes nothing pressable. The element must be a `<button>`, and you set `aria-pressed` yourself, since only you know whether the tag is selected.",
4838
+ "example": "<button {...tag({ selectable: true })} aria-pressed=\"true\">Cutter</button>",
4565
4839
  "options": [
4566
4840
  {
4567
4841
  "name": "variant",
@@ -4573,7 +4847,8 @@
4573
4847
  "filled"
4574
4848
  ]
4575
4849
  },
4576
- "description": "Outline or muted surface. Default: `outline`."
4850
+ "description": "Outline or muted surface. Default: `outline`.",
4851
+ "default": "outline"
4577
4852
  },
4578
4853
  {
4579
4854
  "name": "selectable",
@@ -4612,10 +4887,13 @@
4612
4887
  }
4613
4888
  }
4614
4889
  }
4615
- }
4890
+ },
4891
+ "docs": "https://lo-tek.sobernetics.no/components/tag/"
4616
4892
  },
4617
4893
  {
4618
4894
  "name": "textarea",
4895
+ "description": "The attributes for a multi-line text field.",
4896
+ "example": "<textarea {...textarea({ state: \"invalid\" })} />",
4619
4897
  "options": [
4620
4898
  {
4621
4899
  "name": "state",
@@ -4628,7 +4906,8 @@
4628
4906
  "success"
4629
4907
  ]
4630
4908
  },
4631
- "description": "Validation state. Default: `default`."
4909
+ "description": "Validation state. Default: `default`.",
4910
+ "default": "default"
4632
4911
  }
4633
4912
  ],
4634
4913
  "returns": {
@@ -4655,10 +4934,12 @@
4655
4934
  }
4656
4935
  }
4657
4936
  }
4658
- }
4937
+ },
4938
+ "docs": "https://lo-tek.sobernetics.no/components/textarea/"
4659
4939
  },
4660
4940
  {
4661
4941
  "name": "toast",
4942
+ "description": "The attributes for the toast region.\n\nThe region is `role=\"status\"` and not `role=\"alert\"`. A message that comes because something went well should not interrupt what the screen reader is doing.\n\n`data-ignore-morph` is not optional. `<lt-toast>` creates and removes its message elements inside the region, and without the attribute the Datastar morph tears them away at the next patch, because the server's version of the region is empty. The attribute must be in the HTML the server sends: the component cannot set it on itself, since the morph reads it from the server's node.",
4662
4943
  "options": [
4663
4944
  {
4664
4945
  "name": "label",
@@ -4759,10 +5040,13 @@
4759
5040
  }
4760
5041
  }
4761
5042
  }
4762
- }
5043
+ },
5044
+ "docs": "https://lo-tek.sobernetics.no/components/toast/"
4763
5045
  },
4764
5046
  {
4765
5047
  "name": "toggleGroup",
5048
+ "description": "The attributes for a group where one option is selected at a time.\n\nThe group is radio buttons in disguise. Each option is a `<label>` with an `<input type=\"radio\">` inside, visually shrunk away. You then keep the arrow keys, `FormData` and the screen reader saying \"2 of 3\" without writing any of it again.\n\nIf the choices are actions rather than a setting, such as \"bold, italic, underline\", buttons with `aria-pressed` belong there instead.",
5049
+ "example": "<fieldset {...toggleGroup()}>\n <legend class=\"lt-sr-only\">View</legend>\n <label class={toggleGroup.option}>\n <input type=\"radio\" name=\"view\" value=\"dry\" checked /> Dry\n </label>\n</fieldset>",
4766
5050
  "options": [],
4767
5051
  "returns": {
4768
5052
  "kind": "attributes"
@@ -4778,10 +5062,13 @@
4778
5062
  },
4779
5063
  "classes": {
4780
5064
  "option": "lt-toggle-group__option"
4781
- }
5065
+ },
5066
+ "docs": "https://lo-tek.sobernetics.no/components/toggle-group/"
4782
5067
  },
4783
5068
  {
4784
5069
  "name": "tooltip",
5070
+ "description": "The attributes for the wrapper around a trigger and a tooltip bubble.\n\nThe bubble shows on hover and on focus. The focus part is not optional: a bubble that only comes with the mouse does not exist for keyboard users.\n\nThe text in the bubble must be connected to the trigger with `aria-describedby`. Without it the bubble is just something that appears on screen, and is never announced.\n\nUse it for something extra, never for something the user must have. On a touch screen there is neither hover nor focus before something is tapped.",
5071
+ "example": "<span {...tooltip()}>\n <button aria-describedby=\"hint\">Appraise</button>\n <span class={tooltip.bubble} role=\"tooltip\" id=\"hint\">At street value</span>\n</span>",
4785
5072
  "options": [],
4786
5073
  "returns": {
4787
5074
  "kind": "attributes"
@@ -4797,7 +5084,8 @@
4797
5084
  },
4798
5085
  "classes": {
4799
5086
  "bubble": "lt-tooltip__bubble"
4800
- }
5087
+ },
5088
+ "docs": "https://lo-tek.sobernetics.no/components/tooltip/"
4801
5089
  }
4802
5090
  ],
4803
5091
  "tokens": {