@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,350 @@
1
+ import {
2
+ adjustForContrast,
3
+ buildNeutralScale,
4
+ buildScale,
5
+ contrastRatio,
6
+ parseHex,
7
+ } from "./color.js"
8
+
9
+ /**
10
+ * Bygger et helt fargetema fra noen få merkefarger.
11
+ *
12
+ * Fristil har én innebygd palett. En organisasjon med egne farger måtte
13
+ * ellers overstyre tokenene for hånd, og selv passe på at hvert par holder
14
+ * kontrastkravet, i begge temaer. Det er mange par, og det er lett å gå glipp
15
+ * av ett.
16
+ *
17
+ * Her oppgir du merkefargene, og generatoren bygger skalaene, setter de
18
+ * semantiske verdiene, og flytter lysheten på dem som ikke holder kravet.
19
+ * Hver justering rapporteres, så du ser hva som ble endret og hvorfor.
20
+ *
21
+ * ```ts
22
+ * const tema = buildTheme({ interactive: "#7c3aed", danger: "#b3261e" })
23
+ * console.log(tema.css)
24
+ * ```
25
+ */
26
+
27
+ export type ThemeInput = {
28
+ /** Lenker, knapper og fokusmarkering. */
29
+ interactive: string
30
+ /** Feil, sletting og avslag. */
31
+ danger: string
32
+ /** Fullført og godkjent. */
33
+ success: string
34
+ /** Noe som krever oppmerksomhet. */
35
+ warning: string
36
+ /** Flater, tekst og skillelinjer. Nesten uten kulør. */
37
+ neutral?: string
38
+ /** Besøkte lenker. Utledes fra `interactive` hvis den utelates. */
39
+ visited?: string
40
+ }
41
+
42
+ export type ThemeAdjustment = {
43
+ /** Tokenet som ble flyttet. */
44
+ token: string
45
+ /** Temaet det gjelder. */
46
+ theme: "light" | "dark"
47
+ /** Kontrasten før og etter. */
48
+ before: number
49
+ after: number
50
+ }
51
+
52
+ export type Theme = {
53
+ light: Record<string, string>
54
+ dark: Record<string, string>
55
+ /** Verdiene generatoren flyttet for å holde kontrastkravet. */
56
+ adjustments: ThemeAdjustment[]
57
+ /** Par som ikke kunne reddes. Tom når temaet holder. */
58
+ problems: string[]
59
+ /** Ferdig CSS, klar til å legges etter `tokens.css`. */
60
+ css: string
61
+ }
62
+
63
+ /** Kravet til tekst mot flate. */
64
+ const TEKST = 4.5
65
+ /** Kravet til en grafisk avgrensning, som rammen rundt et felt. */
66
+ const RAMME = 3
67
+
68
+ function forhold(a: string, b: string): number {
69
+ return contrastRatio(parseHex(a), parseHex(b))
70
+ }
71
+
72
+ /**
73
+ * Setter en forgrunnsfarge som holder kravet mot alle flatene den havner på.
74
+ *
75
+ * En farge brukes sjelden bare ett sted. `interactive-main` er både
76
+ * lenkefargen på sideflaten, flaten under teksten på en primærknapp, og
77
+ * fargen på et interaktivt merke. Holder den mot den ene, kan den likevel
78
+ * ryke mot den neste, så alle flatene sendes inn samtidig og den vanskeligste
79
+ * avgjør.
80
+ */
81
+ function sikre(
82
+ verdier: Record<string, string>,
83
+ notater: ThemeAdjustment[],
84
+ problemer: string[],
85
+ tema: "light" | "dark",
86
+ token: string,
87
+ foreslatt: string,
88
+ bakgrunner: string[],
89
+ krav = TEKST,
90
+ ): void {
91
+ let farge = foreslatt
92
+ const start = Math.min(...bakgrunner.map((flate) => forhold(farge, flate)))
93
+
94
+ // Flere runder, fordi en justering mot den ene flaten kan gjøre fargen
95
+ // dårligere mot en annen. I praksis stopper det etter én eller to.
96
+ for (let runde = 0; runde < 4; runde += 1) {
97
+ const verste = bakgrunner.reduce((a, b) =>
98
+ forhold(farge, a) <= forhold(farge, b) ? a : b,
99
+ )
100
+
101
+ if (forhold(farge, verste) >= krav) break
102
+
103
+ farge = adjustForContrast(farge, verste, krav).hex
104
+ }
105
+
106
+ verdier[token] = farge
107
+
108
+ const slutt = Math.min(...bakgrunner.map((flate) => forhold(farge, flate)))
109
+
110
+ if (farge !== foreslatt) {
111
+ notater.push({ token, theme: tema, before: start, after: slutt })
112
+ }
113
+
114
+ if (slutt < krav) {
115
+ const verste = bakgrunner.reduce((a, b) =>
116
+ forhold(farge, a) <= forhold(farge, b) ? a : b,
117
+ )
118
+ problemer.push(
119
+ `${token} i ${tema} tema kommer bare til ${slutt.toFixed(2)}:1 mot ${verste}, og kravet er ${krav}:1`,
120
+ )
121
+ }
122
+ }
123
+
124
+ export function buildTheme(input: ThemeInput): Theme {
125
+ const palett = {
126
+ interactive: buildScale(input.interactive),
127
+ danger: buildScale(input.danger),
128
+ success: buildScale(input.success),
129
+ warning: buildScale(input.warning),
130
+ visited: buildScale(input.visited ?? input.interactive),
131
+ neutral: buildNeutralScale(input.neutral ?? "#1a1a1a"),
132
+ }
133
+
134
+ const notater: ThemeAdjustment[] = []
135
+ const problemer: string[] = []
136
+
137
+ /**
138
+ * Bygger ett tema.
139
+ *
140
+ * Lyst og mørkt tema er samme oppskrift med motsatte trinn: flaten er lys
141
+ * og teksten mørk, eller omvendt. Derfor står de to i samme funksjon, med
142
+ * trinnene som parametre.
143
+ */
144
+ function tema(
145
+ modus: "light" | "dark",
146
+ trinn: {
147
+ flate: number
148
+ tekst: number
149
+ statusFlate: number
150
+ statusTekst: number
151
+ noytralFlate: number
152
+ noytralTekst: number
153
+ interaktiv: number
154
+ interaktivHover: number
155
+ avslattFlate: number
156
+ avslattTekst: number
157
+ dempet: number
158
+ rammeHover: number
159
+ skille: number
160
+ },
161
+ ): Record<string, string> {
162
+ const verdier: Record<string, string> = {}
163
+ const flate = palett.neutral[trinn.flate]
164
+
165
+ // Flatene først. De er utgangspunktet alt annet måles mot.
166
+ verdier["--semantic-page-background"] = flate
167
+ verdier["--semantic-danger-background"] = palett.danger[trinn.statusFlate]
168
+ verdier["--semantic-warning-background"] = palett.warning[trinn.statusFlate]
169
+ verdier["--semantic-success-background"] = palett.success[trinn.statusFlate]
170
+ verdier["--semantic-neutral-background"] =
171
+ palett.neutral[trinn.noytralFlate]
172
+ verdier["--semantic-interactive-background"] =
173
+ palett.interactive[trinn.statusFlate]
174
+ verdier["--semantic-disabled-background"] =
175
+ palett.neutral[trinn.avslattFlate]
176
+ verdier["--semantic-disabled-foreground"] =
177
+ palett.neutral[trinn.avslattTekst]
178
+ verdier["--semantic-field-border-hover"] = palett.neutral[trinn.rammeHover]
179
+ verdier["--semantic-divider-30"] = palett.neutral[trinn.skille]
180
+ verdier["--semantic-divider-100"] = palett.neutral[trinn.tekst]
181
+
182
+ // Teksten oppå en fylt knapp er flaten fra det motsatte temaet.
183
+ const kontrastfarge = modus === "light" ? "#ffffff" : palett.neutral[100]
184
+ verdier["--semantic-interactive-contrast"] = kontrastfarge
185
+ verdier["--semantic-danger-contrast"] = kontrastfarge
186
+
187
+ const sett = (
188
+ token: string,
189
+ foreslatt: string,
190
+ bakgrunner: string[],
191
+ krav?: number,
192
+ ) =>
193
+ sikre(
194
+ verdier,
195
+ notater,
196
+ problemer,
197
+ modus,
198
+ token,
199
+ foreslatt,
200
+ bakgrunner,
201
+ krav,
202
+ )
203
+
204
+ // Brødteksten står både på sideflaten og i felt med statusfarge.
205
+ sett("--semantic-page-foreground", palett.neutral[trinn.tekst], [
206
+ flate,
207
+ verdier["--semantic-danger-background"],
208
+ verdier["--semantic-warning-background"],
209
+ verdier["--semantic-success-background"],
210
+ ])
211
+
212
+ sett("--semantic-muted-foreground", palett.neutral[trinn.dempet], [flate])
213
+
214
+ /*
215
+ * Den interaktive fargen brukes tre steder: som lenke på sideflaten, som
216
+ * flate under teksten på en primærknapp, og som farge på et interaktivt
217
+ * merke. Alle tre må holde.
218
+ */
219
+ sett("--semantic-interactive-main", palett.interactive[trinn.interaktiv], [
220
+ flate,
221
+ verdier["--semantic-interactive-background"],
222
+ kontrastfarge,
223
+ ])
224
+
225
+ /*
226
+ * Hover-flaten på knappen, med den samme teksten oppå. Den brukes også
227
+ * som tekst i avataren, oppå interactive-background, så den flaten må
228
+ * med her.
229
+ */
230
+ sett(
231
+ "--semantic-interactive-foreground",
232
+ palett.interactive[trinn.interaktivHover],
233
+ [flate, kontrastfarge, verdier["--semantic-interactive-background"]],
234
+ )
235
+
236
+ sett(
237
+ "--semantic-interactive-visited",
238
+ palett.visited[trinn.interaktivHover],
239
+ [flate],
240
+ )
241
+
242
+ // Statusfargene står både i sitt eget merke og som tekst på sideflaten.
243
+ for (const status of ["danger", "warning", "success"] as const) {
244
+ sett(
245
+ `--semantic-${status}-foreground`,
246
+ palett[status][trinn.statusTekst],
247
+ [verdier[`--semantic-${status}-background`], flate],
248
+ )
249
+ }
250
+
251
+ sett("--semantic-neutral-foreground", palett.neutral[trinn.noytralTekst], [
252
+ verdier["--semantic-neutral-background"],
253
+ ])
254
+
255
+ // Den fylte sletteknappen, med lys tekst oppå.
256
+ sett("--semantic-danger-main", palett.danger[trinn.interaktiv], [
257
+ kontrastfarge,
258
+ ])
259
+
260
+ // Rammen er en grafisk avgrensning, og har derfor et lavere krav.
261
+ sett("--semantic-field-border", palett.neutral[50], [flate], RAMME)
262
+
263
+ return verdier
264
+ }
265
+
266
+ const light = tema("light", {
267
+ flate: 0,
268
+ tekst: 100,
269
+ statusFlate: 10,
270
+ statusTekst: 100,
271
+ noytralFlate: 10,
272
+ noytralTekst: 70,
273
+ interaktiv: 70,
274
+ interaktivHover: 100,
275
+ avslattFlate: 10,
276
+ avslattTekst: 50,
277
+ dempet: 50,
278
+ rammeHover: 70,
279
+ skille: 30,
280
+ })
281
+
282
+ const dark = tema("dark", {
283
+ flate: 100,
284
+ tekst: 5,
285
+ statusFlate: 100,
286
+ statusTekst: 30,
287
+ noytralFlate: 70,
288
+ noytralTekst: 10,
289
+ interaktiv: 30,
290
+ interaktivHover: 10,
291
+ avslattFlate: 70,
292
+ avslattTekst: 30,
293
+ dempet: 30,
294
+ rammeHover: 30,
295
+ skille: 70,
296
+ })
297
+
298
+ return {
299
+ light,
300
+ dark,
301
+ adjustments: notater,
302
+ problems: problemer,
303
+ css: tilCss(palett, light, dark),
304
+ }
305
+ }
306
+
307
+ function linjer(verdier: Record<string, string>, innrykk: string): string {
308
+ return Object.entries(verdier)
309
+ .map(([navn, verdi]) => `${innrykk}${navn}: ${verdi};`)
310
+ .join("\n")
311
+ }
312
+
313
+ function tilCss(
314
+ paletter: Record<string, Record<number, string>>,
315
+ light: Record<string, string>,
316
+ dark: Record<string, string>,
317
+ ): string {
318
+ const palett: Record<string, string> = {}
319
+ for (const [navn, skala] of Object.entries(paletter)) {
320
+ for (const [trinn, verdi] of Object.entries(skala)) {
321
+ palett[`--palette-${navn}-${trinn}`] = verdi
322
+ }
323
+ }
324
+
325
+ return `/*
326
+ * Generert av @fristil/designsystem. Rediger merkefargene, ikke denne fila.
327
+ *
328
+ * Legges etter tokens.css, og overstyrer fargene der. Størrelser,
329
+ * skriftstørrelser og ikoner kommer fortsatt fra tokens.css.
330
+ */
331
+
332
+ @layer fristil {
333
+ :root {
334
+ ${linjer(palett, " ")}
335
+
336
+ ${linjer(light, " ")}
337
+ }
338
+
339
+ @media (prefers-color-scheme: dark) {
340
+ :root:not([data-theme="light"]) {
341
+ ${linjer(dark, " ")}
342
+ }
343
+ }
344
+
345
+ [data-theme="dark"] {
346
+ ${linjer(dark, " ")}
347
+ }
348
+ }
349
+ `
350
+ }
@@ -0,0 +1,214 @@
1
+ /* Generert. Rediger tokens.ts, ikke denne fila. */
2
+
3
+ @layer fristil {
4
+ :root {
5
+ /* font */
6
+ --font-size-reference: 16px;
7
+ --font-size-xxs: 0.625rem;
8
+ --font-size-xs: 0.75rem;
9
+ --font-size-s: 0.875rem;
10
+ --font-size-m: 1rem;
11
+ --font-size-l: 1.125rem;
12
+ --font-size-xl: 1.375rem;
13
+ --font-size-xxl: 1.875rem;
14
+ --font-size-mega: 2.625rem;
15
+
16
+ /* size */
17
+ --size-px: 1px;
18
+ --size-0-5: 0.125rem;
19
+ --size-1: 0.25rem;
20
+ --size-2: 0.5rem;
21
+ --size-3: 0.75rem;
22
+ --size-4: 1rem;
23
+ --size-5: 1.25rem;
24
+ --size-6: 1.5rem;
25
+ --size-7: 1.75rem;
26
+ --size-8: 2rem;
27
+ --size-10: 2.5rem;
28
+ --size-12: 3rem;
29
+ --size-16: 4rem;
30
+
31
+ /* semantic-size */
32
+ --semantic-size-default: var(--size-4);
33
+
34
+ /* semantic-spacing */
35
+ --semantic-spacing-default: var(--size-4);
36
+
37
+ /* palette-burgundy */
38
+ --palette-burgundy-5: #faf0f3;
39
+ --palette-burgundy-10: #f7e2e8;
40
+ --palette-burgundy-30: #f3a7b0;
41
+ --palette-burgundy-50: #f06674;
42
+ --palette-burgundy-70: #b04c5c;
43
+ --palette-burgundy-100: #6f2c3f;
44
+
45
+ /* palette-forest */
46
+ --palette-forest-5: #f4faf5;
47
+ --palette-forest-10: #e3f5ea;
48
+ --palette-forest-30: #b9e1c8;
49
+ --palette-forest-50: #91d6ac;
50
+ --palette-forest-70: #5d9b73;
51
+ --palette-forest-100: #2b6940;
52
+
53
+ /* palette-ochre */
54
+ --palette-ochre-5: #f9f4f0;
55
+ --palette-ochre-10: #f9ede2;
56
+ --palette-ochre-30: #f0d2b6;
57
+ --palette-ochre-50: #e7b78a;
58
+ --palette-ochre-70: #a9805b;
59
+ --palette-ochre-100: #6b492c;
60
+
61
+ /* palette-denim */
62
+ --palette-denim-5: #eff3f9;
63
+ --palette-denim-10: #e2eaf7;
64
+ --palette-denim-30: #b5cbee;
65
+ --palette-denim-50: #89abe5;
66
+ --palette-denim-70: #5a77a8;
67
+ --palette-denim-100: #2c436b;
68
+
69
+ /* palette-azure */
70
+ --palette-azure-10: #cde1f9;
71
+ --palette-azure-30: #9ccff2;
72
+ --palette-azure-70: #1362ae;
73
+ --palette-azure-100: #093e61;
74
+
75
+ /* palette-graphite */
76
+ --palette-graphite-0: #ffffff;
77
+ --palette-graphite-5: #f4f4f4;
78
+ --palette-graphite-10: #e5e5e5;
79
+ --palette-graphite-30: #b2b2b2;
80
+ --palette-graphite-50: #757575;
81
+ --palette-graphite-70: #4d4d4d;
82
+ --palette-graphite-100: #1a1a1a;
83
+
84
+ /* palette-dark */
85
+ --palette-dark-alpha-50: #1a1a1a80;
86
+
87
+ /* semantic-page */
88
+ --semantic-page-background: var(--palette-graphite-0);
89
+ --semantic-page-foreground: var(--palette-graphite-100);
90
+
91
+ /* semantic-danger */
92
+ --semantic-danger-background: var(--palette-burgundy-10);
93
+ --semantic-danger-foreground: #a82e39;
94
+ --semantic-danger-main: #a82e39;
95
+ --semantic-danger-contrast: var(--palette-graphite-0);
96
+
97
+ /* semantic-warning */
98
+ --semantic-warning-background: var(--palette-ochre-10);
99
+ --semantic-warning-foreground: #896508;
100
+
101
+ /* semantic-success */
102
+ --semantic-success-background: var(--palette-forest-10);
103
+ --semantic-success-foreground: #316f2a;
104
+
105
+ /* semantic-disabled */
106
+ --semantic-disabled-background: var(--palette-graphite-10);
107
+ --semantic-disabled-foreground: var(--palette-graphite-50);
108
+
109
+ /* semantic-muted */
110
+ --semantic-muted-foreground: var(--palette-graphite-50);
111
+
112
+ /* semantic-neutral */
113
+ --semantic-neutral-background: var(--palette-graphite-10);
114
+ --semantic-neutral-foreground: var(--palette-graphite-70);
115
+
116
+ /* semantic-interactive */
117
+ --semantic-interactive-background: var(--palette-azure-10);
118
+ --semantic-interactive-main: var(--palette-azure-70);
119
+ --semantic-interactive-foreground: var(--palette-azure-100);
120
+ --semantic-interactive-contrast: var(--palette-graphite-0);
121
+ --semantic-interactive-visited: var(--palette-denim-70);
122
+
123
+ /* semantic-field */
124
+ --semantic-field-border: var(--palette-graphite-50);
125
+ --semantic-field-border-hover: var(--palette-graphite-70);
126
+
127
+ /* semantic-divider */
128
+ --semantic-divider-30: var(--palette-graphite-30);
129
+ --semantic-divider-100: var(--palette-graphite-100);
130
+
131
+ /* semantic-icon */
132
+ --semantic-icon-search: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%234d4d4d' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='7'/%3E%3Cline x1='16.5' y1='16.5' x2='21' y2='21'/%3E%3C/svg%3E");
133
+ --semantic-icon-check: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23ffffff' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'/%3E%3C/svg%3E");
134
+ --semantic-icon-dash: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23ffffff' stroke-width='3' stroke-linecap='round'%3E%3Cline x1='6' y1='12' x2='18' y2='12'/%3E%3C/svg%3E");
135
+ --semantic-icon-calendar: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%234d4d4d' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'/%3E%3Cline x1='16' y1='2' x2='16' y2='6'/%3E%3Cline x1='8' y1='2' x2='8' y2='6'/%3E%3Cline x1='3' y1='10' x2='21' y2='10'/%3E%3C/svg%3E");
136
+ --semantic-icon-clock: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%234d4d4d' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpolyline points='12 7 12 12 15 15'/%3E%3C/svg%3E");
137
+
138
+ /* semantic-overlay */
139
+ --semantic-overlay-backdrop: var(--palette-dark-alpha-50);
140
+
141
+ /* semantic-shadow */
142
+ --semantic-shadow-overlay: 0 16px 40px rgba(0, 0, 0, 0.12);
143
+ }
144
+
145
+ @media (prefers-color-scheme: dark) {
146
+ :root:not([data-theme="light"]) {
147
+ --semantic-page-background: var(--palette-graphite-100);
148
+ --semantic-page-foreground: var(--palette-graphite-5);
149
+ --semantic-danger-background: var(--palette-burgundy-100);
150
+ --semantic-danger-foreground: var(--palette-burgundy-30);
151
+ --semantic-warning-background: var(--palette-ochre-100);
152
+ --semantic-warning-foreground: var(--palette-ochre-30);
153
+ --semantic-success-background: var(--palette-forest-100);
154
+ --semantic-success-foreground: var(--palette-forest-30);
155
+ --semantic-disabled-background: var(--palette-graphite-70);
156
+ --semantic-disabled-foreground: var(--palette-graphite-30);
157
+ --semantic-muted-foreground: var(--palette-graphite-30);
158
+ --semantic-neutral-background: var(--palette-graphite-70);
159
+ --semantic-neutral-foreground: var(--palette-graphite-10);
160
+ --semantic-interactive-background: var(--palette-azure-100);
161
+ --semantic-interactive-main: var(--palette-azure-30);
162
+ --semantic-interactive-foreground: var(--palette-azure-10);
163
+ --semantic-interactive-contrast: var(--palette-graphite-100);
164
+ --semantic-interactive-visited: var(--palette-denim-30);
165
+ --semantic-field-border: var(--palette-graphite-50);
166
+ --semantic-field-border-hover: var(--palette-graphite-30);
167
+ --semantic-danger-main: var(--palette-burgundy-30);
168
+ --semantic-danger-contrast: var(--palette-graphite-100);
169
+ --semantic-divider-30: var(--palette-graphite-70);
170
+ --semantic-divider-100: var(--palette-graphite-30);
171
+ --semantic-icon-search: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23b2b2b2' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='7'/%3E%3Cline x1='16.5' y1='16.5' x2='21' y2='21'/%3E%3C/svg%3E");
172
+ --semantic-icon-check: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%231a1a1a' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'/%3E%3C/svg%3E");
173
+ --semantic-icon-dash: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%231a1a1a' stroke-width='3' stroke-linecap='round'%3E%3Cline x1='6' y1='12' x2='18' y2='12'/%3E%3C/svg%3E");
174
+ --semantic-icon-calendar: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23b2b2b2' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'/%3E%3Cline x1='16' y1='2' x2='16' y2='6'/%3E%3Cline x1='8' y1='2' x2='8' y2='6'/%3E%3Cline x1='3' y1='10' x2='21' y2='10'/%3E%3C/svg%3E");
175
+ --semantic-icon-clock: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23b2b2b2' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpolyline points='12 7 12 12 15 15'/%3E%3C/svg%3E");
176
+ --semantic-overlay-backdrop: rgba(0, 0, 0, 0.72);
177
+ --semantic-shadow-overlay: 0 16px 40px rgba(0, 0, 0, 0.6);
178
+ }
179
+ }
180
+
181
+ [data-theme="dark"] {
182
+ --semantic-page-background: var(--palette-graphite-100);
183
+ --semantic-page-foreground: var(--palette-graphite-5);
184
+ --semantic-danger-background: var(--palette-burgundy-100);
185
+ --semantic-danger-foreground: var(--palette-burgundy-30);
186
+ --semantic-warning-background: var(--palette-ochre-100);
187
+ --semantic-warning-foreground: var(--palette-ochre-30);
188
+ --semantic-success-background: var(--palette-forest-100);
189
+ --semantic-success-foreground: var(--palette-forest-30);
190
+ --semantic-disabled-background: var(--palette-graphite-70);
191
+ --semantic-disabled-foreground: var(--palette-graphite-30);
192
+ --semantic-muted-foreground: var(--palette-graphite-30);
193
+ --semantic-neutral-background: var(--palette-graphite-70);
194
+ --semantic-neutral-foreground: var(--palette-graphite-10);
195
+ --semantic-interactive-background: var(--palette-azure-100);
196
+ --semantic-interactive-main: var(--palette-azure-30);
197
+ --semantic-interactive-foreground: var(--palette-azure-10);
198
+ --semantic-interactive-contrast: var(--palette-graphite-100);
199
+ --semantic-interactive-visited: var(--palette-denim-30);
200
+ --semantic-field-border: var(--palette-graphite-50);
201
+ --semantic-field-border-hover: var(--palette-graphite-30);
202
+ --semantic-danger-main: var(--palette-burgundy-30);
203
+ --semantic-danger-contrast: var(--palette-graphite-100);
204
+ --semantic-divider-30: var(--palette-graphite-70);
205
+ --semantic-divider-100: var(--palette-graphite-30);
206
+ --semantic-icon-search: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23b2b2b2' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='7'/%3E%3Cline x1='16.5' y1='16.5' x2='21' y2='21'/%3E%3C/svg%3E");
207
+ --semantic-icon-check: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%231a1a1a' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'/%3E%3C/svg%3E");
208
+ --semantic-icon-dash: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%231a1a1a' stroke-width='3' stroke-linecap='round'%3E%3Cline x1='6' y1='12' x2='18' y2='12'/%3E%3C/svg%3E");
209
+ --semantic-icon-calendar: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23b2b2b2' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'/%3E%3Cline x1='16' y1='2' x2='16' y2='6'/%3E%3Cline x1='8' y1='2' x2='8' y2='6'/%3E%3Cline x1='3' y1='10' x2='21' y2='10'/%3E%3C/svg%3E");
210
+ --semantic-icon-clock: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23b2b2b2' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpolyline points='12 7 12 12 15 15'/%3E%3C/svg%3E");
211
+ --semantic-overlay-backdrop: rgba(0, 0, 0, 0.72);
212
+ --semantic-shadow-overlay: 0 16px 40px rgba(0, 0, 0, 0.6);
213
+ }
214
+ }