@lo-tek/interfaces 0.0.0 → 0.37.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 (456) hide show
  1. package/CHANGELOG.md +3254 -0
  2. package/LICENSE +21 -0
  3. package/README.md +128 -1
  4. package/agent/astro.md +400 -0
  5. package/agent/bundles.md +429 -0
  6. package/agent/datastar.md +409 -0
  7. package/agent/html.md +388 -0
  8. package/agent/react.md +440 -0
  9. package/agent/templates.md +472 -0
  10. package/core/lotek-core.wasm +0 -0
  11. package/core/lotek.wasm +0 -0
  12. package/dist/cli.d.ts +12 -0
  13. package/dist/cli.d.ts.map +1 -0
  14. package/dist/cli.js +14 -0
  15. package/dist/cli.js.map +1 -0
  16. package/dist/components/css/accordion/accordion.d.ts +37 -0
  17. package/dist/components/css/accordion/accordion.d.ts.map +1 -0
  18. package/dist/components/css/accordion/accordion.js +31 -0
  19. package/dist/components/css/accordion/accordion.js.map +1 -0
  20. package/dist/components/css/alert/alert.d.ts +35 -0
  21. package/dist/components/css/alert/alert.d.ts.map +1 -0
  22. package/dist/components/css/alert/alert.js +29 -0
  23. package/dist/components/css/alert/alert.js.map +1 -0
  24. package/dist/components/css/avatar/avatar.d.ts +40 -0
  25. package/dist/components/css/avatar/avatar.d.ts.map +1 -0
  26. package/dist/components/css/avatar/avatar.js +30 -0
  27. package/dist/components/css/avatar/avatar.js.map +1 -0
  28. package/dist/components/css/badge/badge.d.ts +24 -0
  29. package/dist/components/css/badge/badge.d.ts.map +1 -0
  30. package/dist/components/css/badge/badge.js +24 -0
  31. package/dist/components/css/badge/badge.js.map +1 -0
  32. package/dist/components/css/breadcrumbs/breadcrumbs.d.ts +26 -0
  33. package/dist/components/css/breadcrumbs/breadcrumbs.d.ts.map +1 -0
  34. package/dist/components/css/breadcrumbs/breadcrumbs.js +24 -0
  35. package/dist/components/css/breadcrumbs/breadcrumbs.js.map +1 -0
  36. package/dist/components/css/button/button.d.ts +26 -0
  37. package/dist/components/css/button/button.d.ts.map +1 -0
  38. package/dist/components/css/button/button.js +25 -0
  39. package/dist/components/css/button/button.js.map +1 -0
  40. package/dist/components/css/card/card.d.ts +36 -0
  41. package/dist/components/css/card/card.d.ts.map +1 -0
  42. package/dist/components/css/card/card.js +28 -0
  43. package/dist/components/css/card/card.js.map +1 -0
  44. package/dist/components/css/checkbox/checkbox.d.ts +32 -0
  45. package/dist/components/css/checkbox/checkbox.d.ts.map +1 -0
  46. package/dist/components/css/checkbox/checkbox.js +27 -0
  47. package/dist/components/css/checkbox/checkbox.js.map +1 -0
  48. package/dist/components/css/divider/divider.d.ts +29 -0
  49. package/dist/components/css/divider/divider.d.ts.map +1 -0
  50. package/dist/components/css/divider/divider.js +23 -0
  51. package/dist/components/css/divider/divider.js.map +1 -0
  52. package/dist/components/css/error-text/error-text.d.ts +24 -0
  53. package/dist/components/css/error-text/error-text.d.ts.map +1 -0
  54. package/dist/components/css/error-text/error-text.js +18 -0
  55. package/dist/components/css/error-text/error-text.js.map +1 -0
  56. package/dist/components/css/fieldset/fieldset.d.ts +55 -0
  57. package/dist/components/css/fieldset/fieldset.d.ts.map +1 -0
  58. package/dist/components/css/fieldset/fieldset.js +39 -0
  59. package/dist/components/css/fieldset/fieldset.js.map +1 -0
  60. package/dist/components/css/file-upload/file-upload.d.ts +41 -0
  61. package/dist/components/css/file-upload/file-upload.d.ts.map +1 -0
  62. package/dist/components/css/file-upload/file-upload.js +32 -0
  63. package/dist/components/css/file-upload/file-upload.js.map +1 -0
  64. package/dist/components/css/heading/heading.d.ts +29 -0
  65. package/dist/components/css/heading/heading.d.ts.map +1 -0
  66. package/dist/components/css/heading/heading.js +23 -0
  67. package/dist/components/css/heading/heading.js.map +1 -0
  68. package/dist/components/css/help-text/help-text.d.ts +24 -0
  69. package/dist/components/css/help-text/help-text.d.ts.map +1 -0
  70. package/dist/components/css/help-text/help-text.js +23 -0
  71. package/dist/components/css/help-text/help-text.js.map +1 -0
  72. package/dist/components/css/icon/icon.d.ts +34 -0
  73. package/dist/components/css/icon/icon.d.ts.map +1 -0
  74. package/dist/components/css/icon/icon.js +25 -0
  75. package/dist/components/css/icon/icon.js.map +1 -0
  76. package/dist/components/css/input/input.d.ts +40 -0
  77. package/dist/components/css/input/input.d.ts.map +1 -0
  78. package/dist/components/css/input/input.js +45 -0
  79. package/dist/components/css/input/input.js.map +1 -0
  80. package/dist/components/css/label/label.d.ts +33 -0
  81. package/dist/components/css/label/label.d.ts.map +1 -0
  82. package/dist/components/css/label/label.js +23 -0
  83. package/dist/components/css/label/label.js.map +1 -0
  84. package/dist/components/css/link/link.d.ts +28 -0
  85. package/dist/components/css/link/link.d.ts.map +1 -0
  86. package/dist/components/css/link/link.js +20 -0
  87. package/dist/components/css/link/link.js.map +1 -0
  88. package/dist/components/css/list/list.d.ts +31 -0
  89. package/dist/components/css/list/list.d.ts.map +1 -0
  90. package/dist/components/css/list/list.js +25 -0
  91. package/dist/components/css/list/list.js.map +1 -0
  92. package/dist/components/css/pagination/pagination.d.ts +27 -0
  93. package/dist/components/css/pagination/pagination.d.ts.map +1 -0
  94. package/dist/components/css/pagination/pagination.js +25 -0
  95. package/dist/components/css/pagination/pagination.js.map +1 -0
  96. package/dist/components/css/paragraph/paragraph.d.ts +36 -0
  97. package/dist/components/css/paragraph/paragraph.d.ts.map +1 -0
  98. package/dist/components/css/paragraph/paragraph.js +26 -0
  99. package/dist/components/css/paragraph/paragraph.js.map +1 -0
  100. package/dist/components/css/progress/progress.d.ts +53 -0
  101. package/dist/components/css/progress/progress.d.ts.map +1 -0
  102. package/dist/components/css/progress/progress.js +38 -0
  103. package/dist/components/css/progress/progress.js.map +1 -0
  104. package/dist/components/css/radio/radio.d.ts +32 -0
  105. package/dist/components/css/radio/radio.d.ts.map +1 -0
  106. package/dist/components/css/radio/radio.js +27 -0
  107. package/dist/components/css/radio/radio.js.map +1 -0
  108. package/dist/components/css/search/search.d.ts +34 -0
  109. package/dist/components/css/search/search.d.ts.map +1 -0
  110. package/dist/components/css/search/search.js +32 -0
  111. package/dist/components/css/search/search.js.map +1 -0
  112. package/dist/components/css/select/select.d.ts +40 -0
  113. package/dist/components/css/select/select.d.ts.map +1 -0
  114. package/dist/components/css/select/select.js +31 -0
  115. package/dist/components/css/select/select.js.map +1 -0
  116. package/dist/components/css/shared.d.ts +90 -0
  117. package/dist/components/css/shared.d.ts.map +1 -0
  118. package/dist/components/css/shared.js +167 -0
  119. package/dist/components/css/shared.js.map +1 -0
  120. package/dist/components/css/skeleton/skeleton.d.ts +29 -0
  121. package/dist/components/css/skeleton/skeleton.d.ts.map +1 -0
  122. package/dist/components/css/skeleton/skeleton.js +23 -0
  123. package/dist/components/css/skeleton/skeleton.js.map +1 -0
  124. package/dist/components/css/skip-link/skip-link.d.ts +18 -0
  125. package/dist/components/css/skip-link/skip-link.d.ts.map +1 -0
  126. package/dist/components/css/skip-link/skip-link.js +16 -0
  127. package/dist/components/css/skip-link/skip-link.js.map +1 -0
  128. package/dist/components/css/spinner/spinner.d.ts +33 -0
  129. package/dist/components/css/spinner/spinner.d.ts.map +1 -0
  130. package/dist/components/css/spinner/spinner.js +25 -0
  131. package/dist/components/css/spinner/spinner.js.map +1 -0
  132. package/dist/components/css/sr-only/sr-only.d.ts +17 -0
  133. package/dist/components/css/sr-only/sr-only.d.ts.map +1 -0
  134. package/dist/components/css/sr-only/sr-only.js +15 -0
  135. package/dist/components/css/sr-only/sr-only.js.map +1 -0
  136. package/dist/components/css/switch/switch.d.ts +37 -0
  137. package/dist/components/css/switch/switch.d.ts.map +1 -0
  138. package/dist/components/css/switch/switch.js +29 -0
  139. package/dist/components/css/switch/switch.js.map +1 -0
  140. package/dist/components/css/table/table.d.ts +76 -0
  141. package/dist/components/css/table/table.d.ts.map +1 -0
  142. package/dist/components/css/table/table.js +59 -0
  143. package/dist/components/css/table/table.js.map +1 -0
  144. package/dist/components/css/tag/tag.d.ts +35 -0
  145. package/dist/components/css/tag/tag.d.ts.map +1 -0
  146. package/dist/components/css/tag/tag.js +27 -0
  147. package/dist/components/css/tag/tag.js.map +1 -0
  148. package/dist/components/css/textarea/textarea.d.ts +23 -0
  149. package/dist/components/css/textarea/textarea.d.ts.map +1 -0
  150. package/dist/components/css/textarea/textarea.js +18 -0
  151. package/dist/components/css/textarea/textarea.js.map +1 -0
  152. package/dist/components/css/toggle-group/toggle-group.d.ts +31 -0
  153. package/dist/components/css/toggle-group/toggle-group.d.ts.map +1 -0
  154. package/dist/components/css/toggle-group/toggle-group.js +29 -0
  155. package/dist/components/css/toggle-group/toggle-group.js.map +1 -0
  156. package/dist/components/css/tooltip/tooltip.d.ts +31 -0
  157. package/dist/components/css/tooltip/tooltip.d.ts.map +1 -0
  158. package/dist/components/css/tooltip/tooltip.js +29 -0
  159. package/dist/components/css/tooltip/tooltip.js.map +1 -0
  160. package/dist/components/default-texts.d.ts +35 -0
  161. package/dist/components/default-texts.d.ts.map +1 -0
  162. package/dist/components/default-texts.js +35 -0
  163. package/dist/components/default-texts.js.map +1 -0
  164. package/dist/components/events.d.ts +77 -0
  165. package/dist/components/events.d.ts.map +1 -0
  166. package/dist/components/events.js +3 -0
  167. package/dist/components/events.js.map +1 -0
  168. package/dist/components/frame/dialog/dialog.d.ts +122 -0
  169. package/dist/components/frame/dialog/dialog.d.ts.map +1 -0
  170. package/dist/components/frame/dialog/dialog.js +112 -0
  171. package/dist/components/frame/dialog/dialog.js.map +1 -0
  172. package/dist/components/frame/dialog/lt-dialog.d.ts +104 -0
  173. package/dist/components/frame/dialog/lt-dialog.d.ts.map +1 -0
  174. package/dist/components/frame/dialog/lt-dialog.js +431 -0
  175. package/dist/components/frame/dialog/lt-dialog.js.map +1 -0
  176. package/dist/components/frame/error-summary/error-summary.d.ts +40 -0
  177. package/dist/components/frame/error-summary/error-summary.d.ts.map +1 -0
  178. package/dist/components/frame/error-summary/error-summary.js +32 -0
  179. package/dist/components/frame/error-summary/error-summary.js.map +1 -0
  180. package/dist/components/frame/error-summary/lt-error-summary.d.ts +82 -0
  181. package/dist/components/frame/error-summary/lt-error-summary.d.ts.map +1 -0
  182. package/dist/components/frame/error-summary/lt-error-summary.js +302 -0
  183. package/dist/components/frame/error-summary/lt-error-summary.js.map +1 -0
  184. package/dist/components/frame/field/field-core.d.ts +92 -0
  185. package/dist/components/frame/field/field-core.d.ts.map +1 -0
  186. package/dist/components/frame/field/field-core.js +63 -0
  187. package/dist/components/frame/field/field-core.js.map +1 -0
  188. package/dist/components/frame/field/lt-field.d.ts +173 -0
  189. package/dist/components/frame/field/lt-field.d.ts.map +1 -0
  190. package/dist/components/frame/field/lt-field.js +580 -0
  191. package/dist/components/frame/field/lt-field.js.map +1 -0
  192. package/dist/components/frame/popover/lt-popover.d.ts +132 -0
  193. package/dist/components/frame/popover/lt-popover.d.ts.map +1 -0
  194. package/dist/components/frame/popover/lt-popover.js +490 -0
  195. package/dist/components/frame/popover/lt-popover.js.map +1 -0
  196. package/dist/components/frame/popover/popover.d.ts +49 -0
  197. package/dist/components/frame/popover/popover.d.ts.map +1 -0
  198. package/dist/components/frame/popover/popover.js +64 -0
  199. package/dist/components/frame/popover/popover.js.map +1 -0
  200. package/dist/components/frame/session-timeout/lt-session-timeout.d.ts +198 -0
  201. package/dist/components/frame/session-timeout/lt-session-timeout.d.ts.map +1 -0
  202. package/dist/components/frame/session-timeout/lt-session-timeout.js +573 -0
  203. package/dist/components/frame/session-timeout/lt-session-timeout.js.map +1 -0
  204. package/dist/components/frame/session-timeout/session-timeout.d.ts +113 -0
  205. package/dist/components/frame/session-timeout/session-timeout.d.ts.map +1 -0
  206. package/dist/components/frame/session-timeout/session-timeout.js +101 -0
  207. package/dist/components/frame/session-timeout/session-timeout.js.map +1 -0
  208. package/dist/components/frame/suggestion/lt-suggestion.d.ts +361 -0
  209. package/dist/components/frame/suggestion/lt-suggestion.d.ts.map +1 -0
  210. package/dist/components/frame/suggestion/lt-suggestion.js +1152 -0
  211. package/dist/components/frame/suggestion/lt-suggestion.js.map +1 -0
  212. package/dist/components/frame/suggestion/suggestion.d.ts +143 -0
  213. package/dist/components/frame/suggestion/suggestion.d.ts.map +1 -0
  214. package/dist/components/frame/suggestion/suggestion.js +127 -0
  215. package/dist/components/frame/suggestion/suggestion.js.map +1 -0
  216. package/dist/components/frame/tabs/lt-tabs.d.ts +404 -0
  217. package/dist/components/frame/tabs/lt-tabs.d.ts.map +1 -0
  218. package/dist/components/frame/tabs/lt-tabs.js +1201 -0
  219. package/dist/components/frame/tabs/lt-tabs.js.map +1 -0
  220. package/dist/components/frame/tabs/tabs.d.ts +112 -0
  221. package/dist/components/frame/tabs/tabs.d.ts.map +1 -0
  222. package/dist/components/frame/tabs/tabs.js +107 -0
  223. package/dist/components/frame/tabs/tabs.js.map +1 -0
  224. package/dist/components/host-element.d.ts +135 -0
  225. package/dist/components/host-element.d.ts.map +1 -0
  226. package/dist/components/host-element.js +366 -0
  227. package/dist/components/host-element.js.map +1 -0
  228. package/dist/components/standalone/connection-status/connection-status.d.ts +28 -0
  229. package/dist/components/standalone/connection-status/connection-status.d.ts.map +1 -0
  230. package/dist/components/standalone/connection-status/connection-status.js +22 -0
  231. package/dist/components/standalone/connection-status/connection-status.js.map +1 -0
  232. package/dist/components/standalone/connection-status/lt-connection-status.d.ts +81 -0
  233. package/dist/components/standalone/connection-status/lt-connection-status.d.ts.map +1 -0
  234. package/dist/components/standalone/connection-status/lt-connection-status.js +216 -0
  235. package/dist/components/standalone/connection-status/lt-connection-status.js.map +1 -0
  236. package/dist/components/standalone/toast/lt-toast.d.ts +109 -0
  237. package/dist/components/standalone/toast/lt-toast.d.ts.map +1 -0
  238. package/dist/components/standalone/toast/lt-toast.js +256 -0
  239. package/dist/components/standalone/toast/lt-toast.js.map +1 -0
  240. package/dist/components/standalone/toast/toast.d.ts +52 -0
  241. package/dist/components/standalone/toast/toast.d.ts.map +1 -0
  242. package/dist/components/standalone/toast/toast.js +42 -0
  243. package/dist/components/standalone/toast/toast.js.map +1 -0
  244. package/dist/diagnostics/core.d.ts +137 -0
  245. package/dist/diagnostics/core.d.ts.map +1 -0
  246. package/dist/diagnostics/core.js +72 -0
  247. package/dist/diagnostics/core.js.map +1 -0
  248. package/dist/diagnostics/default-core.d.ts +10 -0
  249. package/dist/diagnostics/default-core.d.ts.map +1 -0
  250. package/dist/diagnostics/default-core.js +72 -0
  251. package/dist/diagnostics/default-core.js.map +1 -0
  252. package/dist/diagnostics/index.d.ts +84 -0
  253. package/dist/diagnostics/index.d.ts.map +1 -0
  254. package/dist/diagnostics/index.js +106 -0
  255. package/dist/diagnostics/index.js.map +1 -0
  256. package/dist/dom.d.ts +55 -0
  257. package/dist/dom.d.ts.map +1 -0
  258. package/dist/dom.js +162 -0
  259. package/dist/dom.js.map +1 -0
  260. package/dist/index.d.ts +97 -0
  261. package/dist/index.d.ts.map +1 -0
  262. package/dist/index.js +58 -0
  263. package/dist/index.js.map +1 -0
  264. package/dist/jsx/react.d.ts +180 -0
  265. package/dist/jsx/react.d.ts.map +1 -0
  266. package/dist/jsx/react.js +2 -0
  267. package/dist/jsx/react.js.map +1 -0
  268. package/dist/lotek.css +3617 -0
  269. package/dist/lt.d.ts +325 -0
  270. package/dist/lt.d.ts.map +1 -0
  271. package/dist/lt.js +183 -0
  272. package/dist/lt.js.map +1 -0
  273. package/dist/react.d.ts +492 -0
  274. package/dist/react.d.ts.map +1 -0
  275. package/dist/react.js +296 -0
  276. package/dist/react.js.map +1 -0
  277. package/dist/register.d.ts +17 -0
  278. package/dist/register.d.ts.map +1 -0
  279. package/dist/register.js +35 -0
  280. package/dist/register.js.map +1 -0
  281. package/dist/tokens/color.d.ts +43 -0
  282. package/dist/tokens/color.d.ts.map +1 -0
  283. package/dist/tokens/color.js +123 -0
  284. package/dist/tokens/color.js.map +1 -0
  285. package/dist/tokens/contract.d.ts +18 -0
  286. package/dist/tokens/contract.d.ts.map +1 -0
  287. package/dist/tokens/contract.js +18 -0
  288. package/dist/tokens/contract.js.map +1 -0
  289. package/dist/tokens/matrix-cells.d.ts +30 -0
  290. package/dist/tokens/matrix-cells.d.ts.map +1 -0
  291. package/dist/tokens/matrix-cells.js +273 -0
  292. package/dist/tokens/matrix-cells.js.map +1 -0
  293. package/dist/tokens/matrix.d.ts +18 -0
  294. package/dist/tokens/matrix.d.ts.map +1 -0
  295. package/dist/tokens/matrix.js +21 -0
  296. package/dist/tokens/matrix.js.map +1 -0
  297. package/dist/tokens/theme-check.d.ts +42 -0
  298. package/dist/tokens/theme-check.d.ts.map +1 -0
  299. package/dist/tokens/theme-check.js +10 -0
  300. package/dist/tokens/theme-check.js.map +1 -0
  301. package/dist/tokens/theme.d.ts +94 -0
  302. package/dist/tokens/theme.d.ts.map +1 -0
  303. package/dist/tokens/theme.js +14 -0
  304. package/dist/tokens/theme.js.map +1 -0
  305. package/dist/tokens/tokens.d.ts +122 -0
  306. package/dist/tokens/tokens.d.ts.map +1 -0
  307. package/dist/tokens/tokens.js +158 -0
  308. package/dist/tokens/tokens.js.map +1 -0
  309. package/dist/vocabulary/classes.d.ts +3 -0
  310. package/dist/vocabulary/classes.d.ts.map +1 -0
  311. package/dist/vocabulary/classes.js +1320 -0
  312. package/dist/vocabulary/classes.js.map +1 -0
  313. package/dist/vocabulary/elements.d.ts +3 -0
  314. package/dist/vocabulary/elements.d.ts.map +1 -0
  315. package/dist/vocabulary/elements.js +410 -0
  316. package/dist/vocabulary/elements.js.map +1 -0
  317. package/dist/vocabulary/types.d.ts +175 -0
  318. package/dist/vocabulary/types.d.ts.map +1 -0
  319. package/dist/vocabulary/types.js +11 -0
  320. package/dist/vocabulary/types.js.map +1 -0
  321. package/dist/wasi-host.d.ts +24 -0
  322. package/dist/wasi-host.d.ts.map +1 -0
  323. package/dist/wasi-host.js +140 -0
  324. package/dist/wasi-host.js.map +1 -0
  325. package/live-templates.xml +62 -0
  326. package/manifest/files.json +373 -0
  327. package/manifest/manifest.json +4965 -0
  328. package/manifest/manifest.schema.json +513 -0
  329. package/package.json +398 -5
  330. package/src/cli.ts +21 -0
  331. package/src/components/css/accordion/accordion.css +102 -0
  332. package/src/components/css/accordion/accordion.ts +50 -0
  333. package/src/components/css/alert/alert.css +53 -0
  334. package/src/components/css/alert/alert.ts +48 -0
  335. package/src/components/css/avatar/avatar.css +96 -0
  336. package/src/components/css/avatar/avatar.ts +57 -0
  337. package/src/components/css/badge/badge.css +42 -0
  338. package/src/components/css/badge/badge.ts +43 -0
  339. package/src/components/css/breadcrumbs/breadcrumbs.css +62 -0
  340. package/src/components/css/breadcrumbs/breadcrumbs.ts +30 -0
  341. package/src/components/css/button/button.css +128 -0
  342. package/src/components/css/button/button.ts +44 -0
  343. package/src/components/css/card/card.css +65 -0
  344. package/src/components/css/card/card.ts +50 -0
  345. package/src/components/css/checkbox/checkbox.css +114 -0
  346. package/src/components/css/checkbox/checkbox.ts +50 -0
  347. package/src/components/css/divider/divider.css +38 -0
  348. package/src/components/css/divider/divider.ts +42 -0
  349. package/src/components/css/error-text/error-text.css +21 -0
  350. package/src/components/css/error-text/error-text.ts +37 -0
  351. package/src/components/css/fieldset/fieldset.css +72 -0
  352. package/src/components/css/fieldset/fieldset.ts +87 -0
  353. package/src/components/css/file-upload/file-upload.css +102 -0
  354. package/src/components/css/file-upload/file-upload.ts +65 -0
  355. package/src/components/css/heading/heading.css +33 -0
  356. package/src/components/css/heading/heading.ts +42 -0
  357. package/src/components/css/help-text/help-text.css +24 -0
  358. package/src/components/css/help-text/help-text.ts +42 -0
  359. package/src/components/css/icon/icon.css +20 -0
  360. package/src/components/css/icon/icon.ts +44 -0
  361. package/src/components/css/input/input.css +145 -0
  362. package/src/components/css/input/input.ts +77 -0
  363. package/src/components/css/label/label.css +42 -0
  364. package/src/components/css/label/label.ts +51 -0
  365. package/src/components/css/link/link.css +38 -0
  366. package/src/components/css/link/link.ts +38 -0
  367. package/src/components/css/list/list.css +53 -0
  368. package/src/components/css/list/list.ts +44 -0
  369. package/src/components/css/pagination/pagination.css +95 -0
  370. package/src/components/css/pagination/pagination.ts +33 -0
  371. package/src/components/css/paragraph/paragraph.css +31 -0
  372. package/src/components/css/paragraph/paragraph.ts +53 -0
  373. package/src/components/css/progress/progress.css +161 -0
  374. package/src/components/css/progress/progress.ts +78 -0
  375. package/src/components/css/radio/radio.css +94 -0
  376. package/src/components/css/radio/radio.ts +50 -0
  377. package/src/components/css/search/search.css +50 -0
  378. package/src/components/css/search/search.ts +56 -0
  379. package/src/components/css/select/select.css +321 -0
  380. package/src/components/css/select/select.ts +63 -0
  381. package/src/components/css/shared.ts +199 -0
  382. package/src/components/css/skeleton/skeleton.css +62 -0
  383. package/src/components/css/skeleton/skeleton.ts +43 -0
  384. package/src/components/css/skip-link/skip-link.css +41 -0
  385. package/src/components/css/skip-link/skip-link.ts +22 -0
  386. package/src/components/css/spinner/spinner.css +48 -0
  387. package/src/components/css/spinner/spinner.ts +48 -0
  388. package/src/components/css/sr-only/sr-only.css +16 -0
  389. package/src/components/css/sr-only/sr-only.ts +21 -0
  390. package/src/components/css/switch/switch.css +105 -0
  391. package/src/components/css/switch/switch.ts +50 -0
  392. package/src/components/css/table/table.css +159 -0
  393. package/src/components/css/table/table.ts +107 -0
  394. package/src/components/css/tag/tag.css +58 -0
  395. package/src/components/css/tag/tag.ts +49 -0
  396. package/src/components/css/textarea/textarea.css +83 -0
  397. package/src/components/css/textarea/textarea.ts +40 -0
  398. package/src/components/css/toggle-group/toggle-group.css +92 -0
  399. package/src/components/css/toggle-group/toggle-group.ts +37 -0
  400. package/src/components/css/tooltip/tooltip.css +89 -0
  401. package/src/components/css/tooltip/tooltip.ts +37 -0
  402. package/src/components/default-texts.ts +34 -0
  403. package/src/components/events.ts +68 -0
  404. package/src/components/frame/dialog/dialog.css +306 -0
  405. package/src/components/frame/dialog/dialog.ts +166 -0
  406. package/src/components/frame/dialog/lt-dialog.ts +510 -0
  407. package/src/components/frame/error-summary/error-summary.css +47 -0
  408. package/src/components/frame/error-summary/error-summary.ts +56 -0
  409. package/src/components/frame/error-summary/lt-error-summary.ts +344 -0
  410. package/src/components/frame/field/field-core.ts +152 -0
  411. package/src/components/frame/field/field.css +17 -0
  412. package/src/components/frame/field/lt-field.ts +753 -0
  413. package/src/components/frame/popover/lt-popover.ts +555 -0
  414. package/src/components/frame/popover/popover.css +50 -0
  415. package/src/components/frame/popover/popover.ts +102 -0
  416. package/src/components/frame/session-timeout/lt-session-timeout.ts +681 -0
  417. package/src/components/frame/session-timeout/session-timeout.css +50 -0
  418. package/src/components/frame/session-timeout/session-timeout.ts +156 -0
  419. package/src/components/frame/suggestion/lt-suggestion.ts +1358 -0
  420. package/src/components/frame/suggestion/suggestion.css +132 -0
  421. package/src/components/frame/suggestion/suggestion.ts +172 -0
  422. package/src/components/frame/tabs/lt-tabs.ts +1319 -0
  423. package/src/components/frame/tabs/tabs.css +128 -0
  424. package/src/components/frame/tabs/tabs.ts +188 -0
  425. package/src/components/host-element.ts +400 -0
  426. package/src/components/standalone/connection-status/connection-status.css +41 -0
  427. package/src/components/standalone/connection-status/connection-status.ts +41 -0
  428. package/src/components/standalone/connection-status/lt-connection-status.ts +242 -0
  429. package/src/components/standalone/toast/lt-toast.ts +300 -0
  430. package/src/components/standalone/toast/toast.css +78 -0
  431. package/src/components/standalone/toast/toast.ts +70 -0
  432. package/src/diagnostics/core.ts +210 -0
  433. package/src/diagnostics/default-core.ts +81 -0
  434. package/src/diagnostics/index.ts +150 -0
  435. package/src/dom.ts +185 -0
  436. package/src/index.ts +218 -0
  437. package/src/jsx/react.ts +212 -0
  438. package/src/lt.ts +192 -0
  439. package/src/react.ts +411 -0
  440. package/src/register.ts +43 -0
  441. package/src/tailwind/tailwind.css +169 -0
  442. package/src/tokens/boundary.css +104 -0
  443. package/src/tokens/color-contract.json +231 -0
  444. package/src/tokens/color.ts +150 -0
  445. package/src/tokens/contract.ts +24 -0
  446. package/src/tokens/matrix-cells.ts +375 -0
  447. package/src/tokens/matrix.ts +32 -0
  448. package/src/tokens/theme-check.ts +51 -0
  449. package/src/tokens/theme.ts +104 -0
  450. package/src/tokens/tokens.css +569 -0
  451. package/src/tokens/tokens.ts +223 -0
  452. package/src/vocabulary/classes.ts +1322 -0
  453. package/src/vocabulary/elements.ts +412 -0
  454. package/src/vocabulary/types.ts +178 -0
  455. package/src/wasi-host.ts +155 -0
  456. package/web-types.json +800 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,3254 @@
1
+ # Changes in @lo-tek/interfaces
2
+
3
+ Version numbers follow [semantic versioning](https://semver.org/). The
4
+ public API is larger than the functions the package exports, so these count
5
+ as breaking changes and require a new major version:
6
+
7
+ - a class name, a `data-*` attribute or an allowed value that disappears or
8
+ changes meaning,
9
+ - any other attribute a builder function writes, `aria-*` included, that
10
+ disappears or changes meaning. What the builder functions write ends up in
11
+ the consumer's markup, where it can be styled and tested against. What a
12
+ web component sets in the browser is not API in the same way: it is
13
+ behaviour, and is fixed as behaviour,
14
+ - a `part` name in a component with shadow DOM,
15
+ - a token name or an `--lt-*` variable,
16
+ - a function or a type in `lt`,
17
+ - an entry in `exports` in `package.json`.
18
+
19
+ New components, new optional attributes, new attributes a builder function
20
+ adds, and fixes that do not change markup come in a new minor version.
21
+ Before 1.0 a minor version can also contain breaking changes, and they are
22
+ listed under a heading of their own, "Breaking". Versions up to and
23
+ including 0.36.0 were written in Norwegian, and list them under "Brytende".
24
+
25
+ ## Unreleased
26
+
27
+ ## 0.37.0 (2026-10-26)
28
+
29
+ ### Breaking
30
+
31
+ - **Fristil is now LO-TEK, and everything is in English.** The package, the
32
+ prefix, the API, the command line, the check, the default texts, the
33
+ documentation and its addresses all have new names. Nothing else about the
34
+ architecture changes: the same markup, written with the new prefix, behaves
35
+ as before. The old packages stay where they are. `@fristil/designsystem` is
36
+ deprecated on npm with a pointer to `@lo-tek/interfaces`, and the last
37
+ version of each old Maven artifact carries a relocation to the new one, so
38
+ Maven and Gradle follow it and warn. Nothing is deleted. **How to update:**
39
+ install `@lo-tek/interfaces`, then replace the names in the tables below.
40
+
41
+ | Before | Now |
42
+ |---|---|
43
+ | `@fristil/designsystem` | `@lo-tek/interfaces` |
44
+ | `fristil.sobernetics.no` | `lo-tek.sobernetics.no` (every old address redirects) |
45
+ | `github.com/MarkusAugust/fristil` | `github.com/MarkusAugust/lo-tek` |
46
+ | `fs-button`, `<fs-field>`, every `fs-` class and element | `lt-button`, `<lt-field>`, `lt-` |
47
+ | `--fs-…`, every token and component variable | `--lt-…` |
48
+ | `@layer fristil` | `@layer lotek` (`@layer theme, base, lotek, components, utilities;`) |
49
+ | `fristil-tema` (the theme layer) | `lotek-theme` |
50
+ | `data-fs-boundary` | `data-lt-boundary` |
51
+ | `fs` (the builder object) | `lt` |
52
+ | `defineFs()`, `defineFsField()` and the others | `defineLt()`, `defineLtField()` |
53
+ | `FS_FIELD_TAG`, `FS_FIELD_PARTS` and the others | `LT_FIELD_TAG`, `LT_FIELD_PARTS` |
54
+ | `FsField` and the other classes, `FsEventMap` | `LtField`, `LtEventMap` |
55
+ | `dist/fristil.css` | `dist/lotek.css` (`@lo-tek/interfaces/lotek.css`) |
56
+ | exports `./tema`, `./farge`, `./kontrakt`, `./matrise`, `./tema-sjekk` | `./theme`, `./color`, `./contract`, `./matrix`, `./theme-check` |
57
+ | exports `./fargekontrakt.json`, `./kjerne.wasm` | `./color-contract.json`, `./core.wasm` |
58
+ | source folders `ramme/`, `frittstaende/` (in `lotek takeover`) | `frame/`, `standalone/` |
59
+ | the binary `fristil` | `lotek` |
60
+ | `fristil.tema.json` | `lotek.theme.json` |
61
+ | env `FRISTIL_PACKAGE` | `LOTEK_PACKAGE` |
62
+ | the comment `fristil-ignore-next` | `lotek-ignore-next` |
63
+ | Kotlin `no.fristil`, `Fs`, `Fristil`, `Tag.fs(…)` | `no.lotek`, `Lt`, `Lotek`, `Tag.lt(…)` |
64
+ | Maven `io.github.markusaugust:fristil`, `fristil-kotlinx-html`, `fristil-kommandolinje`, `fristil-gradle-plugin` | `lo-tek-interfaces`, `lo-tek-kotlinx-html`, `lo-tek-cli`, `lo-tek-gradle-plugin` |
65
+ | Gradle plugin `io.github.markusaugust.fristil`, tasks `fristilSjekk`, `fristilTema`, `fristilManifest` | `io.github.markusaugust.lo-tek`, `lotekCheck`, `lotekTheme`, `lotekManifest` |
66
+ | WebJar `webjars/fristil/` | `webjars/lo-tek/` |
67
+ | VS Code `Fristil.fristil-vscode` | `lo-tek.vscode` ("LO-TEK for VS Code") |
68
+ | IntelliJ `no.fristil` | `no.lotek` ("LO-TEK for IntelliJ") |
69
+ | rulebook `agent/maler.md` | `agent/templates.md` |
70
+
71
+ - **The command line has English verbs, flags and recipe keys.** There are
72
+ no Norwegian aliases.
73
+
74
+ | Before | Now |
75
+ |---|---|
76
+ | `sjekk`, `sjekk-tema`, `overta`, `tema` | `check`, `check-theme`, `takeover`, `theme` |
77
+ | `--rendret`, `--ut`, `--rammeverk`, `--overskriv=ja`, `--hjelp` | `--rendered`, `--out`, `--framework`, `--overwrite=yes`, `--help` |
78
+ | `--aksent`, `--fare`, `--suksess`, `--advarsel`, `--noytral`, `--besokt`, `--merke1..3` | `--accent`, `--danger`, `--success`, `--warning`, `--neutral`, `--visited`, `--brand1..3` |
79
+ | `--skrift`, `--knapp-hjorner`, `--felt-hjorner`, `--flate-hjorner`, `--knapp-ramme`, `--knapp-vekt` | `--font`, `--button-radius`, `--field-radius`, `--surface-radius`, `--button-border`, `--button-weight` |
80
+ | recipe keys `aksent` … `merke3`, `typografi`, `form` | `accent` … `brand3`, `typography`, `shape` |
81
+
82
+ - **The check's rule ids are English.** A filter or a suppression on an id
83
+ must use the new one.
84
+
85
+ | Before | Now |
86
+ |---|---|
87
+ | `boolsk-med-verdi` | `boolean-with-value` |
88
+ | `deaktivert-med-href` | `disabled-with-href` |
89
+ | `del-flere-ganger` | `part-repeated` |
90
+ | `fane-uten-lenke` | `tab-without-link` |
91
+ | `fanelenke-uten-panel` | `tab-link-without-panel` |
92
+ | `fanerad-uten-navn` | `tab-list-without-name` |
93
+ | `feilmelding-motsier` | `error-text-contradicts` |
94
+ | `felt-uten-kontroll` | `field-without-control` |
95
+ | `felt-uten-ledetekst` | `field-without-label` |
96
+ | `id-fra-komponenten` | `id-from-component` |
97
+ | `ikke-tall` | `not-a-number` |
98
+ | `ikon-uten-navn` | `icon-without-name` |
99
+ | `kontroll-uten-kobling` | `control-without-wiring` |
100
+ | `panel-uten-id` | `panel-without-id` |
101
+ | `tekst-i-ledetekst` | `text-in-label` |
102
+ | `tidsavbrudd-uten-dialog` | `timeout-without-dialog` |
103
+ | `ugyldig-klasseverdi` | `invalid-class-value` |
104
+ | `ugyldig-verdi` | `invalid-value` |
105
+ | `ukjent-attributt` | `unknown-attribute` |
106
+ | `ukjent-element` | `unknown-element` |
107
+ | `ukjent-klasse` | `unknown-class` |
108
+ | `ustylet-klasse` | `unstyled-class` |
109
+ | `ustylet-verdi` | `unstyled-value` |
110
+ | `utgatt-attributt` | `removed-attribute` |
111
+ | `verdi-uten-felt` | `value-without-field` |
112
+ | `duplikat-id` | `duplicate-id` |
113
+ | `id-finnes-ikke` | `id-not-found` |
114
+ | `for-peker-feil` | `for-points-nowhere` |
115
+ | `oppsummering-peker-feil` | `summary-points-nowhere` |
116
+ | `kontroll-uten-ledetekst` | `control-without-label` |
117
+ | `tekst-ikke-koblet` | `text-not-wired` |
118
+
119
+ Every message from the check, the components' console warnings and the
120
+ errors the builder functions throw are in English too.
121
+
122
+ - **The default texts are English.** Each can still be replaced, as before.
123
+
124
+ | Before | Now |
125
+ |---|---|
126
+ | «Varsler» | "Notifications" |
127
+ | «Lukk melding» | "Close message" |
128
+ | «Ingen forbindelse. Det du skriver blir ikke lagret.» | "No connection. What you type is not being saved." |
129
+ | «Forbindelsen er tilbake.» | "Connection restored." |
130
+ | «Ingen treff», «Ett treff», «{n} treff» | "No matches", "One match", "{n} matches" |
131
+ | « (påkrevd)», « (valgfri)» | " (required)", " (optional)" |
132
+
133
+ **How to update:** a Norwegian page that relied on the defaults sets them
134
+ as the translation page shows.
135
+
136
+ - **The documentation is in English, at new addresses.** Every old address
137
+ redirects to its new one, and `/components/…` is unchanged.
138
+
139
+ | Before | Now |
140
+ |---|---|
141
+ | `/introduksjon/`, `/kom-i-gang/`, `/rammeverk/` | `/introduction/`, `/getting-started/`, `/frameworks/` |
142
+ | `/markup-og-oppforsel/`, `/tilpasning/`, `/tilgjengelighet/` | `/markup-and-behaviour/`, `/customising/`, `/accessibility/` |
143
+ | `/oversettelse/`, `/eget-tema/`, `/typesikker-bruk/` | `/translation/`, `/your-own-theme/`, `/type-safe-use/` |
144
+ | `/editoren/`, `/kommandolinjen/`, `/kodeagenter/` | `/the-editor/`, `/command-line/`, `/coding-agents/` |
145
+ | `/lisens/`, `/veikart/`, `/versjonslogg/` | `/licence/`, `/roadmap/`, `/changelog/` |
146
+ | `/monster/skjema/`, `liste`, `sideskjelett`, `bekreftelse`, `dato`, `tema` | `/patterns/form/`, `list`, `page-skeleton`, `confirmation`, `date`, `theme` |
147
+
148
+ ## 0.36.0 (2026-10-25)
149
+
150
+ ### Brytende
151
+
152
+ - **Forslagslista står i det øverste laget.** Den var `position: absolute` i
153
+ `<fs-suggestion>`, og en forelder med `overflow`, som kroppen i en dialog,
154
+ klippet den. Nå er lista `popover="manual"`, og komponenten åpner og lukker
155
+ vinduet etter `hidden`, som fortsatt er det som sier om lista er åpen.
156
+ Komponenten legger den rett under feltet, like bred som det.
157
+ `--fs-suggestion-layer` er tatt ut, siden det øverste laget ligger over alt
158
+ uten `z-index`. **Slik oppdaterer du:** ta bort `--fs-suggestion-layer` fra
159
+ egen CSS. `fs.suggestion()` skriver `popover` selv; skriver du markupen for
160
+ hånd, gjør komponenten det.
161
+ - **En rettelse fra `diagnoseMarkup` er en liste av endringer.** `Fix` har
162
+ `edits`, hver med `start`, `end` og `text`, i tekstens rekkefølge og uten
163
+ overlapp, i stedet for ett `start`, `end` og `text`. Det samme i Kotlin,
164
+ `Fix(title, edits, preferred)` med `Edit`. Grunnen er «Gi feltet id
165
+ «epost»», som skriver `id` på feltet og `for` på ledeteksten under ett.
166
+ **Slik oppdaterer du:** bruk `fix.edits` bakfra, så posisjonene i de
167
+ første holder. Språkserveren og editorene gjør det alt.
168
+ - **Fanene i `<fs-tabs>` er lenker til panelene, ikke knapper.** Raden er en
169
+ `<ul class="fs-tabs__list">` med `<li><a href="#panel">`, og hvert
170
+ `.fs-tabs__panel` har den id-en lenken peker på. Uten JavaScript er raden
171
+ en innholdsfortegnelse, og panelene står etter hverandre under den. Med
172
+ JavaScript skriver komponenten rollene, `aria-selected`, `aria-controls` fra
173
+ `href`, tabbestoppene og `hidden="until-found"` på panelene som ikke er
174
+ valgt, så søk i siden og en lenke til noe inni et panel velger fanen.
175
+ Lenkene har ingen id, og komponenten skriver aldri en, så en oppdatering fra
176
+ serveren kjenner dem igjen og valget står. Hvert valg skrives i adressen,
177
+ som `#sak-vedlegg`, uten ny historikkoppføring, og adressen velger fane når
178
+ siden lastes. En `role="tab"` på noe som ikke er en lenke til et panel, er
179
+ ikke lenger en fane, og komponenten sier fra i konsollen. **Slik oppdaterer
180
+ du:** bytt `<div class="fs-tabs__list">` med knapper til en `<ul>` med
181
+ lenker, gi hvert panel en id og la lenken peke på den.
182
+ - **`fs.tabs()` og `Fs.tabs` tar `keys` i stedet for `count`, og `selected`
183
+ er borte.** Nøklene kommer fra dataene, én per fane, og gir panelene
184
+ `${id}-${nøkkel}` og overskriftene `${id}-${nøkkel}-heading`. En nøkkel som
185
+ er tom, har mellomrom eller står to ganger, kaster, likt i TypeScript og
186
+ Kotlin. Den første fanen er utgangspunktet; et annet valg kommer fra
187
+ adressen, fra `hidden="until-found"` på de andre panelene, eller fra appen
188
+ gjennom `selected` og `select()` på elementet. **Slik oppdaterer du:**
189
+ `fs.tabs({ id, count: 3, selected: 1, label })` blir
190
+ `fs.tabs({ id, keys: ["soknaden", "vedlegg", "meldinger"], label })`.
191
+ - **`fs.tabs()` og `Fs.tabs` skriver bare lenker, id-er og klasser.** `list`
192
+ har klassen og `aria-label`, `tabs` har `href`, `panels` har `id` og
193
+ klassen, og det nye `headings` har `id` til overskriften i hvert panel.
194
+ Ingen roller, `aria-*`, `tabindex` eller `hidden`: uten JavaScript skal
195
+ raden være en liste med lenker, og en `<section>` med navn ville blitt et
196
+ landemerke. Komponenten
197
+ skriver resten når den registreres. **Slik oppdaterer du:** spre
198
+ `faner.tabs[i]` på en `<a>` i en `<li>`, og `faner.headings[i]` på
199
+ overskriften i panelet.
200
+ - **En tom `label` kaster i `fs.tabs()`, i React-inngangen og i `Fs.tabs`.**
201
+ Raden må ha et navn som sier hva fanene velger mellom; uten det sier
202
+ skjermleseren bare «faneliste». `label` sjekkes før `keys`, med samme
203
+ melding i TypeScript og Kotlin. **Slik oppdaterer du:** gi raden et navn,
204
+ som `label: "Deler av saken"`.
205
+ - **`fs.tabs()` i `@fristil/designsystem/react` gir hele tilstanden.** Den tar
206
+ `selected` (standard 0) og skriver rollene, `aria-selected`, `aria-controls`,
207
+ `tabIndex`, `hidden` og `aria-labelledby` fra panelet til lenken, som får id
208
+ `${id}-${nøkkel}-tab`. `item` gir `role="presentation"` til hvert `<li>`.
209
+ Da har komponenten ingenting å skrive når den registreres, og React
210
+ hydrerer uten avvik. `hidden` er boolsk, fordi React 19 skriver
211
+ `hidden="until-found"` som `hidden=""`; søk i siden åpner derfor ikke et
212
+ skjult panel i en React-app. Har markupen `aria-selected` fra serveren,
213
+ gjelder den når siden lastes, og adressen velger ingen fane. **Slik
214
+ oppdaterer du:** importer `fs` fra `@fristil/designsystem/react` og spre
215
+ `faner.item` på `<li>`.
216
+ - **`fs.suggestion()` og `Fs.suggestion` tar `keys` i stedet for `count`.**
217
+ Alternativene får id fra nøkkelen, `${id}-option-${nøkkel}`, ikke fra
218
+ plassen i lista, så en oppdatering fra serveren kjenner dem igjen. Antall
219
+ nøkler avgjør om tommeldingen har `hidden`, som `count` gjorde. Nøklene
220
+ sjekkes som i `fs.tabs()`. **Slik oppdaterer du:**
221
+ `fs.suggestion({ id, count: kommuner.length })` blir
222
+ `fs.suggestion({ id, keys: kommuner.map((k) => k.nr) })`.
223
+
224
+ ### Nytt
225
+
226
+ - **Forslagslista har et navn**, fra ledeteksten. `fs.suggestion()` gir
227
+ ledeteksten id `${id}-label` og lista `aria-labelledby` mot den. Kommer
228
+ markupen uten, gjør komponenten det samme, og står ledeteksten rundt
229
+ feltet, får lista teksten som `aria-label`, uten å skrive id på ledeteksten.
230
+ - **`fs.suggestion({ pending: true })` skriver `aria-busy="true"` på lista**,
231
+ og stilarket skjuler «Ingen treff» mens det står.
232
+ - **Ti regler i kjernen om det malen må skrive selv**, for at komponentene
233
+ skal virke uten JavaScript og overleve en oppdatering fra serveren, i
234
+ `fristil sjekk`, språkserveren, VS Code og IntelliJ: `kontroll-uten-kobling`
235
+ (ledeteksten har verken `for` mot feltet eller står rundt det, med rettelsen
236
+ «Gi feltet id «navn»» fra feltets `name`), `panel-uten-id` i
237
+ `<fs-popover>`, `fanelenke-uten-panel`, `fane-uten-lenke` (`role="tab"` på
238
+ noe som ikke er en lenke), `fanerad-uten-navn` (med rettelse),
239
+ `tekst-i-ledetekst` (hjelpetekst eller feilmelding inni `<label>`),
240
+ `verdi-uten-felt` (`data-value` uten `.fs-suggestion__value`, og omvendt),
241
+ `feilmelding-motsier` (`hidden` på feilmeldingen ved siden av
242
+ `aria-invalid="true"`), `utgatt-attributt` (`invalid`, `required-marker` og
243
+ `control-id` på `<fs-field>`, med rettelse og beskjed om hva som gjelder i
244
+ stedet) og `id-fra-komponenten` (en id komponenten lagde selv, kopiert inn
245
+ fra DevTools; bare i markupmodus). Reglene tier der malen avgjør markupen,
246
+ også ved en bundet id som `:id`, `th:id` eller `asp-for`. Hver bygde side
247
+ i dokumentasjonen gir null funn, også hver forhåndsvisning lest som markup,
248
+ og en vaktpost i bygget krever det.
249
+ - **Manifestet sier hvilke deler som må ha id fra serveren**, `stableId` på
250
+ delen, og hvilke attributter som er tatt ut av et element, `removed` med
251
+ beskjeden. Begge er tillegg; `schemaVersion` er fortsatt 1.
252
+ - **`fs.progress({ valueText })`** skriver `aria-valuetext`, som skjermleseren
253
+ leser i stedet for prosenten: «Steg 3 av 7» sier mer, og `max` leses sjelden
254
+ opp.
255
+ - **Snippetene har id-en som tabbestopp**, og `for` og `href` følger med. Og
256
+ `live-templates.xml` i pakken har de samme oppskriftene som live templates
257
+ for IntelliJ, med id-en som variabel.
258
+
259
+ ### Rettet
260
+
261
+ - **Hvert alternativ i forslagslista har `aria-selected` fra starten.**
262
+ Komponenten skrev det først når lista ble lukket, så alternativene serveren
263
+ sendte, sto uten fram til da, mens resten av lista hadde det.
264
+ - **`<fs-popover>` skriver aldri id på panelet.** Den lagde en når malen ikke
265
+ hadde skrevet en, og Datastar og idiomorph byttet da ut panelet ved neste
266
+ oppdatering, med fokus og det brukeren gjorde i det. Nå beholdes panelet.
267
+ Uten id kan knappen ikke kobles til panelet med `aria-controls`, og
268
+ komponenten sier fra i konsollen. Byttes panelet ut mens det står åpent
269
+ fordi id-en fra serveren endret seg, sier komponenten også fra, som
270
+ `<fs-field>` gjør. Oppskriften har id på panelet og merker ingen forskjell.
271
+ - **`server-controlled` satt etter et klikk lot ikke serveren flytte fanen
272
+ med bare `hidden`.** `aria-selected` komponenten selv hadde skrevet, ble
273
+ regnet som serverens ord. Nå gjør komponenten det samme som når serveren
274
+ svarer på et klikk, og attributtet kan også fjernes igjen mens siden lever.
275
+ - **Søk i siden velger fanen også i faner i faner.** Komponenten venter til
276
+ nettleseren er ferdig med å avdekke treffet før den skriver raden; før
277
+ stoppet Chromium og WebKit før det ytre panelet.
278
+ - **Serverens `aria-selected` vinner over adressen når siden lastes.** En
279
+ React-app rendrer på serveren uten å se fragmentet, og valgte komponenten
280
+ fra adressen før React hydrerte, meldte React avvik. Peker adressen på en
281
+ annen fane i raden, rettes den til den valgte.
282
+ - **`hidden` skrives tilbake i formen serveren brukte**, `hidden=""` eller
283
+ `hidden="until-found"`, og i én form per rad.
284
+ - **Før registrering viser `tabs.css` panelet adressen peker på**, eller
285
+ panelet med det inni, i stedet for alltid det første, så innholdet ikke
286
+ hopper når komponenten registreres. Serverens `hidden` går foran, og under
287
+ `server-controlled` vises det første. Regelen står bak
288
+ `@media (scripting: enabled)`: uten JavaScript vises alle panelene.
289
+
290
+ ## 0.35.0 (2026-10-22)
291
+
292
+ ### Brytende
293
+
294
+ - **`<fs-field>` og `<fs-suggestion>` skriver aldri id på feltet.** Datastar
295
+ og idiomorph kjenner igjen et element på id, og et felt med en id serveren
296
+ ikke sendte, ble byttet ut ved neste oppdatering, med fokus og det brukeren
297
+ hadde skrevet. Nå er det malen som skriver `for` på ledeteksten og `id` på
298
+ feltet, som HTML uansett krever for at feltet skal ha et navn før skriptet
299
+ har kjørt, eller som legger ledeteksten rundt feltet. Komponenten skriver
300
+ `for` fra feltets id når ledeteksten ikke står rundt feltet. Et felt uten id
301
+ med ledeteksten ved siden av gir en advarsel i konsollen. Hjelpeteksten,
302
+ feilmeldingen, lista og alternativene får fortsatt id av komponenten når de
303
+ mangler: byttes de ut, kobler komponenten dem på nytt, og det markerte
304
+ alternativet gjenfinnes på teksten. **Slik oppdaterer du:** skriv `for` og
305
+ `id` i malen, eller legg `<label>` rundt feltet.
306
+ - **`invalid`, `required-marker` og `control-id` er tatt ut av `<fs-field>`.**
307
+ Feltet er ugyldig når feilmeldingen synes, altså har tekst og står uten
308
+ `hidden` og uten `display: none` (som `data-show` i Datastar setter), eller
309
+ når serveren har skrevet `aria-invalid` på feltet. Komponenten rører aldri
310
+ `hidden` på feilmeldingen; før skjulte den en feilmelding serveren hadde
311
+ rendret synlig, fordi verten manglet `invalid`. Påkrevd-merket står som
312
+ `data-required` på ledeteksten, og vises da også uten JavaScript.
313
+ `control-id` fikk komponenten til å skrive en id serveren ikke sendte.
314
+ **Slik oppdaterer du:** `<fs-field invalid>` blir en synlig `.fs-error-text`
315
+ (i Datastar `data-attr:hidden="!$ugyldig"` på feilmeldingen framfor
316
+ `data-attr:invalid` på verten), `required-marker="x"` blir
317
+ `data-required="x"` på `<label>`, og `control-id` blir `id` på feltet.
318
+ Egenskapene `invalid`, `requiredMarker` og `controlId` er borte, og
319
+ React-typene i `react-jsx` er oppdatert.
320
+ - **`<fs-suggestion>` viser alltid teksten i feltet.** Før skrev den
321
+ `data-value` inn i det synlige feltet, så brukeren som valgte Bergen, så
322
+ «4601», og skjemaet sendte koden under feltets `name`. Nå får et skjult
323
+ `.fs-suggestion__value` koden når serveren har skrevet det, og
324
+ `suggestion-select` har både `value` og `label`. Tilbakestilles skjemaet,
325
+ følger koden teksten: serverens når teksten er serverens, ellers det
326
+ valgte, ellers alternativet med den teksten, ellers ingen. **Slik
327
+ oppdaterer du:**
328
+ skal skjemaet sende koden, legg inn `<input class="fs-suggestion__value"
329
+ type="hidden" name="…">`, eller les `evt.detail.value`.
330
+ - **`.fs-suggestion__field` er borte, og statuselementet har verken
331
+ `aria-live` eller `data-ignore-morph`.** Feltet, lista, tommeldingen og
332
+ statuselementet står rett i `<fs-suggestion>`, som selv gir lista plassen
333
+ under feltet. `role="status"` gir `aria-live="polite"` av seg selv, og
334
+ tømmer en oppdatering statuselementet, skriver komponenten antallet på
335
+ nytt (ikke under `prefiltered`, der det nye antallet kommer rett etter),
336
+ så Datastars `data-ignore-morph` trengs ikke, og det samme gjelder med
337
+ htmx. Tommeldingen trenger ikke `hidden`: stilarket skjuler den sammen
338
+ med lista. `fs.suggestion()` gir ikke lenger `field`. **Slik oppdaterer
339
+ du:** ta bort `<div class="fs-suggestion__field">` og `{...forslag.field}`
340
+ rundt feltet, og `aria-live`, `data-ignore-morph` og `hidden` på de to
341
+ elementene.
342
+
343
+ ### Lagt til
344
+
345
+ - **`<fs-tabs>` og `<fs-popover>` sier fra når en patch byttet ut
346
+ elementene fordi serveren ikke skrev id.** Datastar og idiomorph morfer
347
+ ikke et element med id inn i et uten: det gamle fjernes og serverens
348
+ settes inn. Lager komponenten id-ene selv, som den gjør for bar markup,
349
+ byttes fanene og panelet ut ved neste patch som sender området på nytt, og
350
+ fanevalget og fokus er borte. Nå kommer det én advarsel i konsollen i det
351
+ øyeblikket det skjer, med beskjed om å skrive id i malen. Sider som aldri
352
+ patcher, eller patcher smalt, får ingen advarsel. Reprodusert mot Datastar
353
+ v1.0.4 i `fs-tabs-morph.browser.test.ts`, som kjører den ekte bundlen.
354
+ - **`<fs-field>` og `<fs-suggestion>` sier fra når en oppdatering byttet ut
355
+ feltet fordi id-en fra serveren endret seg** mellom to rendringer, som når
356
+ malen lager id-en av et tilfeldig tall eller en teller, og det brukeren
357
+ hadde skrevet, gikk tapt.
358
+ - **`<fs-field>` og `<fs-suggestion>` sier fra når `for` på ledeteksten
359
+ pekte på en annen id enn feltets.** Komponenten skriver den om, men uten
360
+ JavaScript hadde feltet da ingen ledetekst.
361
+ - **`fs.suggestion()` gir det skjulte feltet for koden som `value`**, og
362
+ `.fs-suggestion__value` er en ny del i manifestet og i begge editorene.
363
+ Komponenten sender `input` og `change` på feltet når den skriver eller
364
+ tømmer koden, så `data-bind` i Datastar ser den; de bobler ikke, så en
365
+ `data-on:input` på verten utløses ikke av dem.
366
+ - **`<fs-suggestion>` går til første og siste alternativ med PgUp og PgDn.**
367
+ Home og End røres ikke; de flytter markøren i teksten.
368
+ - **`idOrFallback` sier også at en ny id per rendring bytter ut
369
+ elementene** ved neste patch.
370
+
371
+ ### Rettet
372
+
373
+ - **`<fs-suggestion>` mistet markeringen når serveren sendte lista på
374
+ nytt.** Alternativene får id av komponenten, og en oppdatering bytter dem
375
+ ut med nye id-er. Markeringen gjenfinnes nå på teksten.
376
+ - **`<fs-suggestion>` valgte bare på `mousedown`.** Hjelpemidler som
377
+ aktiverer et element, sender `click`, og den valgte da ingenting. Nå skjer
378
+ valget på `click`, og `mousedown` holder bare fokus i feltet.
379
+ - **`<fs-suggestion>` åpnet lista og endret verdien i et felt med
380
+ `readonly`.** Nå åpner et felt med `readonly` eller `disabled` ikke lista.
381
+ - **Et alternativ med `aria-disabled="true"` kunne velges.** Nå vises det,
382
+ men hoppes over av piltastene og kan ikke velges med mus.
383
+ - **Enter under en IME-komposisjon valgte det markerte alternativet.** Nå
384
+ hører tastene til komposisjonen.
385
+ - **`<fs-popover>` lukket vinduet etter en patch fra en mal uten
386
+ `popover`.** Morfingen tok `open` på verten og `popover` på panelet i
387
+ samme omgang. Komponenten lukket panelet, gjenåpnet det i neste mikrotask,
388
+ og først etterpå kom `toggle` med «closed» fra lukkingen, som en egen
389
+ oppgave. Den ble tatt som brukerens lukking. Nå gjelder `toggle` bare når
390
+ den stemmer med panelets faktiske tilstand.
391
+
392
+ ### Endret
393
+
394
+ - **«Markup og oppførsel» sier hvilke id-er som overlever en oppdatering.**
395
+ Felt og forslagsfelt får `for` og `id` fra malen, fanene og
396
+ sprettoppvinduet trenger id fra serveren når hele området sendes på nytt,
397
+ og id-er i en løkke lages av en nøkkel i dataene, aldri av indeksen eller en
398
+ teller. Regelbøkene i `agent/` og sidene for felt, forslagsfelt og faner
399
+ sier det samme. Før sto det at `outer` morfer og at malen ikke trenger gjøre
400
+ noe, og det holdt bare for markup fra byggefunksjonene.
401
+
402
+ ## 0.34.0 (2026-10-18)
403
+
404
+ ### Brytende
405
+
406
+ - **`hidden` på `<fs-error-summary>` er et flagg i JSX-typene.** `hidden`
407
+ kom fra `HTMLAttributes` som `boolean`, så `hidden={false}` kompilerte,
408
+ mens React 18 skriver det som `hidden="false"` på en web component, som er
409
+ sant: boksen ble aldri vist. Typen er nå `true | undefined`, som `open` på
410
+ `<fs-dialog>`, slik regelboka for React sier om hvert boolske attributt på
411
+ en web component. `fs.errorSummary().host` sendte alt `true` eller
412
+ ingenting.
413
+ Det er brytende for den som skrev `hidden={!harFeil}` eller
414
+ `hidden={false}`: det kompilerte, og i React 19 virket det, siden React 19
415
+ setter egenskapen `hidden` i stedet for attributtet. Skriv
416
+ `hidden={harFeil ? undefined : true}`, eller bruk
417
+ `fs.errorSummary({ count }).host`, som sender det riktig.
418
+ - **`fristil sjekk` meldte grønt for en tom fil og en tom side.** Bare tom
419
+ standard inn ga feilkode. En tom fil, og en adresse som svarte 200 med tom
420
+ HTML, ga «Markupen stemmer» og 0, så en CI-linje mot en side som rendret
421
+ tomt ble grønn. Nå gir en adresse med tom side feilkode, og det gjør også
422
+ filer som alle er tomme. Én tom fil blant flere, som en tom partial i et
423
+ glob, er fortsatt ikke en feil.
424
+ En CI-linje som var grønn mot en tom side, blir rød med denne versjonen.
425
+
426
+ ### Lagt til
427
+
428
+ - **`setAttributes` og typen `ReactSet` fra `/react`.** `fs.setAttributes`
429
+ derfra er den samme funksjonen, for sett med `className`, `htmlFor` og
430
+ `tabIndex` som tall. Hovedinngangen eksporterer `rememberManaged`, som
431
+ `/react` bruker for å gi et omdøpt sett de samme forvaltede navnene; den
432
+ er ikke ment for apper.
433
+ - **Regelen `del-flere-ganger` i `fristil sjekk`, editoren og
434
+ `diagnoseMarkup`.** En del med det nye feltet `unique` i manifestet som
435
+ står mer enn én gang, gir en advarsel: et `<fs-field>` med to kontroller,
436
+ to hjelpetekster i ett felt, en `fs-table` med to `<caption>`. `unique`
437
+ står på delene der en til er en feil og ikke vanlig innhold, og leses av
438
+ `Part` i Kotlin og `Part` i TypeScript. Regelen leser `parts` på
439
+ elementene og klassene i manifestet, så en ny del med `unique` sjekkes
440
+ uten en egen regel. Et treff inni en nøstet vert av samme slag hører til
441
+ den, det som står i en `<template>` teller ikke, og står det malsyntaks i
442
+ verten, sier regelen ingenting.
443
+ - **`tbody` i `fs-table` kan stå flere ganger** i manifestet, som HTML
444
+ tillater.
445
+ - **`Fristil.manifest.builders` i Kotlin-pakken.** Hver byggefunksjon med
446
+ valgene (`name`, `required`, `type` som `values`, `flag`, `text`,
447
+ `number` eller `textList`, og `description`) og formen på svaret
448
+ (`attributes`, `text`, `values`, `list` eller `group`). Tabellen
449
+ generatoren bak `Fs` bruker er fortsatt utelatt. TypeScript-typen
450
+ `Manifest` har fått `builders` med samme form.
451
+ - **`Fristil.buildTheme(oppskrift)` og `Fristil.inspectTheme(css)` i
452
+ Kotlin-pakken.** Temaet kunne før bare bygges med `fristil tema` og
453
+ Gradle-oppgaven `fristilTema`. Oppskriften er JSON-teksten `fristil tema`
454
+ leser, med norske eller engelske nøkler, og svarene `Theme` og
455
+ `ThemeReport` har de samme feltene som `CoreTheme` og `CoreThemeReport`
456
+ i TypeScript. Fargekontrakten ligger nå i jar-en, som manifestet.
457
+ - **Manifestet har en `tokens`-seksjon.** Avstandsskalaen, brekkpunktene
458
+ og containerbreddene fra `tokens.ts`, fargefamiliene, rollene og lagene
459
+ med hva hver er til, hvert `--fs-color-*`-navn, og komponentvariablene
460
+ per komponent med standardverdien stilarket faller tilbake på. Fargene
461
+ står som akser og navn, ikke som verdier, siden et tema kan ha andre
462
+ verdier enn Fristils egne. `ManifestTokens` i Kotlin leser det samme.
463
+ `schemaVersion` er fortsatt 1, og `tokens` i Kotlin er `null` i et manifest
464
+ fra før denne versjonen. Beskrivelsene av familiene, rollene og
465
+ lagene eksporteres også fra `@fristil/designsystem/tokens`, som
466
+ `FAMILY_DESCRIPTIONS`, `ROLE_DESCRIPTIONS` og `LAYER_DESCRIPTIONS`.
467
+ - **Manifestet sier hva hver klasse tar som innhold, og hvilke deler den
468
+ består av.** Hver klasse i `classes` har `content`, med samme ord som
469
+ delene til elementene: `text`, `free`, `parts` eller `none`. Klassene med
470
+ en fast struktur, som `fs-table`, `fs-list` og `fs-toggle-group`, har
471
+ `parts` med `selector`, `description`, `content`, `multiple` og
472
+ `optional`, og en klasse som bare er en del av en annen komponents
473
+ markup, som `fs-card__title`, peker på forelderen med `partOf`.
474
+ `schemaVersion` er fortsatt 1. I Kotlin har `ManifestClass` fått
475
+ `content`, `partOf` og `parts`, og `content` er `null` i et manifest fra
476
+ før denne versjonen, så IntelliJ kan lese manifestet til en eldre pakke.
477
+
478
+ ### Endret
479
+
480
+ - **Pakken sender med kildene kartene peker på.** `dist` har kildekart og
481
+ deklarasjonskart, og de for `fs`, `/react`, `/register`, `/dom`,
482
+ `/diagnostics` og kommandolinja pekte på fjorten filer i `src` som ikke var
483
+ med i tarballen, så «gå til definisjon» og et stakkspor derfra landet i en
484
+ fil som ikke fantes. `src/*.ts`, `src/diagnostics`, `src/jsx` og
485
+ `src/vocabulary` følger nå med, som `src/components` alt gjorde, og
486
+ byggesteget krever at hvert medsendt kart peker på en fil i pakken.
487
+ - **Regelboka for maler sier at Kotlin-pakken ligger på Maven Central.**
488
+ `agent/maler.md` sa at `io.github.markusaugust:fristil` og Gradle-pluginen
489
+ måtte bygges fra kildekoden. Begge ligger på Central fra 0.33.0, med samme
490
+ versjon som npm-pakken, men ikke hver npm-versjon går dit. Regelboka ber
491
+ derfor agenten bruke den nyeste versjonen som finnes på Central, og aldri
492
+ skrive en den ikke har sett der.
493
+
494
+ ### Rettet
495
+
496
+ - **`<fs-error-summary>` tar fokus også i React.** React gir fokus tilbake
497
+ til elementet som hadde det før en commit, og boksen flyttet fokus inne i
498
+ den samme commiten. Fokus endte derfor på innsendingsknappen i Chromium og
499
+ Firefox, og på feltet i alle tre motorene når skjemaet ble sendt med Enter,
500
+ både når boksen ble satt inn og når `hidden` ble tatt bort. Boksen tar nå
501
+ fokus én gang til etter commiten, hvis det gikk tilbake dit det var. Har et skript flyttet fokus
502
+ til noe annet, står det. En React-app som viser boksen mens brukeren står
503
+ i et felt, for eksempel ved `blur`, merker at fokus nå flyttes dit, slik
504
+ det alltid har gjort uten React.
505
+ - **Et merke, en etikett, en knapp eller en lenke i et kort strekkes ikke
506
+ lenger til hele bredden.** `.fs-card` er en flekskolonne, så
507
+ `<span class="fs-badge">Innvilget</span>` ble et bånd tvers over kortet,
508
+ slik eksempelet på komponentsiden viste. Avsnitt, lister og bilder
509
+ strekkes som før.
510
+ - **`fristil lsp` leter ikke over arbeidsområdet for en fil utenfor det.**
511
+ Letingen etter manifestet stoppet ved rota av arbeidsområdet fila lå
512
+ under, men for en fil utenfor det, som en kladdefil, og for hver fil når
513
+ editoren ikke ga noe arbeidsområde, som Neovim uten treff på
514
+ `root_markers`, fantes ingen rot å stoppe ved, så den gikk helt til rota
515
+ av filsystemet og tok en pakke i hjemmemappa som prosjektets. Nå letes det
516
+ ikke fra en slik fil: den sjekkes mot manifestet ved rota av
517
+ arbeidsområdet, om det har et, ellers mot pakkens, slik Editoren-siden og
518
+ IntelliJ-pluginen alt sa.
519
+ - **Tagnavnene er typet fra `/register` og `/react-jsx` alene.** Typesikker
520
+ bruk-siden sa at hendelsestypene følger med hver inngang til
521
+ web-komponentene, men `HTMLElementTagNameMap` ble bare utvidet i
522
+ komponentfilene, og `/register` og `/react-jsx` tar dem ikke med i typene.
523
+ Da var `document.querySelector("fs-dialog")` et `Element`, og lytteren på
524
+ det fikk `Event` uten `detail`, så mønsteret på siden var en typefeil. Kartet
525
+ over tagnavnene står nå i `events.ts`, som hver inngang alt tar med, så
526
+ `querySelector("fs-dialog")` gir `FsDialog | null` fra alle tretten
527
+ inngangene, også hver komponentinngang for de andre komponentene. Bygget
528
+ kompilerer en fil per inngang, med bare den importen, så det ikke kan gli
529
+ tilbake.
530
+ - **`fs.setAttributes` fra `/react` skriver `class` og `for`, og rydder.**
531
+ Funksjonen var hovedinngangens, mens settene fra `/react` har `className`
532
+ og `htmlFor`, så den skrev attributtene `classname` og `htmlfor`, og fant
533
+ ikke byggefunksjonen bak det omdøpte settet: `aria-invalid` ble stående
534
+ etter at feltet var rettet, uten advarsel. Nå oversettes navnene tilbake
535
+ før de settes, `tabIndex` blir `tabindex`, og settet rydder det samme som
536
+ fra hovedinngangen.
537
+ - **`engines` slipper ikke lenger inn Node 21 og 22.0–22.2.** Kjernen lastes
538
+ med `process.getBuiltinModule`, som kom i Node 20.16 og 22.3, men
539
+ `>=20.16` tok med versjonene mellom, og der sa feilmeldingen at Node 22.1
540
+ var eldre enn 20.16. Kravet er nå `^20.16.0 || >=22.3.0`, og meldingen
541
+ nevner begge grensene. Oppføringen under 0.32.0 som sier «20.16 eller
542
+ nyere», gjelder med dette unntaket.
543
+ - **Regelboka for React og Kodeagenter-siden sa at `react-jsx` stopper en
544
+ variant som ikke finnes.** `react-jsx` typer attributtene på
545
+ web-komponentene, og ingen av dem har en variant. Det er
546
+ `fs.button({ variant })` som gjør en ukjent variant til en kompileringsfeil,
547
+ med eller uten `react-jsx`, og en håndskrevet `className` eller
548
+ `data-variant` ser ingen av dem, og heller ikke `fristil sjekk`, som leser
549
+ `class`, ikke `className`. Begge tekstene sier nå hva som fanger hva, og
550
+ at det håndskrevne skal erstattes av byggefunksjonene.
551
+ - **`defineFs*` kaster ikke når klassen alt er registrert under et eget
552
+ navn.** `defineFsToast("app-toast")` fulgt av `defineFs()` ga
553
+ NotSupportedError på `fs-toast`, siden en konstruktør bare kan stå i
554
+ registeret én gang, og `<fs-session-timeout>` og `<fs-connection-status>`
555
+ ble aldri registrert. Nå registreres en underklasse under det andre navnet,
556
+ så begge navnene virker og flere kall er ufarlige, slik dokumentasjonen
557
+ sier. `tagName` er dessuten typet som `string`. Før var typen standardnavnet,
558
+ så TypeScript avviste `defineFsToast("app-toast")` før det kom så langt.
559
+ - **`<fs-toast>` flytter fokus riktig også i en skyggerot.** Komponenten
560
+ leste `document.activeElement`, som peker på skyggeverten når regionen
561
+ står i en skyggerot, slik hver forhåndsvisning i dokumentasjonen gjør. Da
562
+ så det ut som om ingen melding hadde fokus: lukket brukeren meldingen med
563
+ Enter, falt fokus til `body` i stedet for til meldingen ved siden av eller
564
+ dit det kom fra, og det samme skjedde ved `clear()`. Fokus leses nå fra
565
+ komponentens egen rot. `<fs-session-timeout>` husker hvor fokus sto før
566
+ varselet enten feltet står i siden eller i samme skyggerot som
567
+ komponenten, så det får fokus tilbake også når nettleseren ikke setter det
568
+ dit selv.
569
+ - **README sier hva et eget tagnavn gir.** Den lovet at du bestemmer «hva
570
+ det skal hete», men stilarkene til `<fs-field>`, `<fs-tabs>`,
571
+ `<fs-popover>`, `<fs-dialog>` og `<fs-suggestion>` er skrevet for
572
+ `fs-*`-navnene, så en komponent under et annet navn mistet `display`-regelen
573
+ på verten, og `<fs-dialog>` også plasseringen av en dialog som står åpen
574
+ før den er modal. README sier nå at oppførselen følger med navnet, men
575
+ ikke stilen, og hvilke fire komponenter som setter klassen sin selv.
576
+ - **`popover-toggle` er beskrevet slik den sendes.** Beskrivelsen i
577
+ `events.ts` og `web-types.json` sa «Panelet åpnet eller lukket seg», men
578
+ hendelsen kommer bare når brukeren åpner eller lukker panelet, og fra
579
+ `show()`, `hide()` og `toggle()`. Settes `open` som egenskap eller
580
+ attributt, som React 19 og Datastars `data-attr:open` gjør, kommer ingen
581
+ hendelse, ellers hadde Datastar-oppskriften fått sitt eget signal i retur.
582
+ Popover-siden skiller nå `meny.open = false` fra `hide()`.
583
+ - **Regelboka for maler lovet at `<fs-tabs>` husker fanevalget etter enhver
584
+ patch.** Den nevnte `innerHTML`, htmx og Turbo, men valget er knyttet til
585
+ selve knappen og overlever bare en morfing som beholder elementene, som
586
+ Datastars `outer`, htmx med idiomorph og Turbo 8 med morfing. Bytter
587
+ patchen ut nodene, følger komponenten markupen. Regelboka sier nå hvilke
588
+ patcher som beholder valget, og at serveren ellers må skrive det selv
589
+ eller bruke `server-controlled`.
590
+ - **Regelbøkene sa at markupen i en `ramme`-komponent står der uansett.**
591
+ «Kom i gang» og «Rammeverk» ble rettet i 0.33.0, men ikke teksten i
592
+ generatoren, så alle seks regelbøkene manglet unntaket: Tabs viser bare det
593
+ første panelet og Suggestion skjuler lista til de er registrert.
594
+ - **React-oppskriften på Dialog-siden og mønsteret «Bekreft en handling»
595
+ tømmer `returnValue` før `showModal()`.** «Ren HTML»- og Astro-fanene
596
+ fikk tømmingen i 0.33.0, men React-fanen, mønstersiden og demoen åpnet
597
+ dialogen med den gamle verdien stående. Ble dialogen åpnet igjen etter en
598
+ «Slett» og lukket uten knapp, fikk `close`-lytteren «slett» på nytt og
599
+ slettet igjen.
600
+ - **`fristil sjekk-tema` og `inspectTheme` hoppet over en nøstet blokk som
601
+ begynner med et element og et kolon.** `html:not([data-theme="light"]) { … }`
602
+ eller `body:has(…) { … }` inni en `@media`, et `@layer` eller en annen
603
+ blokk ble lest som deklarasjonen `html`, og den slukte alt fram til neste
604
+ semikolon, blokkene etter den med. Et tema med et brudd der fikk «Temaet
605
+ holder hvert løfte». Nå leses blokka som en regel, slik nettleseren gjør.
606
+ - **`fristil sjekk-tema` og `inspectTheme` leste
607
+ `@media not (prefers-color-scheme: dark)` som mørkt tema.** Blokka gjelder
608
+ i lyst tema, og Fristils egne lyse verdier ga brudd. `not` snur nå
609
+ mediespørringen, også skrevet med en medietype, som `not all and (…)` eller
610
+ `not screen and (…)`, og blokka kontrolleres bare som lys. Med en annen
611
+ medietype, som `not print and (…)`, gjelder blokka skjermen i både lyst og
612
+ mørkt tema, og den kontrolleres som en blokk uten betingelse.
613
+ - **`fristil sjekk-tema` og `inspectTheme` meldte systemfargene som feil.**
614
+ `--fs-color-disabled-surface`, `--fs-color-disabled-text` og
615
+ `--fs-color-overlay` er ingen celle, men ble lest som familien `disabled`
616
+ med roller som manglet, og som et token systemet ikke har. En overstyring
617
+ skrevet på hver temagrense, slik dokumentasjonen ber om, ga feilkode, og
618
+ det gjorde Fristils egen `tokens.css` også, med tolv problemer. Nå hopper
619
+ sjekken over dem: de lover ingen kontrast.
620
+ - **Et tema uten farger fra `fristil tema` ga feilkode i `fristil sjekk-tema`.**
621
+ Et tema med bare skrift og form fikk «Fant ingen --fs-color-*-verdier»,
622
+ og `inspectTheme` meldte det som et problem, enda dokumentasjonen sier at
623
+ et tema laget med `tema` holder kravene. Nå svarer sjekken at det ikke er
624
+ noen kontrast å kontrollere, uten feilkode. En fil uten noen
625
+ `--fs-*`-verdier gir fortsatt feilkode.
626
+ - **En `fs-progress` med `value` viste ikke fyllet i høykontrastmodus.**
627
+ Den fylte delen leste `currentColor`, og modusen tvang bakgrunnen på
628
+ pseudoelementet tilbake til `Canvas`, så stolpen så tom ut uansett hvor
629
+ langt den hadde kommet. Fyllet tegnes nå i `Highlight` rett på
630
+ `::-webkit-progress-value` og `::-moz-progress-bar`.
631
+ - **Klassen på et avsnitt i `fs-card` og `fs-alert` vinner over
632
+ komponenten.** `.fs-card p` og `.fs-alert p` slo klassene på avsnittet på
633
+ spesifisitet. I et kort virket ikke `--fs-paragraph-line-height`, en
634
+ `fs-card__title` som var en `<p>` fikk artikkelens linjehøyde, og
635
+ `fs-help-text` og `fs-error-text` mistet marginen i toppen, også i en
636
+ melding. Reglene teller nå bare elementet, så et avsnitt uten klasse ser
637
+ ut som før.
638
+ - **En besøkt `fs-link` skifter farge under musa.** `:hover` sto før
639
+ `:visited` med samme spesifisitet, så besøktfargen vant og hover-fargen
640
+ kom aldri. Rekkefølgen er snudd.
641
+ - **Ringen rundt `fs-avatar` vises over bildet.** Den var en `box-shadow`
642
+ innover, og en slik skygge males under innholdet, så et bilde dekket den
643
+ helt og ringen fantes bare med initialer. Den er nå en `outline` innover,
644
+ også i `fs-avatar-stack`, der skyggen utenfor fortsatt skiller bildene, og
645
+ hver avatar i stabelen males for seg, så ringen til den bak ikke skinner
646
+ gjennom bildet til den foran.
647
+ En slik `outline` slår nettleserens egen fokusmarkering, så en
648
+ `fs-avatar` som er en knapp får `--fs-focus-ring` på `:focus-visible`, og
649
+ i en stabel løftes den med fokus over naboene så fokusringen vises hel.
650
+ - **`--fs-skeleton-radius` virker på `data-variant="text"`.** Tekstlinjene
651
+ hadde en fast radius og leste ikke variabelen. Standarden der er
652
+ fortsatt `var(--fs-spacing-0-5)`, og tabellen på siden sier det.
653
+ - **Et langt valg i `fs-select` med `data-picker="styled"` ble ikke
654
+ klippet.** Regelen som klipper det, treffer `<selectedcontent>`, og
655
+ kommentaren sa at nettleseren lager elementet selv. Det gjør den ikke, så
656
+ med markupen på siden ble et langt valg brutt over flere linjer, og feltet
657
+ ble høyere. Komponentsiden sier nå at
658
+ `<button><selectedcontent></selectedcontent></button>` må stå først i
659
+ `<select>`-en for at valget skal klippes.
660
+ - **Siden om `fs-textarea` sa at `rows` virker som før.** Minstehøyden er
661
+ litt under tre rader, så `rows="1"` og `rows="2"` gir det samme feltet.
662
+ Siden sier nå hvor grensen går, og at `--fs-textarea-min-height: 0` lar
663
+ `rows` bestemme alene.
664
+ - **`fristil tema oppskrift.json --ut tema.css` skrev ingen fil.** Uten
665
+ likhetstegn ble `--ut` og `tema.css` lest som to filnavn etter
666
+ oppskriften og ignorert. Temaet gikk til utdata, og kjøringen avsluttet
667
+ med 0. På samme måte forsvant `--aksent #f00` bak fargen i fila. Nå stopper
668
+ `tema` og ber om likhetstegn, slik `overta` og `agent` gjør, og mer enn én
669
+ oppskriftsfil gir også feilkode. `overta` og `agent` stopper dessuten på et
670
+ ukjent flagg med likhetstegn, som `--utt=src/ui` eller `--rammverk=react`,
671
+ i stedet for å bruke standarden.
672
+ - **`fristil tema` leser oppskriften som `JSON.parse`.** Sto en nøkkel to
673
+ ganger, vant den første, mens `JSON.parse` lar den siste vinne. Tall som
674
+ `0700` og `1.` og et rått kontrolltegn i en streng ble godtatt, og en fil
675
+ med svært dyp nesting stoppet den kjørbare fila med «stack overflow». Nå
676
+ vinner den siste verdien, slike tall og strenger gir «er ikke gyldig
677
+ JSON», og det gjør også mer enn 128 objekter og lister inni hverandre.
678
+ - **Tall i temaet og manifestet skrives som `String(tall)` i JavaScript.**
679
+ `1e-7` ble `0.0000001` og `1e21` ble `1000000000000000000000`, og et
680
+ uendelig tall ble `inf`. Nå får de eksponent, som `1e-7` og `1e+21`, og
681
+ `Infinity` heter `Infinity`. I manifestet blir et uendelig tall `null`,
682
+ som i `JSON.stringify`. Vanlige tall skrives som før.
683
+ - **Meldingen for `aria-controls` som peker på en id som ikke finnes, slutter
684
+ ikke lenger med et mellomrom.** Det sto igjen etter «styrer.», i
685
+ `fristil sjekk`, i editoren og som to mellomrom før regelnavnet i
686
+ `assertFristil`.
687
+ - **En BOM først i en fil eller side gir ikke lenger en kolonne for mye.**
688
+ `fristil sjekk` talte U+FEFF som et tegn på linje 1, så kolonnen var én
689
+ mer enn editoren viser. For en adresse svarte `npx` riktig, fordi Node
690
+ fjerner BOM-en, mens den kjørbare fila og `java -jar` ga én kolonne mer.
691
+ Nå fjernes den for filer, standard inn og adresser i alle verter.
692
+ - **Språkserveren og `fristil sjekk` regner `\r` som linjeskift.** Bare
693
+ `\n` delte linjer, så et dokument med `\r`-linjeskift ble én lang linje,
694
+ og en editor som sender bufferen slik den er, som Neovim med
695
+ `fileformat=mac`, fikk funnene og hurtigrettelsene på feil sted. Nå er
696
+ `\n`, `\r\n` og `\r` alene linjeskift, som LSP krever. Linje og kolonne i
697
+ `diagnosePage`, `assertFristil` og `fristil sjekk` følger samme regel.
698
+ Hurtigrettelsen «Sett inn en ledetekst» gir også `<label>` sin egen linje
699
+ med samme innrykk når linjeskiftene er `\r`.
700
+ - **Eksempelet på siden om Kotlin viste feil regelnavn.** Funnet for en
701
+ `for` som peker på en id som ikke finnes, sto med `[for-peker-feil]`, men
702
+ regelen heter `id-finnes-ikke`. `for-peker-feil` gjelder en `for` som
703
+ peker på et element som ikke kan ha ledetekst. Den som kopierte navnet
704
+ til `<!-- fristil-ignore-next … -->`, fikk funnet likevel.
705
+ - **Oppskriften for å bytte skilletegn i `fs-breadcrumbs` tok ikke med
706
+ alternativteksten.** Siden viste `content: "›"`, og en regel utenfor laget
707
+ slår stilarket, så den som fulgte rådet fikk tilbake feilen 0.33.0 rettet:
708
+ skjermleseren sa «›» mellom hvert steg. Snutten har nå `content: "›" / ""`
709
+ bak samme `@supports` som stilarket, og siden sier hvorfor. En test kjører
710
+ snutten fra siden mot stilarket.
711
+ - **Oppskriften for å bytte pila i `fs-accordion` lot rotasjonen stå.** Siden
712
+ sa at `content` og `border` var nok, men pseudoelementet har fast størrelse
713
+ og `transform`, og vinkelen byttes når panelet er åpent, så et «+» sto på
714
+ skrå og ble et «×» i åpen tilstand. Et synlig tegn uten alternativ tekst
715
+ ble dessuten en del av navnet på knappen, det skjermleseren sier når
716
+ `<summary>` får fokus. Snutten nullstiller nå begge og gir tegnet tom
717
+ alternativ tekst.
718
+ - **JSDoc-eksempelet for `alert` spredte `alert.title`.** `alert.title` er
719
+ klassenavnet, en streng, og `<p {...alert.title}>` gir tegnene som
720
+ attributter og ikke klassen. Eksempelet skriver `class={alert.title}`,
721
+ som accordion og card.
722
+ - **Fieldset-siden sa at en `<legend>` lenger ned ikke kobles til gruppen.**
723
+ Nettleserne bruker den første `<legend>`-en som navn og tegnet ledetekst
724
+ uansett hvor den står. Regelen om at den skal stå først gjelder fortsatt,
725
+ men begrunnelsen er at HTML krever det, og at markupen ellers er ugyldig.
726
+ - **Select-siden sa at listen tegnes i siden fra Chromium 148.** Det eneste
727
+ stilarket spør om, er `@supports (appearance: base-select)`, og den slår
728
+ til i eldre Chromium også; 148 er versjonen Fristil er testet i. Tabellen
729
+ sier nå hva som avgjør, og hvilke versjoner som er testet.
730
+ - **Skip Link-siden begrunnet `tabindex="-1"` med at neste Tab fortsatte i
731
+ menyen.** Nettleseren fortsetter fra målet etter hoppet også uten
732
+ attributtet. Det som mangler uten det, er fokuset og skjermleserens
733
+ opplesning, og det sier siden og mønstersiden «Sideskjelett» nå. Error
734
+ Summary-siden og JSDoc-en til `fs-error-summary` sa det samme om en vanlig
735
+ ankerlenke, at neste tastetrykk fortsatte i oppsummeringen, og sier nå at
736
+ nettleseren bare gir fokus til et mål som kan få det. Siden og
737
+ stilarket sa også at fokus satt fra kode aldri regnes som synlig fokus.
738
+ Det gjelder bare etter et museklikk, og det er det tilfellet `:focus` er
739
+ valgt for.
740
+ - **Lagringseksempelet på Switch-siden rullet ikke tilbake ved et feilsvar,
741
+ og tok fokuset fra bryteren.** `fetch` avviser bare ved nettverksfeil, så
742
+ et 500-svar ble meldt som «Varsler er på.» med bryteren stående i ny
743
+ stilling. Eksemplet slo dessuten av bryteren mens den lagret, og
744
+ `disabled` tar fokuset fra en bryter som har det uten å gi det tilbake, så
745
+ et nytt mellomromstrykk gjorde ingenting. «Ren HTML» og React sjekker nå
746
+ `svar.ok` og sperrer et nytt trykk uten `disabled`, Datastar-fanen og
747
+ eksemplet med `fs.setAttributes` slår ikke lenger av bryteren, og
748
+ JSDoc-en til `disabled` i `switchControl` advarer mot bruken. Det korte
749
+ eksemplet på Connection Status-siden og i JSDoc-en til
750
+ `FsConnectionStatus` sjekker også `svar.ok`. En test kjører «Ren
751
+ HTML»-eksemplet fra siden mot en server som svarer 500.
752
+ - **`fs-list` beholder punktene og tallene sammen med Tailwinds Preflight.**
753
+ Standardvarianten sa ingenting om `list-style` og lente seg på nettleseren,
754
+ mens Preflight setter `list-style: none` på hver `<ul>` og `<ol>`. Med
755
+ oppsettet Tailwind-siden anbefaler, `@import "tailwindcss"` og `base` før
756
+ `fristil`, mistet lista punktene og nummereringen, og det gjorde lista i
757
+ `fs-error-summary` også. Klassen setter nå `list-style-type: revert`, som
758
+ går tilbake til nettleserens eget stilark, så listene ser ut som uten
759
+ Tailwind. Variantene `plain` og `divided` er fortsatt uten punkter. Vil du
760
+ ha et annet punkt på alle `.fs-list`, sett det selv, i CSS uten lag eller
761
+ i et lag etter `fristil`: en `list-style` på `ul` i et lag foran, som
762
+ `base`, når ikke lenger fram til klassen. Testen bygger oppsettet med
763
+ Tailwinds egen `preflight.css`, ikke en håndskrevet utgave med bare
764
+ knappereglene.
765
+ - **Lagrekkefølgen for Tailwind tar med `fristil-tema`.** Et generert tema
766
+ erklærer `@layer fristil, fristil-tema;`, og en `@layer`-setning flytter
767
+ ikke lag som alt er erklært. Med den anbefalte linja
768
+ `@layer theme, base, fristil, components, utilities;` havnet temaet derfor
769
+ sist, etter `utilities`, og slo Tailwind-klassene og `@layer base` på de
770
+ samme elementene, enda Tilpasning- og Eget tema-sidene sa at din egen CSS
771
+ fortsatt vinner. Linja i dokumentasjonen og i kommentaren i `tailwind.css`
772
+ er nå `@layer theme, base, fristil, fristil-tema, components, utilities;`,
773
+ og Eget tema-siden sier at laget må stå der når Tailwind brukes, og at
774
+ linja må stå i `<head>` når temaet lastes som en `<link>` før den
775
+ bundlede CSS-en, for da har temaet alt erklært lagene sine. Temaet
776
+ `fristil tema` skriver, er uendret.
777
+ - **Tailwind-siden og Design tokens-siden sa at skalaen følger med når du
778
+ setter `--fs-spacing-1`.** `p-4` er `calc(var(--fs-spacing-1) * 4)`, men
779
+ `--fs-spacing-4` og de andre trinnene er egne verdier, så eksempelet
780
+ `--fs-spacing-1: 0.3rem` ga `p-4` 1,2rem mens komponentene sto på 1rem.
781
+ Sidene og kommentaren i `tailwind.css` sier nå at trinnene må skrives om
782
+ hver for seg, og eksempelet gjør det. Tabellen over skalaen oppgir også
783
+ `px` som Tailwind-motstykket til `--fs-spacing-px`, der det sto «ingen».
784
+
785
+ ## 0.33.0 (2026-10-12)
786
+
787
+ ### Brytende
788
+
789
+ - **Kjernen bærer ingen data.** `fristil-kjerne.wasm` hadde manifestet og
790
+ fargekontrakten bygget inn. Nå gir verten dem. For `diagnoseMarkup`, `diagnosePage`,
791
+ `buildTheme` og `fristil` fra npm er ingenting endret: pakken gir
792
+ kjernen sitt eget manifest og sin egen fargekontrakt. Den som laster
793
+ kjernen selv, merker det:
794
+ - `loadCore()` gir en kjerne uten noe. Kall `loadManifest()` før
795
+ sjekken og `loadContract()` før temaet, ellers kaster de med en
796
+ forklaring. `resetManifest()` er fjernet, siden det ikke finnes noe
797
+ innebygd å gå tilbake til. `version().manifest` er `null` før et
798
+ manifest er gitt.
799
+ - En vert i et annet språk kaller `load_manifest_raw` og den nye
800
+ `load_contract_raw` først. `diagnose_markup_raw`, `diagnose_page_raw`
801
+ og `inspect_theme_raw` svarer nå 0, eller 1 med `{"error": …}`, som
802
+ `build_theme_raw` og `diagnose_styled_raw` alt gjorde.
803
+ `reset_manifest` er fjernet.
804
+ - Kommandolinja leser manifestet, fargekontrakten, regelbøkene og
805
+ komponentene fra `@fristil/designsystem`: fra mappa
806
+ `FRISTIL_PACKAGE` peker på, eller fra `node_modules` i arbeidsmappa
807
+ eller over den. npm-pakken og `fristil.jar` setter variabelen selv.
808
+ `fristil.wasm` i wasmtime og den kjørbare fila fra `cargo install`
809
+ trenger den, eller pakken installert, og stopper med en forklaring
810
+ uten. `fristil agent` og `fristil overta` svarer dermed for versjonen
811
+ prosjektet har installert.
812
+
813
+ Modulen er 93 kB mindre.
814
+ - **En temagrense har nå en flate.** Et element med `data-theme="light"`
815
+ eller `data-theme="dark"` får temaets bakgrunn og tekstfarge. Før fikk det
816
+ mørke tokens, men beholdt sidens hvite bakgrunn, så brødteksten i en mørk
817
+ seksjon på en lys side ble 1,23:1. Regelen har spesifisitet null i
818
+ `@layer fristil`, så en komponent med `data-theme` på seg, og din egen stil
819
+ uten lag eller i et senere lag, beholder sin egen bakgrunn. Stil i et lag
820
+ foran `fristil` taper. `<html>` og `fs-toast` får ingenting. En innebygd
821
+ komponent med `data-theme` på rotelementet står nå på temaets flate i
822
+ stedet for å være gjennomsiktig over verten.
823
+ - **`--fs-focus-ring`, `--fs-color-disabled-surface` og
824
+ `--fs-color-disabled-text` følger en temagrense.** De sto bare på `:root`,
825
+ der en `var()` regnes ut én gang for hele siden, så en mørk seksjon på en
826
+ lys side fikk den lyse fokusringen, 2,4:1 mot flaten. De deklareres nå i
827
+ hver temablokk. Har du overstyrt en av dem på `:root`, gjelder ikke
828
+ overstyringen lenger inne i et `data-theme`. Skriv den også på
829
+ `[data-theme="light"]` og `[data-theme="dark"]`.
830
+ - **`fristil sjekk-tema` kontrollerer en blokk som ikke er bundet til lyst
831
+ tema, som en bar `:root`, i mørkt tema også.** Fristils mørke blokk
832
+ overstyrer bare tokenene den selv har, så en verdi som ingen mørk blokk
833
+ tar, står også når systemet er i mørkt. Sjekken så den bare som lys:
834
+ `--fs-color-neutral-text: #0d4e8c` på `:root` ga «holder hvert løfte», men
835
+ har 2,22:1 i mørkt. Funnene merkes «i mørkt tema, ingen mørk blokk
836
+ overstyrer». En blokk med `color-scheme: light`, en selektor med `light`
837
+ som eget ord og `@media print` gjelder bare lyst og kontrolleres som før. Et tema som før var grønt kan nå gi funn, og da var
838
+ det brutt i mørkt hele tiden.
839
+ - **`fristil sjekk` melder nye funn.**
840
+ - Et flagg med en tekstmal i verdien, som `data-optional="{{ .Optional }}"`
841
+ eller `open="<%= open %>"`. Malen skriver verdien som tekst, og et boolsk
842
+ attributt er på uansett verdi, også «false» og en tom streng. Regelen er
843
+ `boolsk-med-verdi`. Astro, Svelte, JSX, Razor og JTE tar selv bort et
844
+ usant boolsk attributt, og meldes ikke. Står attributtet alt i en
845
+ betingelse i malen, som `{{ if .Open }} open="{{ .Open }}" {{ end }}`,
846
+ meldes det heller ikke.
847
+ - En `fs-link` eller `fs-button` med både `aria-disabled="true"` og `href`.
848
+ Stilen slår av musa, men Enter følger lenken. Regelen er ny:
849
+ `deaktivert-med-href`.
850
+ - **`fs.link({ disabled: true })` gir `role="link"` og `tabindex="0"`.** En
851
+ deaktivert lenke skal skrives uten `href`, ellers følger Enter den, og uten
852
+ `href` er en `<a>` ingen lenke for skjermleseren og står ikke i
853
+ tabrekkefølgen. De to gir begge tilbake, og `disabled: false` fjerner dem.
854
+ `fs.link()` forvalter dermed `role` og `tabindex`: setter du en av dem
855
+ selv på en lenke, som `role="menuitem"` i en meny, fjerner
856
+ `fs.setAttributes(lenke, fs.link())` den. Sett den etter. Komponentsidene
857
+ for Link og Button viste en deaktivert lenke med `href`, og det gjorde også
858
+ rådet i byggefunksjonen. Har du fulgt det, ta bort `href` mens lenken er
859
+ av.
860
+ - **`hidden` skjuler nå alle komponentene.** Forfatterstil slår nettleserens
861
+ `[hidden] { display: none }`, så `<div class="fs-alert" hidden>` og 41 andre
862
+ klasser, åtte av elementene og barn uten klasse, som `option` i en stylet
863
+ `fs-select`, et ledd i `fs-breadcrumbs` og en rad i fillista, sto
864
+ synlige. En regel i `@layer fristil` med `!important` skjuler dem nå,
865
+ slik nettleseren selv gjør. Den kan ikke overstyres med en regel uten
866
+ `!important`, og heller ikke med `!important` uten lag. `hidden="until-found"`
867
+ får stå.
868
+ - **`.fs-tooltip__bubble` er `display: none` når den er skjult**, ikke
869
+ `visibility: hidden`. En skjult boble tok plass og ga sidelengs rulling ved
870
+ høyre kant. Teksten er fortsatt knappens beskrivelse gjennom
871
+ `aria-describedby`.
872
+
873
+ ### Lagt til
874
+
875
+ - **`.fs-icon` og `fs.icon()`, for et ikon fra appens eget sett.** Klassen
876
+ går på en `<svg>` og gjør den like stor som teksten rundt. `fs.icon()` gir
877
+ `aria-hidden="true"`, og `fs.icon({ label })` gir `role="img"` og
878
+ `aria-label` for et ikon som betyr noe alene. Fristil sender ingen ikoner.
879
+ Stilarket er `@fristil/designsystem/icon.css`.
880
+ - **Regelen `ikon-uten-navn`.** En `<button>` eller `<a href>` med bare et
881
+ ikon i seg og ikke noe navn er en feil i `fristil sjekk`, i editorene og
882
+ i `diagnoseMarkup`. Navnet kan komme fra tekst, også med `fs-sr-only`, fra
883
+ `aria-label`, `aria-labelledby` eller `title`, eller fra `alt` på et bilde.
884
+ Det som er skjult med `aria-hidden="true"` eller `hidden`, teller ikke.
885
+ Står det noe på knappen eller et barn som ikke er vanlig HTML, som
886
+ `:aria-label`, `v-bind`, `x-text` eller `{...props}`, kan navnet komme
887
+ derfra, og regelen tier.
888
+ - **Typen `Manifest` fra `@fristil/designsystem/diagnostics`**, formen på
889
+ `manifest.json` slik skjemaet beskriver den, for den som leser manifestet
890
+ selv. Kotlin-pakken har det samme som `Fristil.manifest`, og leser et
891
+ prosjekts eget manifest med `Manifest.parse`: elementene med attributter,
892
+ deler og hendelser, og klassene med attributtene og de lovlige verdiene.
893
+ - **Delene og beskrivelsene står i manifestet.** Hvert element har nå
894
+ `description` og `parts`: delene web-komponenten finner i markupen, med
895
+ selektoren den leter med, hva delen skal inneholde, og om den kan stå
896
+ flere ganger eller mangle. `<fs-field>` har ledetekst, kontroll,
897
+ hjelpetekst og feilmelding, og `<fs-dialog>` har dialogen, tittelen og
898
+ kroppen. Hvert attributt på et element og hvert valg i en
899
+ byggefunksjon har `description`, og et attributt på en klasse har
900
+ beskrivelsen til valget med samme navn. Et verktøy eller et
901
+ annet språk kan dermed lese hva hvert element leter etter, uten å lese
902
+ kildekoden.
903
+ Feltene er nye og valgfrie i skjemaet, så `schemaVersion` er fortsatt 1.
904
+ Fragmentet fra `fristil overta` gir klassene nye navn også i selektorene
905
+ og beskrivelsene, så kopien beskrives med sine egne klasser.
906
+ Selektorene eksporteres også, fra hovedinngangen og fra inngangen til
907
+ hver av de sju web-komponentene som finner deler, som `FS_FIELD_PARTS`
908
+ fra `@fristil/designsystem/field`.
909
+ - **`@fristil/designsystem/fargekontrakt.json`** i `exports`, så en side
910
+ som laster kjernen i nettleseren kan gi den fargekontrakten med
911
+ `loadContract()`, som den gir manifestet fra `./manifest.json`.
912
+ - **Hendelsene har typer i `addEventListener`.** `HTMLElementEventMap` og
913
+ `DocumentEventMap` utvides med hver hendelse web-komponentene sender,
914
+ med `detail` slik komponenten sender den: `dialog-toggle` gir
915
+ `event.detail.open` som `boolean` og `returnValue` som `string`. Typene
916
+ følger med hver inngang til web-komponentene, `/register`, `/react` og
917
+ `/react-jsx` medregnet, så `as CustomEvent<…>` trengs ikke lenger. Typen
918
+ står også som `FsEventMap` i hovedinngangen. Den genereres fra den samme metadataen
919
+ som manifestet og `web-types.json`, og har ingen avhengighet av React.
920
+ - **`"./package.json"` i `exports`.** Et verktøy som leser pakkens versjon
921
+ med `require.resolve("@fristil/designsystem/package.json")` eller
922
+ `import … with { type: "json" }`, fikk `ERR_PACKAGE_PATH_NOT_EXPORTED`.
923
+ Nå finner de den.
924
+ - **`--fs-progress-border`** styrer kanten rundt sporet i `fs-progress`.
925
+ - **`fs.popover()` skriver `type="button"` på knappen.** En knapp uten `type`
926
+ sender skjemaet den står i.
927
+ - **`session-activity` og `activity-interval` på `fs-session-timeout`.**
928
+ Klokka nullstilles av aktivitet i nettleseren, men serverøkten visste
929
+ ikke om den. En bruker som skrev i et langt skjema uten et eneste kall til
930
+ serveren, fikk aldri varselet, og innsendingen feilet fordi serverøkten
931
+ var ute. Hendelsen kommer i starten av hvert intervall med aktivitet og på
932
+ slutten hvis brukeren har gjort noe siden, høyst én gang per
933
+ `activity-interval` sekunder (standard 60, minst 1), og aldri mens
934
+ varselet står eller etter at økten er ute. `fs.sessionTimeout()` tar
935
+ `activityInterval`. Komponentsiden viser også hvordan flere faner holder
936
+ klokkene i takt.
937
+ - **`--fs-accordion-padding-inline`** gir overskriften og innholdet i
938
+ `fs-accordion` samme luft til sidene. Før gjaldt `--fs-accordion-padding`
939
+ bare overskriften.
940
+ - **Hendelsene står i manifestet, i `web-types.json` og i regelbøkene.**
941
+ Elementene som sender hendelser, har nå `events` i manifestet, med feltene
942
+ i `detail` og typen deres, som `dialog-toggle` med `open` og `returnValue`.
943
+ `web-types.json` har dem som `js.events`, og regelbøkene har en kolonne for
944
+ dem. Før sto de bare på komponentsidene, og noen ikke der heller.
945
+
946
+ - **`manifest` i `Fristil.diagnoseMarkup` og `Fristil.diagnosePage` i
947
+ Kotlin.** Teksten i et `manifest.json`, som det `fristilManifest`
948
+ skriver, gir sjekk mot komponentene prosjektet har overtatt. Uten valget
949
+ gjelder manifestet i pakken, som før. Et manifest kjernen ikke kan lese,
950
+ kaster `IllegalArgumentException` med grunnen.
951
+
952
+ ### Endret
953
+
954
+ - **Pil opp og ned bytter ikke lenger fane i en vannrett `fs-tabs`.** De
955
+ stjal siderullingen og avvek fra WAI-ARIA. De virker når raden har
956
+ `aria-orientation="vertical"`.
957
+
958
+ ### Rettet
959
+
960
+ - **`fs.setAttributes` virker på en `<svg>`.** Klassen ble satt med
961
+ `className`, som er skrivebeskyttet på et svg-element, og kallet kastet
962
+ «Cannot set property className». Den settes nå med `setAttribute`.
963
+ - **`<fs-field>` kobler bare sine egne deler.** Sto et felt inni et annet,
964
+ som et søkefelt i et sprettoppvindu inni feltet, fant det ytre feltet det
965
+ innerste feltets kontroll først og skrev `for`, `id` og
966
+ `aria-describedby` på den. Nå ser hvert felt bare kontrollen, ledeteksten,
967
+ hjelpeteksten og feilmeldingen som hører til det selv, slik de andre
968
+ komponentene alt avgrenset delene sine. Et felt med mer enn én kontroll
969
+ gir en advarsel i konsollen, siden bare den første får koblingen.
970
+ - **En overskrift i kroppen til `<fs-dialog>` blir ikke lenger tittel.**
971
+ Uten `aria-labelledby` og `aria-label` tok komponenten den første
972
+ overskriften i dialogen, også en som sto i `fs-dialog__body`, og ga den
973
+ klassen `fs-dialog__title` og stilen som følger med. Nå er tittelen den
974
+ første overskriften utenfor kroppen, og en dialog som står uten navn,
975
+ gir en advarsel i konsollen i stedet for å låne innholdets overskrift.
976
+ - **En ubestemt `fs-progress` var en tom ramme i høykontrastmodus.** Stripen
977
+ som sveiper over, er en gradient, og modusen fjerner gradienter. Den
978
+ tegnes nå i `Highlight` på `Canvas` med `forced-color-adjust: none`, så
979
+ brukerens systemfarger fortsatt gjelder.
980
+ - **Kotlin skriver tall i attributter slik JavaScript gjør.** `jsNumber` ga
981
+ `1000000000000000000000` for `1e+21`, `0.0000001` for `1e-7` og den
982
+ eksakte binærverdien for heltall over 2^53, som `123456789012345683968`
983
+ for `123456789012345680000`, og byggefunksjonene i
984
+ Kotlin kunne dermed skrive et annet attributt enn `fs` i TypeScript. Nå
985
+ brukes de færreste sifrene som gir det samme tallet tilbake, og
986
+ JavaScripts regler for når eksponent brukes. Etterprøvd mot JavaScript på
987
+ 224 614 tall uten avvik, hver toerpotens medregnet.
988
+ - **`fristilManifest` skriver bare `build/fristil/manifest.json` når
989
+ `manifests` har fragmenter**, og sletter fila når det siste fragmentet er
990
+ fjernet. Språkserveren foretrekker den fila framfor pakken i
991
+ `node_modules`, så en kopi uten fragmenter ble stående med versjonen fra
992
+ da den ble skrevet, også etter at pakken var oppgradert.
993
+ - **Fokusringen på det valgte alternativet i `fs-toggle-group` var 1,23:1.**
994
+ Ringen står oppå den valgte flaten, og har nå `accent-content` som farge
995
+ der.
996
+ - **Sporet i `fs-progress` var 1,12:1 mot siden.** Stolpen har nå en kant i
997
+ `neutral-border`, så det synes hvor langt den går. Kanten er tegnet innover
998
+ og tar ikke av høyden.
999
+ - **`fs-dialog` meldte forrige knapps verdi når dialogen ble lukket uten
1000
+ knapp.** Escape og `close()` uten argument lar `returnValue` stå fra forrige
1001
+ lukking. En dialog serveren åpnet på nytt og så lukket, sendte derfor
1002
+ `dialog-toggle` med for eksempel `returnValue: "slett"` fra gangen før, og en
1003
+ app som skiller «Avbryt» fra «Slett» på verdien kunne slette noe brukeren
1004
+ aldri ba om. Komponenten tømmer nå verdien hver gang den åpner dialogen.
1005
+ Skriver noe `open` på verten etter at brukeren har trykket en knapp, men før
1006
+ `close` er kommet, åpner komponenten heller ikke dialogen igjen. Før gjorde
1007
+ den det, og verdien ble borte for en lytter på `close`.
1008
+ - **`fs-dialog` åpnet seg ikke igjen etter at verten var flyttet.** En morfer
1009
+ som flytter verten kobler den fra og til, og komponenten tok den gamle
1010
+ `returnValue` for en lukking brukeren gjorde før skriptet kom, og fjernet
1011
+ `open` serveren nettopp hadde satt. Den sjekken gjøres nå bare første gang
1012
+ en dialog sees, også når den flyttes inn i en ny vert.
1013
+ - **`fs-dialog` meldte `open: true` for en dialog som var åpen hele tiden.**
1014
+ En morfer som flytter verten eller dialogen, tar dialogen ut av topplaget,
1015
+ og komponenten åpner den igjen. `dialog-toggle` kommer nå bare når
1016
+ tilstanden faktisk endrer seg.
1017
+ - **`data-color="neutral"` på `fs-dialog` ga mer luft enn ingen farge.**
1018
+ Verdien er standardverdien og skal se ut som ingen verdi, men reglene for
1019
+ luften traff bare en dialog uten attributtet. Nå gjelder de alt som ikke er
1020
+ en av de fem fargene, også en tom verdi fra en mal.
1021
+ - **Knappen i `fs-popover` sendte skjemaet den sto i.** Komponenten setter nå
1022
+ `type="button"` på en `<button>` uten `type`, eller med en tom eller ukjent
1023
+ verdi, også etter en patch som river det bort. En knapp som sier
1024
+ `type="submit"` selv, får stå. Eksemplene på komponentsiden og snippeten i editoren har det med.
1025
+ - **Escape i `fs-popover` tok for mye.**
1026
+ - Et trykk i et annet felt flyttet fokus fra feltet til knappen. Nå lukkes
1027
+ vinduet, og fokus blir stående.
1028
+ - Et trykk i en modal dialog åpnet oppå vinduet lukket vinduet også. Nå
1029
+ lukker det bare dialogen.
1030
+ - Et trykk noe annet alt hadde brukt, som en forslagsliste i vinduet som
1031
+ lukket seg, lukket vinduet også. Nå står vinduet.
1032
+ - Med to vinduer inni hverandre lukkes nå det innerste først.
1033
+ - **`fristil sjekk --css` fulgte ikke `@import` fra en pakke.**
1034
+ `@import "@fristil/designsystem/fristil.css"` er et navn i pakkens
1035
+ `exports`, og fila ligger i `dist/`. Sjekken lette etter den som en fil i
1036
+ `node_modules` under arbeidsmappa, fant ingenting og meldte hver klasse som
1037
+ ustylet. Nå slår den opp i `exports`, også mønstre som `./*` og en pakke
1038
+ importert ved navn alene, i nærmeste `node_modules` over stilarket. En
1039
+ relativ sti slås aldri opp som en pakke.
1040
+ - **`fristil sjekk-tema` leste `dark` inne i `:not()`.**
1041
+ `.kort:not([data-theme="dark"])` ble kontrollert som et mørkt tema.
1042
+ `:root:not([data-theme="dark"])` gjelder også i systemets mørke modus, og
1043
+ kontrolleres nå mot begge temaer.
1044
+ - **`fristil sjekk` meldte Bootstraps `fs-1` til `fs-6` som ukjente
1045
+ klasser.** En klasse som er `fs-` og bare sifre, er ikke Fristils.
1046
+ - **`fristil sjekk` kjente ikke `writingsuggestions`, `autocorrect` og
1047
+ `headingoffset`.** De er globale attributter i HTML.
1048
+ - **`fs-suggestion` nådde ikke et felt React styrer.** Valget ble satt rett på
1049
+ noden, React så ingen endring, `onChange` kom aldri, og ved neste rendring
1050
+ skrev React den gamle teksten tilbake. Komponenten bruker nå setteren fra
1051
+ elementets prototype.
1052
+ - **`fs-session-timeout` kunne hoppe over en opplesning.** Et tikk som kom
1053
+ sent, kunne gå fra 61 til 59 sekunder, og «1 minutt» ble aldri lest opp.
1054
+ Opplesningen kommer nå når en terskel krysses.
1055
+ - **`fs-toast` mistet fokus.** Ble den siste meldingen lukket, eller ble alle
1056
+ fjernet med `clear()`, mens en melding hadde fokus, falt fokus til `body`.
1057
+ Det går nå tilbake dit det kom fra. `label` skrev også over en
1058
+ `aria-label` forfatteren hadde satt, og når `label` ble fjernet, sto det
1059
+ gamle navnet igjen.
1060
+ - **`fs-tabs` og `fs-error-summary` holdt fast faner og lenker en morfing
1061
+ hadde byttet ut.** Begge har nå én lytter på verten.
1062
+ - **Dokumentasjonen lovet for mye.** «Kom i gang» og «Rammeverk» sa at
1063
+ markupen i en `ramme`-komponent vises uansett, men Tabs og Suggestion
1064
+ skjuler innhold til de er registrert. Datastar-oppskriften for Error
1065
+ Summary flyttet ikke fokus ved andre feilede innsending, og skjema-mønsteret
1066
+ skrev oppsummeringen med `role="alert"` på nytt ved hvert tastetrykk, så
1067
+ skjermleseren ble avbrutt. Toast og Connection Status sier nå at de er
1068
+ inerte mens en modal dialog står åpen.
1069
+ - **Avslått vant ikke over en tilstand.** Et ugyldig eller vellykket felt
1070
+ som var slått av, så rødt eller grønt ut, ikke avslått. Det gjaldt input,
1071
+ select, textarea, filopplasting, avkryssingsboks og radioknapp.
1072
+ - **Gjeldende side i `fs-pagination` ble uleselig under musa i høykontrast.**
1073
+ Hover-regelen slo høykontrastregelen, og bakgrunn og tekst fikk samme farge.
1074
+ - **Skilletegnet i `fs-breadcrumbs` ble lest opp.** Generert innhold leses
1075
+ opp, så skjermleseren sa «skråstrek» mellom hvert steg. Det har nå en tom
1076
+ alternativ tekst der nettleseren støtter det.
1077
+ - **Boblen i `fs-tooltip` forsvant før musa nådde den.** Gapet mellom knappen
1078
+ og boblen har nå en usynlig bro i knappens bredde mens boblen vises. Flytter
1079
+ du boblen under knappen, må broen også flyttes; snutten på komponentsiden
1080
+ har det med. Komponentsiden sier at boblen ikke kan
1081
+ lukkes med Escape, som WCAG 1.4.13 krever, og når Popover passer bedre.
1082
+ - **Pila i `fs-accordion` pekte sidelengs i høyre-til-venstre**, og pila i
1083
+ `fs-select` sto 6 piksler fra kanten der i stedet for 12.
1084
+ - **En overskrift i `summary` i `fs-accordion` fikk nettleserens marg og
1085
+ størrelse.** Dokumentasjonen viser `<summary><h3>`, og raden ble høyere og
1086
+ teksten større enn uten overskrift.
1087
+ - **Flaggene på klassene manglet i editoren og i regelbøkene.**
1088
+ `data-interactive` på `fs-card`, `data-selectable` på `fs-tag`,
1089
+ `data-hoverable` på `fs-table` og `data-optional` på `fs-label` og
1090
+ `fs-legend` sto i manifestet, men ikke i forklaringen i VS Code eller i
1091
+ tabellene en kodeagent leser. Klasseattributtene til web components, som
1092
+ `data-color` på `.fs-dialog`, sto heller ikke i regelbøkene.
1093
+ - **`fristil sjekk` ga feil på `<fs-error-summary data-autofocus="true">`.**
1094
+ Komponenten flytter fokus for alt annet enn `"false"`, og `"true"` er nå
1095
+ lovlig.
1096
+ - **Editoren sa at `required-marker="text"` skriver «må fylles ut».** Den
1097
+ skriver «(påkrevd)».
1098
+ - **Regelbøkene kalte `data-variant="outline"` oppfunnet.** Det er
1099
+ standardvarianten til `fs-card` og `fs-tag`. Eksempelet er nå `outlined`.
1100
+ `fs-theme-control` sto ikke i lista som kalles «hele lista».
1101
+ - **Språkserveren fant bare pakken ved rota av arbeidsområdet.** I et
1102
+ monorepo ble en side i `apps/web` sjekket mot det innebygde manifestet,
1103
+ enda `apps/web/node_modules` hadde pakken. Den leter nå fra mappa til fila
1104
+ og oppover, men ikke over rota av arbeidsområdet.
1105
+ - **Dokumentasjonen sa at feilmeldingene bare finnes i VS Code.** Pluginen
1106
+ for IntelliJ har dem, i filer og i HTML i en merket streng, og regelbøkene
1107
+ nevner nå den og Fristil for Kotlin.
1108
+ Kotlin-siden sa at hver tråd får sin egen instans av kjernen. Det er en
1109
+ pool.
1110
+
1111
+ ## 0.32.1 (2026-10-09)
1112
+
1113
+ ### Rettet
1114
+
1115
+ - **`fs-divider` var usynlig i flex og grid.** Nettleseren gir `<hr>`
1116
+ auto-marger til sidene, og i en flex-kolonne vinner de over
1117
+ `align-items: stretch`. Streken har ikke noe innhold å være bred av, så den
1118
+ ble 0 piksler bred og forsvant. Det gjaldt også eksempelet på
1119
+ komponentsiden og demosiden for sideskjelettet. Klassen nullstiller nå
1120
+ margene til sidene og strekker seg selv, så streken fyller bredden også i en
1121
+ flex- eller grid-forelder som sentrerer barna eller legger dem til start.
1122
+ Har du gjort streken smalere selv, står den nå til start; sett
1123
+ `margin-inline: auto` for å sentrere den.
1124
+
1125
+ ### Endret
1126
+
1127
+ - **`fs-divider` med `data-variant="subtle"` er litt tydeligere.** Den brukte
1128
+ `--fs-color-neutral-surface`, som lå så nær lerretet at streken nesten
1129
+ forsvant. Nå bruker den `--fs-color-neutral-raised`, som ligger mellom
1130
+ flatefargen og `border-subtle` i både lyst og mørkt tema.
1131
+
1132
+ ## 0.32.0 (2026-10-08)
1133
+
1134
+ ### Brytende
1135
+
1136
+ - **Sjekken er Rust-kjernen.** `diagnoseMarkup` og `diagnosePage` i
1137
+ `@fristil/designsystem/diagnostics` kjører nå den samme
1138
+ WebAssembly-modulen som VS Code, IntelliJ og Fristil for Kotlin, i stedet for
1139
+ en egen utgave i TypeScript. Svarene er de samme: begge utgavene ble kjørt
1140
+ side om side på over 4000 sider uten ett avvik før TypeScript-utgaven ble
1141
+ slettet. Modulen følger pakken og lastes første gang en av funksjonene
1142
+ kalles, i Node, Bun og Deno.
1143
+
1144
+ Disse forsvinner fra `@fristil/designsystem/diagnostics`:
1145
+
1146
+ | Borte | I stedet |
1147
+ | --- | --- |
1148
+ | `diagnose(text, elements, classes)` | `diagnoseMarkup(text)`. Et eget ordforråd gis som manifest med `loadCore(modul).loadManifest(json)`. |
1149
+ | `checkReferences(text)` | `diagnosePage(text)`, som gir koblingsfunnene sammen med resten. Regelnavnet i `rule` skiller dem. |
1150
+ | `pageSource(text)` | Ingen. `diagnosePage` leser siden slik den gjorde. |
1151
+ | `elements`, `classes` | `@fristil/designsystem/manifest.json`, med elementene, klassene og de lovlige verdiene. |
1152
+ | `closest`, `tagEnd`, `withoutHidden` | Ingen. De var hjelpere for sjekken. |
1153
+ | typene `Attribute`, `Element`, `Elements`, `ClassAttribute`, `ClassInfo`, `Classes` | Skjemaet i `@fristil/designsystem/manifest.json` (`manifest.schema.json`). |
1154
+
1155
+ `diagnosePage(text, elementer, klasser)` tar ikke lenger egne lister.
1156
+
1157
+ Ingenting endres i `fs`, i komponentene eller i CSS-en.
1158
+
1159
+ - **Temaet er Rust-kjernen.** `buildTheme` i `@fristil/designsystem/tema` og
1160
+ `inspectTheme` og `checkTheme` i `@fristil/designsystem/tema-sjekk` kjører
1161
+ den samme WebAssembly-modulen som sjekken. Svarene er de samme: de to
1162
+ utgavene ble kjørt side om side på over 6000 oppskrifter og temaer, med
1163
+ ugyldige farger, farlige verdier og ødelagt CSS, uten ett avvik, heller ikke
1164
+ i kontrastforholdene. `buildTheme` laster modulen fra pakken i Node, Bun og
1165
+ Deno. I nettleseren lastes den med `loadCore` fra
1166
+ `@fristil/designsystem/diagnostics`, og modulen er
1167
+ `@fristil/designsystem/kjerne.wasm`.
1168
+
1169
+ Fargekontrakten er data, i `src/tokens/fargekontrakt.json`, og regningen
1170
+ finnes bare i kjernen. Disse forsvinner:
1171
+
1172
+ | Borte | I stedet |
1173
+ | --- | --- |
1174
+ | `buildMatrix(merker, utseende)` fra `matrise` | `buildTheme(merker)`, som gir `light`, `dark` og `violations`. Fristils egne farger er `lightCells` og `darkCells` i `matrise`. |
1175
+ | `buildFamily`, `promisesFor`, `checkPromises` og typen `Violation` fra `kontrakt` | `buildTheme` for et tema, `inspectTheme` for å kontrollere ett. `Violation` står i `tema`. |
1176
+ | `parseBlocks` og typen `ParsedBlock` fra `tema-sjekk` | `inspectTheme`, som leser blokkene. |
1177
+ | typene `Matrix` og `RoleCss` fra `matrise` | `MatrixToken` gir hvert navn. |
1178
+
1179
+ `ROLES`, `NEUTRAL_LAYERS`, `REQUIREMENT`, `FAMILIES`, `FRISTIL_BRANDS`,
1180
+ `roleToCss` og `tokenName` er der som før, og `@fristil/designsystem/farge`
1181
+ er uendret.
1182
+
1183
+ - **Pakken krever Node 20.16 eller nyere** (`engines` i `package.json`), eller
1184
+ Bun eller Deno. Kjernen lastes med `process.getBuiltinModule`, som kom i
1185
+ Node 20.16 og 22.3. En eldre Node får beskjed om at versjonen er for
1186
+ gammel. Node 18 og 20 har ikke lenger støtte fra Node-prosjektet.
1187
+ - **`fristil overta` gir kopien nytt navn.** `fs-button` blir `app-button` i
1188
+ klassene, taggene, `customElements.define` og selektorene, så kopien er
1189
+ din og kan stå ved siden av originalen. Variablene fra temaet, `--fs-*`,
1190
+ beholder navnet. Bytt til det nye navnet i markupen der kopien skal brukes.
1191
+ Kopier du har tatt fra før, røres ikke.
1192
+
1193
+ ### Nytt
1194
+
1195
+ - **Kommandolinja uten Node.** Den samme kommandolinja kjører som
1196
+ `java -jar fristil.jar` (Java 17 eller nyere), i wasmtime som
1197
+ `fristil.wasm`, og som en kjørbar fil med
1198
+ `cargo install --git https://github.com/MarkusAugust/fristil fristil`.
1199
+ Jar-en og modulen ligger ved hver utgivelse på GitHub, med attestasjon av
1200
+ hvilken commit de ble bygd av, og svarer det samme som `npx`, byte for
1201
+ byte.
1202
+ - **`fristil overta` skriver et fragment av manifestet** ved siden av kopien,
1203
+ `fristil-manifest.json`, og `fristil sjekk --manifest=<fil>` sjekker
1204
+ markupen for kopien mot det. Bare kopiens egne navn sjekkes, ikke resten av
1205
+ prosjektets klasser.
1206
+ - **Sjekken ser stilarkene.** `fristil sjekk --css=<fil>`, og `css` som valg
1207
+ til `diagnoseMarkup` og `diagnosePage`, sier fra om en klasse ingen av
1208
+ stilarkene styler (`ustylet-klasse`) og om en verdi uten regel
1209
+ (`ustylet-verdi`). Stilarkene leses av en CSS-leser i kjernen, med
1210
+ nesting, `:is()`, `:where()`, `@layer` og `@import`. Pakkens egen kontroll
1211
+ av at CSS-en og manifestet sier det samme, bruker den også.
1212
+ - **`diagnoseRendered(page)`** sjekker siden slik nettleseren har rendret
1213
+ den, med Playwright: DOM-en etter at web-komponentene og skriptene har
1214
+ kjørt, og stilarkene den faktisk lastet, også fra et CDN.
1215
+ - **`fristil lsp`, språkserveren.** Funnene og hurtigrettelsene i hver editor
1216
+ som snakker LSP: Neovim, Helix, VS Code og andre, med UTF-16-posisjoner.
1217
+ Den sjekker mot manifestet i `build/fristil/` eller i `node_modules`, med
1218
+ det innebygde som reserve, og leser det på nytt når det endres. Den
1219
+ startes med `npx`, `java -jar`, wasmtime eller den kjørbare fila.
1220
+ - **`fristil manifest`** skriver manifestet sjekken bruker, med fragmentene
1221
+ lagt til, for en editor eller et annet verktøy.
1222
+ - **Gradle-pluginen `io.github.markusaugust.fristil`**, med `fristilSjekk`
1223
+ (kjøres av `check`), `fristilTema` og `fristilManifest`, som skriver
1224
+ `build/fristil/manifest.json`. Den kjører den samme kommandolinja i
1225
+ Gradle-prosessen.
1226
+
1227
+ - **Kommandolinja er skrevet i Rust.** `npx @fristil/designsystem` kjører den
1228
+ som en WASI-modul med Node sin egen `node:wasi`, og den samme koden kan
1229
+ bygges som en kjørbar fil. Kommandoene, flaggene, meldingene og feilkodene
1230
+ er de samme: alle 130 kjøringene i pakkens egen test av kommandolinja svarer
1231
+ likt, byte for byte. Én melding er ny: er en oppskrift til `fristil tema`
1232
+ ikke gyldig JSON, er forklaringen kjernens egen og ikke Nodes.
1233
+
1234
+ - **Funnene har regelnavn, linje og kolonne.** `rule` er navnet på regelen,
1235
+ som `ukjent-klasse`, og `line` og `column` er der funnet begynner, fra 1.
1236
+ - **Et funn kan undertrykkes med en kommentar** over taggen:
1237
+ `<!-- fristil-ignore-next ukjent-klasse -->`. Uten regelnavn gjelder den
1238
+ alle reglene for den neste taggen. Det virker i `fristil sjekk`, i
1239
+ editorene og i testene.
1240
+ - **`loadCore(modul)`** i `@fristil/designsystem/diagnostics` laster kjernen
1241
+ fra en modul du har selv, som i nettleseren eller i en utvidelse som pakker
1242
+ den med seg. Den gir også `loadManifest(json)`, `resetManifest()` og
1243
+ `version()`.
1244
+ - **`@fristil/designsystem/manifest.json`** beskriver hele ordforrådet og
1245
+ hver byggefunksjon som data, med skjema. Det er det Fristil for Kotlin
1246
+ genereres fra, og det kjernen sjekker mot.
1247
+ - **`data-color` på `fs-dialog` sjekkes.** Sjekken kjente ikke fargene
1248
+ `fs.dialog({ color })` gir, fordi ordlista bare leste byggefunksjoner som
1249
+ svarer med ett sett. `data-color="red"` sa derfor ingenting. Nå meldes en
1250
+ ukjent farge, med de fem lovlige og `neutral` som standard.
1251
+ - **Flaggene på klassene står i manifestet:** `data-optional` på `fs-label`
1252
+ og `fs-legend`, `data-interactive` på `fs-card`, `data-selectable` på
1253
+ `fs-tag` og `data-hoverable` på `fs-table`. Sjekken sier fra om
1254
+ `data-optional="false"`, som slår flagget på, slik den gjør for boolske
1255
+ attributter på elementene.
1256
+ - **Kommentarer i malspråkene hoppes over.** Markup i `{{-- --}}` i Blade,
1257
+ `@* *@` i Razor, `{{/* */}}` i Go, `{# #}` i Jinja, Twig og Nunjucks, og
1258
+ `<%-- --%>` i JSP ble sjekket som om den ble rendret, og en tagg i en
1259
+ kommentar ga funn.
1260
+ - **`fristil sjekk-tema` og `inspectTheme` leser CSS med en ekte
1261
+ tokenizer**, den fra Servo som Firefox bruker. En klamme eller et semikolon
1262
+ i en streng, i `url(…)` eller i en kommentar delte en blokk eller en
1263
+ deklarasjon, og en farge kunne forsvinne fra kontrollen uten et ord. En
1264
+ blokk som ikke er lukket, leses nå til fila slutter, slik nettleseren gjør,
1265
+ i stedet for å hoppes over, og meldingen om klammene står fortsatt.
1266
+ - **En stor side med mange funn sjekkes raskere.** Linja og kolonnen til hvert
1267
+ funn ble regnet fra starten av teksten. 6000 funn på en side med 3000 linjer
1268
+ tok en kvart sekund, og tar nå 25 millisekunder.
1269
+
1270
+ - **Hver side i dokumentasjonen finnes som Markdown.** `/components/button/`
1271
+ har sin på `/components/button.md`, og hver side lenker til sin med
1272
+ `<link rel="alternate" type="text/markdown">`. Lenkene i `/llms.txt` går dit.
1273
+ Filene lages av de bygde sidene, så «Ren HTML»-fanen med `<link>`-ene,
1274
+ importlinjene og versjonsnummeret står der slik siden viser dem, og fanene
1275
+ står etter hverandre.
1276
+
1277
+ ## 0.31.0 (2026-10-06)
1278
+
1279
+ ### Nytt
1280
+
1281
+ - **`fristil sjekk` sjekker hele sider.** En adresse, som
1282
+ `fristil sjekk http://localhost:8080/skjema`, hentes og sjekkes som en hel
1283
+ side, og `--rendret` gjør det samme for filer og standard inn. På en hel side
1284
+ kreves det i tillegg at hver id i `for`, `aria-describedby`,
1285
+ `aria-labelledby` og `aria-controls` finnes, at `for` peker på et felt, at
1286
+ ingen id står to ganger, at hvert felt med en `fs-`-klasse har en
1287
+ ledetekst, at hver synlige hjelpetekst og feilmelding som ikke er et
1288
+ statusfelt er koblet, og at hver
1289
+ lenke i `<fs-error-summary>` går til en id som finnes. En adresse som svarer
1290
+ med noe annet enn HTML, gir feilkode. Bar markup inni `<fs-field>` gir ingen
1291
+ funn. En mal sjekkes som før uten flagget, siden en id der kan stå i en
1292
+ annen fil.
1293
+ - **`diagnosePage(html)` og `checkReferences(html)`** i
1294
+ `@fristil/designsystem/diagnostics` gir den samme sjekken til tester skrevet
1295
+ i JavaScript. **`pageSource(html)`** gir siden slik sjekken leser den, uten
1296
+ kommentarer, innholdet i `<script>`, `<style>`, `<textarea>` og
1297
+ `<template>`, og markup i attributtverdier, med posisjonene uendret.
1298
+
1299
+ ## 0.30.0 (2026-10-05)
1300
+
1301
+ ### Nytt
1302
+
1303
+ - **Kolonner som kan sorteres i `.fs-table`.** `.fs-table__sort` er knappen
1304
+ i overskriften, og `aria-sort` på `<th>` er tilstanden. Pila tegnes fra
1305
+ `aria-sort`, med begge pilene dempet når kolonnen ikke er sortert, og
1306
+ knappen fyller cellen, så overskriftsraden har samme høyde og innrykk som
1307
+ uten knapp. `fs.tableSort({ direction })` gir `header` og `button`, og
1308
+ `tableSortDirections` er de lovlige retningene. Sorteringen er appens.
1309
+
1310
+ ## 0.29.0 (2026-10-05)
1311
+
1312
+ ### Brytende
1313
+
1314
+ - **`<fs-session-timeout>` lager ikke lenger dialogen selv.** Den som
1315
+ rendrer skriver dialogen og teksten inne i elementet, på sitt eget språk,
1316
+ og komponenten tar tiden: den åpner og lukker dialogen, fyller inn tallet
1317
+ og leser opp nedtellingen. Før var tittelen, teksten og knappene norsk
1318
+ skrevet rett i koden, og kunne ikke oversettes. Komponenten finner delene
1319
+ på `<dialog>`, `.fs-session-timeout__count`, `[role=status]` og knappene i
1320
+ et `<form method="dialog">`, og setter `open` tilbake når en morfing tar
1321
+ det fra en dialog som står i topplaget, slik `<fs-dialog>` gjør. Uten det
1322
+ sto siden fast etter en patch fra Datastar uten `data-ignore-morph`. En
1323
+ knapp med `value="logout"` logger ut, og
1324
+ alt annet, Escape medregnet, forlenger. Hendelsene, `warn-at`,
1325
+ `expires-at`, `extend()` og `reset()` er de samme, men `session-logout`
1326
+ og `session-extend` fra en knapp kommer nå med `close`-hendelsen, altså
1327
+ litt etter klikket og ikke i det. Det du må gjøre: skriv
1328
+ dialogen inn i elementet, slik komponentsiden viser. Et tomt
1329
+ `<fs-session-timeout>` sier fra i konsollen i det varselet skulle kommet,
1330
+ og `fristil sjekk` melder det.
1331
+ - **`fs.sessionTimeout()` gir ett attributtsett per del,** som
1332
+ `fs.dialog()`: `host`, `dialog`, `title`, `text`, `count`, `live`,
1333
+ `actions`, `extend` og `logout`. `titleId` er påkrevd, siden en id som
1334
+ lages av seg selv ikke overlever hydrering.
1335
+ - **Komponenten ligger i `ramme/` og ikke i `frittstaende/`.** Navnene i
1336
+ `exports` er de samme, men stien til fila er ny. Det du må gjøre: lenker
1337
+ du rett til fila på en CDN, bytt `frittstaende/session-timeout` med
1338
+ `ramme/session-timeout` i adressen.
1339
+
1340
+ - **Skrifttokenene heter `--fs-font-size-*` og `--fs-font-weight-*`.** De
1341
+ fjorten navnene var de eneste uten `fs-`, og de står på `:root` i en side
1342
+ Fristil ikke alltid eier. Et annet designsystem på samme side deklarerte de
1343
+ samme navnene, og da avgjorde rekkefølgen på stilarkene hvilken verdi
1344
+ begge systemenes komponenter fikk. Ved 0.22.0 fikk de beholde navnene,
1345
+ siden de alt fulgte CSS-egenskapen de setter; det holder ikke når
1346
+ navnet deles med andre. Det gjelder de ni størrelsene, de fire vektene og
1347
+ `--font-family-base` fra temageneratoren, som nå heter
1348
+ `--fs-font-family-base`. Tailwind-klassene, som `text-fs-m`, heter det
1349
+ samme. Det finnes ingen aliaser, siden et alias fortsatt måtte stått på
1350
+ `:root` under det gamle navnet. Det du må gjøre: bytt `--font-size-` med
1351
+ `--fs-font-size-`, `--font-weight-` med `--fs-font-weight-` og
1352
+ `--font-family-base` med `--fs-font-family-base` i egen CSS, og lag
1353
+ temaet ditt på nytt med `fristil tema` hvis du har ett. Et tema laget med
1354
+ en eldre utgave setter skriften under de gamle navnene, og den delen av
1355
+ temaet virker ikke lenger uten at noe sier fra. Fargene virker som før.
1356
+
1357
+ ### Nytt
1358
+
1359
+ - **`data-fs-boundary` er en grense mot vertssiden.** Bygges en komponent
1360
+ inn i en side du ikke eier, slår vertens CSS uten lag `@layer fristil`, og
1361
+ en `button { background: none }` tar bakgrunnen fra `.fs-button`. Under
1362
+ et element med attributtet ser nettleseren bort fra CSS uten lag, så
1363
+ Fristils lag gjelder igjen, mens skrift og annet som arves fortsatt kommer
1364
+ fra verten og `--fs-*`-variablene virker. Regelen ligger i `fristil.css`,
1365
+ og som `@fristil/designsystem/boundary.css`. Den har spesifisiteten (0,2,0),
1366
+ og egen CSS inne i grensen må ligge i et lag. Se «Vertens CSS stopper ved
1367
+ grensen» under Tilpasning.
1368
+ - **`--fs-select-padding` og `--fs-select-width`.** Luften i nedtrekkslista
1369
+ kan settes som i feltet. I standardutseendet står plassen til pila fast,
1370
+ og `auto` gir et kompakt felt så bredt som innholdet.
1371
+ - **`--fs-input-width`,** av samme grunn.
1372
+ - **Boblen i `.fs-tooltip` har `box-sizing: border-box`,** så
1373
+ `--fs-tooltip-width` er hele boblen også i en side uten en CSS-reset.
1374
+
1375
+ - **`close-label` på `<fs-toast>`** er standard for hva lukkeknappen i hver
1376
+ melding heter for skjermleseren. `fs.toast({ closeLabel })` skriver det,
1377
+ og `closeLabel` i `show()` overstyrer det for én melding.
1378
+ - **Antall treff i `<fs-suggestion>` kan oversettes,** med `count-none`,
1379
+ `count-one` og `count-other`, og `count-zero`, `count-two`, `count-few` og
1380
+ `count-many` for språk som har dem. `{n}` står for antallet, og
1381
+ `Intl.PluralRules` velger form etter `lang` på siden. Uten attributtene er
1382
+ teksten norsk som før.
1383
+ - **Markeringen på `.fs-label` og `.fs-legend` leses fra variabler:**
1384
+ `--fs-label-required-symbol`, `--fs-label-required-text` og
1385
+ `--fs-label-optional-text`, med « *», « (påkrevd)» og « (valgfri)» som
1386
+ reserve.
1387
+ - **Opplesningen av nedtellingen følger sidens språk.** Varigheten skrives
1388
+ med `Intl` etter `lang`, så den er «1 minutt og 30 sekunder» på en norsk
1389
+ side og «1 minute and 30 seconds» på en engelsk.
1390
+ - **`@fristil/designsystem/default-texts`** har hver standardtekst pakken
1391
+ skriver inn i siden. En vaktpost i bygget feller en tekst som står rett i
1392
+ en komponent i stedet for der, en tekst i CSS som ikke leses fra en
1393
+ variabel, og en tekst som mangler i tabellen på siden om oversettelse.
1394
+
1395
+ ## 0.28.0 (2026-10-04)
1396
+
1397
+ ### Brytende
1398
+
1399
+ - **`.fs-toast` er nå klassen på regionen, og meldingen heter
1400
+ `.fs-toast__message`.** Regionen ble stylet med elementnavnet, så en
1401
+ `<fs-toast>` registrert under et annet navn mistet plasseringen i hjørnet.
1402
+ Komponenten setter klassen på seg selv, og `fs.toast().host` skriver den.
1403
+ Elementnavnet `fs-toast` har fortsatt de samme reglene, så en region uten
1404
+ klassen står der den sto.
1405
+ Det du må gjøre: har du egen CSS mot `.fs-toast`, eller en server som
1406
+ skriver meldinger rett inn i regionen, bytt til `.fs-toast__message`.
1407
+ `fs.toast().toast` heter `fs.toast().message`, og `TOAST_MESSAGE_CLASS` er
1408
+ ny ved siden av `TOAST_CLASS`. `--fs-toast-*`-variablene er de samme.
1409
+ - **Varianten `default` på hjelpeteksten heter `strong`.** Standardverdien
1410
+ er `muted`, og overalt ellers betyr `default` «gir ikke attributt». Det du
1411
+ må gjøre: bytt `data-variant="default"` med `data-variant="strong"`, og
1412
+ `fs.helpText({ variant: "default" })` med `"strong"`.
1413
+ - **`fs.suggestion()` leser ikke lenger `open` for tommeldingen.** Den
1414
+ skriver `hidden` på «Ingen treff» når det finnes treff, eller når `pending`
1415
+ er satt. At lista er lukket, tar stilarket seg av: `.fs-suggestion__empty`
1416
+ vises ikke etter en liste med `hidden`. Før måtte en app som filtrerer selv
1417
+ (`prefiltered`) sende `open`, en tilstand bare komponenten kjenner, så
1418
+ meldingen kunne aldri vises. Det du må gjøre: tommeldingen må stå etter
1419
+ lista, som søsken, og et asynkront søk sender `pending: true` mens svaret
1420
+ er underveis.
1421
+ - **Forslagslista åpnes ikke når den er tom og feltet er tomt.** Fokus åpnet
1422
+ en liste uten alternativer, med `aria-expanded="true"` over ingenting. Et
1423
+ felt som venter på sitt første søk viser dermed ikke «Ingen treff» før
1424
+ brukeren har skrevet noe.
1425
+
1426
+ - **`fs.setAttributes` rydder bare det byggefunksjonen selv forvalter.**
1427
+ Før hadde den én felles liste, og `fs.setAttributes(knapp, fs.button())`
1428
+ fjernet `disabled`, `hidden`, `open`, `aria-describedby`, `multiple` og
1429
+ `accept` fra elementet, selv om knappefunksjonen aldri har skrevet noen av
1430
+ dem. En deaktivert knapp ble aktiv, og et åpent `<details>` lukket seg. Nå
1431
+ fjernes et attributt bare når settet kommer fra en byggefunksjon som
1432
+ skriver det: `fs.switch()` kan ta bort `disabled`, `fs.button()` rører det
1433
+ ikke. Regelen går begge veier: har byggefunksjonen et valg for et
1434
+ attributt, eier den det. `fs.spinner()` har `label`, så et kall uten
1435
+ `label` fjerner `role` og `aria-label`, også når du skrev dem for hånd.
1436
+ Det gjorde den ikke før. `id` og `for` fjernes aldri. Det du må gjøre:
1437
+ send valgene på nytt i hvert kall, og skal to sett på samme element, send
1438
+ dem hver for seg, `fs.setAttributes(felt, fs.input(), kobling.control)`,
1439
+ ikke spredd sammen i ett objekt. Et spredd objekt settes riktig, men rydder
1440
+ bare `data-*`, og funksjonen sier fra i konsollen første gang.
1441
+
1442
+ ### Lagt til
1443
+
1444
+ - **`pending` i `fs.suggestion()`.** Holder «Ingen treff» skjult mens svaret
1445
+ på et søk er underveis.
1446
+ - **Regelboka for bundlere forklarer `fs.setAttributes()`.** Ingen av
1447
+ regelbøkene nevnte funksjonen, så en kodeagent som skrev markup uten
1448
+ spredning fikk ikke vite om den, eller om at to sett sendes hver for seg.
1449
+
1450
+ ### Rettet
1451
+
1452
+ - **En dialog styrt av React-tilstand lot seg ikke lukke i React 19.** React
1453
+ skriver `el.open = undefined` når en prop går fra `true` til borte, og
1454
+ setteren vekslet attributtet i stedet for å fjerne det. Dialogen åpnet seg
1455
+ igjen etter både knappen og Escape, og sprettoppvinduet ble stående åpent.
1456
+ Det samme gjaldt `invalid`, `disabled` og `optional` på `<fs-field>` og
1457
+ `prefiltered` på `<fs-suggestion>`.
1458
+ - **Fanene lot seg ikke bytte under `server-controlled`.** Klikket ble angret
1459
+ i neste mikrotask, fordi komponenten leste serverens gamle valg fra minnet.
1460
+ Klikket står nå til noen andre rører `aria-selected` eller `hidden`, og da
1461
+ bestemmer serveren som før.
1462
+ - **`optional` og `required-marker` på `<fs-field>` lot seg ikke slå av.**
1463
+ Komponenten leste sin egen `data-optional` og `data-required` tilbake fra
1464
+ ledeteksten. Markeringer serveren skrev der, står fortsatt.
1465
+ - **`<fs-field>` fjernet en `data-state="invalid"` malen hadde skrevet.** Den
1466
+ fjerner nå bare den den selv satte.
1467
+ - **Feiloppsummeringen tok fokus når en synlig liste ble tømt og fylt igjen.**
1468
+ Brukeren ble rykket ut av feltet han rettet. Fokus flyttes nå bare når
1469
+ boksen går fra skjult til synlig.
1470
+ - **`<fs-connection-status>` ble hengende etter å ha vært ute av dokumentet.**
1471
+ Kvitteringen sto for godt, og linja sa offline etter at nettet var tilbake.
1472
+ - **Økttidsvarselet sluttet å være modalt når elementet ble flyttet,** og
1473
+ opplesningen sa «2 minutter» med 1:30 igjen. Den sier nå «ett minutt og 30
1474
+ sekunder».
1475
+ - **`fs.setAttributes` rydder de boolske valgene.** `data-interactive` på
1476
+ kortet, `data-hoverable` på tabellen, `data-selectable` på etiketten,
1477
+ `data-autofocus` på feiloppsummeringen og `placement` på sprettoppvinduet
1478
+ ble stående etter at valget var tatt bort.
1479
+ - **`fristil tema --merke1`, `--merke2` og `--merke3` ble lest som
1480
+ filnavn.** Flaggleseren godtok ikke sifre.
1481
+ - **`fristil overta button --ut src/ui` ignorerte flagget** og skrev til
1482
+ standardmappa. Den stopper nå og ber om likhetstegn, slik `agent` gjør.
1483
+ - **Advarselen fra `fs.dialog()` uten id ba om `id`.** Valget heter
1484
+ `titleId`, og det står nå i meldingen.
1485
+ - **Eksempellinja i `fristil --hjelp`** hadde en linjefortsettelse som ble
1486
+ til fem mellomrom midt i kommandoen.
1487
+ - **En deaktivert knapp med variant så aktiv ut.** `secondary`, `ghost` og
1488
+ `danger` hadde samme bakgrunn, tekst og kant med og uten `disabled`, og
1489
+ fikk hoverfarge under musa. Regelen for deaktivert sto over variantene og
1490
+ tapte på rekkefølgen.
1491
+ - **Musa tok bort feilrammen og rammen på en valgt** avkryssingsboks, radio
1492
+ og bryter. Hover var løftet over tilstandene med `:not(:disabled)`.
1493
+ - **Hjelpeboblen sto en hel boblebredde til venstre i RTL.**
1494
+ - **Søkefeltet mistet forstørrelsesglasset** når det var ugyldig, gyldig
1495
+ eller deaktivert og `input.css` ble lastet etter `search.css`. Tilstandene
1496
+ setter nå `background-color` og ikke kortformen.
1497
+ - **«(valgfri)» hadde kontrast 4,37:1 i lyst tema.** Teksten brukte fargen
1498
+ for deaktivert tekst. Den bruker nå `--fs-color-neutral-text-subtle`.
1499
+ - **«(påkrevd)» kunne komme ut som «pÃ¥krevd».** Tegnet sto rått i
1500
+ stilarket, og en side uten `<meta charset>` fra en tjener som ikke sender
1501
+ tegnsettet, leste det feil. Det står nå som en escape.
1502
+ - **Markup med både `data-required` og `data-optional` viste «(valgfri)».**
1503
+ Byggefunksjonen velger påkrevd, og stilarket gjør nå det samme.
1504
+ - **Regelbøkene for kodeagenter sa tre ting som ikke stemte.** At
1505
+ registrering på en server gir «customElements is not defined» (den gjør
1506
+ ingenting, uten et ord), at siden «ser tom ut» uten registrering (det gjelder
1507
+ bare de tre frittstående), og at `tokens.css` må lastes før komponentens
1508
+ stilark (rekkefølgen betyr ikke noe). React-boka viser nå `fs.field()` med
1509
+ `useId()` for et felt, ikke `<fs-field>`, som gir hydreringsfeil med
1510
+ server-rendring. Formatet fra `fristil sjekk` står med alvoret,
1511
+ `fil:linje:kolonne: feil: melding`.
1512
+ - **Lenkene til dokumentasjonen peker på `fristil.sobernetics.no`.**
1513
+ `homepage` i pakken, regelbøkene, `web-types.json` og lenkene diagnostikken
1514
+ skriver ut brukte `fristil.netlify.app`.
1515
+ - **Byttet en patch ut et panel i `<fs-tabs>`, sto fanen og pekte på en id
1516
+ som ikke fantes.** Det nye panelet fikk verken id, rolle eller `hidden`.
1517
+ Det får nå den samme id-en fanen alt peker på. `<fs-suggestion>` gjør det
1518
+ samme for feltet og lista: et nytt felt får id-en ledetekstens `for` peker
1519
+ på.
1520
+ - **Fanene tok piltaster med Alt, Ctrl og Cmd.** Alt+venstrepil er «tilbake»
1521
+ i nettleseren, og ble stoppet.
1522
+ - **Escape i et sprettoppvindu inne i en modal dialog lukket begge.**
1523
+ Tastetrykket stopper nå i vinduet.
1524
+ - **Feiloppsummeringen meldte en prosentkodet lenke som brutt.**
1525
+ `href="#f%C3%B8dselsdato"` peker på `id="fødselsdato"`, og oppslaget
1526
+ dekoder nå fragmentet slik nettleseren gjør.
1527
+ - **Økttidsvarselet frøs når `warn-at` ble hevet mens dialogen sto.**
1528
+ - **Tilkoblingslinja viste den gamle teksten** når `offline-text` eller
1529
+ `online-text` ble byttet mens den sto.
1530
+ - **Skillelinja og skjelettet var usynlige i høykontrastmodus.** Begge var
1531
+ bare en bakgrunn. Linja bruker nå en systemfarge, og skjelettet får en
1532
+ kant.
1533
+ - **Et kort som er en `<button>` hadde nettleserens knappeskrift,** 13
1534
+ piksler. Kortet arver nå hele skriften.
1535
+ - **`<span aria-disabled="true">` i sidenavigeringen hadde ikke boksen til
1536
+ lenkene,** så rekka hoppet når «Neste» ble byttet inn på siste side.
1537
+
1538
+ ## 0.27.1 (2026-10-03)
1539
+
1540
+ ### Rettet
1541
+
1542
+ - **Størrelsen på `fristil.css` i regelbøkene.** Tre av dem sa «rundt 75 kB,
1543
+ under 10 kB komprimert» om en fil på 90 kB og 11 kB. Tallet var hardkodet i
1544
+ generatoren; det regnes nå ut av de samme `exports`-oppføringene fila
1545
+ bygges av. `bygg:css` skrev også KiB merket som kB, og telte
1546
+ UTF-16-kodeenheter framfor byte.
1547
+
1548
+ ## 0.27.0 (2026-10-02)
1549
+
1550
+ ### Lagt til
1551
+
1552
+ - **`fs.progress()` og `progress.css`.** En framdriftsstolpe på et ekte
1553
+ `<progress>`, så rollen og opplesningen av tallet kommer fra nettleseren.
1554
+ `data-color` gir `success`, `warning` og `danger`, og uten `value` blir
1555
+ stolpen ubestemt. `max` har standardverdien 100 og skrives alltid ut: HTMLs
1556
+ egen standard er `1`, og en `<progress value="40">` uten `max` viser en full
1557
+ stolpe uten at noe sier fra.
1558
+
1559
+ ## 0.26.1 (2026-10-02)
1560
+
1561
+ ### Rettet
1562
+
1563
+ - **Beskrivelsene i klassekatalogen stemmer med komponentsidene.** `classes.ts`
1564
+ og `web-types.json` leser frontmatteren fra hver komponentside, og fire av dem
1565
+ sa «én klasse» om en komponent med to. Hoverboksen i VS Code og
1566
+ JetBrains-IDE-ene siterte tallet videre.
1567
+
1568
+ ## 0.26.0 (2026-10-02)
1569
+
1570
+ ### Lagt til
1571
+
1572
+ - **`.fs-theme-control` lar brukeren velge tema uten JavaScript.** Klassen står
1573
+ på en radioknapp, og `value` sier hvilket tema den velger: `auto`, `light`
1574
+ eller `dark`. Selektoren er en ekstra linje på de to temablokkene i
1575
+ `tokens.css`, og den samme linja i temaer `buildTheme()` skriver. «Følg
1576
+ systemet» har ingen temablokk, siden en verdi uten blokk nettopp skal falle
1577
+ tilbake på mediespørringen. `light` og `dark` vinner over `data-theme` på
1578
+ `<html>`, slik at serveren kan sende et lagret valg mens et klikk slår
1579
+ igjennom før svaret er tilbake. `auto` gjør det ikke: har serveren skrevet
1580
+ `data-theme`, står serverens verdi, og det som lagrer valget må også fjerne
1581
+ attributtet.
1582
+ - **Hvert av de tre valgene setter `color-scheme` selv.** `light` gir `light`,
1583
+ `dark` gir `dark`, og `auto` gir `light dark` så lenge `<html>` ikke bærer
1584
+ `data-theme="light"` eller `data-theme="dark"`. Det siste er ikke et tema, men beskjeden til nettleseren om at siden
1585
+ fungerer i begge, så den kan tegne sine egne flater etter systemet.
1586
+
1587
+ Bruker du velgeren, skal du ikke skrive `color-scheme` på `<html>` selv: en
1588
+ regel utenfor et lag slår `@layer fristil`, og nettleserens egne flater blir
1589
+ låst til systemet mens brukeren har valgt noe annet. For den som ikke bruker
1590
+ velgeren, er ingenting endret, og bar `:root` har fortsatt ingen
1591
+ `color-scheme`.
1592
+
1593
+ Overstyrer du et token per tema, må dine egne `[data-theme]`-blokker ha
1594
+ velgerens selektor ved siden av. CSS kan ikke skrive et attributt, så
1595
+ velgeren setter ingen `data-theme`.
1596
+
1597
+ ## 0.25.0 (2026-10-01)
1598
+
1599
+ ### Rettet
1600
+
1601
+ - **Språkvask i regelbøkene og dokumentasjonen.** Et «de» som skulle vært
1602
+ «dem», og en forklaring og et eksempel som var lånt fra en konkret
1603
+ feilrapport framfor å stå på egne bein. Teksten beskriver nå mekanismen:
1604
+ en innebygd komponent i en vertsside.
1605
+
1606
+ ## 0.24.0 (2026-10-01)
1607
+
1608
+ ### Lagt til
1609
+
1610
+ - **JetBrains-IDE-ene foreslår nå `fs-`-klassene.** `web-types.json` hadde
1611
+ bare de ni web-komponentene, så du fikk forslag på `<fs-field>` men
1612
+ ingenting på `fs-button`, enda klassene er den eneste delen en
1613
+ CSS-komponent har. Nå står alle 72 under `contributions.css.classes`, med
1614
+ komponentnavn og lenke til dokumentasjonssiden.
1615
+
1616
+ VS Code-utvidelsen har hatt dette hele tiden, i kode framfor i data, så
1617
+ dette retter en skjevhet mellom de to editorene. Lista bygges av
1618
+ `classesData()`, den samme funksjonen som skriver `classes.ts`, så editoren
1619
+ kan ikke foreslå en klasse diagnostikken avviser.
1620
+
1621
+ ## 0.23.0 (2026-10-01)
1622
+
1623
+ ### Brytende
1624
+
1625
+ - **`data-theme="light"` deklarerer nå alle de lyse tokenene, ikke bare
1626
+ `color-scheme`.** Før sto blokken med én linje mens `[data-theme="dark"]`
1627
+ hadde alle 90, så lyst tema virket bare på `<html>`: et barn kan ikke
1628
+ overstyre en variabel det arver uten å deklarere den på nytt. Nå er
1629
+ attributtet en temagrense begge veier, på et hvilket som helst element, og
1630
+ et tema kan ligge inne i et annet.
1631
+
1632
+ **Overstyrer du en farge per tema, må du legge til en `[data-theme="light"]`-
1633
+ blokk.** En deklarasjon på elementet selv vinner over en verdi det arver,
1634
+ uansett hvilket lag arven kom fra, så uten blokken står Fristils egen farge
1635
+ inne i en lys temagrense mens din gjelder ellers på siden. Oppskriften i
1636
+ «Tilpasning» viser alle fire blokkene.
1637
+
1638
+ - **Temageneratoren skriver nå også `[data-theme="light"]`.** `buildTheme()`
1639
+ skrev `:root`, mediespørringen og `[data-theme="dark"]`, men ikke den lyse
1640
+ blokken, så et generert tema hadde nøyaktig den samme asymmetrien: inne i en
1641
+ lys temagrense sto Fristils egen farge, mens temaets farge gjaldt ellers på
1642
+ siden. Har du et generert tema, må fila skrives om igjen med
1643
+ `fristil tema` for å få blokken.
1644
+
1645
+ - **`color-scheme` settes ikke lenger på `:root`.** Den står bare på
1646
+ `[data-theme="light"]` og `[data-theme="dark"]`, altså der noen har valgt et
1647
+ tema. Egenskapen arves nedover, så en verdi på roten gjaldt hele dokumentet,
1648
+ også der pakken bare er en gjest: en komponent som lastet `fristil.css` inn
1649
+ i en vertsside ga hele verten mørke rullefelt og skjemakontroller i mørk
1650
+ modus, langt utenfor komponenten selv.
1651
+
1652
+ **Vil du at nettleserens flater skal følge systemet på hele siden, skriver
1653
+ du `color-scheme: light dark` på `<html>` selv.** Det er den samme avtalen
1654
+ som for lagrekkefølgen.
1655
+
1656
+ ### Rettet
1657
+
1658
+ - **Regelbøkene for kodeagenter og dokumentasjonen sa ulike ord om det samme.**
1659
+ Regelbøkene under `agent/` skrev «byggerne» og «egendefinert element» der
1660
+ dokumentasjonen skrev «byggefunksjon» og «web component». En agent som får
1661
+ begge i konteksten møtte to navn på én ting. `sjekk-ordbruk.ts` vokter nå
1662
+ begge ordene, og leser også `README.md` i pakken, som den ikke gjorde før.
1663
+
1664
+ ## 0.22.0 (2026-09-29)
1665
+
1666
+ ### Brytende
1667
+
1668
+ - **Fargene er en matrise, ikke to lag.** En farge er et punkt i en matrise av
1669
+ **familie** (`accent`, `visited`, `brand1-3`, `neutral`, `danger`, `warning`,
1670
+ `success`) og **rolle** (`surface`, `border-subtle`, `border`,
1671
+ `border-strong`, `fill`, `content`, `text-subtle`, `text`, `text-strong`).
1672
+ Navnet er `--fs-color-<familie>-<rolle>`.
1673
+
1674
+ `--palette-*` og `--semantic-*` er borte. Det samme er `--size-*`, som nå
1675
+ heter `--fs-spacing-*`. Hvert fargenavn, hver avstand, hver linjeavstand,
1676
+ hvert ikon, skyggen og fokusringen heter nå `--fs-<slag>-<navn>`, som hos
1677
+ Tailwind, daisyUI og Digdir, og slaget i navnet er det som lar Tailwind-temaet
1678
+ bli generert framfor skrevet av for hånd. De tretten `--font-size-*` og
1679
+ `--font-weight-*` beholder navnene sine, siden de alt følger CSS-egenskapen
1680
+ de setter.
1681
+
1682
+ - **Kontrasten er garantert av konstruksjonen.** Kuløren er konsumentens,
1683
+ lysheten er rollens. Lyshetene er regnet fram mot et sveip rundt hele
1684
+ fargesirkelen på den høyeste metningen sRGB kan vise, så løftene holder
1685
+ uansett merkefarge. Justeringspasset som flyttet fargen din etterpå er borte,
1686
+ og det samme er metningstaket `maxChroma`: uten et pass å mate finnes det
1687
+ ingenting å dempe for.
1688
+
1689
+ - **Generatoren er 352 linjer mot 744.** `buildScale`, `buildNeutralScale`,
1690
+ `adjustForContrast`, `SCALE_STEPS`, `NEUTRAL_STEPS`, `MAX_CHROMA`,
1691
+ `CHROMA_FLOOR`, `CHROMA_CEILING` og typen `AdjustResult` er fjernet fra
1692
+ `@fristil/designsystem/farge`, som nå bare har fargeregningen. Fra
1693
+ `@fristil/designsystem/tema` er typen `ThemeAdjustment` og feltet
1694
+ `Theme.adjustments` borte.
1695
+ En familie du utelater arver Fristils egen framfor å felle kjøringen, så
1696
+ `fristil tema --aksent=#7c3aed` er nok. Flagget het `--interaktiv`, og heter
1697
+ nå `--aksent`, som familien. To navn på den samme fargen var nettopp det
1698
+ matrisen skulle bli kvitt.
1699
+
1700
+ - **To nye roller og ett nytt lag kom av migreringen.** `border-strong`, fordi koden brukte
1701
+ tre kantnivåer og matrisen hadde to: en feltramme som endrer seg ved hover,
1702
+ og en tabellstrek som er tydeligere enn en radskiller. `text-strong`, fordi
1703
+ brødtekst satt til `text` ble `#4a4d51` der den før var `#1a1a1a`, altså
1704
+ 8,5:1 mot hvitt der den hadde 17,4:1. Kravet holdt, lesbarheten ikke.
1705
+ Brødteksten står nå på `text-strong`, `#27292b`, som er 14,6:1. Og `raised`,
1706
+ som er et lag den nøytrale familien har ved siden av rollene, siden tekst må
1707
+ holde mot et kort som ligger over et kort.
1708
+
1709
+ ### Lagt til
1710
+
1711
+ - **`fristil sjekk-tema <fil…>`** kontrollerer et tema noen har skrevet selv,
1712
+ og sier hvilken celle som ryker og hvorfor. Den teller verdiene konsumenten
1713
+ selv skrev: både filtallet og løftetallet er de samme for én linje som for et
1714
+ helt tema, siden standardverdiene fyller hullene.
1715
+
1716
+ - **Tre nye inngangspunkter:** `./kontrakt` med rollene og løftene, `./matrise`
1717
+ som bygger et tokensett av merkefarger, og `./tema-sjekk`.
1718
+
1719
+ ### Rettet
1720
+
1721
+ - **Gjeldende side i pagineringen forsvant under musa.**
1722
+ `.fs-pagination a:hover` slo `[aria-current="page"]` på spesifisitet, så
1723
+ sidetallet brukeren sto på fikk en lys flate mens teksten ble stående i
1724
+ `accent-content`: 1,09:1 i lyst tema og 1,25:1 i mørkt. Gjeldende side er nå
1725
+ unntatt fra regelen og har sin egen hover. Feilen har vært der siden
1726
+ pagineringen kom, og ingen test så den, fordi `:hover` ikke kan leses av
1727
+ `getComputedStyle`.
1728
+
1729
+ `hover.browser.test.ts` fører nå musa over tolv av de fjorten hover-reglene
1730
+ som bytter bakgrunn, i begge temaer, og krever 4,5:1. De to siste ligger bak
1731
+ et pseudoelement og bak en `@supports` Firefox ikke har, og kontrolleres på
1732
+ tokenparet. Lista er bundet til stilarkene, så en ny slik regel uten et
1733
+ tilfelle feller testen, og hvert tilfelle påstår at flaten under musa er en
1734
+ annen enn flaten uten.
1735
+
1736
+ - **Siste steg i brødsmulestien ble blått under musa.** Samme årsak:
1737
+ `:hover` slo `[aria-current="page"]`. Gjeldende steg er ikke et sted å gå,
1738
+ og står nå urørt.
1739
+
1740
+ - **Tre steder i dokumentasjonen sto «byggefunksjon» der ordet var et verb,**
1741
+ som «generatoren byggefunksjon skalaene». Setningene ga ingen mening.
1742
+
1743
+ - **«Tilpasning» sa «fire veier» over en tabell med tre rader.**
1744
+
1745
+ - **«Eget tema» sa at skriften settes i `@layer fristil`.** Den står i
1746
+ `fristil-tema`.
1747
+
1748
+ ## 0.21.0 (2026-09-28)
1749
+
1750
+ ### Lagt til
1751
+
1752
+ - **Folketelling over attributter bare morferen leser.** `fs.browser.test.ts`
1753
+ kaller hver byggefunksjon og krever at `data-ignore-morph` kommer ut fra
1754
+ nøyaktig `connectionStatus`, `sessionTimeout`, `suggestion` og `toast`, og at
1755
+ `data-preserve-attr` ikke kommer ut fra noen. Vakten feller både et nytt
1756
+ sted attributtet dukker opp og et sted det forsvinner fra, siden begge
1757
+ endrer hva malen må skrive. Hjelperen i `morfing.browser.test.ts` hopper
1758
+ over attributter som står i en fredningsliste, slik den ekte løkka gjør. Står
1759
+ et navn der, fjerner løkka ingenting for nettopp det attributtet, og testen
1760
+ etterprøver ingenting. Noen av fixturene bygges av byggefunksjonene, og der
1761
+ feller folketellingen. Andre er skrevet for hånd, som en Go-mal ville gjort,
1762
+ og der feller en ny påstand i hjelperen om at lista er tom.
1763
+
1764
+ ### Rettet
1765
+
1766
+ - **`<fs-field>` leter ikke lenger etter `data-role` på hjelpetekst og
1767
+ feilmelding.** Komponenten leste `[data-role='help']` og
1768
+ `[data-role='error']` ved siden av klassene `fs-help-text` og
1769
+ `fs-error-text`. Kroken sto bare i en `querySelector` i kilden, uten tester
1770
+ og uten omtale noe sted, så ingen kunne bygge på den med vilje. `data-role`
1771
+ er uprefikset og eies i praksis av andre, og en app som brukte det til noe
1772
+ annet inne i et felt fikk sitt eget avsnitt adoptert som feilmelding, uten at
1773
+ noe sa fra. Komponentsiden sier nå hva komponenten faktisk krever, og en test
1774
+ holder fjerningen på plass.
1775
+
1776
+ Fjerningen er ingen brytende endring etter reglene øverst. Ingen
1777
+ byggefunksjon skrev attributtet og ingen komponent satte det, så ingenting i
1778
+ konsumentens markup endrer betydning: den virker som før, den blir bare ikke
1779
+ koblet.
1780
+
1781
+ - **Dokumentasjonen påsto mer enn den kunne vise.** «Holder markupen der,
1782
+ holder den overalt» var en påstand om alle morfere, mens vi kjører løkka til
1783
+ én. Kravet står nå slik det er: det en komponent har satt, skal overleve at
1784
+ attributter blir fjernet fra et element som består. Og `data-preserve-attr`
1785
+ og `data-ignore-morph` er beskrevet som morferens egne attributtnavn, siden
1786
+ et `data-*`-attributt bare betyr noe for koden som leser det.
1787
+
1788
+ - **«To komponenter eier sitt eget innhold» var tre.** `<fs-toast>` sto ikke i
1789
+ lista i «Markup og oppførsel», og at `fs.suggestion()` setter
1790
+ `data-ignore-morph` på statusområdet sto bare på komponentsiden, ikke der
1791
+ regelen forklares.
1792
+
1793
+ - **`overta` sa ingenting om tilgjengelighetsansvaret.** Siden om tilpasning
1794
+ sier nå at en overtatt og endret komponent ikke dekkes av en
1795
+ tilgjengelighetserklæring som viser til designsystemet.
1796
+
1797
+ ## 0.20.0 (2026-09-28)
1798
+
1799
+ ### Lagt til
1800
+
1801
+ - **`fristil agent` og regelbøkene i `agent/`.** Seks regelbøker skrevet for
1802
+ kodeagenter som Claude Code og GitHub Copilot, én per miljø: `html`, `maler`,
1803
+ `bundles`, `react`, `astro` og `datastar`. Hver er komplett for sitt miljø og
1804
+ nevner ingen andre, siden en agent som leser om `className` i et prosjekt med
1805
+ Go-maler er en agent som skriver `className` i en Go-mal. Vue, Svelte, Solid
1806
+ og Lit deler `bundles`, og `--rammeverk=svelte` peker dit.
1807
+
1808
+ Hver fil har de fire feilene en agent gjør oftest øverst: glemt stilark,
1809
+ oppfunne komponenter og varianter, hardkodede farger og bommet registrering.
1810
+ Så følger alle komponentene med klasse, element, attributter og lovlige
1811
+ verdier, tokenlagene, de tre reglene for web components, og sjekken som
1812
+ retteløkke. Kommandoen leser miljøet av `package.json`, og `--rammeverk=<navn>`
1813
+ overstyrer.
1814
+
1815
+ Kommandoen skriver aldri en fil. `AGENTS.md`, `CLAUDE.md` og
1816
+ `.github/copilot-instructions.md` er konsumentens egne, og et verktøy som
1817
+ skriver i dem må gjette stier, flette med innhold det ikke har skrevet, og
1818
+ holde en kopi i takt med pakken. Regelboka blir derfor stående i pakken, på
1819
+ `node_modules/@fristil/designsystem/agent/<navn>.md`, og følger versjonen.
1820
+ Mappa er med i tarballen.
1821
+
1822
+ De samme filene ligger også på dokumentasjonssiden, på `/llms.txt` og
1823
+ `/agent/<navn>.md`. Den viktigste agenten har ingen `node_modules`: blir noen
1824
+ bedt om å lage et skjema med Fristil i en tom mappe, skrives markupen før
1825
+ `npm install` har kjørt.
1826
+
1827
+ Filene genereres av `scripts/generate-agent.ts`, og `scripts/sjekk-agent.ts`
1828
+ feller hvis de er utdaterte, hvis en komponent mangler i en av dem, hvis et
1829
+ markupeksempel ikke tåler `diagnoseMarkup`, hvis en importsti ikke finnes i
1830
+ `exports`, hvis et tokennavn ikke finnes, eller hvis en `html`-blokk
1831
+ importerer et pakkenavn der nettleseren trenger en URL.
1832
+
1833
+ ### Rettet
1834
+
1835
+ - **Versjonen i dokumentasjonens CDN-adresser kom fra 74 hardkodede steder.**
1836
+ `prepare-version` skrev dem om ved hver utgivelse, men et omskrivingssteg må
1837
+ finne den gamle versjonen for å bytte den, så en adresse som hadde glidd ble
1838
+ stående og pekte på en eldre pakke enn teksten rundt beskrev. Kilden har nå
1839
+ plassholderen `@fristil/designsystem@VERSJON`, og `remark-versjon.mjs` setter
1840
+ inn versjonen fra `package.json` når siden bygges. Omskrivingssteget er
1841
+ fjernet, og fire vaktposter passer på: tre på at kilden bruker plassholderen,
1842
+ og `sjekk-bygget-versjon.ts` på at den faktisk ble byttet ut i det bygde
1843
+ resultatet.
1844
+
1845
+ - **To kodeeksempler i dokumentasjonen importerte et pakkenavn i nettleseren.**
1846
+ Datastar-sporet på [Rammeverk](https://fristil.sobernetics.no/rammeverk/) og
1847
+ dialogen på
1848
+ [Bekreft en handling](https://fristil.sobernetics.no/monster/bekreftelse/) hadde
1849
+ `import … from "@fristil/designsystem/field"` i en `<script type="module">`.
1850
+ Ingen av sidene har et byggesteg, og en nettleser slår ikke opp et pakkenavn
1851
+ uten byggesteg eller importmap, så koden feilet stille for den som limte den
1852
+ inn. Begge bruker nå hele URL-en.
1853
+
1854
+ Regelen fantes i `sjekk-oppskrifter.ts`, men den leser bare komponentsidene
1855
+ og velger regel ut fra navnet på fanen, så de to sto med feilen i flere
1856
+ runder. `sjekk-nettleserimport.ts` dekker nå alle sidene uavhengig av fane:
1857
+ en `html`-blokk er markup for nettleseren uansett hvor den står.
1858
+
1859
+ ## 0.19.0 (2026-09-28)
1860
+
1861
+ ### Lagt til
1862
+
1863
+ - **`maxChroma` i temageneratoren, og `--maks-metning` på kommandolinja.**
1864
+ Taket på metningen i de kulørte skalaene har stått fast på 0,16 i OKLCH. De
1865
+ fleste merkefarger ligger under det, men neon gjør ikke: `#39ff14` ligger på
1866
+ 0,286 og mistet 44 prosent mot taket, `#ff2d6f` på 0,240 og mistet 33. Taket
1867
+ kan nå settes, i `buildTheme({ maxChroma })`, med `--maks-metning` på
1868
+ kommandolinja, eller som `maksMetning` i en oppskriftsfil. Standarden er den
1869
+ samme som før, så et tema som ikke oppgir noe kommer ut likt.
1870
+
1871
+ Kontrastkravet berøres ikke. Taket styrer metning, mens kontrasten kommer av
1872
+ de faste lyshetene i skalaen og av passet som måler hvert par etterpå og
1873
+ flytter lysheten. Mer metning gir flere slike justeringer, ikke en svakere
1874
+ garanti, og et merke med hevet tak kjøres nå gjennom hele listen av par i
1875
+ `theme.browser.test.ts`. Den nøytrale skalaen har sitt eget, mye lavere tak,
1876
+ og er uendret.
1877
+
1878
+ Oppskriftsfila sier nå fra om en ukjent toppnøkkel, slik dokumentasjonen
1879
+ alltid har lovet. En fil som har stått med en skrivefeil og virket, stopper
1880
+ derfor nå. `$schema` er fortsatt lovlig: editorer skriver den inn av seg selv,
1881
+ og den sier ingenting om temaet. En fil som er gyldig JSON, men ikke et
1882
+ objekt, får også en forklaring framfor et stakkspor fra Node.
1883
+
1884
+ `buildTheme` avviser et tak utenfor 0,01 til 0,33. Et negativt tak speilet
1885
+ fargen og gjorde knallgrønt til magenta, `NaN` ga en feilmelding om en
1886
+ heksadesimal farge som aldri var oppgitt, og 0 ga et helt grått tema, alle
1887
+ tre i stillhet.
1888
+
1889
+ ## 0.18.0 (2026-09-27)
1890
+
1891
+ ### Lagt til
1892
+
1893
+ - **`fristil sjekk <fil…>` og `@fristil/designsystem/diagnostics`.** Sjekken
1894
+ editorutvidelsen kjører mens du skriver, elementer som ikke finnes,
1895
+ attributter elementet ikke har, verdier utenfor lista, klasser som
1896
+ `fs-buton` og `<fs-field>` uten kontroll, ligger nå i pakken.
1897
+ Kommandolinjen leser filer eller standard inn og skriver hvert funn som
1898
+ `fil:linje:kolonne: melding`, med feilkode ved funn. `diagnoseMarkup(html)`
1899
+ er den samme sjekken som funksjon, for en test i appen som kjører den over
1900
+ HTML-en serveren faktisk sender, der det ikke finnes noen malfil å sjekke.
1901
+ Editorutvidelsen importerer nå diagnostikken fra pakken, og listene den
1902
+ sjekker mot genereres inn i pakken som `elements.ts` og `classes.ts`.
1903
+ - **`<fs-popover>`, `<fs-suggestion>` og `<fs-dialog>` kobler fra bar
1904
+ struktur,** som fanene og feiloppsummeringen. En knapp og et panel med
1905
+ klassen `fs-popover` er nok: komponenten setter `popover="manual"`, lager
1906
+ id-en og skriver `aria-controls` på knappen, ellers den første knappen
1907
+ utenfor panelet som ikke peker på noe annet. En `<label>`, et `<input>`,
1908
+ en `.fs-suggestion__list` med `<li>` og et statuselement er nok for
1909
+ forslagsfeltet: komponenten setter rollene, lager id-ene, skriver `for`
1910
+ og `aria-controls`, og holder lista lukket til brukeren rører feltet.
1911
+ Dialogen får klassen, og navnet sitt fra den første overskriften når den
1912
+ ikke alt har et fra `aria-labelledby` eller `aria-label`, med en id
1913
+ komponenten lager om den mangler; peker `aria-labelledby` på en overskrift,
1914
+ er det den som får tittelklassen. Bare det som mangler skrives,
1915
+ `suggestion.css` holder en bar liste skjult til komponenten er
1916
+ registrert, og advarslene sier hva som faktisk mangler.
1917
+ `derivedParts` og `uniqueId` i `/host-element` er hjelperne bak.
1918
+ - **`<fs-tabs>` og `<fs-error-summary>` kobler fra bar struktur.** En
1919
+ `.fs-tabs__list` med knapper og ett `.fs-tabs__panel` per knapp er nok:
1920
+ komponenten setter rollene, lager id-ene, kobler `aria-controls` og
1921
+ `aria-labelledby`, og skjuler panelene som ikke er valgt. Valget leses fra
1922
+ `aria-selected`, ellers fra hvilket panel som ikke er `hidden`.
1923
+ Feiloppsummeringen får klassen, `role="alert"` og `tabindex="-1"`, og
1924
+ overskriften klassen sin. Det serveren har skrevet står, og det
1925
+ komponenten fylte inn settes tilbake etter en patch, med de samme id-ene.
1926
+ `tabs.css` viser bare det første synlige panelet til komponenten er
1927
+ registrert, så innholdet ikke hopper. Kotlin-malen i spilldemoen skrev sju
1928
+ attributter per fane for hånd. `fs.tabs()` og `fs.errorSummary()` er som
1929
+ før, for markup som lages med JavaScript.
1930
+
1931
+ To ting endrer seg for markup som alt fantes. Under `server-controlled`
1932
+ setter fanene fortsatt ikke brukerens valg tilbake, men skriver nå ut det
1933
+ markupen sier: `aria-selected`, tabbestoppet og `hidden` i takt, så en
1934
+ patch som bare rørte panelene flytter også markeringen. Det komponenten
1935
+ selv skrev teller ikke som markupens svar. Og advarselen om manglende
1936
+ `tabindex` på feiloppsummeringen er borte, siden komponenten setter den.
1937
+ - **`@fristil/designsystem/register`: `defineFs()` registrerer alle ni
1938
+ web-komponentene i ett kall,** også fra `dist/register.js` på en CDN.
1939
+ Spilldemoen sto med sju `defineFs*`-kall i en `useEffect`, altså etter
1940
+ første tegning, og den første meldingen fra hendelsesstrømmen kom i gapet:
1941
+ «reportSuccess is not a function». Dokumentasjonen sier nå: registrer ved
1942
+ import, aldri i en effekt.
1943
+ - **De frittstående komponentene har funksjoner som venter på
1944
+ registreringen:** `showToast(element, tekst, valg)`,
1945
+ `reportFailure(element)`, `reportSuccess(element)`,
1946
+ `extendSession(element)` og `resetSession(element)`. De venter på
1947
+ `customElements.whenDefined` for elementets tagnavn og kaller så metoden,
1948
+ så et kall som kommer for tidlig blir gjort i stedet for å feile. Et
1949
+ element ingen registrerer gir en advarsel i konsollen etter tre sekunder.
1950
+ `whenUpgraded(element)` i `/host-element` er hjelperen bak dem.
1951
+ - **`@fristil/designsystem/fristil.css`: alle stilarkene i én fil.** For den
1952
+ som lenker fra CDN eller `node_modules` uten byggesteg. De enkelte stilarkene
1953
+ henter delene sine med `@import`, som nettleseren først ser når fila er
1954
+ lastet, og på mobilnett sto feltene uten ramme til den andre runden kom;
1955
+ spilldemoen forhåndslastet fem filer for hånd for å komme rundt det. Fila
1956
+ bygges av CSS-oppføringene i `exports`, med `tokens.css` først, uten
1957
+ `@import` og uten kommentarer. Et byggesteg bruker de enkelte stilarkene som
1958
+ før. Tailwind-temaet er ikke med, siden det ikke er et stilark for en side.
1959
+ - **Dialogen kan ha en farget topp.** `fs.dialog({ color })` gir `data-color`
1960
+ på dialogen og en ny `header`-del som pakker overskriften, med en valgfri
1961
+ `subtitle` under. Fargene er varslerens, `info`, `success`, `warning` og
1962
+ `danger`, og `brand` for merkefargen. Med toppen i markupen flyttes luften
1963
+ fra dialogen til delene, så båndet går helt ut i kantene; uten den står
1964
+ dialogen som før. To dialoger i spilldemoen skrev om padding på tre
1965
+ klasser for å få til dette, og det brakk ved en oppgradering.
1966
+ `fs.dialog.colors` og `fs.dialog.isColor` lister og sjekker verdiene.
1967
+
1968
+ ## 0.17.0 (2026-09-27)
1969
+
1970
+ ### Brytende
1971
+
1972
+ - **`idEllerReserve` heter `idOrFallback`.** Hjelperen i `@fristil/designsystem/shared`
1973
+ var den siste eksporterte identifikatoren på norsk, og identifikatorer i
1974
+ pakken skal være engelske. Ingen alias: pakken har ingen konsumenter ennå,
1975
+ og en brytende endring gjøres én gang. Parameteren `oppgittId` i
1976
+ byggefunksjonene heter `givenId`; den er intern.
1977
+
1978
+ ## 0.16.0 (2026-09-27)
1979
+
1980
+ ### Lagt til
1981
+
1982
+ - **`reset()` på `<fs-session-timeout>`** nullstiller klokka uten å sende
1983
+ `session-extend`, for en app som alt har forlenget økten selv, som når en
1984
+ autolagring gikk gjennom.
1985
+ - **`--fs-toast-layer`** styrer `z-index` på varselregionen, med `60` som
1986
+ reserve.
1987
+ - **`isToastColor`** eksporteres ved siden av `toastColors`.
1988
+ - **`fs.popover()` tar `placement`.** `fs.popover.isPlacement` sjekker en
1989
+ verdi, også i `/react`, og `popoverPlacements` fra hovedinngangen lister
1990
+ dem, med typen `PopoverPlacement`. Før måtte attributtet skrives for hånd
1991
+ uten typesjekk.
1992
+ - **`--fs-suggestion-layer`** styrer `z-index` på forslagslista, med `30`
1993
+ som reserve.
1994
+
1995
+ ### Rettet
1996
+
1997
+ - **Escape i `<fs-session-timeout>` forlenger, og dialogen kommer ikke
1998
+ tilbake.** Komponenten lyttet ikke på `close`, så neste tikk så en lukket
1999
+ dialog etter varselgrensen og åpnet den igjen, ett sekund etter Escape,
2000
+ hver gang, med en ny `session-warn`. Escape regnes nå som «jeg er her» og
2001
+ gjør det samme som knappen.
2002
+ - **Skjermleseren får vite hvor lenge det er igjen i det varselet åpnes.**
2003
+ Tallet i avsnittet er `aria-hidden`, og live-området ble fylt først ved
2004
+ neste terskel, så med standardverdiene hørte skjermleseren «Vi logger deg
2005
+ ut om for å beskytte opplysningene dine», og første tall kom tre minutter
2006
+ senere.
2007
+ - **`<fs-session-timeout>` står stille etter utløpet og etter «Logg ut nå»**
2008
+ til `extend()` eller `reset()` kalles. Før startet syklusen på nytt av seg
2009
+ selv, med ny dialog og ny `session-expired` hvert `expires-at`-sekund, og
2010
+ en app som brukte mer enn ett sekund på utloggingen fikk dialogen tilbake.
2011
+ - **`warn-at` og `expires-at` sjekkes.** Et tall som ikke er et tall, eller
2012
+ et varsel som ikke kommer før utløpet, gir beskjed i konsollen framfor
2013
+ stillhet. `warnAt` og `expiresAt` har fått settere, rulling i en boks
2014
+ teller som aktivitet, og hver forekomst har sin egen overskrift-id.
2015
+ - **Pausen i `<fs-toast>` holder til både musa og fokus har forlatt
2016
+ meldingen.** Den var to uavhengige par, og musa som gikk ut startet klokka
2017
+ igjen mens fokus sto i meldingen, som så forsvant under brukeren.
2018
+ - **`dismiss()` flytter fokus til meldingen ved siden av** når meldingen som
2019
+ lukkes hadde fokus. Før falt fokus til `body`, og neste Tab startet øverst
2020
+ på siden.
2021
+ - **Varselregionen er `aria-atomic="false"`**, både fra `fs.toast()` og fra
2022
+ komponenten. `status` er atomisk som standard, og hele stabelen ble lest
2023
+ opp på nytt for hver ny melding.
2024
+ - **Regionen heter «Varsler» begge steder.** Komponenten sa «Meldinger» når
2025
+ attributtet manglet, `fs.toast()` sa «Varsler». Et tomt `duration` gir
2026
+ standardverdien framfor null, og en ukjent `color` gir ingen kant.
2027
+ - **`<fs-connection-status>` melder `connection-lost` én gang per utfall.**
2028
+ Nettlesere fyrer gjerne flere `offline` på rad, og hver ga en ny hendelse
2029
+ og en ny opplesning av den samme linja. Linja settes nå inn tom og fylles
2030
+ i neste tegning, slik at live-området finnes før innholdet kommer.
2031
+ `offlineText` og `onlineText` har fått settere.
2032
+ - **`<fs-suggestion>` lukker lista når fokus forlater komponenten.** Tab
2033
+ gikk videre til neste felt, og lista ble stående over det med
2034
+ `aria-expanded="true"` på et felt som ikke lenger hadde fokus. Lytteren på
2035
+ `document` er borte: et trykk i lista holder fokus i feltet, fokus på noe
2036
+ inne i komponenten lar lista stå, og alt utenfor lukker den. Dermed virker
2037
+ feltet også inne i en skyggerot, der klikket i
2038
+ feltet før ble regnet som et klikk utenfor og lukket lista i samme klikk
2039
+ som åpnet den.
2040
+ - **«Ingen treff» følger lista.** Meldingen er søsken til lista og ble
2041
+ stående synlig under et lukket felt etter Escape. `fs.suggestion()` skriver
2042
+ den også skjult på et lukket felt uten alternativer, som er det anbefalte
2043
+ oppsettet for asynkront søk; før sto meldingen der alt ved sidelasting.
2044
+ - **Markeringen ryddes i det alternativet filtreres bort**, også med
2045
+ `server-controlled`. Før pekte `aria-activedescendant` på et skjult
2046
+ alternativ, og skjermleseren leste det opp.
2047
+ - **Alternativene kjennes igjen på `role="option"`**, ikke på klassen. En mal
2048
+ med egen styling fikk verken filtrering eller piltaster, uten et ord.
2049
+ - **`<fs-suggestion>` sier fra når `[role="status"]` mangler.** Antall treff
2050
+ leses opp der, og uten elementet sto komponenten stille.
2051
+ - **`fs.suggestion()` skriver `aria-selected` og `aria-activedescendant`
2052
+ under samme vilkår**, altså bare når lista er åpen. Før fikk en lukket
2053
+ liste det ene uten det andre.
2054
+ - **Det markerte alternativet vises i høykontrastmodus**, med systemfargene.
2055
+ - **`<fs-popover>` står på riktig side i `dir="rtl"`.** Posisjonen er
2056
+ fysisk, men stilarket brukte den som `inset-inline-start`, som er høyre
2057
+ kant i en side som leses fra høyre. `start` og `end` i `placement` følger
2058
+ nå leseretningen.
2059
+ - **`top-start` og `top-end` klemmes inn i vinduet.** Et panel ved toppen av
2060
+ siden lå helt utenfor skjermen, uten å kunne rulles fram. Er det ikke
2061
+ plass på den siden plasseringen ber om, legges panelet på den andre.
2062
+ - **Verten følger med når nettleseren selv lukker et `popover="auto"`.**
2063
+ Håndskrevet markup med bare `popover` er `auto`, og etter en lett
2064
+ avvisning sto verten med `open` og knappen med `aria-expanded="true"`
2065
+ over et lukket panel. Komponenten lytter nå på `toggle`.
2066
+ - **Klikk utenfor et sprettoppvindu i en skyggerot** leser
2067
+ `composedPath()`, så et klikk i panelet ikke regnes som utenfor.
2068
+ - **`<fs-dialog server-controlled>` lukker når patchen tar `open` fra en
2069
+ modal dialog.** Reparasjonen var slått av, men ingen kalte `close()`, og
2070
+ dialogen sto igjen i topplaget med `display: none` mens resten av siden
2071
+ var inert.
2072
+ - **`<fs-tabs>` hopper over deaktiverte faner.** Piltastene, Home og End
2073
+ valgte en fane med `disabled`, `focus()` på en deaktivert knapp gjør
2074
+ ingenting, og raden sto uten en eneste fane som kunne få fokus.
2075
+ Tastaturbrukeren var låst ute. `select()` avviser dem også.
2076
+ - **Faner i faner er hver sin rad.** Komponenten fant faner og paneler i hele
2077
+ undertreet, så et klikk på en indre fane skjulte det ytre panelet den sto i.
2078
+ - **Panelet finnes gjennom `aria-controls`.** Koblingen står i markupen og
2079
+ leses derfra, med rekkefølgen som reserve bare for markup uten
2080
+ `aria-controls`. Panelene kan dermed stå i en annen rekkefølge enn fanene,
2081
+ og utenfor verten. Peker `aria-controls` på en id som ikke finnes, sier
2082
+ komponenten fra i konsollen. Peker to faner på det samme panelet, vises
2083
+ det når en av dem er valgt.
2084
+ - **Tabbestoppet flyttes når en patch deaktiverer fanen brukeren valgte**,
2085
+ til den neste fanen som kan få fokus. Valget står. Har brukeren ikke valgt
2086
+ noe, er markupen serverens, og komponenten rører den ikke.
2087
+ - **Venstre og høyre pil bytter retning i `dir="rtl"`.**
2088
+ - **`selected` på `<fs-tabs>` kan settes.** Getteren sto uten setter, og
2089
+ `faner.selected = 1`, som dokumentasjonen viste, kastet.
2090
+ - **`fs.tabs()` med negativt `count` gir en tom rad** framfor å kaste.
2091
+ - **`<fs-error-summary>` prøver igjen når fokus ikke landet.** Sto boksen i et
2092
+ skjult panel eller en lukket dialog i det den kom, feilet `focus()` i
2093
+ stillhet mens flagget «har flyttet fokus» ble satt, og boksen fikk aldri
2094
+ fokus da forelderen ble synlig og lista byttet ut. Gjenforsøket skjer bare
2095
+ når ingen står i et felt, så live-validering som patcher lista ikke river
2096
+ fokus ut av feltet brukeren retter i.
2097
+ - **En lenke til en `<label>` uten kontroll følger `for`** dit den peker,
2098
+ som til en gruppe.
2099
+ - **`<fs-error-summary>` sier fra når `tabindex` mangler.** Uten det gjør
2100
+ `focus()` ingenting, og hele grunnen til komponenten forsvant i stillhet.
2101
+ - **En lenke til en `<label>` som omslutter kontrollen** sender fokus til
2102
+ kontrollen, ikke til ledeteksten.
2103
+ - **Rullingen til feltet følger sidens `scroll-behavior`** framfor å tvinge
2104
+ `smooth`, så `prefers-reduced-motion` gjelder av seg selv.
2105
+ - **`<fs-field>` eier bare det den selv skrev.** Komponenten leser
2106
+ `aria-invalid`, `aria-describedby` og `disabled` fra kontrollen, fordi
2107
+ serveren kan ha skrevet feltet med `fs.field()`. Skillet mellom serverens
2108
+ ord og komponentens eget ekko fantes bare for `aria-invalid`, og de to
2109
+ andre hadde samme feil: `aria-describedby` pekte fortsatt på feilmeldingen
2110
+ etter at feltet var gyldig, og på en hjelpetekst en patch hadde fjernet, og
2111
+ `felt.disabled = false` slo ikke av et `disabled` komponenten selv hadde
2112
+ satt. Minnet er nå nøklet på kontrollen og overlever at feltet flyttes,
2113
+ slik React gjør ved en omstrukturering, og at kontrollen forsvinner og
2114
+ kommer tilbake i en patch. Id-er serveren selv la i `aria-describedby` blir
2115
+ med videre.
2116
+ - **Ingen `aria-disabled` på kontrollen.** Komponenten skrev det ved siden av
2117
+ `disabled`, og strøk samtidig et `aria-disabled` konsumenten selv hadde
2118
+ skrevet. `computeFieldAttributes` er kontrakten, og den skriver det bare på
2119
+ ledeteksten; et ekte `disabled` er alt synlig for hjelpemidlene.
2120
+ - **Serverens `aria-invalid` står ordrett når det er serveren som sier feltet
2121
+ er ugyldig.** `false` er gyldig og vanlig i håndskrevet HTML, og ble strøket
2122
+ ved hver patch. `grammar` og `spelling` betyr ugyldig, men ble lest som
2123
+ «ikke true» og strøket på samme måte. Tom streng leses som `false`, slik
2124
+ ARIA sier.
2125
+ - **`requiredMarker` som egenskap betyr det samme som attributtet.** Setteren
2126
+ skriver `"none"` bokstavelig, siden `required-marker="none"` overstyrer en
2127
+ `data-required` serveren skrev på ledeteksten. Før fjernet setteren
2128
+ attributtet, og markeringen kom tilbake fra ledeteksten. Getteren leser nå
2129
+ markupen, ledeteksten medregnet, framfor bare sitt eget attributt.
2130
+
2131
+ ## 0.15.0 (2026-09-26)
2132
+
2133
+ - **`web-types.json` følger med pakken.** Det er JetBrains sitt format for
2134
+ fullføring og forklaring i HTML, med de ni `<fs-*>`-elementene og
2135
+ attributtene deres. IDE-en finner den selv fra `node_modules`, gjennom
2136
+ feltet `web-types` i `package.json`. Etterprøvd i IntelliJ IDEA Ultimate 2026.2:
2137
+ fullføring av elementene, attributtene og verdiene, og forklaring med
2138
+ lenke. Fila genereres fra `editor/metadata.ts` i repoet, der
2139
+ VS Code-utvidelsen også lages.
2140
+ - `observedAttributes` på komponentklassene er nå skrivebeskyttede tupler
2141
+ (`as const`) i stedet for `string[]`. Verdiene er de samme. En klasse som
2142
+ arver fra en komponent og skriver over `observedAttributes` med `string[]`
2143
+ får en typefeil, og må bruke `as const` den også.
2144
+
2145
+ ## 0.14.0 (2026-09-24)
2146
+
2147
+ ### Brytende
2148
+
2149
+ - **Verten heter `host` i alle byggefunksjonene.** Den delen serveren skriver,
2150
+ og komponenten fester seg på, het tre ting: `host` i `fs.dialog()` og
2151
+ `fs.popover()`, `region` i `fs.toast()` og `container` i `fs.errorSummary()`.
2152
+ Tre ord for den samme tingen, og to av dem sa ikke hva den var. De heter nå
2153
+ `host`, som er ordet arkitekturen bruker om den ellers.
2154
+
2155
+ `fs.connectionStatus()` og `fs.sessionTimeout()` er urørt. De har bare én
2156
+ del, og returnerer attributtene flatt, slik `fs.button()` gjør. Nøkkelen
2157
+ finnes der verten selv bærer attributter. `fs.field()`, `fs.tabs()` og
2158
+ `fs.suggestion()` har mange deler og ingen `host`, fordi verten deres
2159
+ ikke har noe å bære.
2160
+
2161
+ ```diff
2162
+ - <fs-toast {...varsler.region} />
2163
+ + <fs-toast {...varsler.host} />
2164
+ - <fs-error-summary {...feil.container}>
2165
+ + <fs-error-summary {...feil.host}>
2166
+ ```
2167
+
2168
+ ### Lagt til
2169
+
2170
+ - **`setAttributes` tar `null`.** `document.querySelector()` gir
2171
+ `Element | null`, så hvert eneste kallsted i en `strict`-app måtte skrive en
2172
+ vakt eller et utropstegn rundt et oppslag som nesten alltid treffer. Den gjør
2173
+ nå ingenting på `null`, som `element?.classList`. Parameteren er utvidet, så
2174
+ ingen kallsteder brytes.
2175
+
2176
+ ### Rettet
2177
+
2178
+ - **Panelet i `<fs-popover>` nullstiller lista.** Panelet er ofte en `<ul>`
2179
+ med handlinger, og `.fs-popover` hadde ingen `list-style`. Markupen i
2180
+ dokumentasjonen skrev `data-variant="plain"` for å bøte på det, men det
2181
+ attributtet finnes bare på `.fs-list` og traff ingen regel, så panelet sto
2182
+ med nettleserens kuler. Nullstillingen hører i pakken og ikke
2183
+ hos konsumenten.
2184
+
2185
+ - **`ref` virker på de egendefinerte elementene i JSX.** Typene bygget på
2186
+ `HTMLAttributes`, som ikke har `ref`; den ligger i `RefAttributes`. Derfor
2187
+ var `<fs-toast ref={kø} />` en typefeil, altså nøyaktig mønsteret
2188
+ dokumentasjonen anbefaler for å kalle `.show()` og `.hide()` fra React. Alle
2189
+ vertene bygger nå på en felles `Host`-type som har begge.
2190
+
2191
+ - **`warn-at` og `expires-at` godtar det byggefunksjonen sender.** De sto som
2192
+ `number` i JSX-typene, mens `fs.sessionTimeout()` sender strenger, slik
2193
+ HTML-attributter er. Det er den samme feilen som `tabIndex: "0"`, motsatt
2194
+ vei, og den viste seg bare i en ekte React-app.
2195
+
2196
+ - **`fs.connectionStatus()` og `fs.sessionTimeout()` reklamerte med en død
2197
+ klasse.** Begge sender ut `fs-connection-status` og `fs-session-timeout`,
2198
+ men stilarkene stylet bare elementnavnet, så klassen gjorde ingenting. Nå
2199
+ treffer regelen begge, og en konsument som setter klassen på noe annet enn
2200
+ elementet får den samme oppførselen.
2201
+
2202
+ ## 0.13.0 (2026-09-24)
2203
+
2204
+ ### Brytende
2205
+
2206
+ - **`.fs-dialog` er en kolonne når `.fs-dialog__body` er et direkte barn.**
2207
+ Det er rettelsen under, men den endrer hvordan innholdet legger seg for
2208
+ markup som alt finnes, og det er den strukturen dokumentasjonen viser. Marger
2209
+ mellom avsnitt slutter å falle sammen, og tekst og knapper som står rett i
2210
+ dialogen ved siden av kroppen blir egne rader i full bredde. En dialog uten
2211
+ `.fs-dialog__body` er urørt.
2212
+
2213
+ - **`display` kan ikke lenger settes alene på `.fs-dialog`.** En `<dialog>`
2214
+ uten `open` skjules av nettleserens eget stilark, og lagene sorterer bare
2215
+ innenfor ett opphav, så en hvilken som helst forfatterregel med `display`
2216
+ slår den skjulingen. Setter du din egen, må du ta med
2217
+ `dialog.fs-dialog:not([open]):not(:is(:popover-open)) { display: none }`,
2218
+ minst like spesifikk som din egen regel. Det står på komponentsiden og på
2219
+ siden om tilpasning. Dette er det eneste unntaket fra løftet om at en enkel
2220
+ selektor slår hva som helst i pakken.
2221
+
2222
+ ### Rettet
2223
+
2224
+ - **Dialogen hopper heller ikke sidelengs.** 0.12.1 ga den plassen til en
2225
+ modal før den var det, og det loddrette hoppet var borte. Et vannrett kom i
2226
+ stedet: nettleserens eget stilark setter `max-width: calc(100% - 6px - 2em)`
2227
+ på `dialog:modal`, og det hadde vi ikke. På en skjerm som er 390 piksler
2228
+ bred gikk dialogen derfor fra 358 til 352 piksler i det `showModal()` kjørte,
2229
+ og flyttet seg tre piksler mot høyre.
2230
+
2231
+ `.fs-dialog[open]:not(:modal)` inne i `<fs-dialog>` bruker nå `inset: 0` med
2232
+ `margin: auto`, det samme maksmålet på bredden og `overflow: auto`, altså de
2233
+ samme verdiene spesifikasjonen gir en modal, i logisk form. Maksmålet på
2234
+ høyden står med vilje bare på `.fs-dialog`, siden det er det strengeste av
2235
+ de to og dermed gjelder i begge tilstandene. Testen leser boksen før og
2236
+ etter `showModal()`, med to slags innhold, og krever at den står stille.
2237
+
2238
+ - **`.fs-dialog__body` ruller nå faktisk.** Klassen har hatt `overflow-y: auto`
2239
+ siden komponenten kom, uten at den kunne gjøre noe: dialogen var en blokk, så
2240
+ kroppens høyde var innholdsbestemt og ble aldri klippet. Det som rullet var
2241
+ dialogen selv, og bare når den var modal, så overskriften og knapperaden
2242
+ forsvant ut av syne sammen med teksten. `.fs-dialog:has(> .fs-dialog__body)`
2243
+ er nå en kolonne, og da krymper kroppen og ruller mens tittelen og knappene
2244
+ blir stående. Det er også det dokumentasjonen har sagt hele tiden.
2245
+
2246
+ Regelen tar samtidig tilbake det den tok: en `<dialog>` uten `open` er skjult
2247
+ av nettleserens eget stilark, og en forfatterregel med `display` slår den
2248
+ uansett lag og spesifisitet. `dialog.fs-dialog:not([open]):not(:popover-open)`
2249
+ setter `display: none` igjen, ellers ville en lukket dialog stått som et kort
2250
+ oppå innholdet rundt, fra sidelasting og etter hver lukking. Unntaket for
2251
+ popover står der spesifikasjonens eget stilark har det, siden en popover
2252
+ aldri setter `open`.
2253
+
2254
+ **Det gjør `display` til den ene egenskapen en konsument ikke kan sette fritt
2255
+ på `.fs-dialog`.** Gjør du det, må du ta den skjulte tilstanden tilbake selv.
2256
+ Det står nå på komponentsiden.
2257
+
2258
+ Vilkåret i selektoren er en del av oppførselen, og står nå i tabellen over
2259
+ klassene. Kolonnen virker bare når kroppen er et direkte barn, og den endrer
2260
+ samtidig hvordan alt annet legger seg: marger mellom avsnitt slutter å falle
2261
+ sammen, og tekst og knapper som står rett i dialogen blir egne rader i full
2262
+ bredde. En dialog uten kropp er derfor urørt.
2263
+
2264
+ ## 0.12.1 (2026-09-24)
2265
+
2266
+ ### Rettet
2267
+
2268
+ - **Dialogen hopper ikke lenger på plass.** En dialog serveren vil vise sendes
2269
+ med `open`, ellers finnes ikke innholdet uten JavaScript, og nettleseren
2270
+ legger den da i den vanlige flyten. Først når komponenten kaller
2271
+ `showModal()` flyttes den til topplaget og midtstilles, og mellom de to
2272
+ øyeblikkene hopper den. På en treg forbindelse er hoppet godt synlig:
2273
+ testet i Chromium sto dialogen på topp 0 og landet på topp 128, og det ble
2274
+ meldt fra en telefon.
2275
+
2276
+ `.fs-dialog[open]:not(:modal)` inne i `<fs-dialog>` plasseres nå som en
2277
+ modal allerede før den er det, med flaten bak malt av en skygge, siden
2278
+ `::backdrop` bare finnes i topplaget. Laget kan settes med
2279
+ `--fs-dialog-layer`, som er 40 i utgangspunktet.
2280
+
2281
+ Regelen gjelder bare inne i verten. En `.fs-dialog` som brukes uten
2282
+ komponenten, og med vilje ikke er modal, står der den står.
2283
+
2284
+ ## 0.12.0 (2026-09-24)
2285
+
2286
+ ### Brytende
2287
+
2288
+ - **`ThemeInput` er en union.** Merkefargene kan utelates, men da skal ingen av
2289
+ dem stå: enten alle fire, eller ingen. Vilkåret står i typen og ikke bare som
2290
+ en feilmelding i kjøretid, så `buildTheme({ interactive, danger })` er en
2291
+ typefeil. En konsument som leste `input.interactive` fra en variabel av typen
2292
+ `ThemeInput`, må nå skille de to tilfellene. Meldingen i kjøretid står ved
2293
+ siden av, for oppskriften kan komme fra en JSON-fil eller et skript uten
2294
+ typer.
2295
+
2296
+ ### Lagt til
2297
+
2298
+ - **Temageneratoren kan også sette skrift og form.** Den bygget fargene, og
2299
+ bare dem, og to systemer med den samme paletten ser fortsatt ulike ut når
2300
+ skriften og hjørnene er ulike. `buildTheme()` tar nå to valgfrie blokker,
2301
+ `typography` og `shape`. Seks av verdiene finnes også som flagg: `--skrift`,
2302
+ `--knapp-hjorner`, `--felt-hjorner`, `--flate-hjorner`, `--knapp-ramme` og
2303
+ `--knapp-vekt`. Vektene og linjeavstandene settes fra fil. Utelates begge
2304
+ blokkene, er temaet nøyaktig det det var før.
2305
+
2306
+ Hjørnene er delt i tre framfor ett felles tall, fordi ett tall er feil:
2307
+ et annet norsk designsystem har helt runde knapper mens feltene har nesten
2308
+ rette hjørner, og med én verdi blir feltene kapsler. `buttonRadius` treffer knapp,
2309
+ paginering og hopplenke, `fieldRadius` felt, tekstområde og nedtrekksliste,
2310
+ og `surfaceRadius` de elleve flatene, meldingen og hjelpeboblen medregnet.
2311
+ Avkryssingsboksen, merket, etiketten, valggruppa, avataren og skjelettet
2312
+ står utenfor, fordi hjørnet der ikke er et stilvalg, men selve formen.
2313
+
2314
+ Skriften skrives som en ekte regel på `:root` og ikke bare som et token.
2315
+ Fristil arver skrift med vilje, så et token alene ville ikke endret én
2316
+ eneste bokstav.
2317
+
2318
+ - **Den genererte fila ligger i sitt eget lag,** `fristil-tema`, erklært etter
2319
+ `fristil` med `@layer fristil, fristil-tema;`. Den lå i `@layer fristil`
2320
+ først, og da avgjorde rekkefølgen stilarkene ble lastet i. Den rekkefølgen
2321
+ har konsumenten ikke alltid i hånda: både Astro og TanStack Start legger sin
2322
+ bundlede CSS inn rett før `</head>`, altså etter en `<link>` appen selv har
2323
+ skrevet, og temaet tapte da mot pakkens egne verdier. Begge lagene ligger
2324
+ fortsatt foran usortert CSS, så konsumentens egne regler vinner som før.
2325
+
2326
+ - **Et tema kan la fargene stå.** Merkefargene er nå valgfrie, og utelates
2327
+ alle fire, lages et tema som bare setter skrift og form. Det er ikke en
2328
+ kuriositet: bruker organisasjonen allerede Fristils palett, er det nettopp
2329
+ skriften og hjørnene som skiller, og å kjøre fargene gjennom generatoren
2330
+ ville flyttet dem bort fra der de skal være. `#1362ae` kommer ut som
2331
+ `#1e6ab7`, siden skalaene regnes om i OKLCH fra merkefargen. Enten alle fire
2332
+ fargene, eller ingen: to farger kaster, siden resten av temaet da ville blitt
2333
+ bygget av standardfarger uten at noen ba om det. Se «Brytende» over for hva
2334
+ det gjorde med typen.
2335
+
2336
+ - **Verdier i et tema kan ikke bryte ut av regelen de skrives inn i.**
2337
+ Oppskriften er en JSON-fil som kan komme fra et annet repo eller fra et
2338
+ byggesteg, og `«4px; } html { display: none } :root { --x: 1` lukket både
2339
+ erklæringen og `:root`-blokka, og fikk en vilkårlig regel inn i
2340
+ `@layer fristil`. Tre veier til var åpne, og alle er etterprøvd i nettleser:
2341
+ `1px (` åpnet en parentes som slukte resten av stilarket, `Arial\` lot
2342
+ baksnabelen spise semikolonet, og `Arial</style><script>…` kjørte et skript i
2343
+ en side der CSS-en står inline. Verdier med `;`, `{`, `}`, `<`, `>`, `\`,
2344
+ `/*` eller styretegn avvises nå, parenteser og anførselstegn må gå opp, og
2345
+ meldingen navngir nøkkelen i oppskriften framfor variabelen vi skriver.
2346
+
2347
+ - **Et ukjent flagg eller en ukjent nøkkel stopper kjøringen** framfor å bli
2348
+ ignorert. `--knapp-hjørner` med ø er den naturlige norske stavemåten, mens
2349
+ flagget heter `hjorner`, og temaet kom før ut uten hjørnet og uten et ord.
2350
+ Det samme gjaldt en skrivefeil inne i oppskriftsfila, altså den som kan
2351
+ komme fra et annet repo.
2352
+
2353
+ - **Vekt og linjeavstand er tokens.** `--font-weight-regular`,
2354
+ `--font-weight-medium`, `--font-weight-semibold`, `--font-weight-bold`,
2355
+ `--semantic-line-height-default`, `--semantic-line-height-heading`,
2356
+ `--semantic-line-height-article` og `--semantic-line-height-compact`.
2357
+ Verdiene er nøyaktig dem komponentene hadde skrevet ut fra før, så
2358
+ ingenting ser annerledes ut.
2359
+
2360
+ - **Trettisju stilark leser nå vekt og linjeavstand fra tokenene** framfor å
2361
+ skrive tallet. Verdiene er de samme, så ingenting ser annerledes ut, men uten
2362
+ dette ville `typography.weights` og `typography.lineHeights` i et tema truffet
2363
+ tre komponenter og ikke resten. Knappen, overskriften og avsnittet leser dem
2364
+ gjennom sin egen komponentvariabel, de andre leser tokenet direkte: en egen
2365
+ variabel per komponent er verdt det der man vil kunne skille dem, og støy der
2366
+ man ikke vil.
2367
+
2368
+ - **Seks nye komponentvariabler** der form sto skrevet ut i stilarket:
2369
+ `--fs-button-border-width`, `--fs-button-font-weight`,
2370
+ `--fs-button-line-height`, `--fs-heading-font-weight`,
2371
+ `--fs-heading-line-height` og `--fs-paragraph-line-height`. Regelen er at
2372
+ form og størrelse leses fra en komponentvariabel med tokenverdien som
2373
+ reserve, og disse tre komponentene brøt den.
2374
+
2375
+ ## 0.11.0 (2026-09-23)
2376
+
2377
+ ### Brytende
2378
+
2379
+ - **`server-filtered` heter `prefiltered`.** Navnet var misvisende på to
2380
+ måter. Det handler ikke om servere: en React-app som rendrer bare treffene
2381
+ har filtrert like fullt, uten at noen server er involvert. Og det handler
2382
+ ikke om hvem som filtrerte, men om hva de filtrerte på. Ingen konsumenter
2383
+ ennå, så navnet er byttet framfor å dokumenteres rundt.
2384
+
2385
+ - **`id` er påkrevd i `fs.field()`, og i de andre byggerne som tar en.** Den var valgfri, og funksjonen laget en
2386
+ når den manglet. Det var en felle: id-en er tilfeldig, så to kjøringer gir
2387
+ to ulike, og rendres det samme feltet på en server og så i nettleseren,
2388
+ peker `for` og `aria-describedby` på noe annet enn det som står der. React
2389
+ melder avvik ved hydreringen, og advarselen sier selv at avviket ikke blir
2390
+ rettet opp. Det er sett i en ekte app.
2391
+
2392
+ Å kaste når `document` ikke finnes ble vurdert og forkastet: det er å slutte
2393
+ fra miljøet til hva utvikleren mente. `document === undefined` betyr bare
2394
+ «dette er ikke en nettleser», og en Astro-side rendres på serveren og
2395
+ hydrerer ingenting, så der er en laget id helt i orden.
2396
+
2397
+ Typen alene er heller ikke nok, for en konsument uten TypeScript ser ingen
2398
+ type, og ren HTML med `<script type="module">` er en førsteklasses måte å
2399
+ bruke Fristil på. Utelates id-en likevel, lager byggeren en og sier fra i
2400
+ konsollen, én gang, med navnet på byggeren i meldingen. Uten den reserven
2401
+ fikk en JavaScript-konsument `aria-describedby="undefined-help"` og verken
2402
+ `for` eller `id`, altså verre enn den ustabile id-en kravet skulle bli
2403
+ kvitt.
2404
+
2405
+ Reserven gjelder `fs.field()`, `fs.suggestion()`, `fs.tabs()`,
2406
+ `fs.popover()` og `fs.dialog()`. Forslagsfeltet er grunnen til at den ikke
2407
+ kunne bo i feltet alene: uten id ble `list.id` og `options[n].id` til
2408
+ `undefined-list` og `undefined-option-0`, mens meldingen sa «fs.field()» og
2409
+ sendte utvikleren til feil sted.
2410
+
2411
+ Den tomme strengen teller som ingen id. `fs.field({ id: "" })` ga `for=""`
2412
+ og `help.id="-help"`, altså det samme problemet uten at noe sa fra.
2413
+
2414
+ I React kommer id-en fra `useId()`. Ellers er feltets eget navn som regel
2415
+ det opplagte valget. Vet du sikkert at markupen rendres én gang, kall
2416
+ `createFieldId()` selv. Den er nå også eksportert fra
2417
+ `@fristil/designsystem/react`, siden det er inngangen React-dokumentasjonen
2418
+ ber deg bruke.
2419
+
2420
+ `<fs-field>` er upåvirket når komponenten er det eneste som kobler feltet:
2421
+ den lager id-er i nettleseren, etter at HTML-en står der. Bruker du React
2422
+ med server-rendring, skal du bruke `fs.field()`, så koblingen står ferdig i
2423
+ markupen serveren sendte.
2424
+
2425
+ - **`createFieldId` eksporteres også fra `@fristil/designsystem/react`.** Den
2426
+ lå bare i hovedinngangen og i `./field-core`, mens dokumentasjonen peker på
2427
+ den i en seksjon som ber leseren importere fra `/react`.
2428
+
2429
+ ### Rettet
2430
+
2431
+ - **Forslagsfeltet sluttet å melde antall treff når noen andre filtrerte.**
2432
+ `filter()` returnerte med en gang, så en app med attributtet mistet
2433
+ opplesningen i `[role="status"]`. Det sto ikke noe sted, og den som ikke
2434
+ ser skjermen merket det.
2435
+
2436
+ Beskjeden kommer nå av at lista har endret seg, og ikke av tastetrykket.
2437
+ Med `prefiltered` rendrer appen lista, og mellom tastetrykket og det nye
2438
+ svaret kan det gå et halvt sekund over nettverket. Bare en endring i
2439
+ antallet leses opp; det første antallet er utgangspunktet og ikke en nyhet.
2440
+
2441
+ Tommeldingen er derimot en del av det lista viser, og den eier du med
2442
+ `prefiltered`. `fs.suggestion({ count })` skriver `hidden` på den når du
2443
+ har treff, så rendrer du feltet for hvert søk, er det gjort. Komponenten
2444
+ kan ikke se forskjell på «søket ga ingenting» og «svaret er ikke kommet
2445
+ ennå», og meldte derfor «Ingen treff» ved fokus på et tomt felt og i hvert
2446
+ opphold i et asynkront søk.
2447
+
2448
+ - **`prefiltered` kunne ikke settes fra React.** Getteren hadde ingen setter,
2449
+ og React 19 skriver egenskapen framfor attributtet når et egendefinert
2450
+ element har en med det navnet. Skrivingen kastet «Cannot set property»,
2451
+ attributtet landet aldri, og komponenten skjulte det React nettopp hadde
2452
+ rendret. Det gamle navnet `server-filtered` kunne ikke være et
2453
+ egenskapsnavn, så feilen kom med omdøpingen.
2454
+
2455
+ - **`prefiltered` gjorde ingenting før neste tastetrykk.** Attributtet sto i
2456
+ `observedAttributes`, men tilbakekallet svarte bare på `server-controlled`.
2457
+ Slås det av mens siden lever, filtrerer komponenten nå med en gang.
2458
+
2459
+ ### Lagt til
2460
+
2461
+ - **`sjekk-dokumentasjon.ts` avviser et eksempel som kaller `fs.field()` uten
2462
+ `id`.** Ingenting annet i rekka leser kodeblokkene i dokumentasjonen, så et
2463
+ utdatert eksempel kunne stått grønt gjennom hele `bun run sjekk`. Den leser
2464
+ koden: i mdx kodegjerdene, i Astro frontmatteret og skriptene. I brødtekst
2465
+ teller bare et kall som har en argumentliste, siden `fs.field()` uten
2466
+ argumenter er navnet på en funksjon og står slik i dusinvis av setninger.
2467
+ Det skillet måtte til: `fs.suggestion({ count: treff.length })` sto i en
2468
+ setning under et eksempel og slapp gjennom.
2469
+
2470
+ ## 0.10.0 (2026-09-23)
2471
+
2472
+ ### Brytende
2473
+
2474
+ - **`data-preserve-attr` er borte fra hele pakken.** `fs.tabs()`,
2475
+ `fs.popover()`, `fs.suggestion()` og `fs.dialog()` skriver det ikke lenger,
2476
+ og typene deres har ikke feltet. Skriver du markupen for hånd, kan du slette
2477
+ attributtet.
2478
+
2479
+ Lista krevde at malen skrev av navnene på hvert attributt komponenten kom
2480
+ til å røre, ni strenger fordelt på fire komponenter, uten at noen
2481
+ kompilator så på dem. Endret Fristil hva en komponent satte, gikk malen
2482
+ stille i stykker. Komponentene setter nå tilbake det brukeren gjorde, og
2483
+ ingen mal trenger å kjenne attributtene.
2484
+
2485
+ ### Lagt til
2486
+
2487
+ - **`server-controlled` på verten gir serveren tilstanden tilbake.** Det
2488
+ fredningen ga, og som reparasjonen måtte erstatte, er at den som skrev
2489
+ markupen kunne bestemme hvem som eier tilstanden. «Gå videre til steg 2» er
2490
+ en ekte ting en server vil kunne gjøre. Står attributtet der, reparerer
2491
+ komponenten ingenting, og hver patch bestemmer. Ett attributt å huske i
2492
+ stedet for ni, og standardvalget er det som er riktig nesten alltid. Gjelder
2493
+ `<fs-tabs>`, `<fs-popover>`, `<fs-suggestion>` og `<fs-dialog>`, og er
2494
+ deklarert for JSX på alle fire.
2495
+
2496
+ - **`setAttr`, `setFlag`, `setText`, `addClass`, `SERVER_CONTROLLED` og
2497
+ `isServerControlled` er nye eksporter** fra
2498
+ `@fristil/designsystem/host-element`. De fire første skriver bare når noe
2499
+ faktisk endrer seg, og er de eneste lovlige veiene til et attributt, et
2500
+ boolsk flagg, tekst og en klasse i en komponent som observerer sine egne. En
2501
+ overtatt komponent bruker dem.
2502
+
2503
+ - **`scripts/sjekk-skriving.ts` håndhever den regelen.** Den kjører som del av
2504
+ `build`, både i pakken og i rota, og leser kilden, fordi regelen ikke lar seg
2505
+ etterprøve ved å kjøre noe: bryter en komponent den, kaller observatøren seg
2506
+ selv, mikrooppgavekøen tømmes aldri, og en testkjøring **henger** framfor å
2507
+ feile. Ingen stakksporing, ingen påstand, bare en kjøring som må drepes for
2508
+ hånd.
2509
+
2510
+ Den ser etter seks skrivemåter, ikke bare `setAttribute`: `el.hidden = x`,
2511
+ `el.tabIndex = n`, `el.htmlFor = s`, `el.textContent = s`,
2512
+ `classList.add()` og `el.style.cssText = s`. Alle gir en mutasjonspost når
2513
+ ingenting endrer seg, og fem av dem var i bruk. Verten er ikke et unntak:
2514
+ komponentene observerer seg selv, så `this.classList.add()` henger en
2515
+ kjøring like godt som en skriving på et barn.
2516
+
2517
+ - **`sjekk:server` kjøres nå også fra rota.** Den sto bare i pakkens egen
2518
+ `build`, så den kjørte ved publisering og ikke i CI, mens `CLAUDE.md` sa at
2519
+ den var en vaktpost.
2520
+
2521
+ - **`stabilitet.browser.test.ts`** teller mutasjoner i hver av de fem
2522
+ komponentene etter at brukeren har gjort noe, og krever null. Den fanger en
2523
+ komponent som skriver litt for mye.
2524
+
2525
+ ### Endret
2526
+
2527
+ - **Komponentene setter tilbake det brukeren gjorde.** `<fs-tabs>` setter
2528
+ fanevalget tilbake, `<fs-popover>` at vinduet er åpent og hvor det står,
2529
+ `<fs-suggestion>` om lista er utvidet og hva som er markert, og
2530
+ `<fs-dialog>` `open` på selve `<dialog>` når den fortsatt står i topplaget.
2531
+ Alle fire observerer nå de attributtene de selv setter, og hver skriving
2532
+ sammenligner først.
2533
+
2534
+ Sprettoppvinduet reparerer én vei: har noen bedt om at vinduet er åpent,
2535
+ blir det stående, men sender serveren `open`, åpnes det, for det er noe
2536
+ serveren faktisk sa. En app lukker det med egenskapen, altså
2537
+ `meny.open = false`, `hide()` eller `toggle()`; et attributt fjernet utenfra
2538
+ er ikke til å skille fra en morfing, og der er `server-controlled` svaret.
2539
+ Dialogen skiller på samme måte mellom `open` på verten, som er serverens
2540
+ beskjed, og `open` på `<dialog>`, som nettleseren setter.
2541
+
2542
+ Komponentene husker identiteter og ikke posisjoner. `<fs-tabs>` husker selve
2543
+ knappen brukeren valgte: en indeks er ingen identitet, og en id virker ikke
2544
+ i håndskrevet markup, som ofte ikke har noen. En attributtmorfing beholder
2545
+ nodene, så referansen overlever den. `<fs-suggestion>` husker både id-en og
2546
+ teksten på det markerte alternativet, siden id-ene fra `fs.suggestion()` er
2547
+ posisjonelle og en ny liste gjenbruker dem. Er det borte, glemmer
2548
+ komponenten det framfor å gjette, og fanene rydder opp etter seg slik at
2549
+ raden fortsatt svarer.
2550
+
2551
+ ## 0.9.0 (2026-09-23)
2552
+
2553
+ ### Brytende
2554
+
2555
+ - **`FIELD_PRESERVED_ATTRIBUTES` er fjernet.** Lista fantes for at en mal
2556
+ skulle kunne skrive av navnene på alt `<fs-field>` setter, inn i
2557
+ `data-preserve-attr`, slik at en morfing lot dem stå. Den er ikke lenger
2558
+ nødvendig: komponenten ser at attributtene er borte, og setter dem
2559
+ tilbake. Bruker du den i dag, kan du ta bort både lista og
2560
+ `data-preserve-attr` på feltet.
2561
+
2562
+ ### Rettet
2563
+
2564
+ - **Sprettoppvinduet åpnet seg igjen når en patch både fjernet `open` og
2565
+ overlot tilstanden.** En morfing setter ett attributt om gangen, og `open`
2566
+ kommer før `server-controlled` i dokumentrekkefølgen, så komponenten
2567
+ reparerte mens serveren var midt i å si at den overtar. Reparasjonen venter
2568
+ nå til hele patchen har landet, og sjekker vilkåret på nytt der.
2569
+
2570
+ - **Forslagsfeltet satte ikke tilbake `aria-activedescendant` alene.** Rev en
2571
+ patch bare pekeren, mens markeringen sto igjen, så ingenting galt ut i
2572
+ markupen, men skjermleseren hadde mistet lesepunktet sitt. Begge sidene av
2573
+ koblingen sjekkes nå.
2574
+
2575
+ - **En fanerad sluttet å svare når en patch fjernet den valgte fanen.**
2576
+ `<fs-tabs>` glemte valget, men lot markupen stå i utakt: ingen fane markert,
2577
+ alle paneler skjult, og et klikk gjorde ingenting, fordi `select(0)`
2578
+ sammenlignet mot en `selected` som svarer 0 også når ingenting er markert.
2579
+ Komponenten velger nå den første fanen i det tilfellet, og melder fra med
2580
+ `tab-select`, siden det er komponentens eget valg og ikke brukerens. Sendte
2581
+ serveren med vilje en rad uten markering, blir den overkjørt, men bare når
2582
+ brukeren hadde valgt noe fra før.
2583
+
2584
+ - **Et felt kunne ikke bli gyldig igjen.** `<fs-field>` leser `aria-invalid`
2585
+ fra kontrollen, fordi serveren kan ha skrevet feltet med `fs.field()` og da
2586
+ står svaret allerede der. Uten et skille mellom serverens attributt og
2587
+ komponentens eget leste den tilbake sitt eget svar fra forrige runde, så
2588
+ `felt.invalid = false` fjernet flagget på verten mens den røde rammen og
2589
+ feilmeldingen ble stående.
2590
+
2591
+ Komponenten noterer nå verdien den selv skrev, og hvilken kontroll den ble
2592
+ skrevet på. Står det noe annet der neste gang, har noen andre rørt
2593
+ attributtet, og det er serverens ord som gjelder. Avlesningen er dermed
2594
+ alltid utledet av en endring som faktisk har skjedd, så det samme
2595
+ dokumentet gir alltid det samme svaret. Skrev serveren `aria-invalid` selv,
2596
+ står det derfor: `felt.invalid = false` fjerner flagget på verten, men
2597
+ stryker ikke det serveren sa. Bruk den ene av de to kildene, ikke begge.
2598
+
2599
+ ### Endret
2600
+
2601
+ - **`<fs-field>` kobler også en ledetekst som står utenfor elementet.** En
2602
+ `<label for>` som peker på kontrollen navngir feltet like godt som en inni,
2603
+ og komponenten skriver nå `fs-label`, `data-required` og `aria-disabled` på
2604
+ den. Bytter en patch ut kontrollen med en uten id, husker komponenten
2605
+ id-en, så ledetekstens `for` fortsetter å peke på et element som finnes.
2606
+ Én forskjell er verdt å vite: en ledetekst utenfor ligger ikke i det
2607
+ komponenten observerer, så river en patch klassen av den, kommer den ikke
2608
+ tilbake før neste gang feltet synkroniserer.
2609
+
2610
+ - **`<fs-field>` reparerer sin egen kobling.** Bevaringslista var en kontrakt
2611
+ vi ikke kunne kontrollere: en Go- eller Kotlin-mal måtte skrive av ni
2612
+ attributtnavn fra dokumentasjonen, og endret Fristil hva komponenten satte,
2613
+ gikk malen stille i stykker. Komponenten observerer nå de attributtene den
2614
+ selv utleder, og setter dem tilbake i det en patch river dem bort. Id-ene
2615
+ den har laget huskes, så en skjermleser midt i en opplesning ikke følger en
2616
+ peker som skifter under den.
2617
+
2618
+ Skillet er mellom det komponenten har **regnet ut** og det brukeren har
2619
+ **gjort**. Det første kan regnes ut på nytt, og repareres. Det andre, som
2620
+ `open` på et sprettoppvindu eller hvilken fane som er valgt, finnes det
2621
+ ingen kilde til, og en reparasjon ville dessuten kjempet mot en server som
2622
+ med vilje endret noe. Det fredes fortsatt med `data-preserve-attr`, skrevet
2623
+ av byggefunksjonen.
2624
+
2625
+ ### Lagt til
2626
+
2627
+ - **Komponentene sier fra når markupen de fikk ikke henger sammen.** Alle
2628
+ disse ga før stillhet, og feilen viste seg først når noen leste siden med
2629
+ skjermleser:
2630
+
2631
+ - `<fs-field>` uten en kontroll, og uten en ledetekst. Feltet regnes som
2632
+ navngitt av en `<label>` inni, en `<label for>` utenfor, eller
2633
+ `aria-label` og `aria-labelledby` på kontrollen, som i et søkefelt med
2634
+ bare et ikon;
2635
+ - `<fs-tabs>` uten noe med `role="tab"`, og med færre paneler enn faner;
2636
+ - `<fs-dialog>` uten en `<dialog>` som direkte barn;
2637
+ - `<fs-suggestion>` uten en combobox, og uten en listboks;
2638
+ - `<fs-popover>` uten et panel, uten en id på panelet, og uten en knapp som
2639
+ peker på det;
2640
+ - `<fs-error-summary>` med punkter som ikke lenker til feltene, og med en
2641
+ lenke som peker på en id som ikke finnes. Den siste meldes når boksen
2642
+ synkroniserer, ikke først når noen klikker: en lenke som ikke fører noe
2643
+ sted er like ødelagt om ingen prøver den.
2644
+
2645
+ Advarselen kommer én gang per element og melding, og først når siden har
2646
+ falt til ro. Det siste er grunnen til at den kan stoles på: HTML som
2647
+ strømmer fra en server leveres i pakker, og et brudd mellom ledeteksten og
2648
+ feltet er helt vanlig, så komponenten ser ofte halvferdig markup i det den
2649
+ kobles til.
2650
+
2651
+ - **`warnAboutMarkup` er en ny eksport** fra `@fristil/designsystem/host-element`.
2652
+ En overtatt komponent bruker den, så den må være tilgjengelig.
2653
+
2654
+ - **En vaktpost på at hver komponent kan overtas.** `sjekk-cli.ts` kjører
2655
+ `overta` på hver komponent verktøyet selv lister opp, og krever at ingen
2656
+ fil i kopien peker ut av mappa, og at hver `@fristil/…`-henvisning står i
2657
+ `exports`. Faren har stått skrevet ned lenge: en komponent som henter noe
2658
+ fra en ny mappe uten et inngangspunkt gir en kopi som peker i løse lufta,
2659
+ og feilen viser seg først når konsumenten bygger. Nå sier den fra her.
2660
+
2661
+ ## 0.8.3 (2026-09-23)
2662
+
2663
+ ### Rettet
2664
+
2665
+ - **`<fs-dialog>` åpnet igjen en dialog brukeren nettopp hadde lukket.**
2666
+ Serveren skriver `open` begge steder, så innholdet finnes uten JavaScript,
2667
+ og `<form method="dialog">` lukker boksen uten JavaScript også. Skjer det
2668
+ før komponenten har fått kjøre, finnes det ingen lytter, og første
2669
+ synkronisering så en vert som sa «åpen» og en lukket dialog. Da spratt
2670
+ dialogen opp igjen. På mobil skjedde det hver gang, fordi vinduet der er
2671
+ langt nok til å rekke et trykk.
2672
+
2673
+ `returnValue` skiller de to tilfellene: nettleseren setter den til verdien
2674
+ på knappen som lukket dialogen, så en dialog som aldri har vært åpnet har
2675
+ den tom. En mal som bare skriver `open` på verten åpnes derfor som før.
2676
+ Funnet i spilldemoen, på 375 piksler.
2677
+
2678
+ Det holder bare når knappen har en `value`. Lukkes dialogen med en knapp
2679
+ uten verdi, etterlater nettleseren ingenting å se etter. Det står nå på
2680
+ dialogsiden, og eksemplene der har alltid hatt en.
2681
+
2682
+ ## 0.8.2 (2026-09-23)
2683
+
2684
+ ### Rettet
2685
+
2686
+ - **`<fs-field>` mistet koblingen mellom ledetekst og felt ved første
2687
+ patch.** Bytter en morfing ut selve kontrollen, finnes det ingen node å
2688
+ frede, og den nye kommer uten id. Ledeteksten står igjen med sin `for`.
2689
+ Komponenten fant da ingen id, fant opp en ny, skrev den bare på
2690
+ kontrollen, og `for` pekte etter det på et element som ikke fantes. Feltet
2691
+ var altså uten ledetekst for en skjermleser, og det holdt seg til siden
2692
+ ble lastet på nytt.
2693
+
2694
+ Id-en leses nå også fra ledetekstens `for`, og `for` skrives hver gang, ikke
2695
+ bare når den mangler: de to er den samme opplysningen og kan ikke få si
2696
+ hver sin ting. Funnet i spilldemoen, i appen som sender HTML-biter fra en
2697
+ Kotlin-server.
2698
+
2699
+ ## 0.8.1 (2026-09-23)
2700
+
2701
+ ### Endret
2702
+
2703
+ - **`fs.dialog({ open: true })` skriver `open` på `<dialog>` også.** Det sto
2704
+ bare på verten, og da var innholdet borte for den som ikke har
2705
+ JavaScript: en `<dialog>` uten `open` er skjult. Nå vises det, plassert
2706
+ over innholdet under seg slik nettleserens egen stil gjør det, og
2707
+ komponenten gjør den om til en ekte modal med `showModal()` når den får
2708
+ kjøre. I React var det dessuten det eneste som stemte: komponenten setter
2709
+ `open` før React hydrerer, og sto det ikke i serverens HTML, meldte React
2710
+ avvik ved hvert eneste oppslag. Funnet i demoappen i TanStack Start.
2711
+
2712
+ `<fs-dialog>` tar attributtet bort med `removeAttribute` framfor `close()`
2713
+ før den kaller `showModal()`. `close()` sender en ekte `close`-hendelse, og
2714
+ den ville nå kommet ved hver eneste lasting av en åpen dialog. En app som
2715
+ lytter på `close` rett på `<dialog>` fikk altså en lukking før brukeren
2716
+ hadde sett dialogen. `dialog-toggle` så den ikke, siden komponenten
2717
+ stopper på `:modal`.
2718
+
2719
+ Komponenten ser nå på `:modal` og ikke på `open` når den lukker.
2720
+ Byggefunksjonen sender `open` på `<dialog>`, så i React er det React som
2721
+ eier attributtet, og React oppdaterer barn før forelder: lukker appen
2722
+ dialogen, er attributtet borte før komponenten får vite det. `close()` gjør
2723
+ ingenting uten attributtet, så dialogen ble stående i topplaget, usynlig,
2724
+ med resten av siden inert.
2725
+
2726
+ Styrer du dialogen selv fra nettleseren, skal du ikke sende `open` inn i
2727
+ `fs.dialog()`. `showModal()` kaster `InvalidStateError` på en dialog som alt
2728
+ står åpen, så en app som gjorde begge deler virket før og kaster nå. Det
2729
+ står i typen, i JSDoc-en og på siden.
2730
+
2731
+ Har du JavaScript, ser du dessuten dialogen et øyeblikk som en boks der den
2732
+ står i flyten, før komponenten flytter den til midten som en modal. Før var
2733
+ det ingenting å se før modalen kom.
2734
+
2735
+ ### Rettet
2736
+
2737
+ - **`fs.field()` sa ikke fra om at en id som lages av seg selv er
2738
+ tilfeldig.** Rendres feltet både på serveren og i nettleseren, blir det to
2739
+ ulike, og `for` og `aria-describedby` peker på noe annet enn det som står
2740
+ der. React melder avvik ved hydreringen. Det står nå i typen, på feltsiden
2741
+ og i React-fanen, med `useId()` som svaret.
2742
+
2743
+ ## 0.8.0 (2026-09-23)
2744
+
2745
+ ### Brytende
2746
+
2747
+ - **Boolske attributter på en vert er `true`, ikke `""`.** `fs.dialog().host`
2748
+ og `fs.popover().host` sendte ut `open: ""`. React 19 setter egenskaper
2749
+ framfor attributter på egendefinerte elementer, og `el.open = ""` er usant,
2750
+ så setteren i komponenten fjernet attributtet igjen: dialogen og panelet
2751
+ åpnet seg ikke, og om det skjedde kom an på om elementet var oppgradert
2752
+ ennå. `data-*` er noe annet og beholder den tomme strengen, for dem sender
2753
+ React videre som attributter i begge versjoner. `Flag` i `jsx/react.ts` er
2754
+ dermed `true | undefined` igjen, slik dokumentasjonen har sagt hele tiden.
2755
+
2756
+ - **Avslaget på fokus i feiloppsummeringen heter `data-autofocus="false"`.**
2757
+ Det het `autofocus="false"`. `autofocus` er en boolsk egenskap på
2758
+ `HTMLElement`, så React 19 satte `el.autofocus = "false"`, som er sant,
2759
+ mens attributtet aldri kom i markupen. Avslaget virket altså ikke i React
2760
+ 19, og feilen var taus. Samme feilklasse som `open` over.
2761
+
2762
+ - **`ReactAttributes<T>` gir `number` for `tabIndex`.** Typen er eksportert,
2763
+ altså offentlig API, og den sa før `string`. Rettelsen under er grunnen:
2764
+ verdien var feil, og typen beskrev feilen. Kode som tok imot den gamle
2765
+ typen som en streng, for eksempel `const t: string = fs.tabs(...).tabs[0]
2766
+ .tabIndex`, kompilerer ikke lenger. Selve attributtet i DOM-en er uendret.
2767
+
2768
+ ### Rettet
2769
+
2770
+ - **`tabIndex` kom ut som en streng fra React-inngangen.** `NAVN` døpte om
2771
+ `tabindex`, men lot verdien stå. I HTML er den en streng, i React er
2772
+ `tabIndex` et tall, så `fs.tabs()` ga `tabIndex: "0"` og TypeScript avviste
2773
+ den i enhver React-app. Den virket i nettleseren, siden React gjør om
2774
+ verdien selv, så feilen viste seg bare som en typefeil hos konsumenten.
2775
+ Funnet i en ekte React-app, ikke her.
2776
+
2777
+ - **`fs.fieldset` lovet en tilstand som ikke fantes.** `states` har alltid
2778
+ oppgitt `success`, men `fieldset.css` hadde bare en regel for `invalid`.
2779
+ Attributtet ble skrevet, og ingenting skjedde.
2780
+
2781
+ - **JSX-deklarasjonene godtok ikke det byggerne sender ut.** Et boolsk
2782
+ attributt er `""` fra byggeren, mens `Flag` i `jsx/react.ts` bare tillot
2783
+ `true` og `undefined`. `<fs-popover {...fs.popover({ open: true }).host}>`
2784
+ type-sjekket derfor ikke, altså to deler av det samme API-et som var
2785
+ uenige om det samme attributtet.
2786
+
2787
+ ### Lagt til
2788
+
2789
+ - **En vaktpost på at hver lovlig verdi finnes i CSS-en.** `pakke-css.browser.test.ts`
2790
+ går gjennom listene byggerne reklamerer med, `variants`, `colors`, `sizes`,
2791
+ `states`, `pickers`, `markers` og `types`, kaller byggeren med hver av dem,
2792
+ og rendrer elementet to ganger, med og uten attributtet. Noe i den beregnede
2793
+ stilen må være forskjellig, pseudoelementene medregnet. Et tekstsøk ville
2794
+ passert på en tom regel og på en som blir overstyrt lenger nede.
2795
+ Farger regelen et barn framfor elementet selv, som feltsettet gjør med
2796
+ `.fs-legend`, kreves det i stedet at en regel som gjelder her erklærer noe.
2797
+ Uten det ville en tom blokk passert, og det var nettopp en tom blokk som
2798
+ slapp gjennom første utgave av prøven.
2799
+
2800
+ Verdier som ikke sender ut noe attributt hoppes over: standardverdien, og
2801
+ verdier som ikke er ment å se annerledes ut. To egne prøver passer på at
2802
+ hoppelista ikke blir en bakdør. Den ene krever at opsjonsnavnet er et
2803
+ byggefunksjonen kjenner, siden feil navn ellers tar en hel liste ut av
2804
+ prøven i stillhet. Den andre krever at hver liste en byggefunksjon har,
2805
+ står i tabellen.
2806
+
2807
+ Hvilke regler som gjelder hentes fra CSSOM, ikke fra teksten: en regel i en
2808
+ `@supports` motoren ikke har, eller i en `@media` som ikke slår til, gjør
2809
+ ingenting. Den stylede nedtrekkslista står i begge, og skal ikke endre noe
2810
+ i Firefox.
2811
+
2812
+ `dom.browser.test.ts` sjekket at en verdi kan settes og fjernes, ikke at
2813
+ den betyr noe. Fieldset-feilen over hadde ligget der siden komponenten kom.
2814
+
2815
+ - **En vaktpost på at ingen byggefunksjon sender ut et navn React staver
2816
+ annerledes.** `react.browser.test.ts` kalte før ti byggefunksjoner for hånd
2817
+ og så bare etter `class` og `for`. Den går nå gjennom hver byggefunksjon i
2818
+ `/react`, med hver lovlige verdi funksjonen selv oppgir, og avviser hvert
2819
+ navn i Reacts egen tabell, også `readonly`, `maxlength` og `colspan`, som
2820
+ ingen byggefunksjon sender ut ennå. Den krever også at hver byggefunksjon i
2821
+ `fs` finnes i `/react`.
2822
+
2823
+ - **En vaktpost på at byggerne passer i JSX-deklarasjonene.**
2824
+ `src/jsx/typer.browser.test.ts` tilordner det byggerne sender ut til
2825
+ `JSX.IntrinsicElements`, og det er `typecheck:tests` som er prøven. Begge
2826
+ deler er offentlig API, og de kan gå fra hverandre uten at noe sier fra.
2827
+
2828
+ ## 0.7.0 (2026-09-22)
2829
+
2830
+ ### Brytende
2831
+
2832
+ - **`fs.dialog()` tar nå et valgobjekt og gir ett attributtsett per element.**
2833
+ Den ga før `{ class: "fs-dialog" }` til selve `<dialog>`. Nå heter kallet
2834
+ `fs.dialog({ titleId, open })` og gir `host`, `dialog`, `title`, `body` og
2835
+ `footer`, på samme form som `fs.popover()`. Klassenavnene ligger fortsatt på
2836
+ funksjonen: `fs.dialog.title` og de andre.
2837
+
2838
+ - **Dialogen har flyttet fra `css/` til `ramme/`,** siden den nå har en
2839
+ komponent. `@fristil/designsystem/dialog` peker derfor på `<fs-dialog>` og
2840
+ ikke lenger på byggefunksjonen; den hentes fra hovedinngangen, som for de
2841
+ andre sammensatte komponentene. `@fristil/designsystem/dialog.css` er
2842
+ uendret.
2843
+
2844
+ ### Lagt til
2845
+
2846
+ - **`<fs-dialog>` lar en server åpne en dialog.** Å åpne en `<dialog>` er et
2847
+ kall og ikke et attributt: bare `showModal()` flytter fokus inn, holder
2848
+ fokus inne i dialogen, lukker på Escape og gjør resten av siden
2849
+ utilgjengelig. `<dialog open>` er bare en boks på siden. En app som skriver
2850
+ HTML på serveren, i Datastar, htmx, en Go-mal eller en Razor-visning, kunne
2851
+ derfor ikke åpne en dialog i det hele tatt uten å skrive sitt eget skript
2852
+ ved siden av. Nå sier serveren `open` på `<fs-dialog>`, og komponenten gjør
2853
+ kallet.
2854
+
2855
+ Lukker brukeren dialogen, fjernes `open` fra verten igjen, så markupen sier
2856
+ det samme som skjermen. De to `open`-ene er ikke det samme: det på verten er
2857
+ serverens beskjed og er ikke fredet, for ellers kunne serveren aldri åpnet
2858
+ dialogen igjen etter første lukking. Det nettleseren setter på selve
2859
+ `<dialog>` når `showModal()` kalles, er derimot fredet, ellers river
2860
+ morfingen det bort og lukker dialogen i samme øyeblikk som den åpnet den.
2861
+ `morfing.browser.test.ts` kjører hele runden.
2862
+
2863
+ Komponenten melder fra med hendelsen `dialog-toggle`, som bærer `open` og
2864
+ `returnValue`, altså hvilken knapp som lukket dialogen. Den er `composed`,
2865
+ så den kommer ut av en skyggerot.
2866
+
2867
+ Komponenten gjør ingenting når den står løsrevet fra siden. En morfer
2868
+ bygger serverens utgave i et løsrevet tre før den sammenlignes, og et
2869
+ egendefinert element tas i bruk der også; uten den sperren kastet
2870
+ `showModal()` ved hver patch som rørte dialogen.
2871
+
2872
+ ### Rettet
2873
+
2874
+ - **`color-scheme` i `tokens.css`.** Uten den tegner nettleseren sine egne
2875
+ flater lyst uansett hva tokenene sier: nedtrekkslista til en `<select>`,
2876
+ rullefelt og kalenderpanelet i et datofelt. En side i mørkt tema fikk da en
2877
+ hvit liste midt i seg. `:root` sier nå `light dark`, så systemvalget
2878
+ avgjør, og `data-theme` setter den til `light` eller `dark` slik at
2879
+ nettleserens flater følger med når appen tvinger fram et tema.
2880
+
2881
+ Dette ble funnet i en demoapp, ikke i enhetstestene. Det er bare
2882
+ nettleserens eget utseende som røper det.
2883
+
2884
+ - **`<fs-field>` sier selv at det er en blokk.** Et egendefinert element er
2885
+ `display: inline` til noen sier noe annet, og feltet var det eneste som
2886
+ pakker inn andre elementer uten å si det. Barna er blokker, så det så
2887
+ riktig ut helt til noen ga elementet en avstand eller la det i et rutenett.
2888
+ `<fs-suggestion>` har alltid sagt det samme om seg selv.
2889
+
2890
+ ## 0.6.1 (2026-09-22)
2891
+
2892
+ ### Lagt til
2893
+
2894
+ - **`--semantic-focus-ring` samler fokusringen på ett sted.** Den sto skrevet
2895
+ ut med bredde og farge i tjue regler fordelt på atten stilark. Selektorene
2896
+ er forskjellige i hver komponent, og `outline-offset` skal være ulik, så det
2897
+ som faktisk gjentok seg var verdien. Nå skriver hver komponent
2898
+ `outline: var(--semantic-focus-ring)`, og en konsument som vil ha en annen
2899
+ fokusring endrer ett token framfor atten filer.
2900
+
2901
+ De tre stedene som med vilje har en annen farge, feiloppsummeringen og
2902
+ hopplenken, skriver `outline-color` etter kortformen og arver bredden. Da
2903
+ følger også de med hvis bredden endres.
2904
+
2905
+ En ny vaktpost i `pakke-css.browser.test.ts` avviser en `outline` med
2906
+ `solid` skrevet ut i et komponentstilark.
2907
+
2908
+ ## 0.6.0 (2026-09-22)
2909
+
2910
+ ### Brytende
2911
+
2912
+ - **`fs.popover()` sender ikke lenger ut `slot="trigger"`.** Attributtet var
2913
+ et levn fra den gangen komponenten hadde shadow DOM. Uten en skyggerot gjør
2914
+ `slot` ingenting i HTML, så det var en merkelapp som så ut som noe annet enn
2915
+ det var, og dokumentasjonen ba leseren skrive noe nettleseren ignorerte.
2916
+
2917
+ `<fs-popover>` kjenner nå igjen delene på koblingen som må være der uansett:
2918
+ panelet er det som har `popover`, og knappen er den som peker på panelet med
2919
+ `aria-controls`. Begge deler skriver `fs.popover()` fra før, så markup laget
2920
+ med byggefunksjonen virker uendret, og et `slot`-attributt som blir stående
2921
+ fra en server som ikke er oppdatert, gjør ingen skade.
2922
+
2923
+ Skriver du markupen for hånd, må knappen ha `aria-controls` med panelets
2924
+ `id`, og panelet må ha `popover`. Det er de samme to tingene
2925
+ tilgjengeligheten krever.
2926
+
2927
+ Typen `PopoverAttributes` har mistet `slot` fra `trigger`.
2928
+
2929
+ ## 0.5.3 (2026-09-22)
2930
+
2931
+ ### Rettet
2932
+
2933
+ - **Pakken lar seg importere på en server.** `class X extends HTMLElement`
2934
+ blir evaluert i det modulen lastes, og `HTMLElement` finnes bare i
2935
+ nettleseren. Ni av inngangspunktene stoppet derfor med «HTMLElement is not
2936
+ defined» på en server uten DOM, hovedinngangen `@fristil/designsystem`
2937
+ inkludert. Det gjorde `import { fs } from "@fristil/designsystem"` umulig i
2938
+ en Node- eller Bun-server, altså nøyaktig den linja «Kom i gang» ber leseren
2939
+ skrive, i nettopp den situasjonen systemet er bygd for.
2940
+
2941
+ Web-komponentene arver nå fra `HostElement` i det nye inngangspunktet
2942
+ `@fristil/designsystem/host-element`, som faller tilbake på en tom klasse
2943
+ når `HTMLElement` ikke finnes. `defineFs*` gjør ingenting når det ikke
2944
+ finnes noen `customElements` å registrere i, så et kall fra en modul som
2945
+ kjøres begge steder er trygt.
2946
+
2947
+ Ingen nettlesertest kunne se dette, for der finnes `HTMLElement`. En ny
2948
+ vaktpost, `scripts/sjekk-server-import.ts`, kjører i Bun uten DOM og
2949
+ importerer hver JavaScript-oppføring i `exports` fra `dist`.
2950
+
2951
+ ## 0.5.2 (2026-09-22)
2952
+
2953
+ ### Lagt til
2954
+
2955
+ - **`<select>` kan tegne nedtrekkslista inne i siden.** `fs.select({ picker:
2956
+ "styled" })` gir `data-picker="styled"`, og da bruker feltet
2957
+ `appearance: base-select`: lista blir et vanlig element i siden i stedet for
2958
+ et vindu fra operativsystemet, og får Fristils farger, avstander og skygge.
2959
+ Elementet er fortsatt en helt vanlig `<select>`, så innsending, tastatur og
2960
+ skjermleser er uendret.
2961
+
2962
+ Det må slås på, og det er med vilje. Chromium 148 og WebKit 26.4 har
2963
+ `appearance: base-select`; Firefox 150 har det ikke og viser nettleserens
2964
+ egen liste som før. Begge deler er testet i hver sin motor. I
2965
+ høykontrastmodus faller alle tre tilbake til nettleserens egen liste, siden
2966
+ systemfargene er de eneste som er garantert lesbare der.
2967
+
2968
+ Nye variabler: `--fs-select-option-padding` og
2969
+ `--fs-select-viewport-margin`. `--fs-select-radius` gjelder nå også lista.
2970
+
2971
+ ### Rettet
2972
+
2973
+ - **`FIELD_PRESERVED_ATTRIBUTES` dekker nå hjelpeteksten og feilmeldingen.**
2974
+ Skriver malen ingen id-er, lager `<fs-field>` dem selv, og
2975
+ `aria-describedby` på kontrollen peker på dem. `id` sto ikke i noen liste,
2976
+ så en morfing fjernet den, og koblingen pekte på noe som ikke fantes: verken
2977
+ hjelpeteksten eller feilmeldingen ble lest opp. Lista har nå en `help`-nøkkel,
2978
+ `error` har fått `id` og ledeteksten har fått `for`.
2979
+
2980
+ - **`<fs-error-summary>` tar fokus hver gang boksen vises på nytt.** Den tok
2981
+ det bare første gang. Mønsteret i en Datastar-app er at serveren sender
2982
+ boksen skjult og bare slår `hidden` av og på, mens lista står med de samme
2983
+ lenkene, og da nullstilte ingenting flagget. Andre gang det samme skjemaet
2984
+ feilet, ble brukeren stående i feltet uten å få vite hvorfor innsendingen
2985
+ ikke gikk gjennom. Flagget nullstilles nå når boksen skjules.
2986
+
2987
+ - **`<fs-suggestion>` leser hvilket alternativ som er markert fra markupen.**
2988
+ Komponenten holdt en egen teller ved siden av `aria-selected`. Satte
2989
+ serveren markeringen selv med `fs.suggestion({ activeIndex })`, hoppet
2990
+ første piltast til toppen av lista i stedet for til alternativet etter, og
2991
+ markeringen serveren nettopp sendte var borte.
2992
+
2993
+ - **Pil opp åpner lista, og går til det siste alternativet.** Den markerte før
2994
+ et alternativ uten å åpne lista, så `aria-activedescendant` pekte inn i noe
2995
+ feltet samtidig meldte som lukket, og skjermleseren leste opp et alternativ
2996
+ som ikke sto på skjermen. Uten noe markert fra før gikk den dessuten til det
2997
+ nest siste, ikke det siste.
2998
+
2999
+ - **`fs.suggestion().empty` bevarer `hidden` gjennom en oppdatering.**
3000
+ Komponenten skjuler «Ingen treff» når noe passer, men elementet hadde ingen
3001
+ `data-preserve-attr`, så teksten dukket opp igjen ved hver patch, også midt
3002
+ i en liste med treff. Byggefunksjonen sender nå `hidden` når serveren har
3003
+ noe å velge mellom, og navnet står i lista.
3004
+
3005
+ Begge ble funnet av en ny vaktpost, `morfing.browser.test.ts`, som kjører
3006
+ Datastars egen attributtsynkronisering mot ekte markup etter at komponenten
3007
+ har gjort jobben sin. Den dekker felt, forslagsfelt, faner og
3008
+ sprettoppvindu.
3009
+
3010
+ - **`.fs-select` snur riktig i språk som skrives fra høyre mot venstre.**
3011
+ Feltet holdt av plass til pila si med en `padding` på fire verdier, altså
3012
+ over, høyre, under og venstre. De to fysiske sidene snur ikke med språket,
3013
+ så i RTL lå plassen på feil side og teksten la seg oppå pila. Plassen settes
3014
+ nå logisk, og pila, som er to gradienter og derfor ikke har noen logisk
3015
+ variant, speilvendes med `:dir(rtl)`. Nedtrekkslista var ikke dekket av
3016
+ retningstesten i det hele tatt, og er det nå. En ny vaktpost i
3017
+ `pakke-css.browser.test.ts` avviser fysiske kortformer med fire verdier i
3018
+ alle stilark.
3019
+
3020
+ - **`fs.setAttributes` fjerner nå `data-size` og `data-picker` igjen.** Den
3021
+ rydder bare i en lukket liste av attributtnavn, og de to sto utenfor. En
3022
+ avatar eller en overskrift som gikk tilbake til standardstørrelsen beholdt
3023
+ altså `data-size` fra forrige kall, og fikk aldri standardutseendet sitt
3024
+ igjen. En ny vaktpost kaller hver byggefunksjon med hver lovlige verdi den
3025
+ selv oppgir, regner et attributt som valgfritt når det ikke er med i kallet
3026
+ uten argumenter, og krever at hvert av dem lar seg fjerne.
3027
+
3028
+ ## 0.5.1 (2026-09-21)
3029
+
3030
+ ### Rettet
3031
+
3032
+ - **React-inngangen døper nå også om `autocomplete` til `autoComplete`.**
3033
+ `fs.suggestion()` sender det ut på kontrollen, og React advarte «Invalid DOM
3034
+ property `autocomplete`» i konsollen for hvert forslagsfelt. En ny vaktpost
3035
+ kaller hver byggefunksjon, teller opp hvert attributtnavn de sender ut, og
3036
+ krever at ingen av dem er et navn React staver annerledes uten at
3037
+ `react.ts` døper det om.
3038
+
3039
+ - **`<fs-session-timeout>` bygger dialogen sin først når den skal vises.** Den
3040
+ lagde den i `connectedCallback`, altså før React rakk å hydrere, og
3041
+ hydreringen feilet med «server rendered HTML didn't match the client». Nå
3042
+ gjør den det samme som `<fs-toast>` og `<fs-connection-status>`: ingenting
3043
+ står i DOM-en før det trengs.
3044
+
3045
+ ### Endret
3046
+
3047
+ - **`fs.toast()` er dokumentert.** Byggefunksjonen fantes, men sto ikke på
3048
+ komponentsiden. Den gir tre attributtsett, og det er `region` du sprer på
3049
+ `<fs-toast>`, ikke hele objektet.
3050
+
3051
+
3052
+ ## 0.5.0 (2026-09-21)
3053
+
3054
+ ### Brytende
3055
+
3056
+ - **Serveren skriver markupen, komponentene fester bare oppførsel.** Seks
3057
+ komponenter satte tidligere klasser, roller og tilgjengelighetskoblinger på
3058
+ elementer serveren hadde sendt. Det virker ikke i en app med server-rendret
3059
+ HTML: Datastars morfing fjerner hvert attributt som ikke står i HTML-en
3060
+ serveren nettopp sendte, og React kan kaste bort noder et egendefinert
3061
+ element har lagt inn i et tre React eier. Testet med ekte Datastar mistet et
3062
+ `<fs-field>` både `aria-describedby`, klassen på ledeteksten og den skjulte
3063
+ feilmeldingen etter én patch, og fikk dem ikke tilbake.
3064
+
3065
+ Markupen skrives nå av nye byggere i `fs`: `fs.errorSummary()`,
3066
+ `fs.popover()`, `fs.tabs()`, `fs.suggestion()` og `fs.toast()`. Byggerne
3067
+ setter også `data-preserve-attr` for de attributtene komponenten endrer
3068
+ underveis, så du slipper å vite om det. Se «Server først» i dokumentasjonen.
3069
+
3070
+ - **`<fs-calendar>` og `<fs-date-field>` er fjernet.** Nettleserens eget panel
3071
+ i `<input type="date">` gjør jobben, og `fs.field()` sammen med
3072
+ `fs.input({ type: "date" })` gir et komplett datofelt. Mønstersiden «Dato i
3073
+ et skjema» viser oppsettet, med de to forskjellene vi har testet mellom nettleserne.
3074
+ `exports`-oppføringene `./calendar`, `./date-field` og `./date-field.css` er
3075
+ borte, og det samme er `--fs-calendar-*`- og `--fs-date-field-*`-variablene
3076
+ og alle `part`-navnene i kalenderen.
3077
+
3078
+ - **`<fs-suggestion>` er flyttet fra `frittstående` til `ramme`.** Den lagde
3079
+ tidligere hele feltet selv, så det fantes verken ledetekst eller
3080
+ inndatafelt før skriptet hadde kjørt, og ingenting ble med i innsendingen.
3081
+ Nå skriver serveren feltet og lista med `fs.suggestion()`.
3082
+
3083
+ - **`<fs-tabs>` har ingen `selected`- eller `label`-attributter lenger.**
3084
+ Hvilken fane som er valgt står i markupen serveren sendte. `<fs-error-summary>`
3085
+ har ikke lenger `heading`: overskriften skrives av serveren.
3086
+
3087
+ - **Ingen komponent bruker shadow DOM.** `::part()` er dermed ikke lenger en
3088
+ del av det offentlige API-et.
3089
+
3090
+ - **`<fs-field>` leser tilstanden fra markupen, ikke fra egne attributter.**
3091
+ Skrev serveren feltet med `fs.field()`, står `aria-invalid` og
3092
+ `data-required` allerede på elementene. Komponenten regnet tidligere ut sitt
3093
+ eget svar fra attributtene på verten, fant ingenting der, og fjernet det
3094
+ serveren nettopp hadde skrevet. De to halvdelene av API-et kranglet altså
3095
+ med hverandre når de ble brukt sammen. Den setter heller ikke lenger
3096
+ `aria-hidden` på feilmeldingen: `hidden` tar den allerede ut av
3097
+ tilgjengelighetstreet, og attributtet ga hydreringsfeil i React.
3098
+
3099
+ ### Lagt til
3100
+
3101
+ - **`<fs-session-timeout>`** varsler før en innlogget økt går ut, med en
3102
+ nedtelling som leses opp ved terskler i stedet for hvert sekund, og fokus
3103
+ som går tilbake dit brukeren var.
3104
+
3105
+ - **`<fs-connection-status>`** sier fra når forbindelsen til serveren er
3106
+ borte. `navigator.onLine` sier bare at maskinen har et nettverk, så
3107
+ `reportFailure()` og `reportSuccess()` lar appen melde fra selv.
3108
+
3109
+ - **Siden «Server først»** forklarer hvorfor markupen kommer fra serveren, med
3110
+ testene bak, og hva det betyr i TanStack Start, React Server Components og
3111
+ Datastar.
3112
+
3113
+ - **Mønstersiden «Dato i et skjema»** viser hvordan du ber om en dato uten en
3114
+ datovelger fra oss, med de to forskjellene vi har testet mellom nettleserne:
3115
+ WebKit krever skilletegnene, og Chromium tar imot feil rekkefølge som
3116
+ gyldig.
3117
+
3118
+ - **`FIELD_PRESERVED_ATTRIBUTES`** er lista serveren skal skrive som
3119
+ `data-preserve-attr` når den ikke kan kalle `fs.field()` selv.
3120
+
3121
+ ### Rettet
3122
+
3123
+ - **`<fs-toast duration="0">` lot ikke meldingene bli stående.** Attributtet
3124
+ ble lest som «ugyldig» og falt tilbake til seks sekunder, så bare
3125
+ `show(…, { duration: 0 })` virket.
3126
+
3127
+ - **`<fs-error-summary>` fikk ikke fokus når serveren bare tok bort `hidden`.**
3128
+ Komponenten så etter endringer i barna, ikke i attributtene, så en
3129
+ oppsummering som nettopp ble synlig ble stående uten fokus.
3130
+
3131
+ - **`<fs-error-summary>` finner nå feltet i sin egen rot.** Oppslaget gikk mot
3132
+ `document`, som ikke ser inn i en skyggerot, så lenken ble en vanlig
3133
+ ankerlenke uten fokusflytting når skjemaet lå inne i en annen komponent.
3134
+
3135
+ - **`FIELD_PRESERVED_ATTRIBUTES` dekker alle tre elementene.** Den dekket bare
3136
+ kontrollen, men komponenten legger også `fs-label` og markeringene på
3137
+ ledeteksten og skjuler feilmeldingen. Serveren må derfor skrive én liste per
3138
+ element, og konstanten har nå `label`, `control` og `error`.
3139
+
3140
+ - **`<fs-popover>` lot seg ikke åpne etter at den var flyttet i DOM-en.**
3141
+ Referansen til knappen ble stående, så lytteren ble aldri festet på nytt.
3142
+
3143
+ - **`fs.popover({ open: true })` sier det nå på verten.** Før satte den bare
3144
+ `aria-expanded` på knappen, så markupen meldte at panelet var åpent mens
3145
+ komponenten mente det var lukket.
3146
+
3147
+ - **`<fs-suggestion>` mistet musevalg etter at serveren sendte en ny liste.**
3148
+ Alternativene fikk bare lytteren sin når selve feltet var nytt.
3149
+
3150
+ ### Endret
3151
+
3152
+ - **React-inngangen døper også om `tabindex` til `tabIndex`,** og dekker de nye
3153
+ sammensatte byggerne. `fs.errorSummary()`, `fs.popover()`, `fs.tabs()`,
3154
+ `fs.suggestion()` og `fs.toast()` finnes nå også i
3155
+ `@fristil/designsystem/react`, der hvert attributtsett er døpt om for seg.
3156
+ Pakken har fortsatt ingen avhengighet til React: dette er ren omdøping av
3157
+ tre objektnøkler.
3158
+
3159
+ - **`lit` er ikke lenger en avhengighet.** Etter omleggingen rendrer ingen
3160
+ komponent sitt eget innhold, så Lit ble brukt til nesten ingenting.
3161
+ Komponentene er vanlige `HTMLElement`-klasser, og pakken har nå ingen
3162
+ avhengigheter i det hele tatt.
3163
+
3164
+ ## 0.4.0 (2026-09-21)
3165
+
3166
+ ### Endret
3167
+
3168
+ - `lit` er en vanlig avhengighet, ikke en valgfri `peerDependency`. Du trenger
3169
+ bare `npm install @fristil/designsystem`, og ikke lenger vite at Lit finnes.
3170
+ Bygget ditt er upåvirket: ingenting registreres ved import alene, så Lit
3171
+ kommer først med når du kaller en `defineFs*`.
3172
+
3173
+ ## 0.3.0 (2026-09-21)
3174
+
3175
+ ### Rettet
3176
+
3177
+ - `sjekk-eksport.ts` leser svaret fra `npm pack --json` i begge formene npm
3178
+ bruker. npm 11 svarer med en liste, npm 12 med et objekt der pakkenavnet er
3179
+ nøkkelen, og arbeidsflyten som publiserer henter alltid nyeste npm. Utgivelsen
3180
+ stoppet derfor på «{} is not iterable», med en feil som pekte på vår egen kode
3181
+ framfor på npm. Skriptet sier nå fra med npm sin egen feilmelding når
3182
+ pakkingen feiler.
3183
+
3184
+ - `bin` peker på `dist/cli.js` uten `./` foran. npm rettet det selv ved
3185
+ publisering og advarte om at navnet var «invalid and removed», som leste som
3186
+ om kommandolinja forsvant. Den var med hele tiden.
3187
+
3188
+ ## 0.2.0 (2026-09-21)
3189
+
3190
+ ### Lagt til
3191
+
3192
+ - `fristil overta <komponent>` kopierer kildekoden til én komponent inn i
3193
+ prosjektet ditt, og skriver om henvisningene ut av mappa. Til bruk når
3194
+ tilpasning gjennom CSS ikke strekker til. Kopien er din, og oppdateringer av
3195
+ pakken rører den ikke.
3196
+
3197
+ - `fristil --hjelp` (og `--help`, `-h`, `help`) skriver ut hva kommandoen kan.
3198
+ Uten argumenter gjorde den før et forsøk på å lage et tema, og klaget over
3199
+ manglende farger uten å nevne at `overta` fantes.
3200
+
3201
+ - Pakken kan publiseres på npm. Den har lisens (MIT), en README som blir
3202
+ forsiden på npm, og feltene `repository`, `homepage`, `bugs` og `keywords`.
3203
+ `publishConfig` sier `public`, siden en pakke med navnerom ellers blir
3204
+ privat, og `prepublishOnly` bygger, så en publisering fra et rent klon ikke
3205
+ kan komme ut uten `dist`.
3206
+
3207
+ - `bun run sjekk:pakke` kjører [publint](https://publint.dev) mot pakken, som
3208
+ en del av bygget. Den leser `exports` slik nettlesere, byggverktøy og TypeScript
3209
+ faktisk gjør det.
3210
+
3211
+ ### Rettet
3212
+
3213
+ - `types` står først i hver oppføring i `exports`. Betingelsene leses i
3214
+ rekkefølge, og `import` sto først, så TypeScript fant typene bare fordi
3215
+ `.d.ts`-fila lå ved siden av `.js`-fila. Stiene er de samme som før.
3216
+
3217
+ - Kommandolinja svarer med en forklaring i stedet for et stakkspor fra Node
3218
+ når kommandoen er ukjent, eller når temafila mangler eller ikke er JSON.
3219
+
3220
+ - Lista over valgte filer i `file-upload` brekker nå til flere linjer på smal
3221
+ skjerm. Raden var bredere enn en telefon når filnavnet var langt, og knappen
3222
+ «Fjern <filnavn>» ble klippet av.
3223
+ - `<fs-error-summary>` oppdaterer overskriften når `heading` endrer seg. Den
3224
+ ble bare skrevet første gang, så en oppsummering som talte ned fra to feil
3225
+ til én ble stående på to.
3226
+
3227
+ ## 0.1.0 (2026-09-21)
3228
+
3229
+ Første versjon.
3230
+
3231
+ ### Lagt til
3232
+
3233
+ - **Tokens** for farge, størrelse, typografi, radius og skygge, med semantiske
3234
+ navn (`--semantic-*`) over en råskala. Lyst og mørkt tema, og en egen fil for
3235
+ Tailwind 4.
3236
+ - **34 CSS-komponenter** som bare trenger en klasse og `data-*`-attributter:
3237
+ accordion, alert, avatar, badge, breadcrumbs, button, card, checkbox, dialog,
3238
+ divider, error-text, fieldset, file-upload, heading, help-text, input, label,
3239
+ link, list, pagination, paragraph, radio, search, select, skeleton,
3240
+ skip-link, spinner, sr-only, switch, table, tag, textarea, toggle-group og
3241
+ tooltip.
3242
+ - **Fire rammekomponenter** som kobler sammen markupen du selv skriver:
3243
+ `<fs-field>`, `<fs-error-summary>`, `<fs-popover>` og `<fs-tabs>`.
3244
+ - **Fire frittstående komponenter** som eier markupen og interaksjonen:
3245
+ `<fs-calendar>`, `<fs-date-field>`, `<fs-suggestion>` og `<fs-toast>`.
3246
+ - **`fs`-API-et**: én funksjon per komponent som tar et valgobjekt og gir
3247
+ attributter tilbake, med lovlige verdier og vakt hengt på funksjonen. En egen
3248
+ inngang for React (`@fristil/designsystem/react`) med `className` og
3249
+ `htmlFor`, og JSX-deklarasjoner for de egendefinerte elementene.
3250
+ - **`fristil`-kommandoen** som bygger et tema fra egne farger, sjekker
3251
+ kontrasten mot WCAG og skriver ut CSS-en.
3252
+ - **Tilpasning utenfra**: all CSS ligger i `@layer fristil`, form og størrelse
3253
+ leses fra `--fs-*`-variabler med tokenverdien som reserve, og komponenten med
3254
+ shadow DOM eksponerer delene sine med `::part()`.