@lo-tek/interfaces 0.40.0 → 0.41.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 (82) hide show
  1. package/CHANGELOG.md +78 -0
  2. package/agent/astro.md +9 -7
  3. package/agent/bundles.md +8 -6
  4. package/agent/datastar.md +53 -51
  5. package/agent/html.md +55 -53
  6. package/agent/kotlin.md +622 -0
  7. package/agent/react.md +8 -6
  8. package/agent/templates.md +60 -63
  9. package/core/lotek-core.wasm +0 -0
  10. package/core/lotek.wasm +0 -0
  11. package/dist/components/css/steps/steps.d.ts +23 -0
  12. package/dist/components/css/steps/steps.d.ts.map +1 -0
  13. package/dist/components/css/steps/steps.js +21 -0
  14. package/dist/components/css/steps/steps.js.map +1 -0
  15. package/dist/components/events.d.ts +3 -1
  16. package/dist/components/events.d.ts.map +1 -1
  17. package/dist/components/frame/error-summary/lt-error-summary.js +1 -1
  18. package/dist/components/frame/popover/lt-popover.d.ts.map +1 -1
  19. package/dist/components/frame/popover/lt-popover.js +4 -19
  20. package/dist/components/frame/popover/lt-popover.js.map +1 -1
  21. package/dist/components/frame/term/lt-term.d.ts +114 -0
  22. package/dist/components/frame/term/lt-term.d.ts.map +1 -0
  23. package/dist/components/frame/term/lt-term.js +530 -0
  24. package/dist/components/frame/term/lt-term.js.map +1 -0
  25. package/dist/components/frame/term/term.d.ts +55 -0
  26. package/dist/components/frame/term/term.d.ts.map +1 -0
  27. package/dist/components/frame/term/term.js +55 -0
  28. package/dist/components/frame/term/term.js.map +1 -0
  29. package/dist/components/host-element.d.ts +19 -0
  30. package/dist/components/host-element.d.ts.map +1 -1
  31. package/dist/components/host-element.js +37 -0
  32. package/dist/components/host-element.js.map +1 -1
  33. package/dist/index.d.ts +4 -0
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +3 -0
  36. package/dist/index.js.map +1 -1
  37. package/dist/jsx/react.d.ts +3 -1
  38. package/dist/jsx/react.d.ts.map +1 -1
  39. package/dist/lotek.css +351 -1
  40. package/dist/lt.d.ts +8 -1
  41. package/dist/lt.d.ts.map +1 -1
  42. package/dist/lt.js +9 -1
  43. package/dist/lt.js.map +1 -1
  44. package/dist/react.d.ts +19 -0
  45. package/dist/react.d.ts.map +1 -1
  46. package/dist/react.js +14 -0
  47. package/dist/react.js.map +1 -1
  48. package/dist/register.d.ts.map +1 -1
  49. package/dist/register.js +2 -0
  50. package/dist/register.js.map +1 -1
  51. package/dist/vocabulary/classes.d.ts.map +1 -1
  52. package/dist/vocabulary/classes.js +60 -6
  53. package/dist/vocabulary/classes.js.map +1 -1
  54. package/dist/vocabulary/elements.d.ts.map +1 -1
  55. package/dist/vocabulary/elements.js +15 -2
  56. package/dist/vocabulary/elements.js.map +1 -1
  57. package/dist/vocabulary/types.d.ts +8 -3
  58. package/dist/vocabulary/types.d.ts.map +1 -1
  59. package/live-templates-kotlin.xml +15 -10
  60. package/live-templates.xml +12 -6
  61. package/manifest/files.json +18 -0
  62. package/manifest/manifest.json +145 -10
  63. package/package.json +15 -5
  64. package/src/components/css/steps/steps.css +240 -0
  65. package/src/components/css/steps/steps.ts +26 -0
  66. package/src/components/events.ts +3 -1
  67. package/src/components/frame/error-summary/lt-error-summary.ts +1 -1
  68. package/src/components/frame/popover/lt-popover.ts +4 -24
  69. package/src/components/frame/term/lt-term.ts +573 -0
  70. package/src/components/frame/term/term.css +177 -0
  71. package/src/components/frame/term/term.ts +87 -0
  72. package/src/components/host-element.ts +46 -0
  73. package/src/index.ts +4 -0
  74. package/src/jsx/react.ts +4 -0
  75. package/src/lt.ts +9 -1
  76. package/src/react.ts +15 -0
  77. package/src/register.ts +2 -0
  78. package/src/tokens/tokens.css +4 -0
  79. package/src/vocabulary/classes.ts +60 -6
  80. package/src/vocabulary/elements.ts +15 -2
  81. package/src/vocabulary/types.ts +8 -3
  82. package/web-types.json +31 -10
@@ -1,13 +1,13 @@
1
1
  <!-- Generated by interfaces/scripts/generate-vocabulary.ts. Do not edit. -->
2
2
  <templateSet group="LO-TEK">
3
- <template name="lt-field" value="&lt;!-- novalidate: otherwise the browser stops an empty required field before&#10; the submit event, and the error text below is never shown. --&gt;&#10;&lt;form id=&quot;$order_form$&quot; novalidate&gt;&#10; &lt;lt-field&gt;&#10; &lt;label class=&quot;lt-label&quot; for=&quot;$codename$&quot; data-required=&quot;symbol&quot;&gt;Codename&lt;/label&gt;&#10; &lt;input class=&quot;lt-input&quot; id=&quot;$codename$&quot; type=&quot;text&quot; name=&quot;codename&quot; required /&gt;&#10; &lt;p class=&quot;lt-error-text&quot; hidden&gt;Enter a codename.&lt;/p&gt;&#10; &lt;/lt-field&gt;&#10;&#10; &lt;button class=&quot;lt-button&quot; type=&quot;submit&quot;&gt;Order the job&lt;/button&gt;&#10;&lt;/form&gt;" 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." toReformat="false" toShortenFQNames="false">
3
+ <template name="lt-field" value="&lt;!-- novalidate: otherwise the browser stops an empty required field before&#10; the submit event, and the error text below is never shown. --&gt;&#10;&lt;form id=&quot;$order_form$&quot; action=&quot;/jobs&quot; method=&quot;post&quot; novalidate&gt;&#10; &lt;lt-field&gt;&#10; &lt;label class=&quot;lt-label&quot; for=&quot;$codename$&quot; data-required=&quot;symbol&quot;&gt;Codename&lt;/label&gt;&#10; &lt;input class=&quot;lt-input&quot; id=&quot;$codename$&quot; type=&quot;text&quot; name=&quot;codename&quot; required /&gt;&#10; &lt;p class=&quot;lt-error-text&quot; hidden&gt;Enter a codename.&lt;/p&gt;&#10; &lt;/lt-field&gt;&#10;&#10; &lt;button class=&quot;lt-button&quot; type=&quot;submit&quot;&gt;Order the job&lt;/button&gt;&#10;&lt;/form&gt;" 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." toReformat="false" toShortenFQNames="false">
4
4
  <variable name="order_form" expression="" defaultValue="" alwaysStopAt="true" />
5
5
  <variable name="codename" expression="" defaultValue="" alwaysStopAt="true" />
6
6
  <context>
7
7
  <option name="HTML" value="true" />
8
8
  </context>
9
9
  </template>
10
- <template name="lt-tabs" value="&lt;!-- The links point to the panels. The component writes the roles, the&#10; selection and `hidden`. Without JavaScript this is a table of contents. --&gt;&#10;&lt;lt-tabs id=&quot;$claim_tabs$&quot;&gt;&#10; &lt;ul class=&quot;lt-tabs__list&quot; aria-label=&quot;Parts of the claim&quot;&gt;&#10; &lt;li&gt;&lt;a href=&quot;#$claim$&quot;&gt;Claim&lt;/a&gt;&lt;/li&gt;&#10; &lt;li&gt;&lt;a href=&quot;#$attachments$&quot;&gt;Attachments&lt;/a&gt;&lt;/li&gt;&#10; &lt;/ul&gt;&#10; &lt;section class=&quot;lt-tabs__panel&quot; id=&quot;$claim$&quot;&gt;&#10; &lt;h2&gt;Claim&lt;/h2&gt;&#10; &lt;p&gt;Claim AV-2079-0481 was filed on 4 March 2079.&lt;/p&gt;&#10; &lt;/section&gt;&#10; &lt;section class=&quot;lt-tabs__panel&quot; id=&quot;$attachments$&quot;&gt;&#10; &lt;h2&gt;Attachments&lt;/h2&gt;&#10; &lt;p&gt;Three attachments have been uploaded.&lt;/p&gt;&#10; &lt;/section&gt;&#10;&lt;/lt-tabs&gt;" description="Tab list. The tabs are links to the panels in an `.lt-tabs__list`, so without JavaScript the list is a table of contents; `role=&quot;tab&quot;` on anything other than a link is not a tab. The component adds keyboard and focus handling, sets roles and wiring, gives each panel its tab's name, makes only the selected panel a tab stop, and hides the rest with `hidden=&quot;until-found&quot;`, so find in page and links into a panel reach them. Each selection is written to the URL. It never writes an id on a tab or a panel. When the page loads, the selected tab is the one with `aria-selected` from the server, otherwise the one the URL points to, otherwise the first one not hidden with `hidden`. Under `server-controlled` the URL never selects." toReformat="false" toShortenFQNames="false">
10
+ <template name="lt-tabs" value="&lt;!-- The links point to the panels. The component writes the roles, the&#10; selection and `hidden`. Without JavaScript this is a table of contents. --&gt;&#10;&lt;lt-tabs id=&quot;$claim_tabs$&quot;&gt;&#10; &lt;ul class=&quot;lt-tabs__list&quot; aria-label=&quot;Parts of the claim&quot;&gt;&#10; &lt;li&gt;&lt;a href=&quot;#$claim$&quot;&gt;Claim&lt;/a&gt;&lt;/li&gt;&#10; &lt;li&gt;&lt;a href=&quot;#$attachments$&quot;&gt;Attachments&lt;/a&gt;&lt;/li&gt;&#10; &lt;/ul&gt;&#10; &lt;section class=&quot;lt-tabs__panel&quot; id=&quot;$claim$&quot;&gt;&#10; &lt;h2&gt;Claim&lt;/h2&gt;&#10; &lt;p&gt;Claim AV-2079-0481 was filed on 4 March 2079.&lt;/p&gt;&#10; &lt;/section&gt;&#10; &lt;section class=&quot;lt-tabs__panel&quot; id=&quot;$attachments$&quot;&gt;&#10; &lt;h2&gt;Attachments&lt;/h2&gt;&#10; &lt;p&gt;Three attachments have been received. The death certificate arrived on 6 March.&lt;/p&gt;&#10; &lt;/section&gt;&#10;&lt;/lt-tabs&gt;" description="Tab list. The tabs are links to the panels in an `.lt-tabs__list`, so without JavaScript the list is a table of contents; `role=&quot;tab&quot;` on anything other than a link is not a tab. The component adds keyboard and focus handling, sets roles and wiring, gives each panel its tab's name, makes only the selected panel a tab stop, and hides the rest with `hidden=&quot;until-found&quot;`, so find in page and links into a panel reach them. Each selection is written to the URL. It never writes an id on a tab or a panel. When the page loads, the selected tab is the one with `aria-selected` from the server, otherwise the one the URL points to, otherwise the first one not hidden with `hidden`. Under `server-controlled` the URL never selects." toReformat="false" toShortenFQNames="false">
11
11
  <variable name="claim_tabs" expression="" defaultValue="" alwaysStopAt="true" />
12
12
  <variable name="claim" expression="" defaultValue="" alwaysStopAt="true" />
13
13
  <variable name="attachments" expression="" defaultValue="" alwaysStopAt="true" />
@@ -15,7 +15,7 @@
15
15
  <option name="HTML" value="true" />
16
16
  </context>
17
17
  </template>
18
- <template name="lt-error-summary" value="&lt;form novalidate&gt;&#10; &lt;!-- The component sets the class, role=&quot;alert&quot; and tabindex=&quot;-1&quot;; the id and aria-labelledby that name the box are the template's. --&gt;&#10; &lt;lt-error-summary id=&quot;$errors$&quot; aria-labelledby=&quot;$errors_title$&quot; hidden&gt;&#10; &lt;h2 id=&quot;$errors_title$&quot;&gt;&lt;/h2&gt;&#10; &lt;ul class=&quot;lt-list&quot;&gt;&lt;/ul&gt;&#10; &lt;/lt-error-summary&gt;&#10;&#10; &lt;label class=&quot;lt-label&quot; for=&quot;$fixer$&quot;&gt;Fixer's address&lt;/label&gt;&#10; &lt;input class=&quot;lt-input&quot; id=&quot;$fixer$&quot; type=&quot;email&quot; name=&quot;fixer&quot; /&gt;&#10; &lt;button class=&quot;lt-button&quot;&gt;Order the job&lt;/button&gt;&#10;&lt;/form&gt;" description="Error summary. Moves focus to the box when it appears, and to the control when a link in the list is followed." toReformat="false" toShortenFQNames="false">
18
+ <template name="lt-error-summary" value="&lt;form action=&quot;/jobs&quot; method=&quot;post&quot; novalidate&gt;&#10; &lt;!-- The component sets the class, role=&quot;alert&quot; and tabindex=&quot;-1&quot;; the id and aria-labelledby that name the box are the template's. --&gt;&#10; &lt;lt-error-summary id=&quot;$errors$&quot; aria-labelledby=&quot;$errors_title$&quot; hidden&gt;&#10; &lt;h2 id=&quot;$errors_title$&quot;&gt;&lt;/h2&gt;&#10; &lt;ul class=&quot;lt-list&quot;&gt;&lt;/ul&gt;&#10; &lt;/lt-error-summary&gt;&#10;&#10; &lt;label class=&quot;lt-label&quot; for=&quot;$fixer$&quot;&gt;Fixer's address&lt;/label&gt;&#10; &lt;input class=&quot;lt-input&quot; id=&quot;$fixer$&quot; type=&quot;email&quot; name=&quot;fixer&quot; /&gt;&#10; &lt;button class=&quot;lt-button&quot;&gt;Order the job&lt;/button&gt;&#10;&lt;/form&gt;" description="Error summary. Moves focus to the box when it appears, and to the control when a link in the list is followed." toReformat="false" toShortenFQNames="false">
19
19
  <variable name="errors" expression="" defaultValue="" alwaysStopAt="true" />
20
20
  <variable name="errors_title" expression="" defaultValue="" alwaysStopAt="true" />
21
21
  <variable name="fixer" expression="" defaultValue="" alwaysStopAt="true" />
@@ -23,13 +23,19 @@
23
23
  <option name="HTML" value="true" />
24
24
  </context>
25
25
  </template>
26
- <template name="lt-popover" value="&lt;!-- The component sets `popover` and `aria-controls`. You write the id on&#10; the panel: that is what the button points to. --&gt;&#10;&lt;lt-popover id=&quot;$menu$&quot; placement=&quot;bottom-end&quot;&gt;&#10; &lt;button class=&quot;lt-button&quot; data-variant=&quot;secondary&quot; type=&quot;button&quot;&gt;Actions&lt;/button&gt;&#10;&#10; &lt;ul class=&quot;lt-popover&quot; id=&quot;$actions$&quot;&gt;&#10; &lt;li&gt;&lt;button class=&quot;lt-button&quot; data-variant=&quot;ghost&quot; type=&quot;button&quot;&gt;Mark LDG-117 returned&lt;/button&gt;&lt;/li&gt;&#10; &lt;li&gt;&lt;button class=&quot;lt-button&quot; data-variant=&quot;ghost&quot; type=&quot;button&quot;&gt;Charge the late fee&lt;/button&gt;&lt;/li&gt;&#10; &lt;/ul&gt;&#10;&lt;/lt-popover&gt;" description="A panel that hangs from a button and closes itself. A button and a panel with the class `lt-popover` and an id from the template; the component sets `popover` and `aria-controls` where the markup came without them, but never the id." toReformat="false" toShortenFQNames="false">
26
+ <template name="lt-popover" value="&lt;!-- The component sets `popover` and `aria-controls`. You write the id on&#10; the panel: that is what the button points to. --&gt;&#10;&lt;lt-popover id=&quot;$menu$&quot; placement=&quot;bottom-end&quot;&gt;&#10; &lt;button class=&quot;lt-button&quot; data-variant=&quot;secondary&quot; type=&quot;button&quot;&gt;Actions&lt;/button&gt;&#10;&#10; &lt;!-- Each action posts to the server, so the menu closes when the next&#10; page arrives, and no script has to close it. --&gt;&#10; &lt;ul class=&quot;lt-popover&quot; id=&quot;$actions$&quot;&gt;&#10; &lt;li&gt;&#10; &lt;form action=&quot;/loans/LDG-117/return&quot; method=&quot;post&quot;&gt;&#10; &lt;button class=&quot;lt-button&quot; data-variant=&quot;ghost&quot;&gt;Mark LDG-117 returned&lt;/button&gt;&#10; &lt;/form&gt;&#10; &lt;/li&gt;&#10; &lt;li&gt;&#10; &lt;form action=&quot;/loans/LDG-117/late-fee&quot; method=&quot;post&quot;&gt;&#10; &lt;button class=&quot;lt-button&quot; data-variant=&quot;ghost&quot;&gt;Charge the late fee&lt;/button&gt;&#10; &lt;/form&gt;&#10; &lt;/li&gt;&#10; &lt;/ul&gt;&#10;&lt;/lt-popover&gt;" description="A panel that hangs from a button and closes itself. A button and a panel with the class `lt-popover` and an id from the template; the component sets `popover` and `aria-controls` where the markup came without them, but never the id." toReformat="false" toShortenFQNames="false">
27
27
  <variable name="menu" expression="" defaultValue="" alwaysStopAt="true" />
28
28
  <variable name="actions" expression="" defaultValue="" alwaysStopAt="true" />
29
29
  <context>
30
30
  <option name="HTML" value="true" />
31
31
  </context>
32
32
  </template>
33
+ <template name="lt-term" value="&lt;!-- The word links to the entry. The web component sets the class,&#10; aria-details and aria-expanded on the link. --&gt;&#10;&lt;p&gt;&#10; A claim is paid out unless the death was an&#10; &lt;lt-term&gt;&lt;a href=&quot;#$term_act_of_god$&quot;&gt;act of God&lt;/a&gt;&lt;/lt-term&gt;.&#10;&lt;/p&gt;&#10;&#10;&lt;!-- The entry is a &lt;div&gt; in a &lt;dl&gt;, with the id the link points to. --&gt;&#10;&lt;h2&gt;Glossary&lt;/h2&gt;&#10;&lt;dl&gt;&#10; &lt;div id=&quot;$term_act_of_god$&quot;&gt;&#10; &lt;dt&gt;Act of God&lt;/dt&gt;&#10; &lt;dd&gt;A cause of death the policy does not cover. The insurer can add to the list without notice.&lt;/dd&gt;&#10; &lt;/div&gt;&#10;&lt;/dl&gt;" description="A word in the text that shows its glossary entry in a card by the word. A link to the entry's id is enough; the component sets the class, `aria-details` and `aria-expanded` on the link, and shows the entry itself in the top layer while the card is open. Without JavaScript the word is a link to the glossary." toReformat="false" toShortenFQNames="false">
34
+ <variable name="term_act_of_god" expression="" defaultValue="" alwaysStopAt="true" />
35
+ <context>
36
+ <option name="HTML" value="true" />
37
+ </context>
38
+ </template>
33
39
  <template name="lt-suggestion" value="&lt;!-- You write for and id, so the field has a name without JavaScript. The&#10; component sets the roles and the wiring to the list. You write the&#10; status element: that is where the number of matches is announced. --&gt;&#10;&lt;lt-suggestion id=&quot;$breaker_search$&quot;&gt;&#10; &lt;label class=&quot;lt-label&quot; for=&quot;$breaker$&quot;&gt;Breaker&lt;/label&gt;&#10; &lt;input class=&quot;lt-input&quot; id=&quot;$breaker$&quot; name=&quot;breaker&quot; type=&quot;text&quot;&gt;&#10; &lt;ul class=&quot;lt-suggestion__list&quot;&gt;&#10; &lt;li&gt;Crowbar&lt;/li&gt;&#10; &lt;li&gt;Needle&lt;/li&gt;&#10; &lt;/ul&gt;&#10; &lt;p class=&quot;lt-suggestion__empty&quot;&gt;No matches&lt;/p&gt;&#10; &lt;span class=&quot;lt-sr-only&quot; role=&quot;status&quot;&gt;&lt;/span&gt;&#10;&lt;/lt-suggestion&gt;" description="Field with a suggestion list. A `&lt;label for&gt;`, an `&lt;input id&gt;`, an `.lt-suggestion__list` with `&lt;li&gt;` items and a `[role=status]` are enough; the component sets the roles and the wiring where the markup came without them, and gives the list and the options ids. The field never gets an id from the component. Filters as the user types, moves the highlight with the arrow keys and announces the number of matches." toReformat="false" toShortenFQNames="false">
34
40
  <variable name="breaker_search" expression="" defaultValue="" alwaysStopAt="true" />
35
41
  <variable name="breaker" expression="" defaultValue="" alwaysStopAt="true" />
@@ -37,7 +43,7 @@
37
43
  <option name="HTML" value="true" />
38
44
  </context>
39
45
  </template>
40
- <template name="lt-dialog" value="&lt;lt-dialog open&gt;&#10; &lt;dialog class=&quot;lt-dialog&quot; open&gt;&#10; &lt;h2&gt;The order is placed&lt;/h2&gt;&#10; &lt;div class=&quot;lt-dialog__body&quot;&gt;&#10; &lt;p&gt;Job 222-0481 is open to crews.&lt;/p&gt;&#10; &lt;/div&gt;&#10; &lt;form method=&quot;dialog&quot; class=&quot;lt-dialog__footer&quot;&gt;&#10; &lt;button class=&quot;lt-button&quot; value=&quot;close&quot;&gt;Close&lt;/button&gt;&#10; &lt;/form&gt;&#10; &lt;/dialog&gt;&#10;&lt;/lt-dialog&gt;" description="Makes a `&lt;dialog class=&quot;lt-dialog&quot;&gt;` modal with `showModal()`, for servers that cannot call it themselves. The host around the dialog. Gives the dialog its name from the first heading outside `lt-dialog__body` when neither `aria-labelledby` nor `aria-label` is there." toReformat="false" toShortenFQNames="false">
46
+ <template name="lt-dialog" value="&lt;lt-dialog open&gt;&#10; &lt;dialog class=&quot;lt-dialog&quot; open&gt;&#10; &lt;h2&gt;The order is placed&lt;/h2&gt;&#10; &lt;div class=&quot;lt-dialog__body&quot;&gt;&#10; &lt;p&gt;Job 222-0481 is open to crews.&lt;/p&gt;&#10; &lt;/div&gt;&#10; &lt;form method=&quot;dialog&quot; class=&quot;lt-dialog__footer&quot;&gt;&#10; &lt;button class=&quot;lt-button&quot; value=&quot;close&quot;&gt;Close&lt;/button&gt;&#10; &lt;/form&gt;&#10; &lt;/dialog&gt;&#10;&lt;/lt-dialog&gt;" description="Makes a `&lt;dialog class=&quot;lt-dialog&quot;&gt;` the server sends open a real modal, with `showModal()`. The host around the dialog. Gives the dialog its name from the first heading outside `lt-dialog__body` when neither `aria-labelledby` nor `aria-label` is there." toReformat="false" toShortenFQNames="false">
41
47
  <context>
42
48
  <option name="HTML" value="true" />
43
49
  </context>
@@ -49,7 +55,7 @@
49
55
  <option name="HTML" value="true" />
50
56
  </context>
51
57
  </template>
52
- <template name="lt-session-timeout" value="&lt;lt-session-timeout class=&quot;lt-session-timeout&quot; warn-at=&quot;1500&quot; expires-at=&quot;1800&quot;&#10; data-ignore-morph&gt;&#10; &lt;dialog class=&quot;lt-session-timeout__dialog&quot; role=&quot;alertdialog&quot; aria-labelledby=&quot;$session_title$&quot;&gt;&#10; &lt;h2 class=&quot;lt-session-timeout__title&quot; id=&quot;$session_title$&quot;&gt;Your session is ending&lt;/h2&gt;&#10; &lt;p class=&quot;lt-session-timeout__text&quot;&gt;&#10; Your session on the Lattice ends in &lt;span class=&quot;lt-session-timeout__count&quot; aria-hidden=&quot;true&quot;&gt;&lt;/span&gt;.&#10; Unsaved routes will be lost.&#10; &lt;/p&gt;&#10; &lt;span class=&quot;lt-sr-only&quot; role=&quot;status&quot;&gt;&lt;/span&gt;&#10; &lt;form method=&quot;dialog&quot; class=&quot;lt-session-timeout__actions&quot;&gt;&#10; &lt;button class=&quot;lt-button&quot; value=&quot;extend&quot;&gt;Stay connected&lt;/button&gt;&#10; &lt;button class=&quot;lt-button&quot; data-variant=&quot;secondary&quot; value=&quot;logout&quot;&gt;Log out&lt;/button&gt;&#10; &lt;/form&gt;&#10; &lt;/dialog&gt;&#10;&lt;/lt-session-timeout&gt;" description="Warns before a signed-in session expires, and counts down. The renderer writes the dialog and the text inside the element: a `&lt;dialog&gt;` with `.lt-session-timeout__count` for the number, `[role=status]` for the announcement and the buttons in a `&lt;form method=&quot;dialog&quot;&gt;`. Dispatches `session-warn` when the dialog opens, `session-extend` when the user wants to continue, `session-logout` when the user logs out, `session-expired` when time is up, and `session-activity` at most once per `activity-interval` while the user is active, so the app can keep the server session alive." toReformat="false" toShortenFQNames="false">
58
+ <template name="lt-session-timeout" value="&lt;lt-session-timeout class=&quot;lt-session-timeout&quot; warn-at=&quot;1500&quot; expires-at=&quot;1800&quot;&#10; data-ignore-morph&gt;&#10; &lt;dialog class=&quot;lt-session-timeout__dialog&quot; role=&quot;alertdialog&quot; aria-labelledby=&quot;$session_title$&quot;&gt;&#10; &lt;h2 class=&quot;lt-session-timeout__title&quot; id=&quot;$session_title$&quot;&gt;Your session is ending&lt;/h2&gt;&#10; &lt;p class=&quot;lt-session-timeout__text&quot;&gt;&#10; Your session on the Lattice ends in &lt;span class=&quot;lt-session-timeout__count&quot; aria-hidden=&quot;true&quot;&gt;&lt;/span&gt;.&#10; Unsaved routes will be lost.&#10; &lt;/p&gt;&#10; &lt;span class=&quot;lt-sr-only&quot; role=&quot;status&quot;&gt;&lt;/span&gt;&#10; &lt;form method=&quot;dialog&quot; class=&quot;lt-session-timeout__actions&quot;&gt;&#10; &lt;button class=&quot;lt-button&quot; value=&quot;extend&quot;&gt;Stay connected&lt;/button&gt;&#10; &lt;button class=&quot;lt-button&quot; data-variant=&quot;secondary&quot; value=&quot;logout&quot; formmethod=&quot;post&quot; formaction=&quot;/log-out&quot;&gt;Log out&lt;/button&gt;&#10; &lt;/form&gt;&#10; &lt;/dialog&gt;&#10;&lt;/lt-session-timeout&gt;" description="Warns before a signed-in session expires, and counts down. The renderer writes the dialog and the text inside the element: a `&lt;dialog&gt;` with `.lt-session-timeout__count` for the number, `[role=status]` for the announcement and the buttons in a `&lt;form method=&quot;dialog&quot;&gt;`. Dispatches `session-warn` when the dialog opens, `session-extend` when the user wants to continue, `session-logout` when the user logs out with a button that closes the dialog, `session-expired` when time is up, and `session-activity` at most once per `activity-interval` while the user is active, so the app can keep the server session alive." toReformat="false" toShortenFQNames="false">
53
59
  <variable name="session_title" expression="" defaultValue="" alwaysStopAt="true" />
54
60
  <context>
55
61
  <option name="HTML" value="true" />
@@ -4,6 +4,7 @@
4
4
  "bundles",
5
5
  "datastar",
6
6
  "html",
7
+ "kotlin",
7
8
  "react",
8
9
  "templates"
9
10
  ],
@@ -240,6 +241,14 @@
240
241
  "sr-only.ts"
241
242
  ]
242
243
  },
244
+ {
245
+ "category": "css",
246
+ "name": "steps",
247
+ "files": [
248
+ "steps.css",
249
+ "steps.ts"
250
+ ]
251
+ },
243
252
  {
244
253
  "category": "css",
245
254
  "name": "switch",
@@ -351,6 +360,15 @@
351
360
  "tabs.ts"
352
361
  ]
353
362
  },
363
+ {
364
+ "category": "frame",
365
+ "name": "term",
366
+ "files": [
367
+ "lt-term.ts",
368
+ "term.css",
369
+ "term.ts"
370
+ ]
371
+ },
354
372
  {
355
373
  "category": "standalone",
356
374
  "name": "connection-status",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "./manifest.schema.json",
3
3
  "schemaVersion": 1,
4
- "version": "0.40.0",
4
+ "version": "0.41.0",
5
5
  "textLanguage": "en",
6
6
  "elements": {
7
7
  "lt-field": {
@@ -186,6 +186,19 @@
186
186
  }
187
187
  }
188
188
  },
189
+ "lt-term": {
190
+ "link": "https://lo-tek.sobernetics.no/components/term/",
191
+ "description": "A word in the text that shows its glossary entry in a card by the word. A link to the entry's id is enough; the component sets the class, `aria-details` and `aria-expanded` on the link, and shows the entry itself in the top layer while the card is open. Without JavaScript the word is a link to the glossary.",
192
+ "attributes": {},
193
+ "parts": {
194
+ "word": {
195
+ "selector": "a[href^='#']",
196
+ "description": "The word: a link to the glossary entry, such as `<a href=\"#term-signal\">`. The entry is a `<div>` with that id in a `<dl>`, with a `<dt>` and a `<dd>`, anywhere on the page.",
197
+ "content": "text",
198
+ "unique": true
199
+ }
200
+ }
201
+ },
189
202
  "lt-suggestion": {
190
203
  "link": "https://lo-tek.sobernetics.no/components/suggestion/",
191
204
  "description": "Field with a suggestion list. A `<label for>`, an `<input id>`, an `.lt-suggestion__list` with `<li>` items and a `[role=status]` are enough; the component sets the roles and the wiring where the markup came without them, and gives the list and the options ids. The field never gets an id from the component. Filters as the user types, moves the highlight with the arrow keys and announces the number of matches.",
@@ -312,7 +325,7 @@
312
325
  },
313
326
  "lt-dialog": {
314
327
  "link": "https://lo-tek.sobernetics.no/components/dialog/",
315
- "description": "Makes a `<dialog class=\"lt-dialog\">` modal with `showModal()`, for servers that cannot call it themselves. The host around the dialog. Gives the dialog its name from the first heading outside `lt-dialog__body` when neither `aria-labelledby` nor `aria-label` is there.",
328
+ "description": "Makes a `<dialog class=\"lt-dialog\">` the server sends open a real modal, with `showModal()`. The host around the dialog. Gives the dialog its name from the first heading outside `lt-dialog__body` when neither `aria-labelledby` nor `aria-label` is there.",
316
329
  "attributes": {
317
330
  "open": {
318
331
  "type": "flag",
@@ -395,7 +408,7 @@
395
408
  },
396
409
  "lt-session-timeout": {
397
410
  "link": "https://lo-tek.sobernetics.no/components/session-timeout/",
398
- "description": "Warns before a signed-in session expires, and counts down. The renderer writes the dialog and the text inside the element: a `<dialog>` with `.lt-session-timeout__count` for the number, `[role=status]` for the announcement and the buttons in a `<form method=\"dialog\">`. Dispatches `session-warn` when the dialog opens, `session-extend` when the user wants to continue, `session-logout` when the user logs out, `session-expired` when time is up, and `session-activity` at most once per `activity-interval` while the user is active, so the app can keep the server session alive.",
411
+ "description": "Warns before a signed-in session expires, and counts down. The renderer writes the dialog and the text inside the element: a `<dialog>` with `.lt-session-timeout__count` for the number, `[role=status]` for the announcement and the buttons in a `<form method=\"dialog\">`. Dispatches `session-warn` when the dialog opens, `session-extend` when the user wants to continue, `session-logout` when the user logs out with a button that closes the dialog, `session-expired` when time is up, and `session-activity` at most once per `activity-interval` while the user is active, so the app can keep the server session alive.",
399
412
  "attributes": {
400
413
  "warn-at": {
401
414
  "type": "number",
@@ -1275,6 +1288,31 @@
1275
1288
  "content": "text",
1276
1289
  "attributes": {}
1277
1290
  },
1291
+ "lt-steps": {
1292
+ "component": "steps",
1293
+ "title": "Steps",
1294
+ "description": "A step indicator that shows where the user is in a task over several pages. A CSS component with one class and no JavaScript.",
1295
+ "link": "https://lo-tek.sobernetics.no/components/steps/",
1296
+ "content": "parts",
1297
+ "parts": {
1298
+ "step": {
1299
+ "selector": "li",
1300
+ "description": "One step, with its name as text, or a link back to it. The step the user is on has `aria-current=\"step\"`, and the steps before it are drawn as done.",
1301
+ "content": "free",
1302
+ "multiple": true
1303
+ }
1304
+ },
1305
+ "texts": [
1306
+ {
1307
+ "variable": "--lt-steps-done-text",
1308
+ "text": "Completed ",
1309
+ "attribute": "aria-current",
1310
+ "value": "step",
1311
+ "siblingsBefore": true
1312
+ }
1313
+ ],
1314
+ "attributes": {}
1315
+ },
1278
1316
  "lt-switch": {
1279
1317
  "component": "switch",
1280
1318
  "title": "Switch",
@@ -1483,7 +1521,7 @@
1483
1521
  "lt-dialog": {
1484
1522
  "component": "dialog",
1485
1523
  "title": "Dialog",
1486
- "description": "Modal dialog built on the native dialog element. Six classes and no JavaScript, with an optional component for servers that cannot call showModal().",
1524
+ "description": "Modal dialog built on the native dialog element. Six classes and no JavaScript, with an optional component for when the server decides that the dialog is open.",
1487
1525
  "link": "https://lo-tek.sobernetics.no/components/dialog/",
1488
1526
  "content": "free",
1489
1527
  "parts": {
@@ -1535,7 +1573,7 @@
1535
1573
  "lt-dialog__body": {
1536
1574
  "component": "dialog",
1537
1575
  "title": "Dialog",
1538
- "description": "Modal dialog built on the native dialog element. Six classes and no JavaScript, with an optional component for servers that cannot call showModal().",
1576
+ "description": "Modal dialog built on the native dialog element. Six classes and no JavaScript, with an optional component for when the server decides that the dialog is open.",
1539
1577
  "link": "https://lo-tek.sobernetics.no/components/dialog/",
1540
1578
  "content": "free",
1541
1579
  "partOf": "lt-dialog",
@@ -1544,7 +1582,7 @@
1544
1582
  "lt-dialog__title": {
1545
1583
  "component": "dialog",
1546
1584
  "title": "Dialog",
1547
- "description": "Modal dialog built on the native dialog element. Six classes and no JavaScript, with an optional component for servers that cannot call showModal().",
1585
+ "description": "Modal dialog built on the native dialog element. Six classes and no JavaScript, with an optional component for when the server decides that the dialog is open.",
1548
1586
  "link": "https://lo-tek.sobernetics.no/components/dialog/",
1549
1587
  "content": "text",
1550
1588
  "partOf": "lt-dialog",
@@ -1553,7 +1591,7 @@
1553
1591
  "lt-dialog__footer": {
1554
1592
  "component": "dialog",
1555
1593
  "title": "Dialog",
1556
- "description": "Modal dialog built on the native dialog element. Six classes and no JavaScript, with an optional component for servers that cannot call showModal().",
1594
+ "description": "Modal dialog built on the native dialog element. Six classes and no JavaScript, with an optional component for when the server decides that the dialog is open.",
1557
1595
  "link": "https://lo-tek.sobernetics.no/components/dialog/",
1558
1596
  "content": "free",
1559
1597
  "partOf": "lt-dialog",
@@ -1562,7 +1600,7 @@
1562
1600
  "lt-dialog__header": {
1563
1601
  "component": "dialog",
1564
1602
  "title": "Dialog",
1565
- "description": "Modal dialog built on the native dialog element. Six classes and no JavaScript, with an optional component for servers that cannot call showModal().",
1603
+ "description": "Modal dialog built on the native dialog element. Six classes and no JavaScript, with an optional component for when the server decides that the dialog is open.",
1566
1604
  "link": "https://lo-tek.sobernetics.no/components/dialog/",
1567
1605
  "content": "parts",
1568
1606
  "partOf": "lt-dialog",
@@ -1584,7 +1622,7 @@
1584
1622
  "lt-dialog__subtitle": {
1585
1623
  "component": "dialog",
1586
1624
  "title": "Dialog",
1587
- "description": "Modal dialog built on the native dialog element. Six classes and no JavaScript, with an optional component for servers that cannot call showModal().",
1625
+ "description": "Modal dialog built on the native dialog element. Six classes and no JavaScript, with an optional component for when the server decides that the dialog is open.",
1588
1626
  "link": "https://lo-tek.sobernetics.no/components/dialog/",
1589
1627
  "content": "text",
1590
1628
  "partOf": "lt-dialog",
@@ -1827,6 +1865,35 @@
1827
1865
  "content": "free",
1828
1866
  "attributes": {}
1829
1867
  },
1868
+ "lt-term": {
1869
+ "component": "term",
1870
+ "title": "Term",
1871
+ "description": "A word in the text that shows its entry from a glossary on the same page, in a card by the word. Frame component.",
1872
+ "link": "https://lo-tek.sobernetics.no/components/term/",
1873
+ "content": "text",
1874
+ "attributes": {}
1875
+ },
1876
+ "lt-term__entry": {
1877
+ "component": "term",
1878
+ "title": "Term",
1879
+ "description": "A word in the text that shows its entry from a glossary on the same page, in a card by the word. Frame component.",
1880
+ "link": "https://lo-tek.sobernetics.no/components/term/",
1881
+ "content": "parts",
1882
+ "parts": {
1883
+ "term": {
1884
+ "selector": "dt",
1885
+ "description": "The term, as it stands in the glossary.",
1886
+ "content": "text",
1887
+ "unique": true
1888
+ },
1889
+ "description": {
1890
+ "selector": "dd",
1891
+ "description": "The explanation. It can hold links and code.",
1892
+ "content": "free"
1893
+ }
1894
+ },
1895
+ "attributes": {}
1896
+ },
1830
1897
  "lt-connection-status": {
1831
1898
  "component": "connection-status",
1832
1899
  "title": "Connection Status",
@@ -2740,7 +2807,7 @@
2740
2807
  {
2741
2808
  "name": "field",
2742
2809
  "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>",
2810
+ "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 the fixer's address, for example fixer@relay.example.</p>",
2744
2811
  "options": [
2745
2812
  {
2746
2813
  "name": "id",
@@ -4396,6 +4463,25 @@
4396
4463
  },
4397
4464
  "docs": "https://lo-tek.sobernetics.no/components/sr-only/"
4398
4465
  },
4466
+ {
4467
+ "name": "steps",
4468
+ "description": "The attributes for a step indicator, which shows where the user is in a task over several pages, like an order.\n\nThe class goes on an `<ol>` with one `<li>` per step, and the step the user is on has `aria-current=\"step\"`. The steps before it are drawn as done, with a check the screen reader reads as \"Completed\", so the markup only says where the user is. A done step can be a link back to it.",
4469
+ "example": "<ol {...steps()} aria-label=\"Order\">\n <li><a href=\"/order/site\">Site</a></li>\n <li aria-current=\"step\">Walls</li>\n <li>Payment</li>\n</ol>",
4470
+ "options": [],
4471
+ "returns": {
4472
+ "kind": "attributes"
4473
+ },
4474
+ "table": {
4475
+ "kind": "attributes",
4476
+ "table": {
4477
+ "base": {
4478
+ "class": "lt-steps"
4479
+ },
4480
+ "options": {}
4481
+ }
4482
+ },
4483
+ "docs": "https://lo-tek.sobernetics.no/components/steps/"
4484
+ },
4399
4485
  {
4400
4486
  "name": "suggestion",
4401
4487
  "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.",
@@ -4890,6 +4976,39 @@
4890
4976
  },
4891
4977
  "docs": "https://lo-tek.sobernetics.no/components/tag/"
4892
4978
  },
4979
+ {
4980
+ "name": "term",
4981
+ "description": "The attributes for a word in the text that is explained in a glossary on the same page.\n\nOnly what holds without JavaScript: the word is a link to the entry, and `aria-details` says the entry explains it. `<lt-term>` around the link opens the entry as a card by the word, and writes `aria-expanded`.",
4982
+ "example": "const signal = lt.term({ id: \"term-signal\" })\n\n<p>Datastar keeps <lt-term><a {...signal.word}>signals</a></lt-term> in the browser.</p>\n<dl>\n <div {...signal.entry}><dt>Signal</dt><dd>A value Datastar keeps in the browser.</dd></div>\n</dl>",
4983
+ "options": [
4984
+ {
4985
+ "name": "id",
4986
+ "required": true,
4987
+ "type": {
4988
+ "kind": "text"
4989
+ },
4990
+ "description": "Id of the entry in the glossary: the `<div>` around the `<dt>` and `<dd>`. The word links to it, and the web component shows the entry itself as the card. Make it from the term, such as `term-signal`, so it is the same on every render."
4991
+ }
4992
+ ],
4993
+ "returns": {
4994
+ "kind": "group",
4995
+ "fields": {
4996
+ "word": {
4997
+ "kind": "attributes"
4998
+ },
4999
+ "entry": {
5000
+ "kind": "attributes"
5001
+ }
5002
+ }
5003
+ },
5004
+ "docs": "https://lo-tek.sobernetics.no/components/term/",
5005
+ "parts": {
5006
+ "word": {
5007
+ "element": "lt-term",
5008
+ "part": "word"
5009
+ }
5010
+ }
5011
+ },
4893
5012
  {
4894
5013
  "name": "textarea",
4895
5014
  "description": "The attributes for a multi-line text field.",
@@ -5369,6 +5488,11 @@
5369
5488
  "--lt-spinner-size": "var(--lt-spacing-6)",
5370
5489
  "--lt-spinner-thickness": "2px"
5371
5490
  },
5491
+ "steps": {
5492
+ "--lt-steps-marker-size": "var(--lt-spacing-7)",
5493
+ "--lt-steps-done-icon": "var(--lt-icon-check)",
5494
+ "--lt-steps-done-text": "\"Completed \""
5495
+ },
5372
5496
  "suggestion": {
5373
5497
  "--lt-suggestion-max-height": "calc(var(--lt-spacing-10) * 4)",
5374
5498
  "--lt-suggestion-radius": "var(--lt-spacing-1)",
@@ -5390,6 +5514,17 @@
5390
5514
  "--lt-tag-padding": "var(--lt-spacing-0-5) var(--lt-spacing-2)",
5391
5515
  "--lt-tag-radius": "var(--lt-spacing-1)"
5392
5516
  },
5517
+ "term": {
5518
+ "--lt-term-underline-color": "var(--lt-color-accent-fill)",
5519
+ "--lt-term-gap": "var(--lt-spacing-2)",
5520
+ "--lt-term-width": "min(22rem, calc(100vw - var(--lt-spacing-8)))",
5521
+ "--lt-term-padding": "var(--lt-spacing-3) var(--lt-spacing-4)",
5522
+ "--lt-term-accent-color": "var(--lt-color-accent-fill)",
5523
+ "--lt-term-radius": "var(--lt-spacing-1)",
5524
+ "--lt-term-sheet-height": "60dvh",
5525
+ "--lt-term-label-text": "\"Glossary\"",
5526
+ "--lt-term-label-font": "inherit"
5527
+ },
5393
5528
  "textarea": {
5394
5529
  "--lt-textarea-min-height": "calc(var(--lt-spacing-4) * 5)",
5395
5530
  "--lt-textarea-padding": "var(--lt-spacing-2) var(--lt-spacing-3)",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lo-tek/interfaces",
3
- "version": "0.40.0",
3
+ "version": "0.41.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",
@@ -63,6 +63,7 @@
63
63
  "./card.css": "./src/components/css/card/card.css",
64
64
  "./divider.css": "./src/components/css/divider/divider.css",
65
65
  "./progress.css": "./src/components/css/progress/progress.css",
66
+ "./steps.css": "./src/components/css/steps/steps.css",
66
67
  "./spinner.css": "./src/components/css/spinner/spinner.css",
67
68
  "./badge.css": "./src/components/css/badge/badge.css",
68
69
  "./label.css": "./src/components/css/label/label.css",
@@ -74,6 +75,7 @@
74
75
  "./error-text.css": "./src/components/css/error-text/error-text.css",
75
76
  "./field.css": "./src/components/frame/field/field.css",
76
77
  "./tabs.css": "./src/components/frame/tabs/tabs.css",
78
+ "./term.css": "./src/components/frame/term/term.css",
77
79
  "./error-summary.css": "./src/components/frame/error-summary/error-summary.css",
78
80
  "./label": {
79
81
  "types": "./dist/components/css/label/label.d.ts",
@@ -160,6 +162,10 @@
160
162
  "types": "./dist/components/css/file-upload/file-upload.d.ts",
161
163
  "import": "./dist/components/css/file-upload/file-upload.js"
162
164
  },
165
+ "./term": {
166
+ "types": "./dist/components/frame/term/lt-term.d.ts",
167
+ "import": "./dist/components/frame/term/lt-term.js"
168
+ },
163
169
  "./popover": {
164
170
  "types": "./dist/components/frame/popover/lt-popover.d.ts",
165
171
  "import": "./dist/components/frame/popover/lt-popover.js"
@@ -204,6 +210,10 @@
204
210
  "types": "./dist/components/css/progress/progress.d.ts",
205
211
  "import": "./dist/components/css/progress/progress.js"
206
212
  },
213
+ "./steps": {
214
+ "types": "./dist/components/css/steps/steps.d.ts",
215
+ "import": "./dist/components/css/steps/steps.js"
216
+ },
207
217
  "./spinner": {
208
218
  "types": "./dist/components/css/spinner/spinner.d.ts",
209
219
  "import": "./dist/components/css/spinner/spinner.js"
@@ -394,15 +404,15 @@
394
404
  "@types/node": "24.12.4",
395
405
  "@types/react": "19",
396
406
  "@types/react-dom": "19",
397
- "@vitest/browser": "4.1.7",
398
- "@vitest/browser-playwright": "^4.1.7",
407
+ "@vitest/browser": "4.1.12",
408
+ "@vitest/browser-playwright": "^4.1.12",
399
409
  "axe-core": "^4.13.0",
400
410
  "playwright": "1.60.0",
401
411
  "publint": "^0.3.24",
402
412
  "react": "19",
403
413
  "react-dom": "19",
404
414
  "tailwindcss": "4.3.0",
405
- "vite": "7.0.0",
406
- "vitest": "4.1.7"
415
+ "vite": "7.3.7",
416
+ "vitest": "4.1.12"
407
417
  }
408
418
  }