Skip to content

Commit

Permalink
fix(List): css principles (#2348)
Browse files Browse the repository at this point in the history
- Using principles from #2295
- Removing `asChild` on ListOrdered and ListUnordered as using other
tags will break HTML validity and remove built in list-type styling
- Removing the wrapping `div` caused by `List.Root` so root truly
becomes a pure context provider and simplify DOM
- Replacing `List.Root`, `List.Heading`, `List.Unordered`,
`List.Ordered` with `<List variant="unordered | ordered | none">` for
more HTML-aligned API and less verbose DOM
- Automatic connect heading with list if provided - not a must, but a
nice a11y enhancement
- Fix a11y for VoiceOver when `list-style: none`

---------

Co-authored-by: Tobias Barsnes <[email protected]>
Co-authored-by: Michael Marszalek <[email protected]>
  • Loading branch information
3 people authored Sep 12, 2024
1 parent 5a46662 commit f3abcda
Show file tree
Hide file tree
Showing 18 changed files with 456 additions and 546 deletions.
6 changes: 6 additions & 0 deletions .changeset/happy-hounds-tie.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@digdir/designsystemet-css": patch
"@digdir/designsystemet-react": patch
---

List: Remove `List.Root` and `List.Heading`, which changes API
142 changes: 67 additions & 75 deletions apps/storefront/app/god-praksis/tilgjengelighet/kontrast/page.mdx
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import {
Card,
CardContent,
Heading,
List,
ListRoot,
ListHeading,
ListUnordered,
ListItem,
} from '@digdir/designsystemet-react';

{/* The importing should allow likeList.Root, but it throw a react error */}

import { Image } from '@components';
import { PageLayout } from '@layouts';
import { Contributors } from '@blog';
Expand Down Expand Up @@ -40,92 +38,86 @@ Alle brukerne, også de med svekket syn, skal kunne se innholdet i digitale tjen

<Card color='brand3'>
<CardContent>
<ListRoot>
<ListHeading
level={3}
size='xs'
>
Gjeldende regelverk, WCAG 2.1
</ListHeading>
<ListUnordered>
<ListItem>
**1.4.3 Kontrast (minimum) (Nivå AA)**: Kontrastforholdet mellom
teksten og bakgrunnen er minst 4,5:1. [1.4.3 Kontrast (minimum), WCAG
2.1
(w3.org)](https://www.w3.org/Translations/WCAG21-no/#contrast-minimum)
</ListItem>
<ListItem>
**1.4.11 Kontrast for ikke-tekstlig innhold (Nivå AA)**: Den visuelle
presentasjonen av det følgende har et kontrastforhold på minst 3:1 mot
farge(r) som ligger ved siden av. [1.4.11 Kontrast for ikke-tekstlig
innhold, WCAG 2.1
(w3.org)](https://www.w3.org/Translations/WCAG21-no/#non-text-contrast)
</ListItem>
</ListUnordered>
</ListRoot>
<Heading
level={3}
size='xs'
>
Gjeldende regelverk, WCAG 2.1
</Heading>
<ListUnordered>
<ListItem>
**1.4.3 Kontrast (minimum) (Nivå AA)**: Kontrastforholdet mellom
teksten og bakgrunnen er minst 4,5:1. [1.4.3 Kontrast (minimum), WCAG
2.1
(w3.org)](https://www.w3.org/Translations/WCAG21-no/#contrast-minimum)
</ListItem>
<ListItem>
**1.4.11 Kontrast for ikke-tekstlig innhold (Nivå AA)**: Den visuelle
presentasjonen av det følgende har et kontrastforhold på minst 3:1 mot
farge(r) som ligger ved siden av. [1.4.11 Kontrast for ikke-tekstlig
innhold, WCAG 2.1
(w3.org)](https://www.w3.org/Translations/WCAG21-no/#non-text-contrast)
</ListItem>
</ListUnordered>
</CardContent>
</Card>

<br />

<Card color='brand2'>
<CardContent>
<ListRoot>
<ListHeading
level={3}
size='xs'
>
Fremtidig eller strengere:
</ListHeading>
<ListUnordered>
<ListItem>
**1.4.6 Kontrast** (forbedret) (Nivå AAA): Den visuelle presentasjonen
av tekst og bilder av tekst har et kontrastforhold på minst 7:1,
unntatt uvesentlig tekst og skriftstørrelser større enn 18px eller
14px fet. [ 1.4.6 Kontrast (forbedret), WCAG 2.1
(w3.org)](https://www.w3.org/Translations/WCAG21-no/#contrast-enhanced)
</ListItem>
<ListItem>
**WCAG 2.2: 2.4.13** Focus Appearance (Nivå AAA), om utseende til
fokusmarkering krever at fokusindikator har en kontrastverdi på 3:1
mellom samme piksler i fokusert og ikke-fokusert tilstand.
[Understanding Success Criterion 2.4.13: Focus Appearance | WAI |
W3C](https://www.w3.org/WAI/WCAG22/Understanding/focus-appearance.html)
</ListItem>
<ListItem>
**WCAG 3** har et krav om farge og kontrast, visuell kontrast i tekst
(sølv): Sørg for tilstrekkelig kontrast mellom tekst i forgrunnen og
bakgrunnen for teksten. Her brukes det en ny metode, med navn APCA,
for å regne ut kontrasten.
</ListItem>
</ListUnordered>
</ListRoot>
<Heading
level={3}
size='xs'
>
Fremtidig eller strengere:
</Heading>
<ListUnordered>
<ListItem>
**1.4.6 Kontrast** (forbedret) (Nivå AAA): Den visuelle presentasjonen
av tekst og bilder av tekst har et kontrastforhold på minst 7:1,
unntatt uvesentlig tekst og skriftstørrelser større enn 18px eller
14px fet. [ 1.4.6 Kontrast (forbedret), WCAG 2.1
(w3.org)](https://www.w3.org/Translations/WCAG21-no/#contrast-enhanced)
</ListItem>
<ListItem>
**WCAG 2.2: 2.4.13** Focus Appearance (Nivå AAA), om utseende til
fokusmarkering krever at fokusindikator har en kontrastverdi på 3:1
mellom samme piksler i fokusert og ikke-fokusert tilstand.
[Understanding Success Criterion 2.4.13: Focus Appearance | WAI |
W3C](https://www.w3.org/WAI/WCAG22/Understanding/focus-appearance.html)
</ListItem>
<ListItem>
**WCAG 3** har et krav om farge og kontrast, visuell kontrast i tekst
(sølv): Sørg for tilstrekkelig kontrast mellom tekst i forgrunnen og
bakgrunnen for teksten. Her brukes det en ny metode, med navn APCA,
for å regne ut kontrasten.
</ListItem>
</ListUnordered>
</CardContent>
</Card>

## Mer presis metode

WCAG 3.0 foreslår nå en mer presis metode enn dagens standard, for å kalkulere kontrast og sette terskelverdier.

<ListRoot>
<ListUnordered>
<ListItem>
Metoden forbedrer hvordan verdien mellom to farger bestemmes, og skiller
også på om fargene er i forgrunnen eller i bakgrunnen.
</ListItem>
<ListItem>
Den setter også tydelige terskelverdier eller målverdier for valg av font,
tekststørrelse og font-vekt. Metoden heter Advanced Perceptual Contrast
Algorithm (APCA).
</ListItem>
<ListItem>
Målet vårt er å ligge over AAA-krav i WCAG 2.1, og vi vil dermed ligge
nærmere terskelverdiene i APCA. Det øker sjansen for at vi klarer å
oppfylle kravet om at alle, også svaksynte, skal kunne se innholdet på
nettstedet.
</ListItem>
</ListUnordered>
</ListRoot>
<ListUnordered>
<ListItem>
Metoden forbedrer hvordan verdien mellom to farger bestemmes, og skiller
også på om fargene er i forgrunnen eller i bakgrunnen.
</ListItem>
<ListItem>
Den setter også tydelige terskelverdier eller målverdier for valg av font,
tekststørrelse og font-vekt. Metoden heter Advanced Perceptual Contrast
Algorithm (APCA).
</ListItem>
<ListItem>
Målet vårt er å ligge over AAA-krav i WCAG 2.1, og vi vil dermed ligge
nærmere terskelverdiene i APCA. Det øker sjansen for at vi klarer å
oppfylle kravet om at alle, også svaksynte, skal kunne se innholdet på
nettstedet.
</ListItem>
</ListUnordered>

## I dag bruker vi en høyere standard enn kravene til tilgjengelighet

Expand Down
21 changes: 4 additions & 17 deletions apps/storefront/mdx-components.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import {
Link,
ListItem,
ListOrdered,
ListRoot,
ListUnordered,
Paragraph,
Table,
Expand All @@ -26,22 +25,10 @@ import type { MDXComponents } from 'mdx/types';
export function useMDXComponents(components: MDXComponents): MDXComponents {
return {
...components,
p: (props: ParagraphProps) => {
return <Paragraph {...props} spacing />;
},
a: (props) => {
return <Link {...(props as LinkProps)} />;
},
ol: (props: ListOrderedProps) => (
<ListRoot>
<ListOrdered {...props} />
</ListRoot>
),
ul: (props: ListUnorderedProps) => (
<ListRoot>
<ListUnordered {...props} />
</ListRoot>
),
p: (props: ParagraphProps) => <Paragraph {...props} spacing />,
a: (props) => <Link {...(props as LinkProps)} />,
ol: (props: ListOrderedProps) => <ListOrdered {...props} />,
ul: (props: ListUnorderedProps) => <ListUnordered {...props} />,
li: (props: ListItemProps) => <ListItem {...props}></ListItem>,
h1: (props: HeadingProps) => (
<Heading {...props} level={1} size='xl' spacing />
Expand Down
32 changes: 14 additions & 18 deletions apps/storybook/.storybook/preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,32 +49,28 @@ const components = {
/>
),
ol: (props: Props) => (
<List.Root>
<List.Ordered
{...props}
style={{ maxWidth: '70ch' }}
className='sb-unstyled'
data-ds-color-mode='light'
/>
</List.Root>
<List.Ordered
{...props}
style={{ maxWidth: '70ch' }}
className='sb-unstyled'
data-ds-color-mode='light'
/>
),
ul: (props: Props) => (
<List.Root>
<List.Unordered
{...props}
style={{ maxWidth: '70ch' }}
className='sb-unstyled'
data-ds-color-mode='light'
/>
</List.Root>
<List.Unordered
{...props}
style={{ maxWidth: '70ch' }}
className='sb-unstyled'
data-ds-color-mode='light'
/>
),
li: (props: Props) => (
<List.Item
{...props}
className='sb-unstyled'
style={{ maxWidth: '70ch' }}
data-ds-color-mode='light'
></List.Item>
/>
),
a: (props: LinkProps) => {
// if link starts with /, add current path to link
Expand All @@ -86,7 +82,7 @@ const components = {
href={href}
className='sb-unstyled'
data-ds-color-mode='light'
></Link>
/>
);
},
table: (props: Props) => (
Expand Down
36 changes: 23 additions & 13 deletions packages/css/list.css
Original file line number Diff line number Diff line change
@@ -1,22 +1,32 @@
.ds-list {
--dsc-list-font-size: var(--ds-font-size-5);
--dsc-list-padding-left: var(--ds-spacing-6);
--dsc-list-spacing: var(--ds-spacing-3);
--dsc-list-spacing-nested: var(--ds-spacing-2);

font-size: var(--dsc-list-font-size); /* Replace with composes paragraph-md */
line-height: var(--ds-line-height-md); /* Replace with composes paragraph-md */
margin: 0;
padding-left: var(--dsc-list-padding-left);
}

.ds-list--sm {
--dsc-list-padding-left: var(--ds-spacing-4);
}
& > li + li { margin-top: var(--dsc-list-spacing) }
& > li > :is(ol, ul) { --dsc-list-spacing: var(--dsc-list-spacing-nested) } /* Shrink spacing a bit when nested */

.ds-list--md,
.ds-list--lg {
--dsc-list-padding-left: var(--ds-spacing-6);
}
/* Add zero-width space to fix VoiceOver: https://gerardkcohen.me/writing/2017/voiceover-list-style-type.html
* This can also be acheived by using role="list" + role="listitem", but is nice to solve with CSS avoiding cluttered HTML
*/
& > li::before {
content: "\200B";
position: absolute;
}

.ds-list__item {
margin-bottom: var(--ds-spacing-2);
}
&[data-size="sm"] {
--dsc-list-padding-left: var(--ds-spacing-4);
--dsc-list-font-size: var(--ds-font-size-4); /* Replace with composes paragraph-sm */
}

.ds-list__item > .ds-list {
margin-top: var(--ds-spacing-2);
&[data-size="lg"] {
--dsc-list-font-size: var(--ds-font-size-6); /* Replace with composes paragraph-sm */
--dsc-list-spacing: var(--ds-spacing-4);
}
}
48 changes: 31 additions & 17 deletions packages/react/src/components/ErrorSummary/ErrorSummaryHeading.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,45 @@
import { useContext, useEffect } from 'react';
import { forwardRef, useContext, useEffect } from 'react';

import type { ListHeadingProps } from '../List';
import { List } from '../List';
import { Heading, type HeadingProps } from '../Typography/Heading';

import { ErrorSummaryContext } from './ErrorSummaryRoot';
import {
ErrorSummaryContext,
type ErrorSummaryProps,
} from './ErrorSummaryRoot';

export type ErrorSummaryHeadingProps = ListHeadingProps;
export type ErrorSummaryHeadingProps = HeadingProps;

export const ErrorSummaryHeading = ({
id,
...rest
}: ErrorSummaryHeadingProps) => {
const { headingId, setHeadingId } = useContext(ErrorSummaryContext);
const HEADING_SIZE_MAP: {
[key in NonNullable<ErrorSummaryProps['size']>]: HeadingProps['size'];
} = {
sm: '2xs',
md: 'xs',
lg: 'sm',
} as const;

export const ErrorSummaryHeading = forwardRef<
HTMLHeadingElement,
ErrorSummaryHeadingProps
>(function ErrorSummaryHeading(
{ className, id, ...rest }: ErrorSummaryHeadingProps,
ref,
) {
const { size, headingId, setHeadingId } = useContext(ErrorSummaryContext);

useEffect(() => {
if (id && headingId !== id) {
setHeadingId(id);
}
if (id && headingId !== id) setHeadingId(id);
}, [headingId, id, setHeadingId]);

return (
<List.Heading
{...rest}
id={headingId}
<Heading
className='ds-error-summary__heading'
id={headingId}
size={HEADING_SIZE_MAP[size ?? 'md']}
spacing
ref={ref}
{...rest}
/>
);
};
});

ErrorSummaryHeading.displayName = 'ErrorSummaryHeading';
Loading

0 comments on commit f3abcda

Please sign in to comment.