@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.
- package/CHANGELOG.md +85 -0
- package/LICENSE +21 -0
- package/README.md +101 -0
- package/dist/cli.d.ts +31 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +261 -0
- package/dist/cli.js.map +1 -0
- package/dist/components/css/accordion/accordion.d.ts +37 -0
- package/dist/components/css/accordion/accordion.d.ts.map +1 -0
- package/dist/components/css/accordion/accordion.js +31 -0
- package/dist/components/css/accordion/accordion.js.map +1 -0
- package/dist/components/css/alert/alert.d.ts +35 -0
- package/dist/components/css/alert/alert.d.ts.map +1 -0
- package/dist/components/css/alert/alert.js +29 -0
- package/dist/components/css/alert/alert.js.map +1 -0
- package/dist/components/css/avatar/avatar.d.ts +40 -0
- package/dist/components/css/avatar/avatar.d.ts.map +1 -0
- package/dist/components/css/avatar/avatar.js +30 -0
- package/dist/components/css/avatar/avatar.js.map +1 -0
- package/dist/components/css/badge/badge.d.ts +24 -0
- package/dist/components/css/badge/badge.d.ts.map +1 -0
- package/dist/components/css/badge/badge.js +24 -0
- package/dist/components/css/badge/badge.js.map +1 -0
- package/dist/components/css/breadcrumbs/breadcrumbs.d.ts +25 -0
- package/dist/components/css/breadcrumbs/breadcrumbs.d.ts.map +1 -0
- package/dist/components/css/breadcrumbs/breadcrumbs.js +23 -0
- package/dist/components/css/breadcrumbs/breadcrumbs.js.map +1 -0
- package/dist/components/css/button/button.d.ts +26 -0
- package/dist/components/css/button/button.d.ts.map +1 -0
- package/dist/components/css/button/button.js +25 -0
- package/dist/components/css/button/button.js.map +1 -0
- package/dist/components/css/card/card.d.ts +36 -0
- package/dist/components/css/card/card.d.ts.map +1 -0
- package/dist/components/css/card/card.js +28 -0
- package/dist/components/css/card/card.js.map +1 -0
- package/dist/components/css/checkbox/checkbox.d.ts +32 -0
- package/dist/components/css/checkbox/checkbox.d.ts.map +1 -0
- package/dist/components/css/checkbox/checkbox.js +27 -0
- package/dist/components/css/checkbox/checkbox.js.map +1 -0
- package/dist/components/css/dialog/dialog.d.ts +30 -0
- package/dist/components/css/dialog/dialog.d.ts.map +1 -0
- package/dist/components/css/dialog/dialog.js +28 -0
- package/dist/components/css/dialog/dialog.js.map +1 -0
- package/dist/components/css/divider/divider.d.ts +28 -0
- package/dist/components/css/divider/divider.d.ts.map +1 -0
- package/dist/components/css/divider/divider.js +22 -0
- package/dist/components/css/divider/divider.js.map +1 -0
- package/dist/components/css/error-text/error-text.d.ts +24 -0
- package/dist/components/css/error-text/error-text.d.ts.map +1 -0
- package/dist/components/css/error-text/error-text.js +18 -0
- package/dist/components/css/error-text/error-text.js.map +1 -0
- package/dist/components/css/fieldset/fieldset.d.ts +55 -0
- package/dist/components/css/fieldset/fieldset.d.ts.map +1 -0
- package/dist/components/css/fieldset/fieldset.js +39 -0
- package/dist/components/css/fieldset/fieldset.js.map +1 -0
- package/dist/components/css/file-upload/file-upload.d.ts +41 -0
- package/dist/components/css/file-upload/file-upload.d.ts.map +1 -0
- package/dist/components/css/file-upload/file-upload.js +32 -0
- package/dist/components/css/file-upload/file-upload.js.map +1 -0
- package/dist/components/css/heading/heading.d.ts +29 -0
- package/dist/components/css/heading/heading.d.ts.map +1 -0
- package/dist/components/css/heading/heading.js +23 -0
- package/dist/components/css/heading/heading.js.map +1 -0
- package/dist/components/css/help-text/help-text.d.ts +24 -0
- package/dist/components/css/help-text/help-text.d.ts.map +1 -0
- package/dist/components/css/help-text/help-text.js +23 -0
- package/dist/components/css/help-text/help-text.js.map +1 -0
- package/dist/components/css/input/input.d.ts +39 -0
- package/dist/components/css/input/input.d.ts.map +1 -0
- package/dist/components/css/input/input.js +44 -0
- package/dist/components/css/input/input.js.map +1 -0
- package/dist/components/css/label/label.d.ts +33 -0
- package/dist/components/css/label/label.d.ts.map +1 -0
- package/dist/components/css/label/label.js +23 -0
- package/dist/components/css/label/label.js.map +1 -0
- package/dist/components/css/link/link.d.ts +21 -0
- package/dist/components/css/link/link.d.ts.map +1 -0
- package/dist/components/css/link/link.js +14 -0
- package/dist/components/css/link/link.js.map +1 -0
- package/dist/components/css/list/list.d.ts +29 -0
- package/dist/components/css/list/list.d.ts.map +1 -0
- package/dist/components/css/list/list.js +23 -0
- package/dist/components/css/list/list.js.map +1 -0
- package/dist/components/css/pagination/pagination.d.ts +27 -0
- package/dist/components/css/pagination/pagination.d.ts.map +1 -0
- package/dist/components/css/pagination/pagination.js +25 -0
- package/dist/components/css/pagination/pagination.js.map +1 -0
- package/dist/components/css/paragraph/paragraph.d.ts +36 -0
- package/dist/components/css/paragraph/paragraph.d.ts.map +1 -0
- package/dist/components/css/paragraph/paragraph.js +26 -0
- package/dist/components/css/paragraph/paragraph.js.map +1 -0
- package/dist/components/css/radio/radio.d.ts +32 -0
- package/dist/components/css/radio/radio.d.ts.map +1 -0
- package/dist/components/css/radio/radio.js +27 -0
- package/dist/components/css/radio/radio.js.map +1 -0
- package/dist/components/css/search/search.d.ts +33 -0
- package/dist/components/css/search/search.d.ts.map +1 -0
- package/dist/components/css/search/search.js +31 -0
- package/dist/components/css/search/search.js.map +1 -0
- package/dist/components/css/select/select.d.ts +23 -0
- package/dist/components/css/select/select.d.ts.map +1 -0
- package/dist/components/css/select/select.js +18 -0
- package/dist/components/css/select/select.js.map +1 -0
- package/dist/components/css/shared.d.ts +26 -0
- package/dist/components/css/shared.d.ts.map +1 -0
- package/dist/components/css/shared.js +34 -0
- package/dist/components/css/shared.js.map +1 -0
- package/dist/components/css/skeleton/skeleton.d.ts +29 -0
- package/dist/components/css/skeleton/skeleton.d.ts.map +1 -0
- package/dist/components/css/skeleton/skeleton.js +23 -0
- package/dist/components/css/skeleton/skeleton.js.map +1 -0
- package/dist/components/css/skip-link/skip-link.d.ts +18 -0
- package/dist/components/css/skip-link/skip-link.d.ts.map +1 -0
- package/dist/components/css/skip-link/skip-link.js +16 -0
- package/dist/components/css/skip-link/skip-link.js.map +1 -0
- package/dist/components/css/spinner/spinner.d.ts +33 -0
- package/dist/components/css/spinner/spinner.d.ts.map +1 -0
- package/dist/components/css/spinner/spinner.js +25 -0
- package/dist/components/css/spinner/spinner.js.map +1 -0
- package/dist/components/css/sr-only/sr-only.d.ts +17 -0
- package/dist/components/css/sr-only/sr-only.d.ts.map +1 -0
- package/dist/components/css/sr-only/sr-only.js +14 -0
- package/dist/components/css/sr-only/sr-only.js.map +1 -0
- package/dist/components/css/switch/switch.d.ts +33 -0
- package/dist/components/css/switch/switch.d.ts.map +1 -0
- package/dist/components/css/switch/switch.js +29 -0
- package/dist/components/css/switch/switch.js.map +1 -0
- package/dist/components/css/table/table.d.ts +36 -0
- package/dist/components/css/table/table.d.ts.map +1 -0
- package/dist/components/css/table/table.js +28 -0
- package/dist/components/css/table/table.js.map +1 -0
- package/dist/components/css/tag/tag.d.ts +35 -0
- package/dist/components/css/tag/tag.d.ts.map +1 -0
- package/dist/components/css/tag/tag.js +27 -0
- package/dist/components/css/tag/tag.js.map +1 -0
- package/dist/components/css/textarea/textarea.d.ts +23 -0
- package/dist/components/css/textarea/textarea.d.ts.map +1 -0
- package/dist/components/css/textarea/textarea.js +18 -0
- package/dist/components/css/textarea/textarea.js.map +1 -0
- package/dist/components/css/toggle-group/toggle-group.d.ts +31 -0
- package/dist/components/css/toggle-group/toggle-group.d.ts.map +1 -0
- package/dist/components/css/toggle-group/toggle-group.js +29 -0
- package/dist/components/css/toggle-group/toggle-group.js.map +1 -0
- package/dist/components/css/tooltip/tooltip.d.ts +30 -0
- package/dist/components/css/tooltip/tooltip.d.ts.map +1 -0
- package/dist/components/css/tooltip/tooltip.js +28 -0
- package/dist/components/css/tooltip/tooltip.js.map +1 -0
- package/dist/components/frittstaende/calendar/fs-calendar.d.ts +52 -0
- package/dist/components/frittstaende/calendar/fs-calendar.d.ts.map +1 -0
- package/dist/components/frittstaende/calendar/fs-calendar.js +462 -0
- package/dist/components/frittstaende/calendar/fs-calendar.js.map +1 -0
- package/dist/components/frittstaende/date-field/fs-date-field.d.ts +110 -0
- package/dist/components/frittstaende/date-field/fs-date-field.d.ts.map +1 -0
- package/dist/components/frittstaende/date-field/fs-date-field.js +264 -0
- package/dist/components/frittstaende/date-field/fs-date-field.js.map +1 -0
- package/dist/components/frittstaende/suggestion/fs-suggestion.d.ts +133 -0
- package/dist/components/frittstaende/suggestion/fs-suggestion.d.ts.map +1 -0
- package/dist/components/frittstaende/suggestion/fs-suggestion.js +280 -0
- package/dist/components/frittstaende/suggestion/fs-suggestion.js.map +1 -0
- package/dist/components/frittstaende/toast/fs-toast.d.ts +67 -0
- package/dist/components/frittstaende/toast/fs-toast.d.ts.map +1 -0
- package/dist/components/frittstaende/toast/fs-toast.js +108 -0
- package/dist/components/frittstaende/toast/fs-toast.js.map +1 -0
- package/dist/components/ramme/error-summary/fs-error-summary.d.ts +70 -0
- package/dist/components/ramme/error-summary/fs-error-summary.d.ts.map +1 -0
- package/dist/components/ramme/error-summary/fs-error-summary.js +155 -0
- package/dist/components/ramme/error-summary/fs-error-summary.js.map +1 -0
- package/dist/components/ramme/field/field-core.d.ts +70 -0
- package/dist/components/ramme/field/field-core.d.ts.map +1 -0
- package/dist/components/ramme/field/field-core.js +60 -0
- package/dist/components/ramme/field/field-core.js.map +1 -0
- package/dist/components/ramme/field/fs-field.d.ts +49 -0
- package/dist/components/ramme/field/fs-field.d.ts.map +1 -0
- package/dist/components/ramme/field/fs-field.js +118 -0
- package/dist/components/ramme/field/fs-field.js.map +1 -0
- package/dist/components/ramme/popover/fs-popover.d.ts +68 -0
- package/dist/components/ramme/popover/fs-popover.d.ts.map +1 -0
- package/dist/components/ramme/popover/fs-popover.js +167 -0
- package/dist/components/ramme/popover/fs-popover.js.map +1 -0
- package/dist/components/ramme/tabs/fs-tabs.d.ts +61 -0
- package/dist/components/ramme/tabs/fs-tabs.d.ts.map +1 -0
- package/dist/components/ramme/tabs/fs-tabs.js +156 -0
- package/dist/components/ramme/tabs/fs-tabs.js.map +1 -0
- package/dist/dom.d.ts +33 -0
- package/dist/dom.d.ts.map +1 -0
- package/dist/dom.js +77 -0
- package/dist/dom.js.map +1 -0
- package/dist/fs.d.ts +199 -0
- package/dist/fs.d.ts.map +1 -0
- package/dist/fs.js +128 -0
- package/dist/fs.js.map +1 -0
- package/dist/index.d.ts +84 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +48 -0
- package/dist/index.js.map +1 -0
- package/dist/jsx/react.d.ts +127 -0
- package/dist/jsx/react.d.ts.map +1 -0
- package/dist/jsx/react.js +2 -0
- package/dist/jsx/react.js.map +1 -0
- package/dist/react.d.ts +207 -0
- package/dist/react.d.ts.map +1 -0
- package/dist/react.js +109 -0
- package/dist/react.js.map +1 -0
- package/dist/tailwind/preset.d.ts +120 -0
- package/dist/tailwind/preset.d.ts.map +1 -0
- package/dist/tailwind/preset.js +107 -0
- package/dist/tailwind/preset.js.map +1 -0
- package/dist/takeover.d.ts +79 -0
- package/dist/takeover.d.ts.map +1 -0
- package/dist/takeover.js +119 -0
- package/dist/takeover.js.map +1 -0
- package/dist/tokens/color.d.ts +85 -0
- package/dist/tokens/color.d.ts.map +1 -0
- package/dist/tokens/color.js +212 -0
- package/dist/tokens/color.js.map +1 -0
- package/dist/tokens/theme.d.ts +52 -0
- package/dist/tokens/theme.d.ts.map +1 -0
- package/dist/tokens/theme.js +197 -0
- package/dist/tokens/theme.js.map +1 -0
- package/dist/tokens/tokens.d.ts +210 -0
- package/dist/tokens/tokens.d.ts.map +1 -0
- package/dist/tokens/tokens.js +238 -0
- package/dist/tokens/tokens.js.map +1 -0
- package/package.json +334 -0
- package/src/components/css/accordion/accordion.css +74 -0
- package/src/components/css/accordion/accordion.ts +50 -0
- package/src/components/css/alert/alert.css +50 -0
- package/src/components/css/alert/alert.ts +48 -0
- package/src/components/css/avatar/avatar.css +64 -0
- package/src/components/css/avatar/avatar.ts +57 -0
- package/src/components/css/badge/badge.css +42 -0
- package/src/components/css/badge/badge.ts +43 -0
- package/src/components/css/breadcrumbs/breadcrumbs.css +50 -0
- package/src/components/css/breadcrumbs/breadcrumbs.ts +29 -0
- package/src/components/css/button/button.css +101 -0
- package/src/components/css/button/button.ts +44 -0
- package/src/components/css/card/card.css +48 -0
- package/src/components/css/card/card.ts +50 -0
- package/src/components/css/checkbox/checkbox.css +96 -0
- package/src/components/css/checkbox/checkbox.ts +50 -0
- package/src/components/css/dialog/dialog.css +50 -0
- package/src/components/css/dialog/dialog.ts +36 -0
- package/src/components/css/divider/divider.css +18 -0
- package/src/components/css/divider/divider.ts +41 -0
- package/src/components/css/error-text/error-text.css +21 -0
- package/src/components/css/error-text/error-text.ts +37 -0
- package/src/components/css/fieldset/fieldset.css +57 -0
- package/src/components/css/fieldset/fieldset.ts +87 -0
- package/src/components/css/file-upload/file-upload.css +92 -0
- package/src/components/css/file-upload/file-upload.ts +65 -0
- package/src/components/css/heading/heading.css +33 -0
- package/src/components/css/heading/heading.ts +42 -0
- package/src/components/css/help-text/help-text.css +24 -0
- package/src/components/css/help-text/help-text.ts +42 -0
- package/src/components/css/input/input.css +131 -0
- package/src/components/css/input/input.ts +76 -0
- package/src/components/css/label/label.css +34 -0
- package/src/components/css/label/label.ts +51 -0
- package/src/components/css/link/link.css +36 -0
- package/src/components/css/link/link.ts +29 -0
- package/src/components/css/list/list.css +40 -0
- package/src/components/css/list/list.ts +42 -0
- package/src/components/css/pagination/pagination.css +77 -0
- package/src/components/css/pagination/pagination.ts +33 -0
- package/src/components/css/paragraph/paragraph.css +31 -0
- package/src/components/css/paragraph/paragraph.ts +53 -0
- package/src/components/css/radio/radio.css +76 -0
- package/src/components/css/radio/radio.ts +50 -0
- package/src/components/css/search/search.css +49 -0
- package/src/components/css/search/search.ts +52 -0
- package/src/components/css/select/select.css +95 -0
- package/src/components/css/select/select.ts +40 -0
- package/src/components/css/shared.ts +40 -0
- package/src/components/css/skeleton/skeleton.css +49 -0
- package/src/components/css/skeleton/skeleton.ts +43 -0
- package/src/components/css/skip-link/skip-link.css +36 -0
- package/src/components/css/skip-link/skip-link.ts +22 -0
- package/src/components/css/spinner/spinner.css +48 -0
- package/src/components/css/spinner/spinner.ts +48 -0
- package/src/components/css/sr-only/sr-only.css +16 -0
- package/src/components/css/sr-only/sr-only.ts +18 -0
- package/src/components/css/switch/switch.css +101 -0
- package/src/components/css/switch/switch.ts +46 -0
- package/src/components/css/table/table.css +63 -0
- package/src/components/css/table/table.ts +50 -0
- package/src/components/css/tag/tag.css +58 -0
- package/src/components/css/tag/tag.ts +49 -0
- package/src/components/css/textarea/textarea.css +71 -0
- package/src/components/css/textarea/textarea.ts +40 -0
- package/src/components/css/toggle-group/toggle-group.css +82 -0
- package/src/components/css/toggle-group/toggle-group.ts +37 -0
- package/src/components/css/tooltip/tooltip.css +47 -0
- package/src/components/css/tooltip/tooltip.ts +36 -0
- package/src/components/frittstaende/calendar/fs-calendar.ts +525 -0
- package/src/components/frittstaende/date-field/date-field.css +54 -0
- package/src/components/frittstaende/date-field/fs-date-field.ts +309 -0
- package/src/components/frittstaende/suggestion/fs-suggestion.ts +333 -0
- package/src/components/frittstaende/suggestion/suggestion.css +63 -0
- package/src/components/frittstaende/toast/fs-toast.ts +146 -0
- package/src/components/frittstaende/toast/toast.css +71 -0
- package/src/components/ramme/error-summary/error-summary.css +45 -0
- package/src/components/ramme/error-summary/fs-error-summary.ts +197 -0
- package/src/components/ramme/field/field-core.ts +132 -0
- package/src/components/ramme/field/field.css +6 -0
- package/src/components/ramme/field/fs-field.ts +154 -0
- package/src/components/ramme/popover/fs-popover.ts +205 -0
- package/src/components/ramme/popover/popover.css +34 -0
- package/src/components/ramme/tabs/fs-tabs.ts +182 -0
- package/src/components/ramme/tabs/tabs.css +68 -0
- package/src/tailwind/tailwind.css +134 -0
- package/src/tokens/color.ts +267 -0
- package/src/tokens/theme.ts +350 -0
- package/src/tokens/tokens.css +214 -0
- package/src/tokens/tokens.ts +293 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { attributes, createGuard } from "../shared.js"
|
|
2
|
+
|
|
3
|
+
export const ACCORDION_CLASS = "fs-accordion" as const
|
|
4
|
+
/** Innholdet som vises når panelet er åpent. */
|
|
5
|
+
export const ACCORDION_CONTENT_CLASS = "fs-accordion__content" as const
|
|
6
|
+
|
|
7
|
+
export const accordionVariants = ["default", "plain"] as const
|
|
8
|
+
|
|
9
|
+
export type AccordionVariant = (typeof accordionVariants)[number]
|
|
10
|
+
export type NonDefaultAccordionVariant = Exclude<AccordionVariant, "default">
|
|
11
|
+
|
|
12
|
+
export type AccordionOptions = {
|
|
13
|
+
/** Egen ramme, eller bare en strek mellom panelene. Standard: `default`. */
|
|
14
|
+
variant?: AccordionVariant
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type AccordionAttributes = {
|
|
18
|
+
class: typeof ACCORDION_CLASS
|
|
19
|
+
"data-variant"?: NonDefaultAccordionVariant
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Attributtene for et panel som kan foldes ut.
|
|
24
|
+
*
|
|
25
|
+
* Klassen hører på et `<details>` med et `<summary>` først. Nettleseren tar
|
|
26
|
+
* seg av åpning, lukking, tastatur og at skjermlesere melder «utvidet» eller
|
|
27
|
+
* «sammenfoldet». Bygger du det samme av knapper og `aria-expanded`, må alt
|
|
28
|
+
* det skrives på nytt, og nettleserens søk i siden finner ikke lenger tekst
|
|
29
|
+
* inne i et lukket panel.
|
|
30
|
+
*
|
|
31
|
+
* ```ts
|
|
32
|
+
* <details {...accordion()}>
|
|
33
|
+
* <summary>Hvem kan søke?</summary>
|
|
34
|
+
* <div class={accordion.content}>…</div>
|
|
35
|
+
* </details>
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
export const accordion = Object.assign(
|
|
39
|
+
({ variant = "default" }: AccordionOptions = {}): AccordionAttributes =>
|
|
40
|
+
attributes({
|
|
41
|
+
class: ACCORDION_CLASS,
|
|
42
|
+
"data-variant": variant === "default" ? undefined : variant,
|
|
43
|
+
}),
|
|
44
|
+
{
|
|
45
|
+
/** Klassen på innholdet under overskriften. */
|
|
46
|
+
content: ACCORDION_CONTENT_CLASS,
|
|
47
|
+
variants: accordionVariants,
|
|
48
|
+
isVariant: createGuard(accordionVariants),
|
|
49
|
+
},
|
|
50
|
+
)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
@layer fristil {
|
|
2
|
+
.fs-alert {
|
|
3
|
+
box-sizing: border-box;
|
|
4
|
+
display: flex;
|
|
5
|
+
flex-direction: column;
|
|
6
|
+
gap: var(--size-1);
|
|
7
|
+
padding: var(--fs-alert-padding, var(--size-3) var(--size-4));
|
|
8
|
+
font-family: inherit;
|
|
9
|
+
font-size: var(--font-size-m);
|
|
10
|
+
line-height: 1.5;
|
|
11
|
+
color: var(--semantic-page-foreground);
|
|
12
|
+
background: var(--semantic-neutral-background);
|
|
13
|
+
border-radius: var(--fs-alert-radius, var(--size-1));
|
|
14
|
+
/* Fargen ligger i en strek på kanten framfor i hele flaten, så teksten
|
|
15
|
+
beholder kontrasten mot sideflaten i begge temaer. */
|
|
16
|
+
border-inline-start: var(--fs-alert-accent, var(--size-1)) solid
|
|
17
|
+
var(--semantic-neutral-foreground);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.fs-alert__title {
|
|
21
|
+
margin: 0;
|
|
22
|
+
font-size: var(--font-size-m);
|
|
23
|
+
font-weight: 600;
|
|
24
|
+
line-height: 1.5;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.fs-alert p {
|
|
28
|
+
margin: 0;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.fs-alert[data-color="success"] {
|
|
32
|
+
background: var(--semantic-success-background);
|
|
33
|
+
border-inline-start-color: var(--semantic-success-foreground);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.fs-alert[data-color="warning"] {
|
|
37
|
+
background: var(--semantic-warning-background);
|
|
38
|
+
border-inline-start-color: var(--semantic-warning-foreground);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.fs-alert[data-color="danger"] {
|
|
42
|
+
background: var(--semantic-danger-background);
|
|
43
|
+
border-inline-start-color: var(--semantic-danger-foreground);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.fs-alert[data-color="info"] {
|
|
47
|
+
background: var(--semantic-interactive-background);
|
|
48
|
+
border-inline-start-color: var(--semantic-interactive-main);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { attributes, createGuard } from "../shared.js"
|
|
2
|
+
|
|
3
|
+
export const ALERT_CLASS = "fs-alert" as const
|
|
4
|
+
/** Overskriften inne i meldingen. */
|
|
5
|
+
export const ALERT_TITLE_CLASS = "fs-alert__title" as const
|
|
6
|
+
|
|
7
|
+
export const alertColors = ["info", "success", "warning", "danger"] as const
|
|
8
|
+
|
|
9
|
+
export type AlertColor = (typeof alertColors)[number]
|
|
10
|
+
export type NonNeutralAlertColor = AlertColor
|
|
11
|
+
|
|
12
|
+
export type AlertOptions = {
|
|
13
|
+
/** Hva meldingen betyr. Utelatt gir den nøytrale. */
|
|
14
|
+
color?: AlertColor
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type AlertAttributes = {
|
|
18
|
+
class: typeof ALERT_CLASS
|
|
19
|
+
"data-color"?: AlertColor
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Attributtene for en melding som gjelder hele siden eller et helt skjema.
|
|
24
|
+
*
|
|
25
|
+
* Rollen settes ikke her, fordi den avhenger av når meldingen kommer.
|
|
26
|
+
* Står den i markupen fra start, skal den ikke ha noen rolle. Dukker den opp
|
|
27
|
+
* mens siden står stille, trenger den `role="status"`, eller `role="alert"`
|
|
28
|
+
* hvis brukeren må stoppe opp.
|
|
29
|
+
*
|
|
30
|
+
* ```ts
|
|
31
|
+
* <div {...alert({ color: "warning" })}>
|
|
32
|
+
* <p {...alert.title}>Søknaden er ikke sendt</p>
|
|
33
|
+
* </div>
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export const alert = Object.assign(
|
|
37
|
+
({ color }: AlertOptions = {}): AlertAttributes =>
|
|
38
|
+
attributes({
|
|
39
|
+
class: ALERT_CLASS,
|
|
40
|
+
"data-color": color,
|
|
41
|
+
}),
|
|
42
|
+
{
|
|
43
|
+
/** Klassen på overskriften inne i meldingen. */
|
|
44
|
+
title: ALERT_TITLE_CLASS,
|
|
45
|
+
colors: alertColors,
|
|
46
|
+
isColor: createGuard(alertColors),
|
|
47
|
+
},
|
|
48
|
+
)
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
@layer fristil {
|
|
2
|
+
.fs-avatar {
|
|
3
|
+
box-sizing: border-box;
|
|
4
|
+
display: inline-flex;
|
|
5
|
+
align-items: center;
|
|
6
|
+
justify-content: center;
|
|
7
|
+
inline-size: var(--fs-avatar-size, var(--size-10));
|
|
8
|
+
block-size: var(--fs-avatar-size, var(--size-10));
|
|
9
|
+
overflow: hidden;
|
|
10
|
+
font-family: inherit;
|
|
11
|
+
font-size: var(--font-size-s);
|
|
12
|
+
font-weight: 600;
|
|
13
|
+
line-height: 1;
|
|
14
|
+
color: var(--semantic-interactive-foreground);
|
|
15
|
+
background: var(--semantic-interactive-background);
|
|
16
|
+
border-radius: 50%;
|
|
17
|
+
/* Bildet kan være hva som helst. Ringen holder formen tydelig også mot en
|
|
18
|
+
lys flate i bildet. */
|
|
19
|
+
box-shadow: inset 0 0 0 1px var(--semantic-divider-30);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.fs-avatar img {
|
|
23
|
+
inline-size: 100%;
|
|
24
|
+
block-size: 100%;
|
|
25
|
+
object-fit: cover;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.fs-avatar[data-size="small"] {
|
|
29
|
+
inline-size: var(--size-6);
|
|
30
|
+
block-size: var(--size-6);
|
|
31
|
+
font-size: var(--font-size-xs);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.fs-avatar[data-size="large"] {
|
|
35
|
+
inline-size: var(--size-16);
|
|
36
|
+
block-size: var(--size-16);
|
|
37
|
+
font-size: var(--font-size-l);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.fs-avatar[data-variant="square"] {
|
|
41
|
+
border-radius: var(--fs-avatar-radius, var(--size-1));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/* Flere profilbilder som overlapper. Rekkefølgen snus i CSS, så det
|
|
45
|
+
første bildet ligger øverst uten at markupen må skrives baklengs. */
|
|
46
|
+
.fs-avatar-stack {
|
|
47
|
+
display: inline-flex;
|
|
48
|
+
flex-direction: row-reverse;
|
|
49
|
+
justify-content: flex-end;
|
|
50
|
+
margin: 0;
|
|
51
|
+
padding: 0;
|
|
52
|
+
list-style: none;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.fs-avatar-stack > * + * {
|
|
56
|
+
margin-inline-end: var(--fs-avatar-stack-overlap, calc(var(--size-2) * -1));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.fs-avatar-stack .fs-avatar {
|
|
60
|
+
box-shadow:
|
|
61
|
+
inset 0 0 0 1px var(--semantic-divider-30),
|
|
62
|
+
0 0 0 2px var(--semantic-page-background);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { attributes, createGuard } from "../shared.js"
|
|
2
|
+
|
|
3
|
+
export const AVATAR_CLASS = "fs-avatar" as const
|
|
4
|
+
/** Flere profilbilder som overlapper. */
|
|
5
|
+
export const AVATAR_STACK_CLASS = "fs-avatar-stack" as const
|
|
6
|
+
|
|
7
|
+
export const avatarSizes = ["small", "medium", "large"] as const
|
|
8
|
+
export const avatarVariants = ["circle", "square"] as const
|
|
9
|
+
|
|
10
|
+
export type AvatarSize = (typeof avatarSizes)[number]
|
|
11
|
+
export type AvatarVariant = (typeof avatarVariants)[number]
|
|
12
|
+
export type NonDefaultAvatarSize = Exclude<AvatarSize, "medium">
|
|
13
|
+
export type NonDefaultAvatarVariant = Exclude<AvatarVariant, "circle">
|
|
14
|
+
|
|
15
|
+
export type AvatarOptions = {
|
|
16
|
+
/** Størrelse. Standard: `medium`. */
|
|
17
|
+
size?: AvatarSize
|
|
18
|
+
/** Rund eller avrundet firkant. Standard: `circle`. */
|
|
19
|
+
variant?: AvatarVariant
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export type AvatarAttributes = {
|
|
23
|
+
class: typeof AVATAR_CLASS
|
|
24
|
+
"data-size"?: NonDefaultAvatarSize
|
|
25
|
+
"data-variant"?: NonDefaultAvatarVariant
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Attributtene for et profilbilde eller initialer.
|
|
30
|
+
*
|
|
31
|
+
* Står navnet allerede ved siden av, er bildet pynt: sett `aria-hidden="true"`
|
|
32
|
+
* på det, ellers leses navnet opp to ganger. Står bildet alene, må det ha en
|
|
33
|
+
* tekst som sier hvem det er.
|
|
34
|
+
*
|
|
35
|
+
* ```ts
|
|
36
|
+
* <span {...avatar({ size: "small" })} aria-hidden="true">ON</span>
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export const avatar = Object.assign(
|
|
40
|
+
({
|
|
41
|
+
size = "medium",
|
|
42
|
+
variant = "circle",
|
|
43
|
+
}: AvatarOptions = {}): AvatarAttributes =>
|
|
44
|
+
attributes({
|
|
45
|
+
class: AVATAR_CLASS,
|
|
46
|
+
"data-size": size === "medium" ? undefined : size,
|
|
47
|
+
"data-variant": variant === "circle" ? undefined : variant,
|
|
48
|
+
}),
|
|
49
|
+
{
|
|
50
|
+
/** Klassen på lista med profilbilder som overlapper. */
|
|
51
|
+
stack: AVATAR_STACK_CLASS,
|
|
52
|
+
sizes: avatarSizes,
|
|
53
|
+
isSize: createGuard(avatarSizes),
|
|
54
|
+
variants: avatarVariants,
|
|
55
|
+
isVariant: createGuard(avatarVariants),
|
|
56
|
+
},
|
|
57
|
+
)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
@layer fristil {
|
|
2
|
+
.fs-badge {
|
|
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: inline-flex;
|
|
9
|
+
align-items: center;
|
|
10
|
+
gap: var(--size-1);
|
|
11
|
+
padding: var(--fs-badge-padding, var(--size-0-5) var(--size-2));
|
|
12
|
+
font-family: inherit;
|
|
13
|
+
font-size: var(--font-size-xs);
|
|
14
|
+
font-weight: 600;
|
|
15
|
+
line-height: 1.5;
|
|
16
|
+
border-radius: var(--fs-badge-radius, var(--size-4));
|
|
17
|
+
white-space: nowrap;
|
|
18
|
+
user-select: none;
|
|
19
|
+
background: var(--semantic-interactive-background);
|
|
20
|
+
color: var(--semantic-interactive-main);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.fs-badge[data-color="success"] {
|
|
24
|
+
background: var(--semantic-success-background);
|
|
25
|
+
color: var(--semantic-success-foreground);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.fs-badge[data-color="warning"] {
|
|
29
|
+
background: var(--semantic-warning-background);
|
|
30
|
+
color: var(--semantic-warning-foreground);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.fs-badge[data-color="danger"] {
|
|
34
|
+
background: var(--semantic-danger-background);
|
|
35
|
+
color: var(--semantic-danger-foreground);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.fs-badge[data-color="neutral"] {
|
|
39
|
+
background: var(--semantic-neutral-background);
|
|
40
|
+
color: var(--semantic-neutral-foreground);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { attributes, createGuard } from "../shared.js"
|
|
2
|
+
|
|
3
|
+
export const BADGE_CLASS = "fs-badge" as const
|
|
4
|
+
|
|
5
|
+
export const badgeColors = [
|
|
6
|
+
"info",
|
|
7
|
+
"success",
|
|
8
|
+
"warning",
|
|
9
|
+
"danger",
|
|
10
|
+
"neutral",
|
|
11
|
+
] as const
|
|
12
|
+
|
|
13
|
+
export type BadgeColor = (typeof badgeColors)[number]
|
|
14
|
+
export type NonInfoBadgeColor = Exclude<BadgeColor, "info">
|
|
15
|
+
|
|
16
|
+
export type BadgeOptions = {
|
|
17
|
+
/** Hva tilstanden betyr. Standard: `info`. */
|
|
18
|
+
color?: BadgeColor
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type BadgeAttributes = {
|
|
22
|
+
class: typeof BADGE_CLASS
|
|
23
|
+
"data-color"?: NonInfoBadgeColor
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Attributtene for et statusmerke.
|
|
28
|
+
*
|
|
29
|
+
* ```ts
|
|
30
|
+
* <span {...badge({ color: "success" })}>Innvilget</span>
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export const badge = Object.assign(
|
|
34
|
+
({ color = "info" }: BadgeOptions = {}): BadgeAttributes =>
|
|
35
|
+
attributes({
|
|
36
|
+
class: BADGE_CLASS,
|
|
37
|
+
"data-color": color === "info" ? undefined : color,
|
|
38
|
+
}),
|
|
39
|
+
{
|
|
40
|
+
colors: badgeColors,
|
|
41
|
+
isColor: createGuard(badgeColors),
|
|
42
|
+
},
|
|
43
|
+
)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
@layer fristil {
|
|
2
|
+
.fs-breadcrumbs {
|
|
3
|
+
box-sizing: border-box;
|
|
4
|
+
display: flex;
|
|
5
|
+
flex-wrap: wrap;
|
|
6
|
+
align-items: center;
|
|
7
|
+
gap: var(--fs-breadcrumbs-gap, var(--size-2));
|
|
8
|
+
margin: 0;
|
|
9
|
+
padding: 0;
|
|
10
|
+
list-style: none;
|
|
11
|
+
font-family: inherit;
|
|
12
|
+
font-size: var(--font-size-s);
|
|
13
|
+
line-height: 1.5;
|
|
14
|
+
color: var(--semantic-muted-foreground);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.fs-breadcrumbs li {
|
|
18
|
+
display: flex;
|
|
19
|
+
align-items: center;
|
|
20
|
+
gap: var(--fs-breadcrumbs-gap, var(--size-2));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/* Skilletegnet er dekorasjon, og legges derfor inn med CSS. Sto det i
|
|
24
|
+
markupen, ville skjermlesere lest «skråstrek» mellom hvert steg. */
|
|
25
|
+
.fs-breadcrumbs li + li::before {
|
|
26
|
+
content: "/";
|
|
27
|
+
color: var(--semantic-muted-foreground);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.fs-breadcrumbs a {
|
|
31
|
+
color: var(--semantic-interactive-main);
|
|
32
|
+
text-decoration: underline;
|
|
33
|
+
text-underline-offset: 0.14em;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.fs-breadcrumbs a:hover {
|
|
37
|
+
color: var(--semantic-interactive-foreground);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.fs-breadcrumbs a:focus-visible {
|
|
41
|
+
outline: 2px solid var(--semantic-interactive-main);
|
|
42
|
+
outline-offset: 2px;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.fs-breadcrumbs [aria-current="page"] {
|
|
46
|
+
color: var(--semantic-page-foreground);
|
|
47
|
+
font-weight: 600;
|
|
48
|
+
text-decoration: none;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { attributes } from "../shared.js"
|
|
2
|
+
|
|
3
|
+
export const BREADCRUMBS_CLASS = "fs-breadcrumbs" as const
|
|
4
|
+
|
|
5
|
+
export type BreadcrumbsAttributes = {
|
|
6
|
+
class: typeof BREADCRUMBS_CLASS
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Attributtene for en brødsmulesti.
|
|
11
|
+
*
|
|
12
|
+
* Klassen hører på en `<ol>` inne i en `<nav>` med en tekst som sier hva
|
|
13
|
+
* navigasjonen er, for eksempel `aria-label="Du er her"`. Rekkefølgen betyr
|
|
14
|
+
* noe, så listen må være nummerert selv om tallene ikke vises.
|
|
15
|
+
*
|
|
16
|
+
* Skilletegnet mellom stegene legges inn med CSS. Sto det i markupen, ville
|
|
17
|
+
* skjermlesere lest «skråstrek» mellom hvert steg.
|
|
18
|
+
*
|
|
19
|
+
* ```ts
|
|
20
|
+
* <nav aria-label="Du er her">
|
|
21
|
+
* <ol {...breadcrumbs()}>
|
|
22
|
+
* <li><a href="/">Forsiden</a></li>
|
|
23
|
+
* <li><a href="/saker" aria-current="page">Mine saker</a></li>
|
|
24
|
+
* </ol>
|
|
25
|
+
* </nav>
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export const breadcrumbs = (): BreadcrumbsAttributes =>
|
|
29
|
+
attributes({ class: BREADCRUMBS_CLASS })
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
@layer fristil {
|
|
2
|
+
.fs-button {
|
|
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: inline-flex;
|
|
9
|
+
align-items: center;
|
|
10
|
+
gap: var(--size-2);
|
|
11
|
+
padding: var(--fs-button-padding, var(--size-2) var(--size-4));
|
|
12
|
+
font-family: inherit;
|
|
13
|
+
font-size: var(--font-size-m);
|
|
14
|
+
font-weight: 500;
|
|
15
|
+
line-height: 1.5;
|
|
16
|
+
border: 1px solid var(--semantic-interactive-main);
|
|
17
|
+
border-radius: var(--fs-button-radius, var(--size-1));
|
|
18
|
+
background: var(--semantic-interactive-main);
|
|
19
|
+
color: var(--semantic-interactive-contrast);
|
|
20
|
+
cursor: pointer;
|
|
21
|
+
text-decoration: none;
|
|
22
|
+
user-select: none;
|
|
23
|
+
|
|
24
|
+
/* Teksten får brekke. Med `white-space: nowrap` sprengte en knapp med en
|
|
25
|
+
lang norsk ledetekst boksen sin på en telefon, og da ruller hele siden
|
|
26
|
+
sidelengs. Knappen skal aldri bli bredere enn plassen den får.
|
|
27
|
+
|
|
28
|
+
`normal` står eksplisitt fordi `white-space` arves: en konsument med
|
|
29
|
+
`nowrap` på noe rundt ville ellers fått problemet tilbake. Målt i
|
|
30
|
+
Chromium holder `text-wrap: balance` alene, siden den setter
|
|
31
|
+
`text-wrap-mode: wrap` og dermed slår den arvede verdien, men den
|
|
32
|
+
eksplisitte linja gjelder også der `text-wrap` ikke er støttet. */
|
|
33
|
+
white-space: normal;
|
|
34
|
+
max-inline-size: 100%;
|
|
35
|
+
text-align: center;
|
|
36
|
+
text-wrap: balance;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.fs-button:hover {
|
|
40
|
+
background: var(--semantic-interactive-foreground);
|
|
41
|
+
border-color: var(--semantic-interactive-foreground);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.fs-button:focus-visible {
|
|
45
|
+
outline: 2px solid var(--semantic-interactive-main);
|
|
46
|
+
outline-offset: 2px;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.fs-button:disabled,
|
|
50
|
+
.fs-button[aria-disabled="true"] {
|
|
51
|
+
background: var(--semantic-disabled-background);
|
|
52
|
+
color: var(--semantic-disabled-foreground);
|
|
53
|
+
border-color: var(--semantic-disabled-background);
|
|
54
|
+
/* Ingen pointer-events: none her. Nettleseren blokkerer allerede et
|
|
55
|
+
deaktivert felt, og pointer-events ville bare hindret markøren i å
|
|
56
|
+
vises. Elementet blir da ikke truffet av musa i det hele tatt. */
|
|
57
|
+
cursor: not-allowed;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/* aria-disabled er bare en opplysning til skjermlesere; elementet virker
|
|
61
|
+
fortsatt. Her stopper pointer-events musa, på bekostning av markøren.
|
|
62
|
+
Skal feltet faktisk være av, bruk `disabled` eller `readonly`. */
|
|
63
|
+
.fs-button[aria-disabled="true"] {
|
|
64
|
+
pointer-events: none;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/* Sekundær: omriss, gjennomsiktig bakgrunn */
|
|
68
|
+
.fs-button[data-variant="secondary"] {
|
|
69
|
+
background: transparent;
|
|
70
|
+
color: var(--semantic-interactive-main);
|
|
71
|
+
border-color: var(--semantic-interactive-main);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.fs-button[data-variant="secondary"]:hover {
|
|
75
|
+
background: var(--semantic-interactive-background);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/* Ghost: ingen kant, ingen bakgrunn */
|
|
79
|
+
.fs-button[data-variant="ghost"] {
|
|
80
|
+
background: transparent;
|
|
81
|
+
color: var(--semantic-interactive-main);
|
|
82
|
+
border-color: transparent;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.fs-button[data-variant="ghost"]:hover {
|
|
86
|
+
background: var(--semantic-interactive-background);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/* Danger */
|
|
90
|
+
.fs-button[data-variant="danger"] {
|
|
91
|
+
background: var(--semantic-danger-background);
|
|
92
|
+
color: var(--semantic-danger-foreground);
|
|
93
|
+
border-color: var(--semantic-danger-foreground);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.fs-button[data-variant="danger"]:hover {
|
|
97
|
+
background: var(--semantic-danger-main);
|
|
98
|
+
border-color: var(--semantic-danger-main);
|
|
99
|
+
color: var(--semantic-danger-contrast);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { attributes, createGuard } from "../shared.js"
|
|
2
|
+
|
|
3
|
+
export const BUTTON_CLASS = "fs-button" as const
|
|
4
|
+
|
|
5
|
+
export const buttonVariants = [
|
|
6
|
+
"primary",
|
|
7
|
+
"secondary",
|
|
8
|
+
"ghost",
|
|
9
|
+
"danger",
|
|
10
|
+
] as const
|
|
11
|
+
|
|
12
|
+
export type ButtonVariant = (typeof buttonVariants)[number]
|
|
13
|
+
export type NonPrimaryButtonVariant = Exclude<ButtonVariant, "primary">
|
|
14
|
+
|
|
15
|
+
export type ButtonOptions = {
|
|
16
|
+
/** Hvor tung handlingen er. Standard: `primary`. */
|
|
17
|
+
variant?: ButtonVariant
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type ButtonAttributes = {
|
|
21
|
+
class: typeof BUTTON_CLASS
|
|
22
|
+
"data-variant"?: NonPrimaryButtonVariant
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Attributtene for en knapp.
|
|
27
|
+
*
|
|
28
|
+
* ```ts
|
|
29
|
+
* <button {...button({ variant: "secondary" })}>Lagre utkast</button>
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export const button = Object.assign(
|
|
33
|
+
({ variant = "primary" }: ButtonOptions = {}): ButtonAttributes =>
|
|
34
|
+
attributes({
|
|
35
|
+
class: BUTTON_CLASS,
|
|
36
|
+
"data-variant": variant === "primary" ? undefined : variant,
|
|
37
|
+
}),
|
|
38
|
+
{
|
|
39
|
+
/** De lovlige variantene, for oppslag og forgrening. */
|
|
40
|
+
variants: buttonVariants,
|
|
41
|
+
/** Sjekker om en streng utenfra er en gyldig variant. */
|
|
42
|
+
isVariant: createGuard(buttonVariants),
|
|
43
|
+
},
|
|
44
|
+
)
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
@layer fristil {
|
|
2
|
+
.fs-card {
|
|
3
|
+
box-sizing: border-box;
|
|
4
|
+
display: flex;
|
|
5
|
+
flex-direction: column;
|
|
6
|
+
gap: var(--size-2);
|
|
7
|
+
padding: var(--fs-card-padding, var(--size-4));
|
|
8
|
+
font-family: inherit;
|
|
9
|
+
color: var(--semantic-page-foreground);
|
|
10
|
+
background: var(--semantic-page-background);
|
|
11
|
+
border: 1px solid var(--semantic-divider-30);
|
|
12
|
+
border-radius: var(--fs-card-radius, var(--size-2));
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.fs-card__title {
|
|
16
|
+
margin: 0;
|
|
17
|
+
font-size: var(--font-size-l);
|
|
18
|
+
font-weight: 600;
|
|
19
|
+
line-height: 1.4;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.fs-card p {
|
|
23
|
+
margin: 0;
|
|
24
|
+
line-height: 1.6;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.fs-card[data-variant="filled"] {
|
|
28
|
+
background: var(--semantic-neutral-background);
|
|
29
|
+
border-color: transparent;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/* Hele kortet er klikkbart. Elementet er da en <a> eller en <button>, og
|
|
33
|
+
fokusmarkeringen kommer derfra, ikke herfra. */
|
|
34
|
+
.fs-card[data-interactive] {
|
|
35
|
+
text-align: start;
|
|
36
|
+
text-decoration: none;
|
|
37
|
+
cursor: pointer;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.fs-card[data-interactive]:hover {
|
|
41
|
+
border-color: var(--semantic-interactive-main);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.fs-card[data-interactive]:focus-visible {
|
|
45
|
+
outline: 2px solid var(--semantic-interactive-main);
|
|
46
|
+
outline-offset: 2px;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { attributes, createGuard } from "../shared.js"
|
|
2
|
+
|
|
3
|
+
export const CARD_CLASS = "fs-card" as const
|
|
4
|
+
/** Overskriften inne i kortet. */
|
|
5
|
+
export const CARD_TITLE_CLASS = "fs-card__title" as const
|
|
6
|
+
|
|
7
|
+
export const cardVariants = ["outline", "filled"] as const
|
|
8
|
+
|
|
9
|
+
export type CardVariant = (typeof cardVariants)[number]
|
|
10
|
+
export type NonDefaultCardVariant = Exclude<CardVariant, "outline">
|
|
11
|
+
|
|
12
|
+
export type CardOptions = {
|
|
13
|
+
/** Flate eller omriss. Standard: `outline`. */
|
|
14
|
+
variant?: CardVariant
|
|
15
|
+
/** Hele kortet er klikkbart. Bruk bare på `<a>` eller `<button>`. */
|
|
16
|
+
interactive?: boolean
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export type CardAttributes = {
|
|
20
|
+
class: typeof CARD_CLASS
|
|
21
|
+
"data-variant"?: NonDefaultCardVariant
|
|
22
|
+
"data-interactive"?: ""
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Attributtene for et kort.
|
|
27
|
+
*
|
|
28
|
+
* `interactive` gir hover og fokusmarkering, men gjør ingenting klikkbart av
|
|
29
|
+
* seg selv. Skal hele kortet kunne trykkes på, må elementet være en `<a>`
|
|
30
|
+
* eller en `<button>`. Et `<div>` med en klikklytter kan hverken nås med
|
|
31
|
+
* tastatur eller meldes som noe å trykke på.
|
|
32
|
+
*
|
|
33
|
+
* ```ts
|
|
34
|
+
* <a {...card({ interactive: true })} href="/sak/481">…</a>
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export const card = Object.assign(
|
|
38
|
+
({ variant = "outline", interactive }: CardOptions = {}): CardAttributes =>
|
|
39
|
+
attributes({
|
|
40
|
+
class: CARD_CLASS,
|
|
41
|
+
"data-variant": variant === "outline" ? undefined : variant,
|
|
42
|
+
"data-interactive": interactive ? ("" as const) : undefined,
|
|
43
|
+
}),
|
|
44
|
+
{
|
|
45
|
+
/** Klassen på overskriften inne i kortet. */
|
|
46
|
+
title: CARD_TITLE_CLASS,
|
|
47
|
+
variants: cardVariants,
|
|
48
|
+
isVariant: createGuard(cardVariants),
|
|
49
|
+
},
|
|
50
|
+
)
|