diff --git a/.gitignore b/.gitignore index 0123e304ea..d6f19fabce 100644 --- a/.gitignore +++ b/.gitignore @@ -54,3 +54,5 @@ apps/storybook/debug-storybook.log CLAUDE.md .claude/settings.local.json registry +apps/www/public/twins +apps/www/public/llms.txt diff --git a/apps/www/app/root.tsx b/apps/www/app/root.tsx index a1e5c35c08..8261ca0be8 100644 --- a/apps/www/app/root.tsx +++ b/apps/www/app/root.tsx @@ -141,6 +141,9 @@ function Document({ children }: DocumentProps) { + {/* Machine-readable documentation index for AI agents, see /llms.txt */} + + diff --git a/apps/www/package.json b/apps/www/package.json index 2aa32d7c9a..50cbb635f2 100644 --- a/apps/www/package.json +++ b/apps/www/package.json @@ -3,7 +3,8 @@ "private": true, "type": "module", "scripts": { - "build": "react-router typegen && react-router build", + "build": "pnpm twins && react-router typegen && react-router build", + "twins": "node ../../scripts/generate-twins.mjs --out apps/www/public/twins --link-prefix twins/ --index apps/www/public/llms.txt && node ../../scripts/merge-authored-twins.mjs --out apps/www/public/twins", "dev": "cross-env NODE_ENV=development node server.js", "start": "node server.js", "types": "react-router typegen && tsc" diff --git a/scripts/generate-twins.mjs b/scripts/generate-twins.mjs index a4f771f054..40adc1cc18 100644 --- a/scripts/generate-twins.mjs +++ b/scripts/generate-twins.mjs @@ -33,12 +33,17 @@ const CSS_SRC = join(ROOT, 'packages/css/src'); const PKG = JSON.parse( readFileSync(join(ROOT, 'packages/react/package.json'), 'utf8'), ); -const OUT = resolve( - ROOT, - process.argv.includes('--out') - ? process.argv[process.argv.indexOf('--out') + 1] - : 'registry', -); +const arg = (flag, fallback) => + process.argv.includes(flag) + ? process.argv[process.argv.indexOf(flag) + 1] + : fallback; + +const OUT = resolve(ROOT, arg('--out', 'registry')); +/* Where the llms.txt index is written, and the path prefix its links carry. + * Serving the registry at a site's /twins/ needs the index at the site root + * (/llms.txt per the convention) with links pointing into twins/. */ +const INDEX = arg('--index', null); +const LINK_PREFIX = arg('--link-prefix', ''); /* Docs links carry /en/: export names and the code agents write are English. */ const docsUrl = (slug, page) => @@ -205,7 +210,7 @@ for (const p of patterns) { * commands inside fetched files as prompt injection and refuse them. */ writeFileSync( - join(OUT, 'llms.txt'), + INDEX ? resolve(ROOT, INDEX) : join(OUT, 'llms.txt'), [ '# Designsystemet — machine-readable index', '', @@ -221,12 +226,14 @@ writeFileSync( '', ...patterns.map( (p) => - `- [${p.name}](patterns/${p.slug}.json)${p.links?.docs ? `: ${p.links.docs}` : ''}`, + `- [${p.name}](${LINK_PREFIX}patterns/${p.slug}.json)${p.links?.docs ? `: ${p.links.docs}` : ''}`, ), '', '## Components', '', - ...written.sort().map((n) => `- [${n}](components/${n}.json)`), + ...written + .sort() + .map((n) => `- [${n}](${LINK_PREFIX}components/${n}.json)`), '', '## Human documentation', '', diff --git a/scripts/merge-authored-twins.mjs b/scripts/merge-authored-twins.mjs index 004dafc608..2b68a7e820 100644 --- a/scripts/merge-authored-twins.mjs +++ b/scripts/merge-authored-twins.mjs @@ -1,33 +1,40 @@ /** - * Merge the authored layer into the generated twins, verifying every quote. + * Merge the authored layer into the generated twins. * * The authored layer (scripts/twins-authored.json) carries the rules that exist * only as prose on designsystemet.no: accessibility requirements, relations * ("use Radio instead"), and composition rules. Each rule cites a verbatim - * quote from the documentation. This script fetches the cited pages and checks - * that every quote still appears there, then writes the rules into the twins - * that `generate-twins.mjs` produced. + * quote from the documentation, and carries the result of its last + * verification. `reviewedAgainst` records the package version a human + * sign-off was made against. * - * Two guards keep a wrong rule out of a twin: - * 1. mechanical: a quote that no longer appears on the page marks the rule - * unverified, the component loses its reviewed status, and the rule lands - * on the review sheet (registry/REVIEW.md). This is how documentation - * changes surface within a day instead of silently going stale. - * 2. human: the machine proves the sentence is on the page; it cannot prove - * the rule follows from the sentence. `reviewedAgainst` in the data file - * records the package version a human sign-off was made against. + * Default run is offline and deterministic (safe for site builds): it merges + * the rules and their stored verification state into the twins produced by + * generate-twins.mjs. * - * node scripts/merge-authored-twins.mjs (run after generate-twins.mjs) + * `--verify` is the maintenance mode: it fetches every cited docs page, + * rechecks that each quote still appears there, and writes the result back + * into the data file. A quote that no longer verifies drops the component's + * reviewed status and lands on the review sheet (registry/REVIEW.md), so a + * documentation change surfaces on the next verify run instead of going + * silently stale. + * + * node scripts/merge-authored-twins.mjs [--out ] [--verify] */ import { existsSync, readFileSync, writeFileSync } from 'node:fs'; import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..'); -const REGISTRY = join(ROOT, 'registry/components'); -const AUTHORED = JSON.parse( - readFileSync(join(ROOT, 'scripts/twins-authored.json'), 'utf8'), -); +const arg = (flag, fallback) => + process.argv.includes(flag) + ? process.argv[process.argv.indexOf(flag) + 1] + : fallback; +const REGISTRY = join(ROOT, arg('--out', 'registry'), 'components'); +const VERIFY = process.argv.includes('--verify'); + +const DATA_PATH = join(ROOT, 'scripts/twins-authored.json'); +const AUTHORED = JSON.parse(readFileSync(DATA_PATH, 'utf8')); const SUBPAGES = ['overview', 'code', 'accessibility']; const docsUrl = (slug, page) => @@ -46,9 +53,9 @@ const ENTITIES = { }; /** - * Both the page text and the quotes go through the same normalisation, or a - * quote containing any transformed character could never match: typographic - * quotes become ASCII, markdown/formatting characters are stripped, whitespace + * The page text and the quotes go through the same normalisation, or a quote + * containing any transformed character could never match: typographic quotes + * become ASCII, markdown/formatting characters are stripped, whitespace * collapses, and the space that tag-stripping strands before punctuation is * removed. */ @@ -92,33 +99,37 @@ for (const [slug, entry] of Object.entries(AUTHORED)) { continue; } - const pages = {}; - for (const sub of SUBPAGES) pages[sub] = await pageText(docsUrl(slug, sub)); - - const twin = JSON.parse(readFileSync(twinPath, 'utf8')); - let allVerified = true; - - for (const key of ['a11y', 'relations', 'composition']) { - for (const rule of fields[key] ?? []) { - const quotes = [rule.quote, rule.quote2].filter(Boolean).map(normalise); - const found = SUBPAGES.find((sub) => - quotes.every((q) => pages[sub].includes(q)), - ); - rule.verified = found !== undefined; - rule.sourcePage = found ? docsUrl(slug, found) : null; - if (!rule.verified) { - allVerified = false; - reviewRows.push([ - name, - key, - rule.rule ?? `${rule.component}: ${rule.note ?? ''}`, - rule.quote ?? '', - ]); + if (VERIFY) { + const pages = {}; + for (const sub of SUBPAGES) pages[sub] = await pageText(docsUrl(slug, sub)); + for (const key of ['a11y', 'relations', 'composition']) { + for (const rule of fields[key] ?? []) { + const quotes = [rule.quote, rule.quote2].filter(Boolean).map(normalise); + const found = SUBPAGES.find((sub) => + quotes.every((q) => pages[sub].includes(q)), + ); + rule.verified = found !== undefined; + rule.sourcePage = found ? docsUrl(slug, found) : null; } } } - // Stamp after verification, so no field can claim a review the twin loses later. + const rules = ['a11y', 'relations', 'composition'].flatMap((k) => + (fields[k] ?? []).map((r) => [k, r]), + ); + const allVerified = rules.every(([, r]) => r.verified === true); + for (const [key, rule] of rules) { + if (!rule.verified) { + reviewRows.push([ + name, + key, + rule.rule ?? `${rule.component}: ${rule.note ?? ''}`, + rule.quote ?? '', + ]); + } + } + + const twin = JSON.parse(readFileSync(twinPath, 'utf8')); const method = allVerified && reviewedAgainst ? `quote-verified against the cited page; human-reviewed against ${reviewedAgainst}` @@ -132,37 +143,39 @@ for (const [slug, entry] of Object.entries(AUTHORED)) { value: fields[key], }; } - twin.reviewedAgainst = allVerified ? (reviewedAgainst ?? null) : null; twin.needsReview = !(allVerified && reviewedAgainst); if (!twin.needsReview) reviewed += 1; writeFileSync(twinPath, `${JSON.stringify(twin, null, 2)}\n`, 'utf8'); merged += 1; - console.log(` merged ${name}${twin.needsReview ? ' [needs review]' : ''}`); } -const sheet = [ - '# Authored-layer review sheet', - '', - 'Rules whose quote no longer verifies against the cited documentation page.', - 'For each row, the question is: does the current documentation still support the rule?', - '', - ...(reviewRows.length - ? [ - '| Component | Field | Rule | Quote |', - '|---|---|---|---|', - ...reviewRows.map( - (r) => - `| ${r.map((c) => String(c).replace(/\|/g, '\\|').slice(0, 160)).join(' | ')} |`, - ), - ] - : [ - 'Nothing pending: every quote verifies and every component is human-reviewed.', - ]), - '', -]; -writeFileSync(join(ROOT, 'registry/REVIEW.md'), sheet.join('\n')); +if (VERIFY) { + writeFileSync(DATA_PATH, `${JSON.stringify(AUTHORED, null, 2)}\n`, 'utf8'); + const sheet = [ + '# Authored-layer review sheet', + '', + 'Rules whose quote no longer verifies against the cited documentation page.', + 'For each row, the question is: does the current documentation still support the rule?', + '', + ...(reviewRows.length + ? [ + '| Component | Field | Rule | Quote |', + '|---|---|---|---|', + ...reviewRows.map( + (r) => + `| ${r.map((c) => String(c).replace(/\|/g, '\\|').slice(0, 160)).join(' | ')} |`, + ), + ] + : [ + 'Nothing pending: every quote verifies and every component is human-reviewed.', + ]), + '', + ]; + writeFileSync(join(ROOT, 'registry/REVIEW.md'), sheet.join('\n')); +} console.log( - `merged ${merged} components: ${reviewed} reviewed, ${reviewRows.length} rules pending review`, + `merged ${merged} components: ${reviewed} reviewed, ${reviewRows.length} rules pending review` + + (VERIFY ? ' (verification state written back to twins-authored.json)' : ''), ); diff --git a/scripts/twins-authored.json b/scripts/twins-authored.json index f706eb03b1..f1c3fb98bf 100644 --- a/scripts/twins-authored.json +++ b/scripts/twins-authored.json @@ -7,25 +7,33 @@ "rule": "Use role='alert' for critical messages that require immediate attention; use role='status' for less critical messages", "wcag": null, "quote": "Use `role=\"alert\"` for critical messages that require immediate attention.", - "quote2": "Use `role=\"status\"` for less critical messages." + "quote2": "Use `role=\"status\"` for less critical messages.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/alert/code" } ], "relations": [ { "component": "ValidationMessage", "note": "Use for a single form field error instead of Alert", - "quote": "you need to notify the user about an error in a single form field, use the component's own error message" + "quote": "you need to notify the user about an error in a single form field, use the component's own error message", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/alert/overview" }, { "component": "ErrorSummary", "note": "Use to summarise multiple errors instead of Alert", - "quote": "to summarise multiple errors" + "quote": "to summarise multiple errors", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/alert/overview" } ], "composition": [ { "rule": "Avoid multiple alerts on the same page", - "quote": "Avoid multiple alerts on the same page, as it can confuse the user and make it difficult to understand what is important." + "quote": "Avoid multiple alerts on the same page, as it can confuse the user and make it difficult to understand what is important.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/alert/overview" } ] }, @@ -36,91 +44,129 @@ { "rule": "Informative avatars must have an accessible name (e.g. via aria-label); if multiple avatars appear together, each name must be unique.", "wcag": "1.3.1", - "quote": "Informative avatar must have an accessible name. The accessible name can be provided using aria-label . If multiple avatars are displayed together, each name must be unique." + "quote": "Informative avatar must have an accessible name. The accessible name can be provided using aria-label . If multiple avatars are displayed together, each name must be unique.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/avatar/accessibility" }, { "rule": "Decorative avatars must be hidden from screen readers when purely decorative and not conveying information.", "wcag": "1.1.1", - "quote": "Decorative avatar must be hidden from screen readers. This applies when the avatar is purely decorative and does not convey information." + "quote": "Decorative avatar must be hidden from screen readers. This applies when the avatar is purely decorative and does not convey information.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/avatar/accessibility" }, { "rule": "A decorative avatar can be hidden when it appears alongside text that already identifies the person, e.g. inside a button.", "wcag": "1.1.1", - "quote": "Example: If an avatar is shown inside a button with the text “Ola Nordmann”, the avatar can be hidden because the name already identifies the person." + "quote": "Example: If an avatar is shown inside a button with the text “Ola Nordmann”, the avatar can be hidden because the name already identifies the person.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/avatar/accessibility" }, { "rule": "Clickable avatars must have an accessible name; avoid using just initials or the word \"Avatar\".", "wcag": "4.1.2", - "quote": "Clickable avatar must have an accessible name. The accessible name can be provided using aria-label . Example: “Profile for Ola Nordmann”. Avoid: just initials or “Avatar”." + "quote": "Clickable avatar must have an accessible name. The accessible name can be provided using aria-label . Example: “Profile for Ola Nordmann”. Avoid: just initials or “Avatar”.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/avatar/accessibility" }, { "rule": "The avatar must have an accessible name.", "wcag": null, - "quote": "Ensure the avatar has an accessible name ." + "quote": "Ensure the avatar has an accessible name .", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/avatar/code" }, { "rule": "If you add an image or icon as content inside the avatar, set aria-hidden=\"true\" to avoid duplicate information.", "wcag": null, - "quote": "If you add an image or icon as content inside the avatar, make sure to set aria-hidden=\"true\" to avoid duplicate information." + "quote": "If you add an image or icon as content inside the avatar, make sure to set aria-hidden=\"true\" to avoid duplicate information.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/avatar/code" }, { "rule": "You must add an aria-label to the avatar yourself to explain who or what it represents.", "wcag": null, - "quote": "You must add an aria-label to the avatar yourself to explain who or what the avatar represents." + "quote": "You must add an aria-label to the avatar yourself to explain who or what the avatar represents.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/avatar/code" }, { "rule": "Non-text children (like an image or icon) added inside Avatar automatically receive aria-hidden=\"true\" to avoid duplicate information.", "wcag": null, - "quote": "If you add anything other than text as a child inside (like an image or icon), it will automatically receive aria-hidden=\"true\" to avoid duplicate information." + "quote": "If you add anything other than text as a child inside (like an image or icon), it will automatically receive aria-hidden=\"true\" to avoid duplicate information.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/avatar/code" } ], "relations": [ { "component": "Icon", "note": "Use icons or other visual symbols instead of avatar when representing anything other than a person or an entity, such as a document.", - "quote": "Avoid avatar when representing anything other than a person or an entity, such as a document. Use icons or other visual symbols instead" + "quote": "Avoid avatar when representing anything other than a person or an entity, such as a document. Use icons or other visual symbols instead", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/avatar/overview" } ], "composition": [ { "rule": "Use avatar when representing a person or an entity.", - "quote": "Use avatar when representing a person or an entity" + "quote": "Use avatar when representing a person or an entity", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/avatar/overview" }, { "rule": "Avoid avatar when it is used purely for decoration without function or meaning.", - "quote": "it is used purely for decoration without function or meaning" + "quote": "it is used purely for decoration without function or meaning", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/avatar/overview" }, { "rule": "Avatar can be round or square; use the chosen shape consistently throughout the solution.", - "quote": "Avatar can be round or square. Use them consistently throughout your solution." + "quote": "Avatar can be round or square. Use them consistently throughout your solution.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/avatar/overview" }, { "rule": "Avatar should help make the user interface more recognisable and easier to navigate without distracting or taking up unnecessary space.", - "quote": "Avatar should help make the user interface more recognisable and easier to navigate without distracting or taking up unnecessary space." + "quote": "Avatar should help make the user interface more recognisable and easier to navigate without distracting or taking up unnecessary space.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/avatar/overview" }, { "rule": "Use the same avatar shape consistently throughout the solution.", - "quote": "Use the same avatar shape consistently throughout the solution." + "quote": "Use the same avatar shape consistently throughout the solution.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/avatar/overview" }, { "rule": "Avatar should usually be combined with text, unless it is entirely clear who or what it represents.", - "quote": "The avatar should usually be combined with text, unless it is entirely clear who or what it represents." + "quote": "The avatar should usually be combined with text, unless it is entirely clear who or what it represents.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/avatar/overview" }, { "rule": "Only use an avatar when it actually adds value to the user experience, e.g. to show who authored something or who is responsible.", - "quote": "Only use an avatar when it actually adds value to the user experience, for example to show who authored something or who is responsible." + "quote": "Only use an avatar when it actually adds value to the user experience, for example to show who authored something or who is responsible.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/avatar/overview" }, { "rule": "Avatar should not contain text inside the component, except when using initials.", - "quote": "Avatar should not contain text inside the component, except when using initials." + "quote": "Avatar should not contain text inside the component, except when using initials.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/avatar/overview" }, { "rule": "Other text, such as full names, roles or organisations, should be placed outside the avatar to ensure good readability.", - "quote": "Other text, such as full names, roles or organisations, should be placed outside the avatar to ensure good readability." + "quote": "Other text, such as full names, roles or organisations, should be placed outside the avatar to ensure good readability.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/avatar/overview" }, { "rule": "The Avatar component should be able to be composed into other components.", - "quote": "The component should be able to be composed into other components." + "quote": "The component should be able to be composed into other components.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/avatar/code" } ] }, @@ -131,35 +177,49 @@ { "rule": "aria-label or aria-description can be used on the avatar stack to give screen readers context about what the stack represents", "wcag": null, - "quote": "aria-label or aria-description can be used on avatar stack to provide context to screen readers about what the stack represents." + "quote": "aria-label or aria-description can be used on avatar stack to provide context to screen readers about what the stack represents.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/avatar-stack/code" }, { "rule": "Avatars in an avatar stack can have tooltips and be links, but accessibility considerations must be kept in mind", "wcag": null, - "quote": "Avatars in avatar stack can have tooltips and be links, but be aware of accessibility considerations." + "quote": "Avatars in avatar stack can have tooltips and be links, but be aware of accessibility considerations.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/avatar-stack/overview" }, { "rule": "tabindex=\"0\" should be added to an expandable avatar stack (done automatically in React)", "wcag": null, - "quote": "tabindex=\"0\" should be added, this is automatically added with expandable in React." + "quote": "tabindex=\"0\" should be added, this is automatically added with expandable in React.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/avatar-stack/code" } ], "composition": [ { "rule": "In plain HTML, build an avatar stack with the ds-avatar-stack class on a span, containing elements with the ds-avatar class", - "quote": "To create an avatar stack in plain HTML, use the ds-avatar-stack class on a . Inside this, place elements with the ds-avatar class." + "quote": "To create an avatar stack in plain HTML, use the ds-avatar-stack class on a . Inside this, place elements with the ds-avatar class.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/avatar-stack/code" }, { "rule": "An avatar stack supports round and square avatar variants, but only one variant should be used per stack", - "quote": "Avatar stack supports both round and square variants, but only one variant per stack." + "quote": "Avatar stack supports both round and square variants, but only one variant per stack.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/avatar-stack/overview" }, { "rule": "An avatar stack does not support wrapping across multiple lines", - "quote": "Avatar stack does not support wrapping across multiple lines." + "quote": "Avatar stack does not support wrapping across multiple lines.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/avatar-stack/code" }, { "rule": "When there are more avatars than fit in the stack, a suffix can be used to show the count as text to the right of the stack", - "quote": "If there are more avatars than can fit in the stack, you can use suffix to indicate this as text to the right of the stack." + "quote": "If there are more avatars than can fit in the stack, you can use suffix to indicate this as text to the right of the stack.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/avatar-stack/code" } ] }, @@ -170,49 +230,67 @@ { "rule": "Badge used without text inside it must have accompanying text next to it", "wcag": null, - "quote": "Badge can be used without text inside it, but it must then have accompanying text next to it." + "quote": "Badge can be used without text inside it, but it must then have accompanying text next to it.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/badge/overview" }, { "rule": "Information should not be communicated through colour or shape alone", "wcag": null, - "quote": "Information should not be communicated through colour or shape alone." + "quote": "Information should not be communicated through colour or shape alone.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/badge/overview" }, { "rule": "A badge showing important information must be included in the interactive element's accessible name", "wcag": "1.3.1", - "quote": "Badge that shows important information must be included in the interactive element’s accessible name." + "quote": "Badge that shows important information must be included in the interactive element’s accessible name.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/badge/accessibility" }, { "rule": "A badge that changes value must be announced to screen readers", "wcag": "4.1.3", - "quote": "Badge that changes value (for example 1 → 2) must be announced to screen readers" + "quote": "Badge that changes value (for example 1 → 2) must be announced to screen readers", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/badge/accessibility" } ], "relations": [ { "component": "Chip", "note": "Avoid Badge when interactive actions are required; use Chip instead", - "quote": "interactive actions are required, use chip or button instead" + "quote": "interactive actions are required, use chip or button instead", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/badge/overview" }, { "component": "Button", "note": "Avoid Badge when interactive actions are required; use Button instead", - "quote": "interactive actions are required, use chip or button instead" + "quote": "interactive actions are required, use chip or button instead", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/badge/overview" }, { "component": "Tag", "note": "Badge is not intended for text; use Tag to display words or short phrases", - "quote": "This component is not intended for text. If you need to display words or short phrases, use Tag instead." + "quote": "This component is not intended for text. If you need to display words or short phrases, use Tag instead.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/badge/overview" } ], "composition": [ { "rule": "Badge can be placed next to other text or components", - "quote": "Badge can be placed next to other text or components." + "quote": "Badge can be placed next to other text or components.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/badge/overview" }, { "rule": "To float a badge, the ds-badge--position class must be used on a wrapper around the element the badge is placed over", - "quote": "If you want it to be floating, the class ds-badge--position must be used on a wrapper around the element that the badge is to be placed over." + "quote": "If you want it to be floating, the class ds-badge--position must be used on a wrapper around the element that the badge is to be placed over.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/badge/code" } ] }, @@ -227,74 +305,106 @@ { "rule": "Breadcrumbs must be handled so that the trail does not become too long on small screens; if it becomes too long, it should be replaced with a back link.", "wcag": "1.4.10", - "quote": "Breadcrumbs must be handled so that it does not become too long on small screens. Check that the breadcrumb trail does not extend beyond the visible area at a narrow viewport or high zoom. If the trail becomes too long, it should be replaced with a back link." + "quote": "Breadcrumbs must be handled so that it does not become too long on small screens. Check that the breadcrumb trail does not extend beyond the visible area at a narrow viewport or high zoom. If the trail becomes too long, it should be replaced with a back link.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/breadcrumbs/accessibility" }, { "rule": "Skip link must bypass breadcrumbs and go directly to the main content, so users can skip repeated content without navigating through the breadcrumbs.", "wcag": "2.4.1", - "quote": "Skip link must bypass breadcrumbs and go directly to the main content. Users should be able to skip repeated content without navigating through the breadcrumbs." + "quote": "Skip link must bypass breadcrumbs and go directly to the main content. Users should be able to skip repeated content without navigating through the breadcrumbs.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/breadcrumbs/accessibility" }, { "rule": "Link text in breadcrumbs must be understandable; avoid generic names or unclear abbreviations.", "wcag": "2.4.4", - "quote": "The link text in breadcrumbs must be understandable. Avoid generic names or unclear abbreviations." + "quote": "The link text in breadcrumbs must be understandable. Avoid generic names or unclear abbreviations.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/breadcrumbs/accessibility" }, { "rule": "Breadcrumbs must be consistently placed and structured across pages, appearing in the same location and following the same order everywhere they are used.", "wcag": "3.2.3", - "quote": "Breadcrumbs must be consistently placed and structured across pages. The breadcrumb trail should appear in the same location and follow the same order on all pages where it is used." + "quote": "Breadcrumbs must be consistently placed and structured across pages. The breadcrumb trail should appear in the same location and follow the same order on all pages where it is used.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/breadcrumbs/accessibility" }, { "rule": "Breadcrumbs must not be the only way to navigate; users should have multiple ways to navigate to content.", "wcag": "2.4.5", - "quote": "Breadcrumbs must not be the only way to navigate. Users should have multiple ways to navigate to content." + "quote": "Breadcrumbs must not be the only way to navigate. Users should have multiple ways to navigate to content.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/breadcrumbs/accessibility" }, { "rule": "The last link should correspond to the current page and be coded as a link with aria-current=\"page\" so screen readers get correct information without other users mistaking it for a clickable link.", "wcag": null, - "quote": "The last link should correspond to the current page. It looks like regular text, but is coded as a link with aria-current=\"page\" . This way, screen readers get the correct information about which page the user is on, without other users mistakenly perceiving it as a clickable link." + "quote": "The last link should correspond to the current page. It looks like regular text, but is coded as a link with aria-current=\"page\" . This way, screen readers get the correct information about which page the user is on, without other users mistakenly perceiving it as a clickable link.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/breadcrumbs/accessibility" } ], "composition": [ { "rule": "Use breadcrumbs when users may want to navigate back to previous levels.", - "quote": "Use breadcrumbs when users may want to navigate back to previous levels" + "quote": "Use breadcrumbs when users may want to navigate back to previous levels", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/breadcrumbs/overview" }, { "rule": "Use breadcrumbs when users need help understanding where they are in the structure.", - "quote": "users need help understanding where they are in the structure" + "quote": "users need help understanding where they are in the structure", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/breadcrumbs/overview" }, { "rule": "Avoid breadcrumbs when the navigation is linear, for example across steps in a workflow or a form.", - "quote": "Avoid breadcrumbs when the navigation is linear, for example across steps in a workflow or a form" + "quote": "Avoid breadcrumbs when the navigation is linear, for example across steps in a workflow or a form", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/breadcrumbs/overview" }, { "rule": "On mobile or with a long trail, show only a link to the nearest parent page", - "quote": "On mobile, or when the breadcrumb trail becomes long, you can show only a link to the nearest parent page." + "quote": "On mobile, or when the breadcrumb trail becomes long, you can show only a link to the nearest parent page.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/breadcrumbs/overview" }, { "rule": "Be consistent: use either the back-link variant or the full breadcrumb trail, not both", - "quote": "Be consistent in your approach, and use either this variant or the full breadcrumb trail, not both." + "quote": "Be consistent in your approach, and use either this variant or the full breadcrumb trail, not both.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/breadcrumbs/overview" }, { "rule": "Breadcrumbs are only needed when there are three or more levels on a website.", - "quote": "Breadcrumbs are only needed when there are three or more levels on a website." + "quote": "Breadcrumbs are only needed when there are three or more levels on a website.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/breadcrumbs/overview" }, { "rule": "Breadcrumbs should always be placed at the top left of the page when using left-to-right text.", - "quote": "Breadcrumbs should always be placed at the top left of the page when using left-to-right text." + "quote": "Breadcrumbs should always be placed at the top left of the page when using left-to-right text.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/breadcrumbs/overview" }, { "rule": "Breadcrumbs should appear below the menu and main navigation, but above the page title.", - "quote": "They should appear below the menu and main navigation, but above the page title." + "quote": "They should appear below the menu and main navigation, but above the page title.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/breadcrumbs/overview" }, { "rule": "For deep navigation, the component can be configured to show only the first and last parts of the trail on mobile, automatically on screens narrower than 650px.", - "quote": "For deep navigation, you can configure the component to show only the first and last parts of the trail on mobile. This can happen automatically on screens narrower than 650px, where only the link one level down is shown." + "quote": "For deep navigation, you can configure the component to show only the first and last parts of the trail on mobile. This can happen automatically on screens narrower than 650px, where only the link one level down is shown.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/breadcrumbs/overview" }, { "rule": "The text should match the page headings or navigation labels so that each level is easy to recognise.", - "quote": "The text should match the page headings or navigation labels so that each level is easy to recognise." + "quote": "The text should match the page headings or navigation labels so that each level is easy to recognise.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/breadcrumbs/overview" } ] }, @@ -305,25 +415,33 @@ { "rule": "An icon-only button needs an accessible label", "wcag": null, - "quote": "When the button has no text, you must add an accessible label describing what it does." + "quote": "When the button has no text, you must add an accessible label describing what it does.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/button/overview" }, { "rule": "An icon next to text must be hidden from screen readers", "wcag": null, - "quote": "When using an icon together with text, the icon should have `aria-hidden` so screen readers don't read unnecessary content." + "quote": "When using an icon together with text, the icon should have `aria-hidden` so screen readers don't read unnecessary content.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/button/overview" } ], "relations": [ { "component": "Link", "note": "Use Link for navigation", - "quote": "navigating to other pages or out of the service, use Link instead" + "quote": "navigating to other pages or out of the service, use Link instead", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/button/overview" } ], "composition": [ { "rule": "Only one primary button per page", - "quote": "There should be only one primary button per page." + "quote": "There should be only one primary button per page.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/button/overview" } ] }, @@ -334,72 +452,100 @@ { "rule": "Link text in a card must clearly describe where the link goes; avoid generic text like 'Read more'", "wcag": "2.4.4", - "quote": "Link text in card must clearly describe where the link goes. Use a clear title or link text that explains the destination. Avoid generic text such as “Read more” or “Click here”." + "quote": "Link text in card must clearly describe where the link goes. Use a clear title or link text that explains the destination. Avoid generic text such as “Read more” or “Click here”.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/card/accessibility" }, { "rule": "The accessible name must match the visible text, even if overridden with aria-label or similar", "wcag": "2.5.3", - "quote": "The accessible name must match the visible text. If you use aria-label or other techniques that override the accessible name, it must still match the visible text." + "quote": "The accessible name must match the visible text. If you use aria-label or other techniques that override the accessible name, it must still match the visible text.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/card/accessibility" }, { "rule": "Images in link cards must be decorative, with an empty alt attribute", "wcag": "1.1.1", - "quote": "Images in link cards must be decorative. If the entire card is a link with an image, the image must not be informative and should have an empty alt attribute." + "quote": "Images in link cards must be decorative. If the entire card is a link with an image, the image must not be informative and should have an empty alt attribute.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/card/accessibility" }, { "rule": "Headings in a card must use the correct level relative to the rest of the page; do not skip heading levels", "wcag": "1.3.1", - "quote": "Headings in card must use the correct level relative to the rest of the page. Do not skip heading levels (for example from h1 to h3 )." + "quote": "Headings in card must use the correct level relative to the rest of the page. Do not skip heading levels (for example from h1 to h3 ).", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/card/accessibility" }, { "rule": "When Card renders as a button, its content loses all semantics, so this must be used carefully", "wcag": null, - "quote": "When the card is a button, the content inside loses all semantics, so use this carefully." + "quote": "When the card is a button, the content inside loses all semantics, so use this carefully.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/card/overview" }, { "rule": "Use as the outermost element so screen readers read the whole card as one continuous link", "wcag": null, - "quote": "The entire card can be used as a link by using as the outermost element. This is useful when you want all text and content in Card to be read by screen readers as one continuous link." + "quote": "The entire card can be used as a link by using as the outermost element. This is useful when you want all text and content in Card to be read by screen readers as one continuous link.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/card/code" }, { "rule": "Use data-clickdelegatefor to make a card with multiple interactive elements fully clickable while retaining the semantics of those elements", "wcag": null, - "quote": "If you have a card with multiple interactive elements, such as links or buttons, you can use data-clickdelegatefor to make the entire card clickable while retaining the semantics of the interactive elements." + "quote": "If you have a card with multiple interactive elements, such as links or buttons, you can use data-clickdelegatefor to make the entire card clickable while retaining the semantics of the interactive elements.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/card/code" }, { "rule": "Prefer placing a link in the card's title over making the whole card a link, since screen reader users get a better experience", "wcag": null, - "quote": "If the card should link to another page, you can place a link in the card's title. The entire card then becomes clickable, but screen reader users get a better experience than if the whole card were a link (adrianroselli.com) ." + "quote": "If the card should link to another page, you can place a link in the card's title. The entire card then becomes clickable, but screen reader users get a better experience than if the whole card were a link (adrianroselli.com) .", + "verified": false, + "sourcePage": null }, { "rule": "The keyboard focus ring should surround only the link text, not the entire card, so it matches the focus area for assistive technology users", "wcag": null, - "quote": "Note that with keyboard navigation, the focus ring only surrounds the text in the link, not the entire card. This is because link cards can also contain multiple clickable elements (secondary actions), and it is less confusing for users of assistive technology, such as voice control or screen readers, if the link text matches the focus area." + "quote": "Note that with keyboard navigation, the focus ring only surrounds the text in the link, not the entire card. This is because link cards can also contain multiple clickable elements (secondary actions), and it is less confusing for users of assistive technology, such as voice control or screen readers, if the link text matches the focus area.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/card/code" } ], "relations": [ { "component": "Alert", "note": "Use Alert instead of Card for presenting important messages", - "quote": "important messages should be presented as alerts (use Alert instead)" + "quote": "important messages should be presented as alerts (use Alert instead)", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/card/overview" } ], "composition": [ { "rule": "Card can be divided into sections separated by dividers, which may include images or video extending to the edge", - "quote": "You can divide the card into sections. These get dividers between them and may include images or video that extend to the edge." + "quote": "You can divide the card into sections. These get dividers between them and may include images or video that extend to the edge.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/card/overview" }, { "rule": "When using ds-card__block to create sections, all content must be placed inside block elements rather than directly in Card", - "quote": "If you need sections in the card, use ds-card__block if you want to divide the card with dividers or add images or video that extend to the edge. Note that when you use ds-card__block , all content must be placed inside elements with ds-card__block - not placed directly in Card ." + "quote": "If you need sections in the card, use ds-card__block if you want to divide the card with dividers or add images or video that extend to the edge. Note that when you use ds-card__block , all content must be placed inside elements with ds-card__block - not placed directly in Card .", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/card/code" }, { "rule": "Card blocks can be arranged horizontally by switching to flex or grid display", - "quote": "You can switch between display: flex and display: grid to place Card.Block next to each other:" + "quote": "You can switch between display: flex and display: grid to place Card.Block next to each other:", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/card/code" }, { "rule": "data-clickdelegatefor is automatically added when Card finds a link inside its heading, for cards with text or media that navigate to another page", - "quote": "When Card finds an inside the heading element, data-clickdelegatefor will automatically be added to the card. This is useful when Card contains text or media and should navigate to another page." + "quote": "When Card finds an inside the heading element, data-clickdelegatefor will automatically be added to the card. This is useful when Card contains text or media and should navigate to another page.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/card/code" } ] }, @@ -410,122 +556,172 @@ { "rule": "Users must be able to move focus to Checkbox with Tab", "wcag": null, - "quote": "It must be possible to move focus to the Checkbox by pressing Tab on the keyboard." + "quote": "It must be possible to move focus to the Checkbox by pressing Tab on the keyboard.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/checkbox/accessibility" }, { "rule": "Users must be able to check and uncheck Checkbox with Space", "wcag": null, - "quote": "It must also be possible to check and uncheck it by pressing Space ." + "quote": "It must also be possible to check and uncheck it by pressing Space .", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/checkbox/accessibility" }, { "rule": "A Checkbox must always have an accessible name", "wcag": null, - "quote": "A Checkbox must always have an accessible name." + "quote": "A Checkbox must always have an accessible name.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/checkbox/accessibility" }, { "rule": "In special cases, aria-label or aria-labelledby may be used instead of a visible label", "wcag": null, - "quote": "In special cases, it may make sense to omit the
element, which provides good accessibility and semantics without extra JavaScript.", "wcag": null, - "quote": "The name comes from the built-in HTML
element, which provides good accessibility and semantics without additional JavaScript." + "quote": "The name comes from the built-in HTML
element, which provides good accessibility and semantics without additional JavaScript.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/details/overview" }, { "rule": "Large amounts of text can be demanding for users, so details lets them decide what is relevant to read.", "wcag": null, - "quote": "Large amounts of text can be demanding for many users. Details allows users to decide for themselves what is relevant to read." + "quote": "Large amounts of text can be demanding for many users. Details allows users to decide for themselves what is relevant to read.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/details/overview" }, { "rule": "Hidden content risks going unnoticed by users entirely.", "wcag": null, - "quote": "When content is hidden, there is a risk that users will not notice it at all." + "quote": "When content is hidden, there is a risk that users will not notice it at all.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/details/overview" }, { "rule": "Nesting details makes it hard for users to understand what is open and closed.", "wcag": null, - "quote": "Nested lists can make it difficult for users to understand what is open and what is closed, especially when several levels can be expanded at once." + "quote": "Nested lists can make it difficult for users to understand what is open and what is closed, especially when several levels can be expanded at once.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/details/overview" }, { "rule": "Headings strongly affect whether users find, read, and can access the content.", "wcag": null, - "quote": "Headings can strongly influence whether users find what they need, whether the content is read, and whether it is considered accessible to all." + "quote": "Headings can strongly influence whether users find what they need, whether the content is read, and whether it is considered accessible to all.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/details/overview" }, { "rule": "Details is implemented as a native
element.", "wcag": null, - "quote": "Details is a native
element." + "quote": "Details is a native
element.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/details/code" }, { "rule": "A Firefox/Android Talkback bug is polyfilled so state and role are announced correctly.", "wcag": null, - "quote": "We polyfill a bug in Firefox when used with Android Talkback screen reader to announce state and role correctly." + "quote": "We polyfill a bug in Firefox when used with Android Talkback screen reader to announce state and role correctly.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/details/code" }, { "rule": "The component builds on the semantic HTML elements
and .", "wcag": null, - "quote": "For the Details component, we use u-details (github.io) , which builds on the semantic HTML elements
and ." + "quote": "For the Details component, we use u-details (github.io) , which builds on the semantic HTML elements
and .", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/details/accessibility" }, { "rule": "Space or Enter opens or closes the Details.", "wcag": null, - "quote": "Press Space or Enter to open or close the Details." + "quote": "Press Space or Enter to open or close the Details.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/details/accessibility" } ], "relations": [ { "component": "ErrorSummary", "note": "Use error summary instead of details when you need to summarise error messages.", - "quote": "Avoid details when you need to show important content that everyone should see you need to display an action button you need to summarise error messages, use error summary instead" + "quote": "Avoid details when you need to show important content that everyone should see you need to display an action button you need to summarise error messages, use error summary instead", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/details/overview" } ], "composition": [ { "rule": "Use details for information relevant only to some users, for examples/FAQs, and where reading is optional.", - "quote": "Use details when you want to provide information that is only relevant to some users you want to give examples or answer frequently asked questions it should be up to the user whether they choose to read the content" + "quote": "Use details when you want to provide information that is only relevant to some users you want to give examples or answer frequently asked questions it should be up to the user whether they choose to read the content", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/details/overview" }, { "rule": "Details can be placed inside a card to give the content a framed appearance.", - "quote": "You can use details inside a card to give the content a framed appearance." + "quote": "You can use details inside a card to give the content a framed appearance.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/details/overview" }, { "rule": "Details inside a card gives the content a framed appearance; works well when it doesn't span the full page width or holds a single line", - "quote": "You can use details inside a card to give the content a framed appearance. This works well when details does not span the full width of the page, or when it contains only a single line." + "quote": "You can use details inside a card to give the content a framed appearance. This works well when details does not span the full width of the page, or when it contains only a single line.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/details/overview" }, { "rule": "Avoid using details just to make a page look tidier.", - "quote": "However, you should not use details to hide content just to make the page look tidier." + "quote": "However, you should not use details to hide content just to make the page look tidier.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/details/overview" }, { "rule": "Do not nest one details inside another.", - "quote": "Do not place one details inside another." + "quote": "Do not place one details inside another.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/details/overview" }, { "rule": "The heading must clearly describe what the details contains.", - "quote": "Make sure the heading clearly describes what the details contains." + "quote": "Make sure the heading clearly describes what the details contains.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/details/overview" }, { "rule": "Vague headings like \"Show more\" or \"Read more here\" are not good enough.", - "quote": "“Show more” or “Read more here” are not good enough headings." + "quote": "“Show more” or “Read more here” are not good enough headings.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/details/overview" }, { "rule": "When there are many expandable sections, add a main or thematic heading above the whole list.", - "quote": "If you have many expandable sections, consider adding a main heading or thematic heading above the entire list." + "quote": "If you have many expandable sections, consider adding a main heading or thematic heading above the entire list.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/details/overview" }, { "rule": "Keep the content inside details short so it relates easily to the heading.", - "quote": "Keep the content inside details short so it is easy to relate to the heading." + "quote": "Keep the content inside details short so it is easy to relate to the heading.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/details/overview" }, { "rule": "If content is too long, split it into several details.", - "quote": "If the content is too long, divide it into several details." + "quote": "If the content is too long, divide it into several details.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/details/overview" }, { "rule": "For very large amounts of content, prefer separate pages over details.", - "quote": "For very large amounts of content, it may be better to create separate pages." + "quote": "For very large amounts of content, it may be better to create separate pages.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/details/overview" } ] }, @@ -644,85 +882,119 @@ { "rule": "Make sure elements receiving focus are not hidden behind the dialog", "wcag": null, - "quote": "Note: Make sure that elements receiving focus are not hidden behind the dialog ." + "quote": "Note: Make sure that elements receiving focus are not hidden behind the dialog .", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/dialog/overview" }, { "rule": "Add aria-haspopup=\"dialog\" to the element that opens the dialog so screen reader users know it will open a dialog", "wcag": null, - "quote": "The attribute aria-haspopup=\"dialog\" should be added to the element that opens the dialog to make it clear to screen reader users that it will open a dialog." + "quote": "The attribute aria-haspopup=\"dialog\" should be added to the element that opens the dialog to make it clear to screen reader users that it will open a dialog.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/dialog/code" }, { "rule": "Always provide a visible and simple way to close a dialog", "wcag": null, - "quote": "Always ensure that users have a visible and simple way to close a dialog." + "quote": "Always ensure that users have a visible and simple way to close a dialog.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/dialog/overview" }, { "rule": "The close button (X) should stay visible even when the dialog's content requires scrolling", "wcag": null, - "quote": "The most common approach is a close button (X) in the top right corner. It should remain visible even when the dialog contains a lot of content and the user needs to scroll." + "quote": "The most common approach is a close button (X) in the top right corner. It should remain visible even when the dialog contains a lot of content and the user needs to scroll.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/dialog/overview" }, { "rule": "In non-modal dialogs, elements receiving keyboard focus must not end up hidden behind the dialog", "wcag": "2.4.11", - "quote": "When using non-modal dialogs, you must ensure that elements receiving focus through keyboard navigation do not end up behind the dialog and become hidden. See criteria 2.4.11 (w3.org) (AA) and 2.4.12 (w3.org) (AAA)." + "quote": "When using non-modal dialogs, you must ensure that elements receiving focus through keyboard navigation do not end up behind the dialog and become hidden. See criteria 2.4.11 (w3.org) (AA) and 2.4.12 (w3.org) (AAA).", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/dialog/accessibility" }, { "rule": "Focusable elements must never be completely covered by the dialog during Tab navigation", "wcag": null, - "quote": "Ensure that focusable elements are never completely covered by the dialog when users navigate the page using the Tab key." + "quote": "Ensure that focusable elements are never completely covered by the dialog when users navigate the page using the Tab key.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/dialog/accessibility" }, { "rule": "Focus order must be logical and focus must always be visible", "wcag": null, - "quote": "Ensure that the focus order is logical and that focus is always visible, so users navigating with a keyboard or screen reader can understand where focus is located." + "quote": "Ensure that the focus order is logical and that focus is always visible, so users navigating with a keyboard or screen reader can understand where focus is located.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/dialog/accessibility" }, { "rule": "Esc closes the dialog and returns focus to the trigger", "wcag": null, - "quote": "Esc closes the dialog (focus returns to the trigger)" + "quote": "Esc closes the dialog (focus returns to the trigger)", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/dialog/accessibility" } ], "relations": [ { "component": "Alert", "note": "Use alert instead of a modal dialog when notifying users of something that does not require action", - "quote": "you need to notify users about something that does not require action. Use alert or a non-modal dialog instead" + "quote": "you need to notify users about something that does not require action. Use alert or a non-modal dialog instead", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/dialog/overview" }, { "component": "Popover", "note": "Use popover instead of a modal dialog for short, optional information", - "quote": "you want to provide short, optional information, use popover" + "quote": "you want to provide short, optional information, use popover", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/dialog/overview" }, { "component": "System notifications pattern", "note": "Consult the system notifications pattern for guidance on when a modal dialog should be used", - "quote": "See also the system notifications pattern for more guidance on when a modal dialog should be used." + "quote": "See also the system notifications pattern for more guidance on when a modal dialog should be used.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/dialog/overview" } ], "composition": [ { "rule": "Using the placement prop, a dialog can behave like a drawer positioned from a given side", - "quote": "Using placement , dialog can behave like a so-called “drawer” from the position you specify." + "quote": "Using placement , dialog can behave like a so-called “drawer” from the position you specify.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/dialog/overview" }, { "rule": "Use multiple ds-dialog__block elements to divide the dialog with dividers into separate areas", - "quote": "Use multiple ds-dialog__block if you want to divide the dialog with dividers into, for example, a top and bottom area." + "quote": "Use multiple ds-dialog__block if you want to divide the dialog with dividers into, for example, a top and bottom area.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/dialog/code" }, { "rule": "Content cannot be placed directly in the dialog if ds-dialog__block is used; all content must then be inside a block", - "quote": "Note that content cannot be placed directly in dialog if you use ds-dialog__block ; then all content should be inside one of the dialog's ds-dialog__block sections." + "quote": "Note that content cannot be placed directly in dialog if you use ds-dialog__block ; then all content should be inside one of the dialog's ds-dialog__block sections.", + "verified": false, + "sourcePage": null }, { "rule": "A close button that is the dialog's first direct child floats to the top right", - "quote": "If the button is a direct child of the dialog and the first element, it will float to the top right." + "quote": "If the button is a direct child of the dialog and the first element, it will float to the top right.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/dialog/code" }, { "rule": "Use Dialog.TriggerContext together with Dialog.Trigger so the dialog and trigger can communicate without id/commandfor", - "quote": "Use Dialog.TriggerContext and Dialog.Trigger together, so Dialog can communicate without having to use id and commandfor ." + "quote": "Use Dialog.TriggerContext and Dialog.Trigger together, so Dialog can communicate without having to use id and commandfor .", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/dialog/code" }, { "rule": "Use buttons in the dialog to give users a clear path forward", - "quote": "Use buttons to give users a clear path forward." + "quote": "Use buttons to give users a clear path forward.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/dialog/overview" } ] }, @@ -733,48 +1005,68 @@ { "rule": "Apply aria-hidden=\"true\" to the divider to avoid noise for screen reader users.", "wcag": null, - "quote": "Apply aria-hidden=\"true\" to avoid noise in screen readers." + "quote": "Apply aria-hidden=\"true\" to avoid noise in screen readers.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/divider/code" }, { "rule": "Divider is a purely visual element and is not announced by screen readers.", "wcag": null, - "quote": "Divider is a purely visual element, and it is not announced by screen readers." + "quote": "Divider is a purely visual element, and it is not announced by screen readers.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/divider/accessibility" }, { "rule": "Avoid using a divider solely for visual effect without functional meaning.", "wcag": null, - "quote": "However, avoid using a divider solely for visual effect without functional meaning." + "quote": "However, avoid using a divider solely for visual effect without functional meaning.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/divider/accessibility" }, { "rule": "Since a divider represents a thematic separation, you must also use elements a screen reader can interpret to communicate that separation accessibly.", "wcag": null, - "quote": "A divider represents a thematic separation of content. To communicate this separation in an accessible way, you must also use elements that a screen reader can interpret." + "quote": "A divider represents a thematic separation of content. To communicate this separation in an accessible way, you must also use elements that a screen reader can interpret.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/divider/accessibility" } ], "composition": [ { "rule": "Use divider when you want a clear separation between sections of content and whitespace alone does not provide sufficient separation.", - "quote": "Use divider when you want to create a clear separation between sections of content whitespace alone does not provide sufficient separation" + "quote": "Use divider when you want to create a clear separation between sections of content whitespace alone does not provide sufficient separation", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/divider/overview" }, { "rule": "Avoid divider when natural spacing or other visual elements already provide enough separation, since too many dividers create visual clutter.", - "quote": "Avoid divider when natural spacing or other visual elements provide enough separation too many divider elements create a cluttered visual appearance" + "quote": "Avoid divider when natural spacing or other visual elements provide enough separation too many divider elements create a cluttered visual appearance", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/divider/overview" }, { "rule": "Divider is used to break content into smaller sections for readability, and can also separate related content that still benefits from visual distinction.", - "quote": "Divider is used to break content into smaller sections, making it easier to read and understand. It can also be used to separate content that is related but still benefits from a visual distinction." + "quote": "Divider is used to break content into smaller sections, making it easier to read and understand. It can also be used to separate content that is related but still benefits from a visual distinction.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/divider/overview" }, { "rule": "Use divider to mark a boundary between content that is not logically connected, such as different sections in an article or different functional areas on a page.", - "quote": "Use the component to mark a boundary between content that is not logically connected, such as different sections in an article or different functional areas on a page." + "quote": "Use the component to mark a boundary between content that is not logically connected, such as different sections in an article or different functional areas on a page.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/divider/accessibility" }, { "rule": "Use divider to visually clarify structure, not simply to fill empty space or decorate the page.", - "quote": "Use it to visually clarify the structure, not simply to fill empty space or “decorate” the page." + "quote": "Use it to visually clarify the structure, not simply to fill empty space or “decorate” the page.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/divider/accessibility" }, { "rule": "The accessible structural elements to pair with a divider can include a heading, a list,
elements, or elements with role=\"group\".", - "quote": "This can include a heading , a list ,
elements , or elements with role=\"group\" ." + "quote": "This can include a heading , a list ,
elements , or elements with role=\"group\" .", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/divider/accessibility" } ] }, @@ -785,66 +1077,92 @@ { "rule": "Dropdown does not create a semantic menu itself; it provides the foundation for building one yourself", "wcag": null, - "quote": "Dropdown does not create a semantic menu, but provides the foundation for building one yourself." + "quote": "Dropdown does not create a semantic menu, but provides the foundation for building one yourself.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/dropdown/overview" }, { "rule": "The Popover API gives built-in accessibility for open/closed state and keyboard navigation.", "wcag": null, - "quote": "There is built-in accessibility in the Popover API for open and closed state, as well as keyboard navigation." + "quote": "There is built-in accessibility in the Popover API for open and closed state, as well as keyboard navigation.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/dropdown/accessibility" }, { "rule": "Enter or Space opens or closes the menu.", "wcag": null, - "quote": "Enter or Space opens or closes the menu" + "quote": "Enter or Space opens or closes the menu", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/dropdown/accessibility" }, { "rule": "Enter or Space selects the highlighted item.", "wcag": null, - "quote": "Enter or Space selects the highlighted item" + "quote": "Enter or Space selects the highlighted item", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/dropdown/accessibility" }, { "rule": "Esc closes the menu.", "wcag": null, - "quote": "Esc closes the menu" + "quote": "Esc closes the menu", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/dropdown/accessibility" }, { "rule": "When building a menu on Dropdown, follow the W3C Menu and Menubar Pattern", "wcag": null, - "quote": "Read the \"Menu and Menubar Pattern\" on w3.org for more information on how to create accessible menus." + "quote": "Read the \"Menu and Menubar Pattern\" on w3.org for more information on how to create accessible menus.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/dropdown/overview" }, { "rule": "Combine Dropdown with focusgroup to enable arrow-key navigation between items", "wcag": null, - "quote": "You can combine Dropdown with focusgroup to enable arrow navigation between items." + "quote": "You can combine Dropdown with focusgroup to enable arrow navigation between items.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/dropdown/code" } ], "relations": [ { "component": "Button", "note": "Use a visible Button instead of Dropdown when the action is primary or frequently used.", - "quote": "Avoid dropdown when an action is primary or frequently used; use a visible button instead" + "quote": "Avoid dropdown when an action is primary or frequently used; use a visible button instead", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/dropdown/overview" } ], "composition": [ { "rule": "Use Dropdown to offer multiple secondary options compactly, that should still remain easily accessible.", - "quote": "Use dropdown when you want to offer multiple options without taking up much space in the interface the options are secondary but should still be easily accessible" + "quote": "Use dropdown when you want to offer multiple options without taking up much space in the interface the options are secondary but should still be easily accessible", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/dropdown/overview" }, { "rule": "Use Dropdown for actions or navigation that do not need to be visible at all times.", - "quote": "Use Dropdown for actions or navigation that do not need to be visible at all times" + "quote": "Use Dropdown for actions or navigation that do not need to be visible at all times", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/dropdown/overview" }, { "rule": "Keep the number of items in a dropdown manageable.", - "quote": "Keep the number of items manageable" + "quote": "Keep the number of items manageable", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/dropdown/overview" }, { "rule": "Use headings to group related items in a dropdown.", - "quote": "Use headings to group related items" + "quote": "Use headings to group related items", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/dropdown/overview" }, { "rule": "Avoid nesting multiple levels of dropdowns (nested menus).", - "quote": "Avoid multiple levels of dropdowns (nested menus)" + "quote": "Avoid multiple levels of dropdowns (nested menus)", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/dropdown/overview" } ] }, @@ -855,33 +1173,45 @@ { "rule": "The summary takes focus when it appears or its content changes", "wcag": null, - "quote": "The summary automatically receives focus when it becomes visible and when its content changes." + "quote": "The summary automatically receives focus when it becomes visible and when its content changes.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/error-summary/overview" }, { "rule": "Every error message must link directly to its field", "wcag": null, - "quote": "Error messages must link directly to the relevant field." + "quote": "Error messages must link directly to the relevant field.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/error-summary/overview" } ], "relations": [ { "component": "Alert", "note": "Use Alert for system-level messages instead", - "quote": "you need to display system-level messages; use alert instead" + "quote": "you need to display system-level messages; use alert instead", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/error-summary/overview" } ], "composition": [ { "rule": "The summary should contain every error message present on the page", - "quote": "Error summary should contain all error messages present on the page" + "quote": "Error summary should contain all error messages present on the page", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/error-summary/overview" }, { "rule": "Summary messages should be phrased exactly the same as the field-level messages", - "quote": "error messages in the summary should be phrased exactly the same as the error messages displayed at the relevant fields" + "quote": "error messages in the summary should be phrased exactly the same as the error messages displayed at the relevant fields", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/error-summary/overview" }, { "rule": "Where one error covers several fields, link to the first", - "quote": "If the error applies to several fields, for example when two fields are validated together, link to the first instance of the error." + "quote": "If the error applies to several fields, for example when two fields are validated together, link to the first instance of the error.", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/error-summary/overview" } ] }, @@ -892,14 +1222,18 @@ { "rule": "Field links label, description, validation message and counter so assistive technology reads them as one unit", "wcag": null, - "quote": "It automatically links label, description, validation message and character count to the relevant field, so that assistive technology like screen readers reads them as one unit" + "quote": "It automatically links label, description, validation message and character count to the relevant field, so that assistive technology like screen readers reads them as one unit", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/field/overview" } ], "relations": [ { "component": "Fieldset", "note": "Use Fieldset to group multiple fields", - "quote": "use fieldset instead" + "quote": "use fieldset instead", + "verified": true, + "sourcePage": "https://designsystemet.no/en/components/docs/field/overview" } ] }, @@ -914,20 +1248,26 @@ { "rule": "Avoid repeating the same text in both label and legend", "wcag": null, - "quote": "Avoid using the same text in both `