@fellesdatakatalog/ui 1.0.15 → 1.0.16
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/package.json +11 -2
- package/src/components/app-bar/app-bar.stories.tsx +22 -0
- package/src/components/app-bar/index.tsx +12 -0
- package/src/components/app-bar/styles.module.scss +7 -0
- package/src/components/checkbox-group/checkbox-group.stories.tsx +143 -0
- package/src/components/checkbox-group/index.tsx +105 -0
- package/src/components/copy-button/copy-button.stories.tsx +28 -0
- package/src/components/copy-button/index.tsx +54 -0
- package/src/components/core/ds-overrides.scss +105 -0
- package/src/components/core/mixins.scss +143 -0
- package/src/components/core/reset.css +131 -0
- package/src/components/core/tokens.scss +26 -0
- package/src/components/helptext/helptext.module.scss +46 -0
- package/src/components/helptext/helptext.stories.tsx +42 -0
- package/src/components/helptext/index.tsx +37 -0
- package/src/components/hstack/hstack.module.scss +5 -0
- package/src/components/hstack/hstack.stories.tsx +38 -0
- package/src/components/hstack/index.tsx +17 -0
- package/src/components/org-logo/index.tsx +27 -0
- package/src/components/org-logo/org-logo.stories.tsx +35 -0
- package/src/components/org-logo/styles.module.scss +20 -0
- package/src/components/radio-group/index.tsx +101 -0
- package/src/components/radio-group/radio-group.stories.tsx +141 -0
- package/src/components/tag-list/index.tsx +39 -0
- package/src/components/tag-list/styles.module.scss +8 -0
- package/src/components/tag-list/tag-list.stories.tsx +107 -0
- package/src/components/vstack/index.tsx +16 -0
- package/src/components/vstack/vstack.module.scss +5 -0
- package/src/components/vstack/vstack.stories.tsx +37 -0
- package/src/index.ts +11 -0
- package/src/styles/storybook.scss +34 -0
- package/src/types/scss.d.ts +9 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fellesdatakatalog/ui",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.16",
|
|
4
4
|
"description": "React UI components for Felles Datakatalog",
|
|
5
5
|
"main": "dist/shared-ui.cjs",
|
|
6
6
|
"module": "dist/shared-ui.es.js",
|
|
@@ -15,10 +15,19 @@
|
|
|
15
15
|
"import": "./dist/shared-ui.es.js",
|
|
16
16
|
"require": "./dist/shared-ui.cjs"
|
|
17
17
|
},
|
|
18
|
-
"./dist/styles.css": "./dist/styles.css"
|
|
18
|
+
"./dist/styles.css": "./dist/styles.css",
|
|
19
|
+
"./src/components/core/tokens.scss": "./src/components/core/tokens.scss",
|
|
20
|
+
"./src/components/core/tokens": "./src/components/core/tokens.scss",
|
|
21
|
+
"./src/components/core/mixins.scss": "./src/components/core/mixins.scss",
|
|
22
|
+
"./src/components/core/mixins": "./src/components/core/mixins.scss",
|
|
23
|
+
"./src/components/core/ds-overrides.scss": "./src/components/core/ds-overrides.scss",
|
|
24
|
+
"./src/components/core/ds-overrides": "./src/components/core/ds-overrides.scss",
|
|
25
|
+
"./src/components/core/reset.css": "./src/components/core/reset.css",
|
|
26
|
+
"./src/components/core/reset": "./src/components/core/reset.css"
|
|
19
27
|
},
|
|
20
28
|
"files": [
|
|
21
29
|
"dist",
|
|
30
|
+
"src",
|
|
22
31
|
"README.md"
|
|
23
32
|
],
|
|
24
33
|
"scripts": {
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
|
|
3
|
+
import { AppBar } from '.';
|
|
4
|
+
|
|
5
|
+
const meta: Meta<typeof AppBar> = {
|
|
6
|
+
component: AppBar,
|
|
7
|
+
title: 'AppBar',
|
|
8
|
+
parameters: {
|
|
9
|
+
layout: 'centered',
|
|
10
|
+
},
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export default meta;
|
|
14
|
+
type Story = StoryObj<typeof AppBar>;
|
|
15
|
+
|
|
16
|
+
export const Default: Story = {
|
|
17
|
+
render: () => (
|
|
18
|
+
<div style={{ padding: '1rem' }}>
|
|
19
|
+
<AppBar />
|
|
20
|
+
</div>
|
|
21
|
+
),
|
|
22
|
+
};
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import CheckboxGroup from '.';
|
|
4
|
+
|
|
5
|
+
const meta: Meta<typeof CheckboxGroup> = {
|
|
6
|
+
component: CheckboxGroup,
|
|
7
|
+
title: 'CheckboxGroup',
|
|
8
|
+
argTypes: {
|
|
9
|
+
legend: {
|
|
10
|
+
control: 'text',
|
|
11
|
+
description: 'The legend text for the fieldset',
|
|
12
|
+
},
|
|
13
|
+
description: {
|
|
14
|
+
control: 'text',
|
|
15
|
+
description: 'Optional description text',
|
|
16
|
+
},
|
|
17
|
+
error: {
|
|
18
|
+
control: 'text',
|
|
19
|
+
description: 'Error message to display',
|
|
20
|
+
},
|
|
21
|
+
disabled: {
|
|
22
|
+
control: 'boolean',
|
|
23
|
+
description: 'Disable all checkboxes',
|
|
24
|
+
},
|
|
25
|
+
readOnly: {
|
|
26
|
+
control: 'boolean',
|
|
27
|
+
description: 'Make all checkboxes read-only',
|
|
28
|
+
},
|
|
29
|
+
required: {
|
|
30
|
+
control: 'boolean',
|
|
31
|
+
description: 'Make all checkboxes required',
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export default meta;
|
|
37
|
+
type Story = StoryObj<typeof CheckboxGroup>;
|
|
38
|
+
|
|
39
|
+
const defaultOptions = [
|
|
40
|
+
{ value: 'epost', label: 'E-post' },
|
|
41
|
+
{ value: 'telefon', label: 'Telefon' },
|
|
42
|
+
{ value: 'sms', label: 'SMS' },
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
export const Primary: Story = {
|
|
46
|
+
render: () => {
|
|
47
|
+
const [value, setValue] = useState<string[]>(['epost']);
|
|
48
|
+
return (
|
|
49
|
+
<CheckboxGroup
|
|
50
|
+
legend="Hvordan vil du helst at vi skal kontakte deg?"
|
|
51
|
+
description="Velg alle alternativene som er relevante for deg."
|
|
52
|
+
options={defaultOptions}
|
|
53
|
+
value={value}
|
|
54
|
+
onChange={setValue}
|
|
55
|
+
/>
|
|
56
|
+
);
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export const Uncontrolled: Story = {
|
|
61
|
+
render: () => (
|
|
62
|
+
<CheckboxGroup
|
|
63
|
+
legend="Hvordan vil du helst at vi skal kontakte deg?"
|
|
64
|
+
description="Velg alle alternativene som er relevante for deg."
|
|
65
|
+
options={defaultOptions}
|
|
66
|
+
/>
|
|
67
|
+
),
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export const WithError: Story = {
|
|
71
|
+
render: () => (
|
|
72
|
+
<CheckboxGroup
|
|
73
|
+
legend="Hvordan vil du helst at vi skal kontakte deg?"
|
|
74
|
+
options={defaultOptions}
|
|
75
|
+
error="Du må velge minst ett alternativ."
|
|
76
|
+
required
|
|
77
|
+
/>
|
|
78
|
+
),
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export const Disabled: Story = {
|
|
82
|
+
render: () => (
|
|
83
|
+
<CheckboxGroup
|
|
84
|
+
legend="Hvordan vil du helst at vi skal kontakte deg?"
|
|
85
|
+
options={defaultOptions}
|
|
86
|
+
disabled
|
|
87
|
+
/>
|
|
88
|
+
),
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
export const ReadOnly: Story = {
|
|
92
|
+
render: () => (
|
|
93
|
+
<CheckboxGroup
|
|
94
|
+
legend="Hvordan vil du helst at vi skal kontakte deg?"
|
|
95
|
+
options={defaultOptions}
|
|
96
|
+
value={['epost', 'telefon']}
|
|
97
|
+
readOnly
|
|
98
|
+
/>
|
|
99
|
+
),
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
export const WithIndeterminate: Story = {
|
|
103
|
+
render: () => {
|
|
104
|
+
const [value, setValue] = useState<string[]>([]);
|
|
105
|
+
return (
|
|
106
|
+
<CheckboxGroup
|
|
107
|
+
legend="Velg kommunikasjonskanaler"
|
|
108
|
+
options={[
|
|
109
|
+
{
|
|
110
|
+
value: 'all',
|
|
111
|
+
label: 'Velg alle',
|
|
112
|
+
allowIndeterminate: true,
|
|
113
|
+
},
|
|
114
|
+
{ value: 'epost', label: 'E-post' },
|
|
115
|
+
{ value: 'telefon', label: 'Telefon' },
|
|
116
|
+
{ value: 'sms', label: 'SMS' },
|
|
117
|
+
]}
|
|
118
|
+
value={value}
|
|
119
|
+
onChange={setValue}
|
|
120
|
+
/>
|
|
121
|
+
);
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
export const WithDescriptions: Story = {
|
|
126
|
+
render: () => {
|
|
127
|
+
const [value, setValue] = useState<string[]>([]);
|
|
128
|
+
return (
|
|
129
|
+
<CheckboxGroup
|
|
130
|
+
legend="Velg dine interesser"
|
|
131
|
+
description="Du kan velge flere alternativer"
|
|
132
|
+
options={[
|
|
133
|
+
{ value: 'sport', label: 'Sport', description: 'Fotball, håndball, og mer' },
|
|
134
|
+
{ value: 'musikk', label: 'Musikk', description: 'Konserter og festivaler' },
|
|
135
|
+
{ value: 'mat', label: 'Mat og drikke', description: 'Restauranter og oppskrifter' },
|
|
136
|
+
]}
|
|
137
|
+
value={value}
|
|
138
|
+
onChange={setValue}
|
|
139
|
+
/>
|
|
140
|
+
);
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import React, { type ReactNode } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
Fieldset,
|
|
4
|
+
Checkbox,
|
|
5
|
+
ValidationMessage,
|
|
6
|
+
type UseCheckboxGroupProps,
|
|
7
|
+
useCheckboxGroup,
|
|
8
|
+
} from '@digdir/designsystemet-react';
|
|
9
|
+
|
|
10
|
+
export interface CheckboxGroupProps extends Omit<UseCheckboxGroupProps, 'value' | 'onChange'> {
|
|
11
|
+
/**
|
|
12
|
+
* The legend text for the fieldset
|
|
13
|
+
*/
|
|
14
|
+
legend?: ReactNode;
|
|
15
|
+
/**
|
|
16
|
+
* Optional description text for the fieldset
|
|
17
|
+
*/
|
|
18
|
+
description?: ReactNode;
|
|
19
|
+
/**
|
|
20
|
+
* Array of checkbox options
|
|
21
|
+
*/
|
|
22
|
+
options: Array<{
|
|
23
|
+
value: string;
|
|
24
|
+
label: string;
|
|
25
|
+
description?: string;
|
|
26
|
+
disabled?: boolean;
|
|
27
|
+
allowIndeterminate?: boolean;
|
|
28
|
+
}>;
|
|
29
|
+
/**
|
|
30
|
+
* Controlled value - array of selected checkbox values
|
|
31
|
+
*/
|
|
32
|
+
value?: string[];
|
|
33
|
+
/**
|
|
34
|
+
* Callback when the selected values change
|
|
35
|
+
*/
|
|
36
|
+
onChange?: (value: string[]) => void;
|
|
37
|
+
/**
|
|
38
|
+
* Optional className for the fieldset
|
|
39
|
+
*/
|
|
40
|
+
className?: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* CheckboxGroup component that wraps multiple Checkbox components in a Fieldset.
|
|
45
|
+
* Handles state management, onChange, and value automatically.
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* <CheckboxGroup
|
|
49
|
+
* legend="Hvordan vil du kontaktes?"
|
|
50
|
+
* description="Velg alle relevante alternativer"
|
|
51
|
+
* options={[
|
|
52
|
+
* { value: 'epost', label: 'E-post' },
|
|
53
|
+
* { value: 'telefon', label: 'Telefon' },
|
|
54
|
+
* { value: 'sms', label: 'SMS' },
|
|
55
|
+
* ]}
|
|
56
|
+
* value={selectedValues}
|
|
57
|
+
* onChange={(values) => setSelectedValues(values)}
|
|
58
|
+
* />
|
|
59
|
+
*/
|
|
60
|
+
export const CheckboxGroup = ({
|
|
61
|
+
legend,
|
|
62
|
+
description,
|
|
63
|
+
options,
|
|
64
|
+
value,
|
|
65
|
+
onChange,
|
|
66
|
+
error,
|
|
67
|
+
disabled,
|
|
68
|
+
readOnly,
|
|
69
|
+
required,
|
|
70
|
+
name,
|
|
71
|
+
className,
|
|
72
|
+
}: CheckboxGroupProps) => {
|
|
73
|
+
const { getCheckboxProps, validationMessageProps } = useCheckboxGroup({
|
|
74
|
+
value,
|
|
75
|
+
onChange: onChange ? (nextValue) => onChange(nextValue) : undefined,
|
|
76
|
+
error,
|
|
77
|
+
disabled,
|
|
78
|
+
readOnly,
|
|
79
|
+
required,
|
|
80
|
+
name,
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
return (
|
|
84
|
+
<Fieldset className={className}>
|
|
85
|
+
{legend && <Fieldset.Legend>{legend}</Fieldset.Legend>}
|
|
86
|
+
{description && <Fieldset.Description>{description}</Fieldset.Description>}
|
|
87
|
+
{options.map((option) => (
|
|
88
|
+
<Checkbox
|
|
89
|
+
key={option.value}
|
|
90
|
+
label={option.label}
|
|
91
|
+
description={option.description}
|
|
92
|
+
disabled={option.disabled}
|
|
93
|
+
{...getCheckboxProps({
|
|
94
|
+
value: option.value,
|
|
95
|
+
allowIndeterminate: option.allowIndeterminate,
|
|
96
|
+
})}
|
|
97
|
+
/>
|
|
98
|
+
))}
|
|
99
|
+
{error && <ValidationMessage {...validationMessageProps} />}
|
|
100
|
+
</Fieldset>
|
|
101
|
+
);
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
export default CheckboxGroup;
|
|
105
|
+
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
|
|
3
|
+
import CopyButton from '.';
|
|
4
|
+
|
|
5
|
+
const meta: Meta<typeof CopyButton> = {
|
|
6
|
+
component: CopyButton,
|
|
7
|
+
title: 'CopyButton',
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export default meta;
|
|
11
|
+
type Story = StoryObj<typeof CopyButton>;
|
|
12
|
+
|
|
13
|
+
export const Primary: Story = {
|
|
14
|
+
parameters: {
|
|
15
|
+
nextjs: {
|
|
16
|
+
appDirectory: true,
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
render: () => (
|
|
20
|
+
<div style={{ padding: '1rem' }}>
|
|
21
|
+
<CopyButton
|
|
22
|
+
copyLabel={'Kopier'}
|
|
23
|
+
copiedLabel={'Kopiert!'}
|
|
24
|
+
copyOnClick={'Tekst å kopiere'}
|
|
25
|
+
/>
|
|
26
|
+
</div>
|
|
27
|
+
),
|
|
28
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useState } from 'react';
|
|
4
|
+
import { Button, type ButtonProps, Tooltip } from '@digdir/designsystemet-react';
|
|
5
|
+
import { FilesIcon, CheckmarkCircleFillIcon } from '@navikt/aksel-icons';
|
|
6
|
+
import HStack from '../hstack';
|
|
7
|
+
|
|
8
|
+
type CopyButtonProps = {
|
|
9
|
+
copyLabel: string;
|
|
10
|
+
copiedLabel: string;
|
|
11
|
+
copyOnClick: string;
|
|
12
|
+
} & ButtonProps & React.HTMLAttributes<HTMLButtonElement>;
|
|
13
|
+
|
|
14
|
+
const CopyButton = ({ copyLabel, copiedLabel, copyOnClick, ...props }: CopyButtonProps) => {
|
|
15
|
+
const [clicked, setClicked] = useState(false);
|
|
16
|
+
|
|
17
|
+
const copyToClipboard = (text: string) => {
|
|
18
|
+
setClicked(true);
|
|
19
|
+
navigator.clipboard.writeText(text);
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<Tooltip
|
|
24
|
+
// @ts-expect-error: ignore complaint that Element cannot be set as content
|
|
25
|
+
content={
|
|
26
|
+
clicked ? (
|
|
27
|
+
<HStack>
|
|
28
|
+
<CheckmarkCircleFillIcon
|
|
29
|
+
color='#D1F4E1'
|
|
30
|
+
fontSize='1.2em'
|
|
31
|
+
/>
|
|
32
|
+
{copiedLabel}
|
|
33
|
+
</HStack>
|
|
34
|
+
) : (
|
|
35
|
+
copyLabel
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
placement='top'
|
|
39
|
+
>
|
|
40
|
+
<Button
|
|
41
|
+
data-size='sm'
|
|
42
|
+
variant='secondary'
|
|
43
|
+
onClick={() => copyToClipboard(copyOnClick)}
|
|
44
|
+
onMouseOver={() => setClicked(false)}
|
|
45
|
+
{...props}
|
|
46
|
+
>
|
|
47
|
+
<FilesIcon aria-hidden />
|
|
48
|
+
<span className={'sr-only'}>{copyLabel}</span>
|
|
49
|
+
</Button>
|
|
50
|
+
</Tooltip>
|
|
51
|
+
);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export default CopyButton;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
@use './mixins' as core-mixins;
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
Remove borders when table only has one row and data-border is false or missing
|
|
5
|
+
*/
|
|
6
|
+
.ds-table {
|
|
7
|
+
&:not([data-border='true']) {
|
|
8
|
+
tr:only-child {
|
|
9
|
+
td {
|
|
10
|
+
border: 0;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/*
|
|
17
|
+
Give Alerts proper content spacing
|
|
18
|
+
*/
|
|
19
|
+
.ds-alert {
|
|
20
|
+
@include core-mixins.article();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/*
|
|
24
|
+
Alert disables icon when first-child is heading, this fix also removes
|
|
25
|
+
left extra left padding when icon is disabled/hidden
|
|
26
|
+
*/
|
|
27
|
+
.ds-alert {
|
|
28
|
+
&:has(> :is(h1, h2, h3, h4, h5, h6):first-child) {
|
|
29
|
+
padding-inline-start: var(--ds-size-6);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/*
|
|
34
|
+
Remove dark link focus style on link button focus
|
|
35
|
+
*/
|
|
36
|
+
.ds-link {
|
|
37
|
+
&.ds-button {
|
|
38
|
+
&:focus-visible {
|
|
39
|
+
// Set appropriate background on various buttons
|
|
40
|
+
&[data-variant='primary'] {
|
|
41
|
+
background: var(--ds-color-accent-base-default);
|
|
42
|
+
}
|
|
43
|
+
&[data-variant='secondary'],
|
|
44
|
+
&[data-variant='tertiary'] {
|
|
45
|
+
background: transparent;
|
|
46
|
+
color: var(--ds-color-accent-text-subtle);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/*
|
|
53
|
+
Remove link styling for links missing href attribute
|
|
54
|
+
*/
|
|
55
|
+
.ds-link {
|
|
56
|
+
&:not([href]) {
|
|
57
|
+
color: var(--fdk-color-text);
|
|
58
|
+
text-decoration: none;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/*
|
|
63
|
+
DS override: Disable :visited link styling for links and link buttons
|
|
64
|
+
*/
|
|
65
|
+
.ds-link {
|
|
66
|
+
&:not(.ds-button) {
|
|
67
|
+
// Target only passive state
|
|
68
|
+
&:visited:not(:hover):not(:focus):not(:focus-visible):not(:active) {
|
|
69
|
+
color: var(--ds-color-text-subtle);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
&.ds-button {
|
|
73
|
+
// Target only passive state
|
|
74
|
+
&:visited:not(:hover):not(:focus):not(:focus-visible):not(:active) {
|
|
75
|
+
color: var(--dsc-button-color);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/*
|
|
81
|
+
Make icons in links have same color as text
|
|
82
|
+
*/
|
|
83
|
+
.ds-link {
|
|
84
|
+
svg {
|
|
85
|
+
fill: currentColor;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/*
|
|
90
|
+
Disable pointer events on SVG icons inside buttons to prevent click interference
|
|
91
|
+
*/
|
|
92
|
+
.ds-button {
|
|
93
|
+
svg {
|
|
94
|
+
pointer-events: none;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/*
|
|
99
|
+
Hack to prevent underline on links inside tags
|
|
100
|
+
*/
|
|
101
|
+
.ds-tag {
|
|
102
|
+
a:not(:hover) {
|
|
103
|
+
text-decoration: none;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
@mixin sr-only {
|
|
2
|
+
border: 0;
|
|
3
|
+
clip: rect(0 0 0 0);
|
|
4
|
+
height: 1px;
|
|
5
|
+
overflow: hidden;
|
|
6
|
+
padding: 0;
|
|
7
|
+
position: absolute;
|
|
8
|
+
white-space: nowrap;
|
|
9
|
+
width: 1px;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
@mixin focus-inline {
|
|
13
|
+
background: var(--ds-color-focus-outer);
|
|
14
|
+
color: var(--ds-color-focus-inner);
|
|
15
|
+
outline: 2px solid transparent;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
@mixin focus-block {
|
|
19
|
+
box-shadow: var(--_ds--focus, var(--dsc-focus-boxShadow));
|
|
20
|
+
outline: var(--_ds--focus, var(--dsc-focus-outline));
|
|
21
|
+
outline-offset: var(--_ds--focus, var(--ds-border-width-focus));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/*
|
|
25
|
+
Remember to set data-color-scheme='dark' on element or container
|
|
26
|
+
so that focus-block color tokens are correct.
|
|
27
|
+
*/
|
|
28
|
+
@mixin focus-block-dark {
|
|
29
|
+
@include focus-block();
|
|
30
|
+
background: transparent;
|
|
31
|
+
color: var(--fdk-color-link-negative);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
@mixin article {
|
|
35
|
+
line-height: 1.5em;
|
|
36
|
+
|
|
37
|
+
> * + * {
|
|
38
|
+
margin-top: 1rem;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
> * + div {
|
|
42
|
+
margin: 2rem 0;
|
|
43
|
+
|
|
44
|
+
&:last-child {
|
|
45
|
+
margin-bottom: 0;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
> h1,
|
|
50
|
+
> h2,
|
|
51
|
+
> h3,
|
|
52
|
+
> h4,
|
|
53
|
+
> h5,
|
|
54
|
+
> h6 {
|
|
55
|
+
font-weight: 500;
|
|
56
|
+
margin-bottom: 1em;
|
|
57
|
+
line-height: 1.25em;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
> h1 {
|
|
61
|
+
font-size: 2rem;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
> h2 {
|
|
65
|
+
font-size: 1.5rem;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
> h3 {
|
|
69
|
+
font-size: 1.25rem;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
> h4 {
|
|
73
|
+
font-size: 1.125rem;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
> h1,
|
|
77
|
+
> h2,
|
|
78
|
+
> h3,
|
|
79
|
+
> h4,
|
|
80
|
+
> h5 {
|
|
81
|
+
&:not(:first-child) {
|
|
82
|
+
margin-top: 1.5em;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
ul,
|
|
87
|
+
ol {
|
|
88
|
+
&:not([class]) {
|
|
89
|
+
list-style: none;
|
|
90
|
+
padding: 0 0 0 1.25rem;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
ul,
|
|
95
|
+
ol {
|
|
96
|
+
&:not([class]) {
|
|
97
|
+
li {
|
|
98
|
+
margin-bottom: 0.5rem;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
ul {
|
|
104
|
+
&:not([class]) {
|
|
105
|
+
list-style: disc;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
ol {
|
|
110
|
+
&:not([class]) {
|
|
111
|
+
list-style: decimal;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
hr {
|
|
116
|
+
height: 1px;
|
|
117
|
+
border: 0;
|
|
118
|
+
background: var(--fds-semantic-border-neutral-subtle);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
img {
|
|
122
|
+
max-width: 100%;
|
|
123
|
+
padding: 0.5rem;
|
|
124
|
+
border: 1px solid #eee;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
code {
|
|
128
|
+
&:not([class]) {
|
|
129
|
+
background: rgba(0, 0, 0, 0.05);
|
|
130
|
+
font-size: 0.85em;
|
|
131
|
+
padding: 0.2em 0.4em;
|
|
132
|
+
white-space: break-spaces;
|
|
133
|
+
border-radius: 6px;
|
|
134
|
+
color: #444;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
pre {
|
|
139
|
+
> div:focus-visible {
|
|
140
|
+
@include focus-block();
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|