@fristil/designsystem 0.2.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 (314) hide show
  1. package/CHANGELOG.md +85 -0
  2. package/LICENSE +21 -0
  3. package/README.md +101 -0
  4. package/dist/cli.d.ts +31 -0
  5. package/dist/cli.d.ts.map +1 -0
  6. package/dist/cli.js +261 -0
  7. package/dist/cli.js.map +1 -0
  8. package/dist/components/css/accordion/accordion.d.ts +37 -0
  9. package/dist/components/css/accordion/accordion.d.ts.map +1 -0
  10. package/dist/components/css/accordion/accordion.js +31 -0
  11. package/dist/components/css/accordion/accordion.js.map +1 -0
  12. package/dist/components/css/alert/alert.d.ts +35 -0
  13. package/dist/components/css/alert/alert.d.ts.map +1 -0
  14. package/dist/components/css/alert/alert.js +29 -0
  15. package/dist/components/css/alert/alert.js.map +1 -0
  16. package/dist/components/css/avatar/avatar.d.ts +40 -0
  17. package/dist/components/css/avatar/avatar.d.ts.map +1 -0
  18. package/dist/components/css/avatar/avatar.js +30 -0
  19. package/dist/components/css/avatar/avatar.js.map +1 -0
  20. package/dist/components/css/badge/badge.d.ts +24 -0
  21. package/dist/components/css/badge/badge.d.ts.map +1 -0
  22. package/dist/components/css/badge/badge.js +24 -0
  23. package/dist/components/css/badge/badge.js.map +1 -0
  24. package/dist/components/css/breadcrumbs/breadcrumbs.d.ts +25 -0
  25. package/dist/components/css/breadcrumbs/breadcrumbs.d.ts.map +1 -0
  26. package/dist/components/css/breadcrumbs/breadcrumbs.js +23 -0
  27. package/dist/components/css/breadcrumbs/breadcrumbs.js.map +1 -0
  28. package/dist/components/css/button/button.d.ts +26 -0
  29. package/dist/components/css/button/button.d.ts.map +1 -0
  30. package/dist/components/css/button/button.js +25 -0
  31. package/dist/components/css/button/button.js.map +1 -0
  32. package/dist/components/css/card/card.d.ts +36 -0
  33. package/dist/components/css/card/card.d.ts.map +1 -0
  34. package/dist/components/css/card/card.js +28 -0
  35. package/dist/components/css/card/card.js.map +1 -0
  36. package/dist/components/css/checkbox/checkbox.d.ts +32 -0
  37. package/dist/components/css/checkbox/checkbox.d.ts.map +1 -0
  38. package/dist/components/css/checkbox/checkbox.js +27 -0
  39. package/dist/components/css/checkbox/checkbox.js.map +1 -0
  40. package/dist/components/css/dialog/dialog.d.ts +30 -0
  41. package/dist/components/css/dialog/dialog.d.ts.map +1 -0
  42. package/dist/components/css/dialog/dialog.js +28 -0
  43. package/dist/components/css/dialog/dialog.js.map +1 -0
  44. package/dist/components/css/divider/divider.d.ts +28 -0
  45. package/dist/components/css/divider/divider.d.ts.map +1 -0
  46. package/dist/components/css/divider/divider.js +22 -0
  47. package/dist/components/css/divider/divider.js.map +1 -0
  48. package/dist/components/css/error-text/error-text.d.ts +24 -0
  49. package/dist/components/css/error-text/error-text.d.ts.map +1 -0
  50. package/dist/components/css/error-text/error-text.js +18 -0
  51. package/dist/components/css/error-text/error-text.js.map +1 -0
  52. package/dist/components/css/fieldset/fieldset.d.ts +55 -0
  53. package/dist/components/css/fieldset/fieldset.d.ts.map +1 -0
  54. package/dist/components/css/fieldset/fieldset.js +39 -0
  55. package/dist/components/css/fieldset/fieldset.js.map +1 -0
  56. package/dist/components/css/file-upload/file-upload.d.ts +41 -0
  57. package/dist/components/css/file-upload/file-upload.d.ts.map +1 -0
  58. package/dist/components/css/file-upload/file-upload.js +32 -0
  59. package/dist/components/css/file-upload/file-upload.js.map +1 -0
  60. package/dist/components/css/heading/heading.d.ts +29 -0
  61. package/dist/components/css/heading/heading.d.ts.map +1 -0
  62. package/dist/components/css/heading/heading.js +23 -0
  63. package/dist/components/css/heading/heading.js.map +1 -0
  64. package/dist/components/css/help-text/help-text.d.ts +24 -0
  65. package/dist/components/css/help-text/help-text.d.ts.map +1 -0
  66. package/dist/components/css/help-text/help-text.js +23 -0
  67. package/dist/components/css/help-text/help-text.js.map +1 -0
  68. package/dist/components/css/input/input.d.ts +39 -0
  69. package/dist/components/css/input/input.d.ts.map +1 -0
  70. package/dist/components/css/input/input.js +44 -0
  71. package/dist/components/css/input/input.js.map +1 -0
  72. package/dist/components/css/label/label.d.ts +33 -0
  73. package/dist/components/css/label/label.d.ts.map +1 -0
  74. package/dist/components/css/label/label.js +23 -0
  75. package/dist/components/css/label/label.js.map +1 -0
  76. package/dist/components/css/link/link.d.ts +21 -0
  77. package/dist/components/css/link/link.d.ts.map +1 -0
  78. package/dist/components/css/link/link.js +14 -0
  79. package/dist/components/css/link/link.js.map +1 -0
  80. package/dist/components/css/list/list.d.ts +29 -0
  81. package/dist/components/css/list/list.d.ts.map +1 -0
  82. package/dist/components/css/list/list.js +23 -0
  83. package/dist/components/css/list/list.js.map +1 -0
  84. package/dist/components/css/pagination/pagination.d.ts +27 -0
  85. package/dist/components/css/pagination/pagination.d.ts.map +1 -0
  86. package/dist/components/css/pagination/pagination.js +25 -0
  87. package/dist/components/css/pagination/pagination.js.map +1 -0
  88. package/dist/components/css/paragraph/paragraph.d.ts +36 -0
  89. package/dist/components/css/paragraph/paragraph.d.ts.map +1 -0
  90. package/dist/components/css/paragraph/paragraph.js +26 -0
  91. package/dist/components/css/paragraph/paragraph.js.map +1 -0
  92. package/dist/components/css/radio/radio.d.ts +32 -0
  93. package/dist/components/css/radio/radio.d.ts.map +1 -0
  94. package/dist/components/css/radio/radio.js +27 -0
  95. package/dist/components/css/radio/radio.js.map +1 -0
  96. package/dist/components/css/search/search.d.ts +33 -0
  97. package/dist/components/css/search/search.d.ts.map +1 -0
  98. package/dist/components/css/search/search.js +31 -0
  99. package/dist/components/css/search/search.js.map +1 -0
  100. package/dist/components/css/select/select.d.ts +23 -0
  101. package/dist/components/css/select/select.d.ts.map +1 -0
  102. package/dist/components/css/select/select.js +18 -0
  103. package/dist/components/css/select/select.js.map +1 -0
  104. package/dist/components/css/shared.d.ts +26 -0
  105. package/dist/components/css/shared.d.ts.map +1 -0
  106. package/dist/components/css/shared.js +34 -0
  107. package/dist/components/css/shared.js.map +1 -0
  108. package/dist/components/css/skeleton/skeleton.d.ts +29 -0
  109. package/dist/components/css/skeleton/skeleton.d.ts.map +1 -0
  110. package/dist/components/css/skeleton/skeleton.js +23 -0
  111. package/dist/components/css/skeleton/skeleton.js.map +1 -0
  112. package/dist/components/css/skip-link/skip-link.d.ts +18 -0
  113. package/dist/components/css/skip-link/skip-link.d.ts.map +1 -0
  114. package/dist/components/css/skip-link/skip-link.js +16 -0
  115. package/dist/components/css/skip-link/skip-link.js.map +1 -0
  116. package/dist/components/css/spinner/spinner.d.ts +33 -0
  117. package/dist/components/css/spinner/spinner.d.ts.map +1 -0
  118. package/dist/components/css/spinner/spinner.js +25 -0
  119. package/dist/components/css/spinner/spinner.js.map +1 -0
  120. package/dist/components/css/sr-only/sr-only.d.ts +17 -0
  121. package/dist/components/css/sr-only/sr-only.d.ts.map +1 -0
  122. package/dist/components/css/sr-only/sr-only.js +14 -0
  123. package/dist/components/css/sr-only/sr-only.js.map +1 -0
  124. package/dist/components/css/switch/switch.d.ts +33 -0
  125. package/dist/components/css/switch/switch.d.ts.map +1 -0
  126. package/dist/components/css/switch/switch.js +29 -0
  127. package/dist/components/css/switch/switch.js.map +1 -0
  128. package/dist/components/css/table/table.d.ts +36 -0
  129. package/dist/components/css/table/table.d.ts.map +1 -0
  130. package/dist/components/css/table/table.js +28 -0
  131. package/dist/components/css/table/table.js.map +1 -0
  132. package/dist/components/css/tag/tag.d.ts +35 -0
  133. package/dist/components/css/tag/tag.d.ts.map +1 -0
  134. package/dist/components/css/tag/tag.js +27 -0
  135. package/dist/components/css/tag/tag.js.map +1 -0
  136. package/dist/components/css/textarea/textarea.d.ts +23 -0
  137. package/dist/components/css/textarea/textarea.d.ts.map +1 -0
  138. package/dist/components/css/textarea/textarea.js +18 -0
  139. package/dist/components/css/textarea/textarea.js.map +1 -0
  140. package/dist/components/css/toggle-group/toggle-group.d.ts +31 -0
  141. package/dist/components/css/toggle-group/toggle-group.d.ts.map +1 -0
  142. package/dist/components/css/toggle-group/toggle-group.js +29 -0
  143. package/dist/components/css/toggle-group/toggle-group.js.map +1 -0
  144. package/dist/components/css/tooltip/tooltip.d.ts +30 -0
  145. package/dist/components/css/tooltip/tooltip.d.ts.map +1 -0
  146. package/dist/components/css/tooltip/tooltip.js +28 -0
  147. package/dist/components/css/tooltip/tooltip.js.map +1 -0
  148. package/dist/components/frittstaende/calendar/fs-calendar.d.ts +52 -0
  149. package/dist/components/frittstaende/calendar/fs-calendar.d.ts.map +1 -0
  150. package/dist/components/frittstaende/calendar/fs-calendar.js +462 -0
  151. package/dist/components/frittstaende/calendar/fs-calendar.js.map +1 -0
  152. package/dist/components/frittstaende/date-field/fs-date-field.d.ts +110 -0
  153. package/dist/components/frittstaende/date-field/fs-date-field.d.ts.map +1 -0
  154. package/dist/components/frittstaende/date-field/fs-date-field.js +264 -0
  155. package/dist/components/frittstaende/date-field/fs-date-field.js.map +1 -0
  156. package/dist/components/frittstaende/suggestion/fs-suggestion.d.ts +133 -0
  157. package/dist/components/frittstaende/suggestion/fs-suggestion.d.ts.map +1 -0
  158. package/dist/components/frittstaende/suggestion/fs-suggestion.js +280 -0
  159. package/dist/components/frittstaende/suggestion/fs-suggestion.js.map +1 -0
  160. package/dist/components/frittstaende/toast/fs-toast.d.ts +67 -0
  161. package/dist/components/frittstaende/toast/fs-toast.d.ts.map +1 -0
  162. package/dist/components/frittstaende/toast/fs-toast.js +108 -0
  163. package/dist/components/frittstaende/toast/fs-toast.js.map +1 -0
  164. package/dist/components/ramme/error-summary/fs-error-summary.d.ts +70 -0
  165. package/dist/components/ramme/error-summary/fs-error-summary.d.ts.map +1 -0
  166. package/dist/components/ramme/error-summary/fs-error-summary.js +155 -0
  167. package/dist/components/ramme/error-summary/fs-error-summary.js.map +1 -0
  168. package/dist/components/ramme/field/field-core.d.ts +70 -0
  169. package/dist/components/ramme/field/field-core.d.ts.map +1 -0
  170. package/dist/components/ramme/field/field-core.js +60 -0
  171. package/dist/components/ramme/field/field-core.js.map +1 -0
  172. package/dist/components/ramme/field/fs-field.d.ts +49 -0
  173. package/dist/components/ramme/field/fs-field.d.ts.map +1 -0
  174. package/dist/components/ramme/field/fs-field.js +118 -0
  175. package/dist/components/ramme/field/fs-field.js.map +1 -0
  176. package/dist/components/ramme/popover/fs-popover.d.ts +68 -0
  177. package/dist/components/ramme/popover/fs-popover.d.ts.map +1 -0
  178. package/dist/components/ramme/popover/fs-popover.js +167 -0
  179. package/dist/components/ramme/popover/fs-popover.js.map +1 -0
  180. package/dist/components/ramme/tabs/fs-tabs.d.ts +61 -0
  181. package/dist/components/ramme/tabs/fs-tabs.d.ts.map +1 -0
  182. package/dist/components/ramme/tabs/fs-tabs.js +156 -0
  183. package/dist/components/ramme/tabs/fs-tabs.js.map +1 -0
  184. package/dist/dom.d.ts +33 -0
  185. package/dist/dom.d.ts.map +1 -0
  186. package/dist/dom.js +77 -0
  187. package/dist/dom.js.map +1 -0
  188. package/dist/fs.d.ts +199 -0
  189. package/dist/fs.d.ts.map +1 -0
  190. package/dist/fs.js +128 -0
  191. package/dist/fs.js.map +1 -0
  192. package/dist/index.d.ts +84 -0
  193. package/dist/index.d.ts.map +1 -0
  194. package/dist/index.js +48 -0
  195. package/dist/index.js.map +1 -0
  196. package/dist/jsx/react.d.ts +127 -0
  197. package/dist/jsx/react.d.ts.map +1 -0
  198. package/dist/jsx/react.js +2 -0
  199. package/dist/jsx/react.js.map +1 -0
  200. package/dist/react.d.ts +207 -0
  201. package/dist/react.d.ts.map +1 -0
  202. package/dist/react.js +109 -0
  203. package/dist/react.js.map +1 -0
  204. package/dist/tailwind/preset.d.ts +120 -0
  205. package/dist/tailwind/preset.d.ts.map +1 -0
  206. package/dist/tailwind/preset.js +107 -0
  207. package/dist/tailwind/preset.js.map +1 -0
  208. package/dist/takeover.d.ts +79 -0
  209. package/dist/takeover.d.ts.map +1 -0
  210. package/dist/takeover.js +119 -0
  211. package/dist/takeover.js.map +1 -0
  212. package/dist/tokens/color.d.ts +85 -0
  213. package/dist/tokens/color.d.ts.map +1 -0
  214. package/dist/tokens/color.js +212 -0
  215. package/dist/tokens/color.js.map +1 -0
  216. package/dist/tokens/theme.d.ts +52 -0
  217. package/dist/tokens/theme.d.ts.map +1 -0
  218. package/dist/tokens/theme.js +197 -0
  219. package/dist/tokens/theme.js.map +1 -0
  220. package/dist/tokens/tokens.d.ts +210 -0
  221. package/dist/tokens/tokens.d.ts.map +1 -0
  222. package/dist/tokens/tokens.js +238 -0
  223. package/dist/tokens/tokens.js.map +1 -0
  224. package/package.json +334 -0
  225. package/src/components/css/accordion/accordion.css +74 -0
  226. package/src/components/css/accordion/accordion.ts +50 -0
  227. package/src/components/css/alert/alert.css +50 -0
  228. package/src/components/css/alert/alert.ts +48 -0
  229. package/src/components/css/avatar/avatar.css +64 -0
  230. package/src/components/css/avatar/avatar.ts +57 -0
  231. package/src/components/css/badge/badge.css +42 -0
  232. package/src/components/css/badge/badge.ts +43 -0
  233. package/src/components/css/breadcrumbs/breadcrumbs.css +50 -0
  234. package/src/components/css/breadcrumbs/breadcrumbs.ts +29 -0
  235. package/src/components/css/button/button.css +101 -0
  236. package/src/components/css/button/button.ts +44 -0
  237. package/src/components/css/card/card.css +48 -0
  238. package/src/components/css/card/card.ts +50 -0
  239. package/src/components/css/checkbox/checkbox.css +96 -0
  240. package/src/components/css/checkbox/checkbox.ts +50 -0
  241. package/src/components/css/dialog/dialog.css +50 -0
  242. package/src/components/css/dialog/dialog.ts +36 -0
  243. package/src/components/css/divider/divider.css +18 -0
  244. package/src/components/css/divider/divider.ts +41 -0
  245. package/src/components/css/error-text/error-text.css +21 -0
  246. package/src/components/css/error-text/error-text.ts +37 -0
  247. package/src/components/css/fieldset/fieldset.css +57 -0
  248. package/src/components/css/fieldset/fieldset.ts +87 -0
  249. package/src/components/css/file-upload/file-upload.css +92 -0
  250. package/src/components/css/file-upload/file-upload.ts +65 -0
  251. package/src/components/css/heading/heading.css +33 -0
  252. package/src/components/css/heading/heading.ts +42 -0
  253. package/src/components/css/help-text/help-text.css +24 -0
  254. package/src/components/css/help-text/help-text.ts +42 -0
  255. package/src/components/css/input/input.css +131 -0
  256. package/src/components/css/input/input.ts +76 -0
  257. package/src/components/css/label/label.css +34 -0
  258. package/src/components/css/label/label.ts +51 -0
  259. package/src/components/css/link/link.css +36 -0
  260. package/src/components/css/link/link.ts +29 -0
  261. package/src/components/css/list/list.css +40 -0
  262. package/src/components/css/list/list.ts +42 -0
  263. package/src/components/css/pagination/pagination.css +77 -0
  264. package/src/components/css/pagination/pagination.ts +33 -0
  265. package/src/components/css/paragraph/paragraph.css +31 -0
  266. package/src/components/css/paragraph/paragraph.ts +53 -0
  267. package/src/components/css/radio/radio.css +76 -0
  268. package/src/components/css/radio/radio.ts +50 -0
  269. package/src/components/css/search/search.css +49 -0
  270. package/src/components/css/search/search.ts +52 -0
  271. package/src/components/css/select/select.css +95 -0
  272. package/src/components/css/select/select.ts +40 -0
  273. package/src/components/css/shared.ts +40 -0
  274. package/src/components/css/skeleton/skeleton.css +49 -0
  275. package/src/components/css/skeleton/skeleton.ts +43 -0
  276. package/src/components/css/skip-link/skip-link.css +36 -0
  277. package/src/components/css/skip-link/skip-link.ts +22 -0
  278. package/src/components/css/spinner/spinner.css +48 -0
  279. package/src/components/css/spinner/spinner.ts +48 -0
  280. package/src/components/css/sr-only/sr-only.css +16 -0
  281. package/src/components/css/sr-only/sr-only.ts +18 -0
  282. package/src/components/css/switch/switch.css +101 -0
  283. package/src/components/css/switch/switch.ts +46 -0
  284. package/src/components/css/table/table.css +63 -0
  285. package/src/components/css/table/table.ts +50 -0
  286. package/src/components/css/tag/tag.css +58 -0
  287. package/src/components/css/tag/tag.ts +49 -0
  288. package/src/components/css/textarea/textarea.css +71 -0
  289. package/src/components/css/textarea/textarea.ts +40 -0
  290. package/src/components/css/toggle-group/toggle-group.css +82 -0
  291. package/src/components/css/toggle-group/toggle-group.ts +37 -0
  292. package/src/components/css/tooltip/tooltip.css +47 -0
  293. package/src/components/css/tooltip/tooltip.ts +36 -0
  294. package/src/components/frittstaende/calendar/fs-calendar.ts +525 -0
  295. package/src/components/frittstaende/date-field/date-field.css +54 -0
  296. package/src/components/frittstaende/date-field/fs-date-field.ts +309 -0
  297. package/src/components/frittstaende/suggestion/fs-suggestion.ts +333 -0
  298. package/src/components/frittstaende/suggestion/suggestion.css +63 -0
  299. package/src/components/frittstaende/toast/fs-toast.ts +146 -0
  300. package/src/components/frittstaende/toast/toast.css +71 -0
  301. package/src/components/ramme/error-summary/error-summary.css +45 -0
  302. package/src/components/ramme/error-summary/fs-error-summary.ts +197 -0
  303. package/src/components/ramme/field/field-core.ts +132 -0
  304. package/src/components/ramme/field/field.css +6 -0
  305. package/src/components/ramme/field/fs-field.ts +154 -0
  306. package/src/components/ramme/popover/fs-popover.ts +205 -0
  307. package/src/components/ramme/popover/popover.css +34 -0
  308. package/src/components/ramme/tabs/fs-tabs.ts +182 -0
  309. package/src/components/ramme/tabs/tabs.css +68 -0
  310. package/src/tailwind/tailwind.css +134 -0
  311. package/src/tokens/color.ts +267 -0
  312. package/src/tokens/theme.ts +350 -0
  313. package/src/tokens/tokens.css +214 -0
  314. package/src/tokens/tokens.ts +293 -0
@@ -0,0 +1,42 @@
1
+ import { attributes, createGuard } from "../shared.js"
2
+
3
+ export const HEADING_CLASS = "fs-heading" as const
4
+
5
+ export const headingSizes = ["xs", "s", "m", "l", "xl", "mega"] as const
6
+
7
+ export type HeadingSize = (typeof headingSizes)[number]
8
+ export type NonDefaultHeadingSize = Exclude<HeadingSize, "l">
9
+
10
+ export type HeadingOptions = {
11
+ /** Visuell størrelse. Standard: `l`. */
12
+ size?: HeadingSize
13
+ }
14
+
15
+ export type HeadingAttributes = {
16
+ class: typeof HEADING_CLASS
17
+ "data-size"?: NonDefaultHeadingSize
18
+ }
19
+
20
+ /**
21
+ * Attributtene for en overskrift.
22
+ *
23
+ * Størrelsen og nivået er to forskjellige ting. Nivået er `<h1>` til `<h6>`
24
+ * og bestemmer strukturen skjermlesere navigerer etter; `size` bestemmer bare
25
+ * hvor stor teksten ser ut. En `<h2>` som skal se liten ut er riktig; en
26
+ * `<h4>` valgt fordi den var liten nok, hopper over et nivå i strukturen.
27
+ *
28
+ * ```ts
29
+ * <h2 {...heading({ size: "m" })}>Vedlegg</h2>
30
+ * ```
31
+ */
32
+ export const heading = Object.assign(
33
+ ({ size = "l" }: HeadingOptions = {}): HeadingAttributes =>
34
+ attributes({
35
+ class: HEADING_CLASS,
36
+ "data-size": size === "l" ? undefined : size,
37
+ }),
38
+ {
39
+ sizes: headingSizes,
40
+ isSize: createGuard(headingSizes),
41
+ },
42
+ )
@@ -0,0 +1,24 @@
1
+ @layer fristil {
2
+ .fs-help-text {
3
+ display: block;
4
+ /* Hele margin settes, ikke bare toppen: uten dette arver <p>
5
+ nettleserens 1em i bunn hos konsumenter uten reset. */
6
+ margin: var(--size-1) 0 0;
7
+ font-family: inherit;
8
+ font-size: var(--font-size-xs);
9
+ line-height: 1.4;
10
+ color: var(--semantic-muted-foreground);
11
+ }
12
+
13
+ .fs-help-text[data-variant="default"] {
14
+ color: var(--semantic-page-foreground);
15
+ }
16
+
17
+ .fs-help-text[data-variant="success"] {
18
+ color: var(--semantic-success-foreground);
19
+ }
20
+
21
+ .fs-help-text[data-variant="warning"] {
22
+ color: var(--semantic-warning-foreground);
23
+ }
24
+ }
@@ -0,0 +1,42 @@
1
+ import { attributes, createGuard } from "../shared.js"
2
+
3
+ export const HELP_TEXT_CLASS = "fs-help-text" as const
4
+
5
+ export const helpTextVariants = [
6
+ "muted",
7
+ "default",
8
+ "success",
9
+ "warning",
10
+ ] as const
11
+
12
+ export type HelpTextVariant = (typeof helpTextVariants)[number]
13
+ export type NonDefaultHelpTextVariant = Exclude<HelpTextVariant, "muted">
14
+
15
+ export type HelpTextOptions = {
16
+ /** Hvor mye teksten skal rope. Standard: `muted`. */
17
+ variant?: HelpTextVariant
18
+ }
19
+
20
+ export type HelpTextAttributes = {
21
+ class: typeof HELP_TEXT_CLASS
22
+ "data-variant"?: NonDefaultHelpTextVariant
23
+ }
24
+
25
+ /**
26
+ * Attributtene for hjelpetekst under et felt.
27
+ *
28
+ * ```ts
29
+ * <p {...helpText({ variant: "warning" })}>…</p>
30
+ * ```
31
+ */
32
+ export const helpText = Object.assign(
33
+ ({ variant = "muted" }: HelpTextOptions = {}): HelpTextAttributes =>
34
+ attributes({
35
+ class: HELP_TEXT_CLASS,
36
+ "data-variant": variant === "muted" ? undefined : variant,
37
+ }),
38
+ {
39
+ variants: helpTextVariants,
40
+ isVariant: createGuard(helpTextVariants),
41
+ },
42
+ )
@@ -0,0 +1,131 @@
1
+ @layer fristil {
2
+ .fs-input {
3
+ /* Designsystemet kan ikke regne med at konsumenten har en CSS-reset.
4
+ Uten border-box legges padding og ramme utenpå width, og feltet
5
+ blir bredere enn boksen det ligger i. */
6
+ box-sizing: border-box;
7
+
8
+ display: block;
9
+ width: 100%;
10
+ padding: var(--fs-input-padding, var(--size-2) var(--size-3));
11
+ font-family: inherit;
12
+ font-size: var(--font-size-m);
13
+ line-height: 1.5;
14
+ color: var(--semantic-page-foreground);
15
+ background: var(--semantic-page-background);
16
+ border: 1px solid var(--semantic-field-border);
17
+ border-radius: var(--fs-input-radius, var(--size-1));
18
+ }
19
+
20
+ .fs-input::placeholder {
21
+ color: var(--semantic-muted-foreground);
22
+ }
23
+
24
+ .fs-input:hover {
25
+ border-color: var(--semantic-field-border-hover);
26
+ }
27
+
28
+ .fs-input:focus-visible {
29
+ outline: 2px solid var(--semantic-interactive-main);
30
+ outline-offset: 2px;
31
+ border-color: var(--semantic-interactive-main);
32
+ }
33
+
34
+ .fs-input:disabled,
35
+ .fs-input[aria-disabled="true"] {
36
+ background: var(--semantic-disabled-background);
37
+ color: var(--semantic-disabled-foreground);
38
+ border-color: var(--semantic-disabled-background);
39
+ /* Ingen pointer-events: none her. Nettleseren blokkerer allerede et
40
+ deaktivert felt, og pointer-events ville bare hindret markøren i å
41
+ vises. Elementet blir da ikke truffet av musa i det hele tatt. */
42
+ cursor: not-allowed;
43
+ }
44
+
45
+ /* aria-disabled er bare en opplysning til skjermlesere; elementet virker
46
+ fortsatt. Her stopper pointer-events musa, på bekostning av markøren.
47
+ Skal feltet faktisk være av, bruk `disabled` eller `readonly`. */
48
+ .fs-input[aria-disabled="true"] {
49
+ pointer-events: none;
50
+ }
51
+
52
+ .fs-input[data-state="invalid"] {
53
+ border-color: var(--semantic-danger-foreground);
54
+ background: var(--semantic-danger-background);
55
+ }
56
+
57
+ .fs-input[data-state="invalid"]:focus-visible {
58
+ outline-color: var(--semantic-danger-foreground);
59
+ }
60
+
61
+ .fs-input[data-state="success"] {
62
+ border-color: var(--semantic-success-foreground);
63
+ background: var(--semantic-success-background);
64
+ }
65
+
66
+ .fs-input[data-state="success"]:focus-visible {
67
+ outline-color: var(--semantic-success-foreground);
68
+ }
69
+
70
+ .fs-input[data-variant="date"],
71
+ .fs-input[data-variant="time"],
72
+ .fs-input[data-variant="datetime-local"] {
73
+ position: relative;
74
+ min-height: calc(var(--size-4) * 2.5);
75
+ font-variant-numeric: tabular-nums;
76
+ letter-spacing: 0.01em;
77
+ padding-inline-end: calc(var(--size-6) + var(--size-2));
78
+ background-repeat: no-repeat;
79
+ background-position: right var(--size-2) center;
80
+ background-size: 0.95rem 0.95rem;
81
+ }
82
+
83
+ /* background-position har ingen logisk variant, så retningen må snus for
84
+ hånd. Alt annet her er logiske egenskaper, som snur av seg selv. */
85
+ .fs-input[data-variant="date"]:dir(rtl),
86
+ .fs-input[data-variant="time"]:dir(rtl),
87
+ .fs-input[data-variant="datetime-local"]:dir(rtl) {
88
+ background-position: left var(--size-2) center;
89
+ }
90
+
91
+ .fs-input[data-variant="date"],
92
+ .fs-input[data-variant="datetime-local"] {
93
+ background-image: var(--semantic-icon-calendar);
94
+ }
95
+
96
+ .fs-input[data-variant="time"] {
97
+ background-image: var(--semantic-icon-clock);
98
+ }
99
+
100
+ .fs-input[data-variant="date"]::-webkit-calendar-picker-indicator,
101
+ .fs-input[data-variant="time"]::-webkit-calendar-picker-indicator,
102
+ .fs-input[data-variant="datetime-local"]::-webkit-calendar-picker-indicator {
103
+ position: absolute;
104
+ inset-inline-end: var(--size-2);
105
+ top: 50%;
106
+ transform: translateY(-50%);
107
+ opacity: 0;
108
+ width: var(--size-6);
109
+ height: var(--size-6);
110
+ min-width: var(--size-6);
111
+ min-height: var(--size-6);
112
+ cursor: pointer;
113
+ }
114
+
115
+ /* Høykontrast: ikonet er malt inn i bildet med fast farge, og kan bli
116
+ usynlig i et mørkt tema. Nettleserens egen knapp er skjult bak vårt ikon,
117
+ og hentes fram igjen her. */
118
+ @media (forced-colors: active) {
119
+ .fs-input[data-variant="date"],
120
+ .fs-input[data-variant="time"],
121
+ .fs-input[data-variant="datetime-local"] {
122
+ background-image: none;
123
+ }
124
+
125
+ .fs-input[data-variant="date"]::-webkit-calendar-picker-indicator,
126
+ .fs-input[data-variant="time"]::-webkit-calendar-picker-indicator,
127
+ .fs-input[data-variant="datetime-local"]::-webkit-calendar-picker-indicator {
128
+ opacity: 1;
129
+ }
130
+ }
131
+ }
@@ -0,0 +1,76 @@
1
+ import {
2
+ attributes,
3
+ createGuard,
4
+ type FieldState,
5
+ fieldStates,
6
+ isFieldState,
7
+ type NonDefaultFieldState,
8
+ } from "../shared.js"
9
+
10
+ export const INPUT_CLASS = "fs-input" as const
11
+
12
+ export const inputTypes = [
13
+ "text",
14
+ "password",
15
+ "email",
16
+ "number",
17
+ "date",
18
+ "datetime-local",
19
+ "week",
20
+ "month",
21
+ "tel",
22
+ "url",
23
+ "search",
24
+ "time",
25
+ ] as const
26
+
27
+ export type InputType = (typeof inputTypes)[number]
28
+
29
+ /** Typene som får et ikon i feltet, og som åpner nettleserens egen velger. */
30
+ const TYPER_MED_IKON = ["date", "time", "datetime-local"] as const
31
+ type TypeMedIkon = (typeof TYPER_MED_IKON)[number]
32
+
33
+ export type InputOptions = {
34
+ /** HTML-typen. Settes som `type`, og styrer ikonet. Standard: `text`. */
35
+ type?: InputType
36
+ /** Valideringstilstand. Standard: `default`. */
37
+ state?: FieldState
38
+ }
39
+
40
+ export type InputAttributes = {
41
+ class: typeof INPUT_CLASS
42
+ type: InputType
43
+ "data-variant"?: TypeMedIkon
44
+ "data-state"?: NonDefaultFieldState
45
+ "aria-invalid"?: "true"
46
+ }
47
+
48
+ /**
49
+ * Attributtene for et tekstfelt.
50
+ *
51
+ * `type` setter både HTML-typen og ikonvarianten, så de to kan ikke komme i
52
+ * utakt. Skriver du dem for hånd, er det lett å få et datofelt som ser ut
53
+ * som et datofelt, men der ingenting skjer når brukeren trykker på ikonet.
54
+ *
55
+ * ```ts
56
+ * <input {...input({ type: "date", state: "invalid" })} />
57
+ * ```
58
+ */
59
+ export const input = Object.assign(
60
+ ({ type = "text", state = "default" }: InputOptions = {}): InputAttributes =>
61
+ attributes({
62
+ class: INPUT_CLASS,
63
+ type,
64
+ "data-variant": (TYPER_MED_IKON as readonly string[]).includes(type)
65
+ ? (type as TypeMedIkon)
66
+ : undefined,
67
+ "data-state": state === "default" ? undefined : state,
68
+ "aria-invalid": state === "invalid" ? ("true" as const) : undefined,
69
+ }),
70
+ {
71
+ types: inputTypes,
72
+ isType: createGuard(inputTypes),
73
+ states: fieldStates,
74
+ isState: isFieldState,
75
+ },
76
+ )
@@ -0,0 +1,34 @@
1
+ @layer fristil {
2
+ .fs-label {
3
+ display: block;
4
+ font-family: inherit;
5
+ font-size: var(--font-size-s);
6
+ font-weight: 600;
7
+ line-height: 1.5;
8
+ color: var(--semantic-page-foreground);
9
+ }
10
+
11
+ .fs-label[data-required]::after {
12
+ content: " *";
13
+ color: var(--semantic-danger-foreground);
14
+ font-weight: 700;
15
+ }
16
+
17
+ .fs-label[data-required="text"]::after {
18
+ content: " (påkrevd)";
19
+ color: var(--semantic-danger-foreground);
20
+ font-weight: 400;
21
+ font-size: var(--font-size-xs);
22
+ }
23
+
24
+ .fs-label[data-optional]::after {
25
+ content: " (valgfri)";
26
+ color: var(--semantic-disabled-foreground);
27
+ font-weight: 400;
28
+ font-size: var(--font-size-xs);
29
+ }
30
+
31
+ .fs-label[aria-disabled="true"] {
32
+ color: var(--semantic-muted-foreground);
33
+ }
34
+ }
@@ -0,0 +1,51 @@
1
+ import {
2
+ attributes,
3
+ createGuard,
4
+ type RequiredMarker,
5
+ requiredMarkers,
6
+ } from "../shared.js"
7
+
8
+ export const LABEL_CLASS = "fs-label" as const
9
+
10
+ export type { RequiredMarker }
11
+ export { requiredMarkers }
12
+
13
+ export type LabelOptions = {
14
+ /** Marker feltet som påkrevd, med stjerne eller med ordet «(påkrevd)». */
15
+ required?: RequiredMarker
16
+ /** Marker feltet som valgfritt. Velg én av `required` og `optional`. */
17
+ optional?: boolean
18
+ /** Demper teksten når kontrollen er slått av. */
19
+ disabled?: boolean
20
+ }
21
+
22
+ export type LabelAttributes = {
23
+ class: typeof LABEL_CLASS
24
+ "data-required"?: RequiredMarker
25
+ "data-optional"?: ""
26
+ "aria-disabled"?: "true"
27
+ }
28
+
29
+ /**
30
+ * Attributtene for en ledetekst.
31
+ *
32
+ * Markeringen er bare visuell, så sett `required` på selve feltet i tillegg,
33
+ * ellers får skjermleseren ikke vite at det må fylles ut.
34
+ *
35
+ * ```ts
36
+ * <label {...label({ required: "symbol" })} htmlFor="epost">E-postadresse</label>
37
+ * ```
38
+ */
39
+ export const label = Object.assign(
40
+ ({ required, optional, disabled }: LabelOptions = {}): LabelAttributes =>
41
+ attributes({
42
+ class: LABEL_CLASS,
43
+ "data-required": required,
44
+ "data-optional": optional && !required ? ("" as const) : undefined,
45
+ "aria-disabled": disabled ? ("true" as const) : undefined,
46
+ }),
47
+ {
48
+ markers: requiredMarkers,
49
+ isMarker: createGuard(requiredMarkers),
50
+ },
51
+ )
@@ -0,0 +1,36 @@
1
+ @layer fristil {
2
+ .fs-link {
3
+ color: var(--semantic-interactive-main);
4
+ font-family: inherit;
5
+ font-size: inherit;
6
+ line-height: 1.5;
7
+ text-decoration-line: underline;
8
+ text-decoration-thickness: 0.08em;
9
+ text-underline-offset: 0.14em;
10
+ text-decoration-color: currentColor;
11
+ transition:
12
+ color 120ms ease,
13
+ text-decoration-color 120ms ease;
14
+ }
15
+
16
+ .fs-link:hover {
17
+ color: var(--semantic-interactive-foreground);
18
+ }
19
+
20
+ .fs-link:visited {
21
+ color: var(--semantic-interactive-visited);
22
+ }
23
+
24
+ .fs-link:focus-visible {
25
+ outline: 2px solid var(--semantic-interactive-main);
26
+ outline-offset: 2px;
27
+ border-radius: var(--size-0-5);
28
+ }
29
+
30
+ .fs-link[aria-disabled="true"] {
31
+ color: var(--semantic-muted-foreground);
32
+ text-decoration: none;
33
+ cursor: not-allowed;
34
+ pointer-events: none;
35
+ }
36
+ }
@@ -0,0 +1,29 @@
1
+ import { attributes } from "../shared.js"
2
+
3
+ export const LINK_CLASS = "fs-link" as const
4
+
5
+ export type LinkOptions = {
6
+ /**
7
+ * Lenken er ikke klikkbar ennå. Gir `aria-disabled` framfor å fjerne
8
+ * `href`, slik at skjermlesere fortsatt finner den og kan si fra.
9
+ */
10
+ disabled?: boolean
11
+ }
12
+
13
+ export type LinkAttributes = {
14
+ class: typeof LINK_CLASS
15
+ "aria-disabled"?: "true"
16
+ }
17
+
18
+ /**
19
+ * Attributtene for en lenke.
20
+ *
21
+ * ```ts
22
+ * <a {...link()} href="/kvittering">Se kvittering</a>
23
+ * ```
24
+ */
25
+ export const link = ({ disabled = false }: LinkOptions = {}): LinkAttributes =>
26
+ attributes({
27
+ class: LINK_CLASS,
28
+ "aria-disabled": disabled ? ("true" as const) : undefined,
29
+ })
@@ -0,0 +1,40 @@
1
+ @layer fristil {
2
+ .fs-list {
3
+ box-sizing: border-box;
4
+ margin: 0;
5
+ padding-inline-start: var(--size-5);
6
+ font-family: inherit;
7
+ font-size: var(--font-size-m);
8
+ line-height: 1.6;
9
+ color: var(--semantic-page-foreground);
10
+ }
11
+
12
+ .fs-list li + li {
13
+ margin-block-start: var(--fs-list-gap, var(--size-1));
14
+ }
15
+
16
+ .fs-list .fs-list {
17
+ margin-block-start: var(--fs-list-gap, var(--size-1));
18
+ }
19
+
20
+ /* Uten punkter og innrykk, for lister som bærer sin egen struktur, som
21
+ rader med kort. Listen blir værende en liste for skjermlesere. */
22
+ .fs-list[data-variant="plain"] {
23
+ list-style: none;
24
+ padding-inline-start: 0;
25
+ }
26
+
27
+ .fs-list[data-variant="divided"] {
28
+ list-style: none;
29
+ padding-inline-start: 0;
30
+ }
31
+
32
+ .fs-list[data-variant="divided"] li + li {
33
+ margin-block-start: 0;
34
+ border-block-start: 1px solid var(--semantic-divider-30);
35
+ }
36
+
37
+ .fs-list[data-variant="divided"] li {
38
+ padding-block: var(--fs-list-gap, var(--size-2));
39
+ }
40
+ }
@@ -0,0 +1,42 @@
1
+ import { attributes, createGuard } from "../shared.js"
2
+
3
+ export const LIST_CLASS = "fs-list" as const
4
+
5
+ export const listVariants = ["default", "plain", "divided"] as const
6
+
7
+ export type ListVariant = (typeof listVariants)[number]
8
+ export type NonDefaultListVariant = Exclude<ListVariant, "default">
9
+
10
+ export type ListOptions = {
11
+ /** Punkter, rene rader, eller rader med strek mellom. Standard: `default`. */
12
+ variant?: ListVariant
13
+ }
14
+
15
+ export type ListAttributes = {
16
+ class: typeof LIST_CLASS
17
+ "data-variant"?: NonDefaultListVariant
18
+ }
19
+
20
+ /**
21
+ * Attributtene for en liste.
22
+ *
23
+ * Klassen hører på `<ul>` eller `<ol>`. `plain` og `divided` fjerner punktene
24
+ * visuelt, men listen blir værende en liste, så skjermleseren sier fortsatt
25
+ * hvor mange elementer den har. Det er grunnen til at varianten finnes: uten
26
+ * den ville folk brukt `<div>` for å bli kvitt punktene.
27
+ *
28
+ * ```ts
29
+ * <ul {...list({ variant: "divided" })}>…</ul>
30
+ * ```
31
+ */
32
+ export const list = Object.assign(
33
+ ({ variant = "default" }: ListOptions = {}): ListAttributes =>
34
+ attributes({
35
+ class: LIST_CLASS,
36
+ "data-variant": variant === "default" ? undefined : variant,
37
+ }),
38
+ {
39
+ variants: listVariants,
40
+ isVariant: createGuard(listVariants),
41
+ },
42
+ )
@@ -0,0 +1,77 @@
1
+ @layer fristil {
2
+ .fs-pagination {
3
+ box-sizing: border-box;
4
+ display: flex;
5
+ flex-wrap: wrap;
6
+ align-items: center;
7
+ gap: var(--fs-pagination-gap, var(--size-1));
8
+ margin: 0;
9
+ padding: 0;
10
+ list-style: none;
11
+ font-family: inherit;
12
+ font-size: var(--font-size-m);
13
+ }
14
+
15
+ .fs-pagination a,
16
+ .fs-pagination button {
17
+ box-sizing: border-box;
18
+ display: inline-flex;
19
+ align-items: center;
20
+ justify-content: center;
21
+ /* 40 piksler i begge retninger. WCAG 2.2 krever 24, og et nummer i en
22
+ lang rekke er lett å bomme på med finger. */
23
+ min-inline-size: var(--fs-pagination-size, var(--size-10));
24
+ min-block-size: var(--fs-pagination-size, var(--size-10));
25
+ padding-inline: var(--size-2);
26
+ font-family: inherit;
27
+ font-size: inherit;
28
+ line-height: 1;
29
+ color: var(--semantic-interactive-main);
30
+ background: transparent;
31
+ border: 1px solid transparent;
32
+ border-radius: var(--fs-pagination-radius, var(--size-1));
33
+ text-decoration: none;
34
+ cursor: pointer;
35
+ }
36
+
37
+ .fs-pagination a:hover,
38
+ .fs-pagination button:hover:not(:disabled) {
39
+ background: var(--semantic-interactive-background);
40
+ }
41
+
42
+ .fs-pagination a:focus-visible,
43
+ .fs-pagination button:focus-visible {
44
+ outline: 2px solid var(--semantic-interactive-main);
45
+ outline-offset: 2px;
46
+ }
47
+
48
+ .fs-pagination [aria-current="page"] {
49
+ color: var(--semantic-interactive-contrast);
50
+ background: var(--semantic-interactive-main);
51
+ border-color: var(--semantic-interactive-main);
52
+ font-weight: 600;
53
+ }
54
+
55
+ .fs-pagination button:disabled,
56
+ .fs-pagination [aria-disabled="true"] {
57
+ color: var(--semantic-disabled-foreground);
58
+ cursor: not-allowed;
59
+ }
60
+
61
+ /* Hoppet i nummerrekken, som «1 2 … 9 10». */
62
+ .fs-pagination__gap {
63
+ display: inline-flex;
64
+ align-items: center;
65
+ justify-content: center;
66
+ min-inline-size: var(--fs-pagination-size, var(--size-10));
67
+ color: var(--semantic-muted-foreground);
68
+ }
69
+
70
+ /* Høykontrast: gjeldende side skilles bare med farge. */
71
+ @media (forced-colors: active) {
72
+ .fs-pagination [aria-current="page"] {
73
+ background-color: Highlight;
74
+ color: HighlightText;
75
+ }
76
+ }
77
+ }
@@ -0,0 +1,33 @@
1
+ import { attributes } from "../shared.js"
2
+
3
+ export const PAGINATION_CLASS = "fs-pagination" as const
4
+ /** Hoppet i nummerrekken, som «1 2 … 9 10». */
5
+ export const PAGINATION_GAP_CLASS = "fs-pagination__gap" as const
6
+
7
+ export type PaginationAttributes = {
8
+ class: typeof PAGINATION_CLASS
9
+ }
10
+
11
+ /**
12
+ * Attributtene for sidenavigering.
13
+ *
14
+ * Klassen hører på en `<ul>` inne i en `<nav aria-label="Sidenavigering">`.
15
+ * Hver side er en lenke med egen adresse, ikke en knapp: da kan brukeren
16
+ * åpne side tre i ny fane, og komme tilbake med tilbakeknappen.
17
+ *
18
+ * Gjeldende side markeres med `aria-current="page"`, som både gir fargen og
19
+ * er det skjermleseren leser opp.
20
+ *
21
+ * ```ts
22
+ * <nav aria-label="Sidenavigering">
23
+ * <ul {...pagination()}>…</ul>
24
+ * </nav>
25
+ * ```
26
+ */
27
+ export const pagination = Object.assign(
28
+ (): PaginationAttributes => attributes({ class: PAGINATION_CLASS }),
29
+ {
30
+ /** Klassen på hoppet i nummerrekken. */
31
+ gap: PAGINATION_GAP_CLASS,
32
+ },
33
+ )
@@ -0,0 +1,31 @@
1
+ @layer fristil {
2
+ .fs-paragraph {
3
+ margin: 0;
4
+ font-family: inherit;
5
+ font-size: var(--font-size-m);
6
+ line-height: 1.6;
7
+ color: var(--semantic-page-foreground);
8
+ /* Under 75 tegn per linje. Lengre linjer gjør det vanskelig å finne
9
+ tilbake til starten av neste. */
10
+ max-inline-size: var(--fs-paragraph-width, 70ch);
11
+ }
12
+
13
+ .fs-paragraph + .fs-paragraph {
14
+ margin-block-start: var(--fs-paragraph-gap, var(--size-3));
15
+ }
16
+
17
+ .fs-paragraph[data-size="small"] {
18
+ font-size: var(--font-size-s);
19
+ }
20
+
21
+ .fs-paragraph[data-size="large"] {
22
+ font-size: var(--font-size-l);
23
+ line-height: 1.55;
24
+ }
25
+
26
+ .fs-paragraph[data-variant="lead"] {
27
+ font-size: var(--font-size-l);
28
+ line-height: 1.55;
29
+ color: var(--semantic-muted-foreground);
30
+ }
31
+ }