From 4ae473cfd440d00865d4bb626030c596c26794d0 Mon Sep 17 00:00:00 2001 From: "Kenneth G. Franqueiro" Date: Wed, 10 Jul 2024 11:53:57 -0400 Subject: [PATCH 01/24] WIP Add support for generating WCAG 2.1 from main branch --- 11ty/README.md | 8 +++-- 11ty/guidelines.ts | 86 ++++++++++++++++++++++++++-------------------- eleventy.config.ts | 77 ++++++++++++++++++++++++++++++++++------- 3 files changed, 117 insertions(+), 54 deletions(-) diff --git a/11ty/README.md b/11ty/README.md index 168db395e5..45a3725721 100644 --- a/11ty/README.md +++ b/11ty/README.md @@ -49,12 +49,14 @@ but may be useful if you're not seeing what you expect in the output files. ### `WCAG_VERSION` -**Usage context:** currently this should not be changed, pending future improvements to `21` support +**Usage context:** for building older versions of techniques and understanding docs Indicates WCAG version being built, in `XY` format (i.e. no `.`). -Influences base URLs for links to guidelines, techniques, and understanding pages. +Influences which pages get included, and a few details within pages +(e.g. titles/URLs, "New in ..." sentences). +Also influences base URLs for links to guidelines, techniques, and understanding pages. -**Default:** `22` +Possible values: `22` **(default)**, `21` ### `WCAG_MODE` diff --git a/11ty/guidelines.ts b/11ty/guidelines.ts index 08c4ec1c3e..95804c00ee 100644 --- a/11ty/guidelines.ts +++ b/11ty/guidelines.ts @@ -34,39 +34,20 @@ export const actRules = ( )["act-rules"]; /** - * Returns an object with keys for each existing WCAG 2 version, - * each mapping to an array of basenames of HTML files under understanding/ - * (Functionally equivalent to "guidelines-versions" target in build.xml) + * Returns an object mapping each existing WCAG 2 SC slug to the earliest WCAG version it applies to. + * (Functionally equivalent to "guidelines-versions" target in build.xml, structurally inverted) */ -export async function getGuidelinesVersions() { +async function getSuccessCriteriaVersions() { const paths = await glob("*/*.html", { cwd: "understanding" }); - const versions: Record = { "20": [], "21": [], "22": [] }; + const map: Record = {}; for (const path of paths) { - const [version, filename] = path.split("/"); - assertIsWcagVersion(version); - versions[version].push(basename(filename, ".html")); + const [fileVersion, filename] = path.split("/"); + assertIsWcagVersion(fileVersion); + map[basename(filename, ".html")] = fileVersion; } - for (const version of Object.keys(versions)) { - assertIsWcagVersion(version); - versions[version].sort(); - } - return versions; -} - -/** - * Like getGuidelinesVersions, but mapping each basename to the version it appears in - */ -export async function getInvertedGuidelinesVersions() { - const versions = await getGuidelinesVersions(); - const invertedVersions: Record = {}; - for (const [version, basenames] of Object.entries(versions)) { - for (const basename of basenames) { - invertedVersions[basename] = version; - } - } - return invertedVersions; + return map; } export interface DocNode { @@ -79,7 +60,7 @@ export interface DocNode { export interface Principle extends DocNode { content: string; num: `${number}`; // typed as string for consistency with guidelines/SC - version: "WCAG20"; + version: "20"; guidelines: Guideline[]; type: "Principle"; } @@ -87,7 +68,7 @@ export interface Principle extends DocNode { export interface Guideline extends DocNode { content: string; num: `${Principle["num"]}.${number}`; - version: `WCAG${"20" | "21"}`; + version: "20" | "21"; successCriteria: SuccessCriterion[]; type: "Guideline"; } @@ -97,7 +78,7 @@ export interface SuccessCriterion extends DocNode { num: `${Guideline["num"]}.${number}`; /** Level may be empty for obsolete criteria */ level: "A" | "AA" | "AAA" | ""; - version: `WCAG${WcagVersion}`; + version: WcagVersion; type: "SC"; } @@ -105,6 +86,13 @@ export function isSuccessCriterion(criterion: any): criterion is SuccessCriterio return !!(criterion?.type === "SC" && "level" in criterion); } +/** Defines version-dependent partial overrides of SC metadata for older versions. */ +export const scOverrides: Record Partial> = { + parsing: (version) => (version < "22" ? { level: "A", name: "Parsing" } : {}), + "target-size-enhanced": (version) => + version < "22" ? { id: "target-size", name: "Target Size" } : {}, +}; + /** * Returns HTML content used for Understanding guideline/SC boxes. * @param $el Cheerio element of the full section from flattened guidelines/index.html @@ -121,7 +109,7 @@ const getContentHtml = ($el: Cheerio) => { * comparable to the principles section of wcag.xml from the guidelines-xml Ant task. */ export async function getPrinciples() { - const versions = await getInvertedGuidelinesVersions(); + const versions = await getSuccessCriteriaVersions(); const $ = await flattenDomFromFile("guidelines/index.html"); const principles: Principle[] = []; @@ -130,17 +118,15 @@ export async function getPrinciples() { $(".guideline", el).each((j, guidelineEl) => { const successCriteria: SuccessCriterion[] = []; $(".sc", guidelineEl).each((k, scEl) => { - const resolvedVersion = versions[scEl.attribs.id]; - assertIsWcagVersion(resolvedVersion); - + const scId = scEl.attribs.id; successCriteria.push({ content: getContentHtml($(scEl)), - id: scEl.attribs.id, + id: scId, name: $("h4", scEl).text().trim(), num: `${i + 1}.${j + 1}.${k + 1}`, level: $("p.conformance-level", scEl).text().trim() as SuccessCriterion["level"], type: "SC", - version: `WCAG${resolvedVersion}`, + version: versions[scId], }); }); @@ -150,7 +136,7 @@ export async function getPrinciples() { name: $("h3", guidelineEl).text().trim(), num: `${i + 1}.${j + 1}`, type: "Guideline", - version: guidelineEl.attribs.id === "input-modalities" ? "WCAG21" : "WCAG20", + version: guidelineEl.attribs.id === "input-modalities" ? "21" : "20", successCriteria, }); }); @@ -161,7 +147,7 @@ export async function getPrinciples() { name: $("h2", el).text().trim(), num: `${i + 1}`, type: "Principle", - version: "WCAG20", + version: "20", guidelines, }); }); @@ -169,6 +155,30 @@ export async function getPrinciples() { return principles; } +/** + * Given an unfiltered principles array and a target WCAG version, + * applies version-specific patches and filters out irrelevant Guidelines/SC. + * @param allPrinciples Unfiltered principles array from getPrinciples + * @param version WCAG version to filter and patch principles data for + * @returns + */ +export function getPrinciplesForVersion(allPrinciples: Principle[], version: WcagVersion) { + return allPrinciples.map((principle) => ({ + ...principle, + guidelines: principle.guidelines + .filter((guideline) => guideline.version <= version) + .map((guideline) => ({ + ...guideline, + successCriteria: guideline.successCriteria + .filter((sc) => sc.version <= version) + .map((sc) => ({ + ...sc, + ...(sc.id in scOverrides && scOverrides[sc.id](version)), + })), + })), + })); +} + /** * Returns a flattened object hash, mapping shortcodes to each principle/guideline/SC. */ diff --git a/eleventy.config.ts b/eleventy.config.ts index fc9fe5ff69..98789839dc 100644 --- a/eleventy.config.ts +++ b/eleventy.config.ts @@ -9,6 +9,9 @@ import { assertIsWcagVersion, getFlatGuidelines, getPrinciples, + getPrinciplesForVersion, + scOverrides, + type FlatGuidelinesMap, type Guideline, type Principle, type SuccessCriterion, @@ -43,18 +46,25 @@ const isTechniqueObsolete = (technique: Technique | undefined) => const isGuidelineObsolete = (guideline: Principle | Guideline | SuccessCriterion | undefined) => guideline?.type === "SC" && guideline.level === ""; -const principles = await getPrinciples(); +/** Tree of Principles/Guidelines/SC across all versions (including later than selected) */ +const allPrinciples = await getPrinciples(); +/** Flattened Principles/Guidelines/SC across all versions (including later than selected) */ +const allFlatGuidelines = getFlatGuidelines(allPrinciples); + +/** Tree of Principles/Guidelines/SC relevant to selected version */ +const principles = getPrinciplesForVersion(allPrinciples, version); +/** Flattened Principles/Guidelines/SC relevant to selected version */ const flatGuidelines = getFlatGuidelines(principles); +/** Flattened Principles/Guidelines/SC that only exist in later versions (to filter techniques) */ +const futureGuidelines: FlatGuidelinesMap = {}; +for (const [key, value] of Object.entries(allFlatGuidelines)) { + if (value.version > version) futureGuidelines[key] = value; +} + const techniques = await getTechniquesByTechnology(); const flatTechniques = getFlatTechniques(techniques); -for (const [technology, list] of Object.entries(techniques)) { - // Prune obsolete techniques from ToC - techniques[technology as Technology] = list.filter( - (technique) => !technique.obsoleteSince || technique.obsoleteSince > version - ); -} - +/** Maps technique IDs to SCs found in target version */ const techniqueAssociations = await getTechniqueAssociations(flatGuidelines); for (const [id, associations] of Object.entries(techniqueAssociations)) { // Prune associations from non-obsolete techniques to obsolete SCs @@ -62,6 +72,27 @@ for (const [id, associations] of Object.entries(techniqueAssociations)) { ({ criterion }) => criterion.level !== "" || isTechniqueObsolete(flatTechniques[id]) ); } +/** Maps technique IDs to SCs only found in later versions */ +const futureTechniqueAssociations = await getTechniqueAssociations(futureGuidelines); +/** Subset of futureTechniqueAssociations not overlapping with techniqueAssociations */ +const futureExclusiveTechniqueAssociations: typeof techniqueAssociations = {}; + +for (const [id, associations] of Object.entries(futureTechniqueAssociations)) { + if (!techniqueAssociations[id]) futureExclusiveTechniqueAssociations[id] = associations; +} +const skippedTechniques = Object.keys(futureExclusiveTechniqueAssociations).sort().join(", "); +if (skippedTechniques) + console.log(`Skipping techniques that only reference later-version SCs: ${skippedTechniques}`); + +for (const [technology, list] of Object.entries(techniques)) { + // Prune techniques that are obsolete or associated with SCs from later versions + // (only prune hierarchical structure for ToC; keep all in flatTechniques for lookups) + techniques[technology as Technology] = list.filter( + (technique) => + (!technique.obsoleteSince || technique.obsoleteSince > version) && + !futureExclusiveTechniqueAssociations[technique.id] + ); +} const understandingDocs = await getUnderstandingDocs(version); const understandingNav = await generateUnderstandingNavMap(principles, understandingDocs); @@ -108,6 +139,16 @@ if (process.env.WCAG_MODE === "editors") { baseUrls.understanding = `https://www.w3.org/WAI/WCAG${version}/Understanding/`; } +/** Applies any overridden SC IDs to incoming Understanding fileSlugs */ +function resolveUnderstandingFileSlug(fileSlug: string) { + if (fileSlug in scOverrides) { + assertIsWcagVersion(version); + const { id } = scOverrides[fileSlug](version); + if (id) return id; + } + return fileSlug; +} + export default function (eleventyConfig: any) { for (const [name, value] of Object.entries(globalData)) eleventyConfig.addGlobalData(name, value); @@ -124,13 +165,22 @@ export default function (eleventyConfig: any) { // we have access to typings here, and can keep the latter fully static. eleventyConfig.addGlobalData("eleventyComputed", { // permalink determines output structure; see https://www.11ty.dev/docs/permalinks/ - permalink: ({ page, isUnderstanding }: GlobalData) => { + permalink: ({ page, isTechniques, isUnderstanding }: GlobalData) => { if (page.inputPath === "./index.html" && process.env.WCAG_MODE) return false; - if (isUnderstanding) { + if (isTechniques) { + if (futureExclusiveTechniqueAssociations[page.fileSlug]) return false; + } else if (isUnderstanding) { // understanding-metadata.html exists in 2 places; top-level wins in XSLT process if (/\/20\/understanding-metadata/.test(page.inputPath)) return false; - // Flatten pages into top-level directory, out of version subdirectories - return page.inputPath.replace(/\/2\d\//, "/"); + + // Exclude files from newer versions than what's being built + if (page.fileSlug in flatGuidelines && flatGuidelines[page.fileSlug].version > version) + return false; + + // Flatten pages into top-level directory, out of version subdirectories. + // Revise any filename that differs between versions, reusing data from guidelines.ts + if (page.fileSlug in allFlatGuidelines) + return `understanding/${resolveUnderstandingFileSlug(page.fileSlug)}.html`; } // Preserve existing structure: write to x.html instead of x/index.html return page.inputPath; @@ -147,13 +197,14 @@ export default function (eleventyConfig: any) { // Data for individual technique pages technique: ({ page, isTechniques }: GlobalData) => + // Reference unfiltered map to avoid breaking non-emitted (but still processed) pages isTechniques ? flatTechniques[page.fileSlug] : null, techniqueAssociations: ({ page, isTechniques }: GlobalData) => isTechniques ? techniqueAssociations[page.fileSlug] : null, // Data for individual understanding pages guideline: ({ page, isUnderstanding }: GlobalData) => - isUnderstanding ? flatGuidelines[page.fileSlug] : null, + isUnderstanding ? flatGuidelines[resolveUnderstandingFileSlug(page.fileSlug)] : null, }); // See https://www.11ty.dev/docs/copy/#emulate-passthrough-copy-during-serve From dfb874945036e9e4d93baa89719bdf0d5983b09e Mon Sep 17 00:00:00 2001 From: "Kenneth G. Franqueiro" Date: Mon, 15 Jul 2024 17:00:58 -0400 Subject: [PATCH 02/24] 2.1 support: Pin against published guidelines, fix permalink filter --- 11ty/README.md | 4 +- 11ty/guidelines.ts | 110 ++++++++++++++++++++++++++------------------- eleventy.config.ts | 23 +++++----- package-lock.json | 91 +++++++++++++++++++++++++++++++++++++ package.json | 1 + 5 files changed, 171 insertions(+), 58 deletions(-) diff --git a/11ty/README.md b/11ty/README.md index 45a3725721..914f7a4126 100644 --- a/11ty/README.md +++ b/11ty/README.md @@ -52,8 +52,8 @@ but may be useful if you're not seeing what you expect in the output files. **Usage context:** for building older versions of techniques and understanding docs Indicates WCAG version being built, in `XY` format (i.e. no `.`). -Influences which pages get included, and a few details within pages -(e.g. titles/URLs, "New in ..." sentences). +Influences which pages get included, guideline/SC content, +and a few details within pages (e.g. titles/URLs, "New in ..." content). Also influences base URLs for links to guidelines, techniques, and understanding pages. Possible values: `22` **(default)**, `21` diff --git a/11ty/guidelines.ts b/11ty/guidelines.ts index 95804c00ee..bcd33fc9d5 100644 --- a/11ty/guidelines.ts +++ b/11ty/guidelines.ts @@ -1,4 +1,5 @@ -import type { Cheerio, Element } from "cheerio"; +import axios from "axios"; +import type { Cheerio, CheerioAPI, Element } from "cheerio"; import { glob } from "glob"; import { readFile } from "fs/promises"; @@ -34,10 +35,10 @@ export const actRules = ( )["act-rules"]; /** - * Returns an object mapping each existing WCAG 2 SC slug to the earliest WCAG version it applies to. - * (Functionally equivalent to "guidelines-versions" target in build.xml, structurally inverted) + * Flattened object hash, mapping each WCAG 2 SC slug to the earliest WCAG version it applies to. + * (Functionally equivalent to "guidelines-versions" target in build.xml; structurally inverted) */ -async function getSuccessCriteriaVersions() { +const scVersions = await (async function () { const paths = await glob("*/*.html", { cwd: "understanding" }); const map: Record = {}; @@ -48,7 +49,7 @@ async function getSuccessCriteriaVersions() { } return map; -} +})(); export interface DocNode { id: string; @@ -86,13 +87,22 @@ export function isSuccessCriterion(criterion: any): criterion is SuccessCriterio return !!(criterion?.type === "SC" && "level" in criterion); } -/** Defines version-dependent partial overrides of SC metadata for older versions. */ -export const scOverrides: Record Partial> = { - parsing: (version) => (version < "22" ? { level: "A", name: "Parsing" } : {}), - "target-size-enhanced": (version) => - version < "22" ? { id: "target-size", name: "Target Size" } : {}, +/** Version-dependent overrides of SC shortcodes for older versions */ +export const scSlugOverrides: Record string> = { + "target-size-enhanced": (version) => (version < "22" ? "target-size" : "target-size-enhanced"), }; +/** Selectors ignored when capturing content of each Principle / Guideline / SC */ +const contentIgnores = [ + "h1, h2, h3, h4, h5, h6", + "section", + ".change", + ".conformance-level", + // Selectors below are specific to pre-published guidelines (for previous versions) + ".header-wrapper", + ".doclinks", +]; + /** * Returns HTML content used for Understanding guideline/SC boxes. * @param $el Cheerio element of the full section from flattened guidelines/index.html @@ -100,33 +110,32 @@ export const scOverrides: Record Partial) => { // Load HTML into a new instance, remove elements we don't want, then return the remainder const $ = load($el.html()!, null, false); - $("h1, h2, h3, h4, h5, h6, section, .change, .conformance-level").remove(); - return $.html(); + $(contentIgnores.join(", ")).remove(); + return $.html().trim(); }; -/** - * Resolves information from guidelines/index.html; - * comparable to the principles section of wcag.xml from the guidelines-xml Ant task. - */ -export async function getPrinciples() { - const versions = await getSuccessCriteriaVersions(); - const $ = await flattenDomFromFile("guidelines/index.html"); - +/** Performs processing common across WCAG versions */ +function processPrinciples($: CheerioAPI) { const principles: Principle[] = []; $(".principle").each((i, el) => { const guidelines: Guideline[] = []; - $(".guideline", el).each((j, guidelineEl) => { + $("> .guideline", el).each((j, guidelineEl) => { const successCriteria: SuccessCriterion[] = []; - $(".sc", guidelineEl).each((k, scEl) => { + // Source uses sc class, published uses guideline class (again) + $("> .guideline, > .sc", guidelineEl).each((k, scEl) => { const scId = scEl.attribs.id; successCriteria.push({ content: getContentHtml($(scEl)), id: scId, name: $("h4", scEl).text().trim(), num: `${i + 1}.${j + 1}.${k + 1}`, - level: $("p.conformance-level", scEl).text().trim() as SuccessCriterion["level"], + // conformance-level contains only letters in source, full (Level ...) in publish + level: $("p.conformance-level", scEl) + .text() + .trim() + .replace(/^\(Level (.*)\)$/, "$1") as SuccessCriterion["level"], type: "SC", - version: versions[scId], + version: scVersions[scId], }); }); @@ -156,28 +165,39 @@ export async function getPrinciples() { } /** - * Given an unfiltered principles array and a target WCAG version, - * applies version-specific patches and filters out irrelevant Guidelines/SC. - * @param allPrinciples Unfiltered principles array from getPrinciples - * @param version WCAG version to filter and patch principles data for - * @returns + * Resolves information from guidelines/index.html; + * comparable to the principles section of wcag.xml from the guidelines-xml Ant task. */ -export function getPrinciplesForVersion(allPrinciples: Principle[], version: WcagVersion) { - return allPrinciples.map((principle) => ({ - ...principle, - guidelines: principle.guidelines - .filter((guideline) => guideline.version <= version) - .map((guideline) => ({ - ...guideline, - successCriteria: guideline.successCriteria - .filter((sc) => sc.version <= version) - .map((sc) => ({ - ...sc, - ...(sc.id in scOverrides && scOverrides[sc.id](version)), - })), - })), - })); -} +export const getPrinciples = async () => + processPrinciples(await flattenDomFromFile("guidelines/index.html")); + +/** + * Retrieves and processes a pinned WCAG version using published guidelines. + */ +export const getPrinciplesForVersion = async (version: WcagVersion) => { + const $ = load( + (await axios.get(`https://www.w3.org/TR/WCAG${version}/`, { responseType: "text" })).data + ); + + // Re-collapse definition links and notes, to be processed by this build system + $(".guideline a.internalDFN").removeAttr("class data-link-type id href title"); + $(".guideline [role='note'] .marker").remove(); + $(".guideline [role='note']").find("> div, > p").addClass("note").unwrap(); + + // Bibliography references are not processed in Understanding SC boxes + $(".guideline cite:has(a.bibref:only-child)").each((_, el) => { + const $el = $(el); + const $parent = $el.parent(); + $el.remove(); + // Remove surrounding square brackets (which aren't in a dedicated element) + $parent.html($parent.html()!.replace(/ \[\]/g, "")); + }); + + // Remove extra markup from headings so they can be parsed for names + $("bdi").remove(); + + return processPrinciples($); +}; /** * Returns a flattened object hash, mapping shortcodes to each principle/guideline/SC. diff --git a/eleventy.config.ts b/eleventy.config.ts index 98789839dc..847752d721 100644 --- a/eleventy.config.ts +++ b/eleventy.config.ts @@ -10,7 +10,7 @@ import { getFlatGuidelines, getPrinciples, getPrinciplesForVersion, - scOverrides, + scSlugOverrides, type FlatGuidelinesMap, type Guideline, type Principle, @@ -52,7 +52,9 @@ const allPrinciples = await getPrinciples(); const allFlatGuidelines = getFlatGuidelines(allPrinciples); /** Tree of Principles/Guidelines/SC relevant to selected version */ -const principles = getPrinciplesForVersion(allPrinciples, version); +const principles = process.env.WCAG_VERSION + ? await getPrinciplesForVersion(version) + : allPrinciples; /** Flattened Principles/Guidelines/SC relevant to selected version */ const flatGuidelines = getFlatGuidelines(principles); /** Flattened Principles/Guidelines/SC that only exist in later versions (to filter techniques) */ @@ -141,10 +143,9 @@ if (process.env.WCAG_MODE === "editors") { /** Applies any overridden SC IDs to incoming Understanding fileSlugs */ function resolveUnderstandingFileSlug(fileSlug: string) { - if (fileSlug in scOverrides) { + if (fileSlug in scSlugOverrides) { assertIsWcagVersion(version); - const { id } = scOverrides[fileSlug](version); - if (id) return id; + return scSlugOverrides[fileSlug](version); } return fileSlug; } @@ -173,14 +174,14 @@ export default function (eleventyConfig: any) { // understanding-metadata.html exists in 2 places; top-level wins in XSLT process if (/\/20\/understanding-metadata/.test(page.inputPath)) return false; - // Exclude files from newer versions than what's being built - if (page.fileSlug in flatGuidelines && flatGuidelines[page.fileSlug].version > version) - return false; + if (page.fileSlug in allFlatGuidelines) { + // Exclude files not present in the version being built + if (!flatGuidelines[page.fileSlug]) return false; - // Flatten pages into top-level directory, out of version subdirectories. - // Revise any filename that differs between versions, reusing data from guidelines.ts - if (page.fileSlug in allFlatGuidelines) + // Flatten pages into top-level directory, out of version subdirectories. + // Revise any filename that differs between versions, reusing data from guidelines.ts return `understanding/${resolveUnderstandingFileSlug(page.fileSlug)}.html`; + } } // Preserve existing structure: write to x.html instead of x/index.html return page.inputPath; diff --git a/package-lock.json b/package-lock.json index 8f37dfbe7d..1135bac189 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "license": "W3C", "dependencies": { "@11ty/eleventy": "^3.0.0", + "axios": "^1.7.2", "cheerio": "^1.0.0", "glob": "^10.3.16", "gray-matter": "^4.0.3", @@ -800,6 +801,21 @@ "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/axios": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz", + "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -1076,6 +1092,17 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/commander": { "version": "10.0.1", "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", @@ -1216,6 +1243,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -1570,6 +1605,25 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "node_modules/follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/foreground-child": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", @@ -1585,6 +1639,19 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", @@ -2117,6 +2184,25 @@ "node": ">=10.0.0" } }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/minimatch": { "version": "9.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", @@ -2448,6 +2534,11 @@ "asap": "~2.0.3" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/prr": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", diff --git a/package.json b/package.json index b481b35df8..75dc4278b7 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "license": "W3C", "dependencies": { "@11ty/eleventy": "^3.0.0", + "axios": "^1.7.2", "cheerio": "^1.0.0", "glob": "^10.3.16", "gray-matter": "^4.0.3", From 63585fe48d3643074a8c7d0636dc39e7e0486f84 Mon Sep 17 00:00:00 2001 From: "Kenneth G. Franqueiro" Date: Tue, 16 Jul 2024 12:34:29 -0400 Subject: [PATCH 03/24] 2.1 support: Universally update version in titles/h1s of other docs Also update other docs metadata in understanding.ts to match --- 11ty/CustomLiquid.ts | 9 ++++++--- 11ty/understanding.ts | 4 ++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/11ty/CustomLiquid.ts b/11ty/CustomLiquid.ts index 9afe6829a3..26cffde42c 100644 --- a/11ty/CustomLiquid.ts +++ b/11ty/CustomLiquid.ts @@ -387,9 +387,12 @@ export class CustomLiquid extends Liquid { `Understanding ${type} ${scope.guideline.num}: ${scope.guideline.name}${titleSuffix}` ); } else { - $title.text( - $title.text().replace(/WCAG 2( |$)/, `WCAG ${scope.versionDecimal}$1`) + titleSuffix - ); + // Update WCAG version in title and top-level heading in other understanding docs + const wcagPattern = /WCAG 2(?:\.\d)?( |$)/; + const wcagReplacement = `WCAG ${scope.versionDecimal}$1`; + $title.text($title.text().replace(wcagPattern, wcagReplacement) + titleSuffix); + const $h1 = $("h1"); + $h1.text($h1.text().replace(wcagPattern, wcagReplacement)); } // Remove Techniques section from obsolete SCs (e.g. Parsing in 2.2) diff --git a/11ty/understanding.ts b/11ty/understanding.ts index c9b414b63f..1379b1915c 100644 --- a/11ty/understanding.ts +++ b/11ty/understanding.ts @@ -24,11 +24,11 @@ export async function getUnderstandingDocs(version: WcagVersion): Promise Date: Tue, 16 Jul 2024 12:53:14 -0400 Subject: [PATCH 04/24] 2.1 support: Replace hard-coded version references in refer-to-wcag --- understanding/refer-to-wcag.html | 50 ++++++++++++++++---------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/understanding/refer-to-wcag.html b/understanding/refer-to-wcag.html index 22b4450799..31f561342d 100644 --- a/understanding/refer-to-wcag.html +++ b/understanding/refer-to-wcag.html @@ -1,61 +1,61 @@ - How to refer to WCAG 2 from other documents + How to refer to WCAG {{ versionDecimal }} from other documents -

How to refer to WCAG 2 from other documents

-

The following examples show how to reference WCAG 2 in various situations. For additional guidance, see Referencing and Linking to WAI Guidelines and Technical Documents.

-

Please note that the following language for referencing WCAG 2 can be inserted into your own documents.

+

How to refer to WCAG {{ versionDecimal }} from other documents

+

The following examples show how to reference WCAG {{ versionDecimal }} in various situations. For additional guidance, see Referencing and Linking to WAI Guidelines and Technical Documents.

+

Please note that the following language for referencing WCAG {{ versionDecimal }} can be inserted into your own documents.

Information references

-

When referencing WCAG 2 in an informational fashion, the following format can be used.

-

Web Content Accessibility Guidelines 2.2, W3C World Wide Web Consortium Recommendation XX Month Year (https://www.w3.org/TR/YYYY/REC-WCAG22-YYYYMMDD/, Latest version at https://www.w3.org/TR/WCAG22/)

+

When referencing WCAG {{ versionDecimal }} in an informational fashion, the following format can be used.

+

Web Content Accessibility Guidelines {{ versionDecimal }}, W3C World Wide Web Consortium Recommendation XX Month Year (https://www.w3.org/TR/YYYY/REC-WCAG{{ version }}-YYYYMMDD/, Latest version at https://www.w3.org/TR/WCAG{{ version }}/)

-

When referring to WCAG 2 from another standard with a "should" statement

-

When referencing WCAG 2 from within a should statement in a standard (or advisory statement in a regulation), then the full WCAG 2 should be referenced. This would mean that all three levels of WCAG 2 should be considered but that none are required. The format for referencing WCAG 2 from a "should" statement therefore, is:

-

Web Content Accessibility Guidelines 2.2, W3C World Wide Web Consortium Recommendation XX Month Year. (https://www.w3.org/TR/YYYY/REC-WCAG22-YYYYMMDD/)

+

When referring to WCAG {{ versionDecimal }} from another standard with a "should" statement

+

When referencing WCAG {{ versionDecimal }} from within a should statement in a standard (or advisory statement in a regulation), then the full WCAG {{ versionDecimal }} should be referenced. This would mean that all three levels of WCAG {{ versionDecimal }} should be considered but that none are required. The format for referencing WCAG {{ versionDecimal }} from a "should" statement therefore, is:

+

Web Content Accessibility Guidelines {{ versionDecimal }}, W3C World Wide Web Consortium Recommendation XX Month Year. (https://www.w3.org/TR/YYYY/REC-WCAG{{ version }}-YYYYMMDD/)

-

When referring to WCAG 2 from another standard with a "shall or must" statement

-

When citing WCAG 2 as part of a requirement (e.g., a shall or must statement in a standard or regulation), the reference must include the specific parts of WCAG 2 that are intended to be required. When referencing WCAG 2 in this manner, the following rules apply:

+

When referring to WCAG {{ versionDecimal }} from another standard with a "shall or must" statement

+

When citing WCAG {{ versionDecimal }} as part of a requirement (e.g., a shall or must statement in a standard or regulation), the reference must include the specific parts of WCAG {{ versionDecimal }} that are intended to be required. When referencing WCAG {{ versionDecimal }} in this manner, the following rules apply:

  1. -

    Conformance at any level of WCAG 2 requires that all of the Level A success criteria be met. References to WCAG 2 conformance cannot be for any subset of Level A.

    +

    Conformance at any level of WCAG {{ versionDecimal }} requires that all of the Level A success criteria be met. References to WCAG {{ versionDecimal }} conformance cannot be for any subset of Level A.

  2. Beyond Level A, a "shall or must" reference may include any subset of provisions in Levels AA and AAA. For example, "all of Level A and [some specific list of success criteria in Level AA and Level AAA]" be met.

  3. -

    If Level AA conformance to WCAG 2 is specified, then all Level A and all Level AA success criteria must be met.

    +

    If Level AA conformance to WCAG {{ versionDecimal }} is specified, then all Level A and all Level AA success criteria must be met.

  4. -

    If Level AAA conformance to WCAG 2 is specified, then all Level A, all Level AA, and all Level AAA success criteria must be met.

    +

    If Level AAA conformance to WCAG {{ versionDecimal }} is specified, then all Level A, all Level AA, and all Level AAA success criteria must be met.

    Note 1: It is not recommended that Level AAA conformance ever be required for entire sites as a general policy because it is not possible to satisfy all Level AAA success criteria for some content.

    -

    Note 2: The sets of success criteria defined in WCAG 2 are interdependent and individual success criteria rely on each other's definitions in ways which may not be immediately obvious to the reader. Any set of success criteria must include all of the Level A provisions.

    +

    Note 2: The sets of success criteria defined in WCAG {{ versionDecimal }} are interdependent and individual success criteria rely on each other's definitions in ways which may not be immediately obvious to the reader. Any set of success criteria must include all of the Level A provisions.

Examples

To cite only the Level A Success Criteria (Single-A conformance):

-

Web Content Accessibility Guidelines 2.2, W3C World Wide Web Consortium Recommendation XX Month Year, Level A Success Criteria. (https://www.w3.org/TR/YYYY/REC-WCAG22-YYYYMMDD/)

+

Web Content Accessibility Guidelines {{ versionDecimal }}, W3C World Wide Web Consortium Recommendation XX Month Year, Level A Success Criteria. (https://www.w3.org/TR/YYYY/REC-WCAG{{ version }}-YYYYMMDD/)

To cite the Levels A and AA Success Criteria (Double-A conformance):

-

Web Content Accessibility Guidelines 2.2, W3C World Wide Web Consortium Recommendation XX Month Year, Level A & Level AA Success Criteria. (https://www.w3.org/TR/YYYY/REC-WCAG22-YYYYMMDD/)

+

Web Content Accessibility Guidelines {{ versionDecimal }}, W3C World Wide Web Consortium Recommendation XX Month Year, Level A & Level AA Success Criteria. (https://www.w3.org/TR/YYYY/REC-WCAG{{ version }}-YYYYMMDD/)

To cite Level A Success Criteria and selected Success Criteria from Level AA and Level AAA:

-

Web Content Accessibility Guidelines 2.2, W3C World Wide Web Consortium Recommendation XX Month Year, Level A Success Criteria plus Success Criteria 1.x.x, 2.y.y, … 3.z.z. (https://www.w3.org/TR/YYYY/REC-WCAG22-YYYYMMDD/)

-

Example of use of a WCAG 2 reference in a "shall or must" statement.

-

All Web content on publicly available Web sites shall conform to Web Content Accessibility Guidelines 2.2, W3C World Wide Web Consortium Recommendation XX Month Year, Level A Success Criteria plus Success Criteria 1.2.3, 2.4.5-6, 3.1.2 (https://www.w3.org/TR/YYYY/REC-WCAG22-YYYYMMDD/)

+

Web Content Accessibility Guidelines {{ versionDecimal }}, W3C World Wide Web Consortium Recommendation XX Month Year, Level A Success Criteria plus Success Criteria 1.x.x, 2.y.y, … 3.z.z. (https://www.w3.org/TR/YYYY/REC-WCAG{{ version }}-YYYYMMDD/)

+

Example of use of a WCAG {{ versionDecimal }} reference in a "shall or must" statement.

+

All Web content on publicly available Web sites shall conform to Web Content Accessibility Guidelines {{ versionDecimal }}, W3C World Wide Web Consortium Recommendation XX Month Year, Level A Success Criteria plus Success Criteria 1.2.3, 2.4.5-6, 3.1.2 (https://www.w3.org/TR/YYYY/REC-WCAG{{ version }}-YYYYMMDD/)

-

Referring to content from WCAG 2 support documents

-

Techniques, which are listed in Understanding WCAG 2 and described in other supporting documents, are not part of the normative WCAG 2 Recommendation and should not be cited using the citation for the WCAG 2 Recommendation itself. References to techniques in support documents should be cited separately.

-

Techniques can be cited based on the individual Technique document or on the master WCAG 2 Techniques document. For example, the technique "Using alt attributes on img elements" could be cited as

+

Referring to content from WCAG {{ versionDecimal }} support documents

+

Techniques, which are listed in Understanding WCAG {{ versionDecimal }} and described in other supporting documents, are not part of the normative WCAG {{ versionDecimal }} Recommendation and should not be cited using the citation for the WCAG {{ versionDecimal }} Recommendation itself. References to techniques in support documents should be cited separately.

+

Techniques can be cited based on the individual Technique document or on the master WCAG {{ versionDecimal }} Techniques document. For example, the technique "Using alt attributes on img elements" could be cited as

"Using alt attributes on img elements," W3C World Wide Web Consortium Note. (URI: {URI of technique})

or

-

W3C World Wide Web Consortium (20xx): WCAG2.2 HTML Techniques (URI: {URI of HTML Techniques})

-

Techniques are not designed to be referenced as "required" from any standard or regulation.Standards and regulations should not make any specific technique mandatory, though they may choose to recommend techniques.

+

W3C World Wide Web Consortium (20xx): WCAG{{ versionDecimal }} HTML Techniques (URI: {URI of HTML Techniques})

+

Techniques are not designed to be referenced as "required" from any standard or regulation. Standards and regulations should not make any specific technique mandatory, though they may choose to recommend techniques.

From 7f74093e068ad755ceca8f91a54df3bfa5aa75d5 Mon Sep 17 00:00:00 2001 From: "Kenneth G. Franqueiro" Date: Tue, 16 Jul 2024 13:16:11 -0400 Subject: [PATCH 05/24] 2.1 support: Update hard-coded version in understanding-techniques --- understanding/understanding-techniques.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/understanding/understanding-techniques.html b/understanding/understanding-techniques.html index be18332fc6..9332ed5fee 100644 --- a/understanding/understanding-techniques.html +++ b/understanding/understanding-techniques.html @@ -138,7 +138,7 @@

Support Notes Change Over Time

Using the Techniques

-

Techniques for WCAG 2 is not intended to be used as a stand-alone document. Instead, it is expected that content authors will usually use How to Meet WCAG 2.2: A customizable quick reference to read the WCAG 2 success criteria, and follow links from there to specific topics in Understanding WCAG 2 and to specific techniques.

+

Techniques for WCAG 2 is not intended to be used as a stand-alone document. Instead, it is expected that content authors will usually use How to Meet WCAG {{ versionDecimal }}: A customizable quick reference to read the WCAG 2 success criteria, and follow links from there to specific topics in Understanding WCAG 2 and to specific techniques.

Alternatives must meet success criteria

Some techniques describe how to provide alternate ways for users to get content. For example, G73: Providing a long description in another location... mentions a transcript as an alternative for an audio file. Some alternatives must also conform to WCAG 2. For example, the transcript itself must meet all relevant success criteria.

From 0d4d4e7e0e9ccd4b95892ef9b9b0e9890e4771dc Mon Sep 17 00:00:00 2001 From: "Kenneth G. Franqueiro" Date: Tue, 16 Jul 2024 14:29:42 -0400 Subject: [PATCH 06/24] Add support for an element being both wcagXY and note --- 11ty/CustomLiquid.ts | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/11ty/CustomLiquid.ts b/11ty/CustomLiquid.ts index 26cffde42c..f6786841d5 100644 --- a/11ty/CustomLiquid.ts +++ b/11ty/CustomLiquid.ts @@ -497,7 +497,8 @@ export class CustomLiquid extends Liquid { // (This is also needed for techniques/about) $("div.note").each((_, el) => { const $el = $(el); - $el.replaceWith(`
+ const classes = el.attribs.class; + $el.replaceWith(`

Note

${$el.html()}
`); @@ -505,7 +506,8 @@ export class CustomLiquid extends Liquid { // Handle p variant after div (the reverse would double-process) $("p.note").each((_, el) => { const $el = $(el); - $el.replaceWith(`
+ const classes = el.attribs.class; + $el.replaceWith(`

Note

${$el.html()}

`); @@ -523,13 +525,19 @@ export class CustomLiquid extends Liquid { // Handle new-in-version content $("[class^='wcag']").each((_, el) => { // Just like the XSLT process, this naively assumes that version numbers are the same length - const classVersion = +el.attribs.class.replace(/^wcag/, ""); - const buildVersion = +scope.version; + const classMatch = el.attribs.class.match(/\bwcag(\d\d)\b/); + if (!classMatch) throw new Error(`Invalid wcagXY class found: ${el.attribs.class}`); + const classVersion = +classMatch[1]; if (isNaN(classVersion)) throw new Error(`Invalid wcagXY class found: ${el.attribs.class}`); + const buildVersion = +scope.version; + if (classVersion > buildVersion) { $(el).remove(); } else if (classVersion === buildVersion) { - $(el).prepend(`New in WCAG ${scope.versionDecimal}: `); + if (/\bnote\b/.test(el.attribs.class)) + $(el).find(".marker").append(` (new in WCAG ${scope.versionDecimal})`); + else + $(el).prepend(`New in WCAG ${scope.versionDecimal}: `); } // Output as-is if content pertains to a version older than what's being built }); From 6306ff3792ef5280c0994f66b9411de04371d6ae Mon Sep 17 00:00:00 2001 From: "Kenneth G. Franqueiro" Date: Tue, 16 Jul 2024 14:30:41 -0400 Subject: [PATCH 07/24] 2.1: Add wcag22 class to 2.2-specific three-flashes-or-below note --- understanding/20/three-flashes-or-below-threshold.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/understanding/20/three-flashes-or-below-threshold.html b/understanding/20/three-flashes-or-below-threshold.html index 9fa345e15f..dd361df119 100644 --- a/understanding/20/three-flashes-or-below-threshold.html +++ b/understanding/20/three-flashes-or-below-threshold.html @@ -92,8 +92,8 @@

Intent of Three Flashes or Below Threshold

-
-

The new (in WCAG 2.2) working definition in the field for "pair of opposing transitions involving a saturated red" is a pair of opposing transitions where, one transition is either to or from a state with a value R/(R + G + B) that is greater than or equal to 0.8, and the difference between states is more than 0.2 (unitless) in the CIE 1976 UCS chromaticity diagram. [ISO 9241-391]

+
+

The new working definition in the field for "pair of opposing transitions involving a saturated red" is a pair of opposing transitions where, one transition is either to or from a state with a value R/(R + G + B) that is greater than or equal to 0.8, and the difference between states is more than 0.2 (unitless) in the CIE 1976 UCS chromaticity diagram. [ISO 9241-391]

The chromaticity difference is calculated as:

  • SQRT( (u'1 - u'2)^2 + (v'1 - v'2)^2 )
  • From ce1807e8ac6bc3b4b82d41da7a398773b1852662 Mon Sep 17 00:00:00 2001 From: "Kenneth G. Franqueiro" Date: Tue, 16 Jul 2024 14:59:01 -0400 Subject: [PATCH 08/24] 2.1: Conditionalize 2.2 SC reference in pointer-gestures --- understanding/21/pointer-gestures.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/understanding/21/pointer-gestures.html b/understanding/21/pointer-gestures.html index 1d935f749d..f6634457c9 100644 --- a/understanding/21/pointer-gestures.html +++ b/understanding/21/pointer-gestures.html @@ -41,7 +41,8 @@

    Intent of this Success Criterion

    Examples of path-based gestures include swiping, sliders and carousels dependent on the direction of interaction, and other gestures which trace a prescribed path such as drawing a specific shape. Such paths may be drawn with a finger or stylus on a touchscreen, graphics tablet, or trackpad, or with a mouse, joystick, or similar pointer device.

    The difference between Pointer Gestures and Dragging

    -

    Dragging is a movement where the user picks up an object with a pointer (such as mouse cursor or a finger) and moves it to some other position. This movement from start point to end point does not require the user to follow any particular path or direction. Dragging is therefore not path-based. In contrast, a path-based pointer gesture requires the traversal of an intermediate point, which is a technical way of expressing that the directionality and possibly speed of the gesture communicates a particular command to the system. Dragging motions are covered in Success Criterion 2.5.7: Dragging Movements.

    +

    Dragging is a movement where the user picks up an object with a pointer (such as mouse cursor or a finger) and moves it to some other position. This movement from start point to end point does not require the user to follow any particular path or direction. Dragging is therefore not path-based. In contrast, a path-based pointer gesture requires the traversal of an intermediate point, which is a technical way of expressing that the directionality and possibly speed of the gesture communicates a particular command to the system. + Dragging motions are covered in Success Criterion 2.5.7: Dragging Movements.

    Hand showing a starting touch, 1. Going to a second point, 2. It follows a very random path. From c7988216e0ea2f4dc700f3945a09744bb33ba9ef Mon Sep 17 00:00:00 2001 From: "Kenneth G. Franqueiro" Date: Thu, 18 Jul 2024 13:00:35 -0400 Subject: [PATCH 09/24] 2.1 support: Avoid linking to future-version-only techniques --- eleventy.config.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/eleventy.config.ts b/eleventy.config.ts index 847752d721..f6b211515a 100644 --- a/eleventy.config.ts +++ b/eleventy.config.ts @@ -279,6 +279,21 @@ export default function (eleventyConfig: any) { } return; } + // Same for techniques only introduced in later WCAG versions + if ( + technique.id in futureExclusiveTechniqueAssociations && + !futureExclusiveTechniqueAssociations[this.page.fileSlug] && + (!allFlatGuidelines[this.page.fileSlug] || + allFlatGuidelines[this.page.fileSlug].version <= version) + ) { + if (process.env.WCAG_VERBOSE) { + console.warn( + `linkTechniques in ${this.page.inputPath}: ` + + `skipping future-version technique ${id}` + ); + } + return; + } // Support relative technique links from other techniques or from techniques/index.html, // otherwise path-absolute when cross-linked from understanding/* From 421b7e686c569f561c3a856f1f89247c0bed4eb0 Mon Sep 17 00:00:00 2001 From: "Kenneth G. Franqueiro" Date: Fri, 19 Jul 2024 17:24:51 -0400 Subject: [PATCH 10/24] 2.1 support: Reference variable for version in index pages --- eleventy.config.ts | 5 +++-- techniques/index.html | 4 ++-- understanding/index.html | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/eleventy.config.ts b/eleventy.config.ts index f6b211515a..500b26faa2 100644 --- a/eleventy.config.ts +++ b/eleventy.config.ts @@ -4,6 +4,7 @@ import { rimraf } from "rimraf"; import { copyFile } from "fs/promises"; import { CustomLiquid } from "11ty/CustomLiquid"; +import { resolveDecimalVersion } from "11ty/common"; import { actRules, assertIsWcagVersion, @@ -102,7 +103,7 @@ const understandingNav = await generateUnderstandingNavMap(principles, understan // Declare static global data up-front so we can build typings from it const globalData = { version, - versionDecimal: version.split("").join("."), + versionDecimal: resolveDecimalVersion(version), techniques, // Used for techniques/index.html technologies, // Used for techniques/index.html technologyTitles, // Used for techniques/index.html @@ -271,7 +272,7 @@ export default function (eleventyConfig: any) { !isGuidelineObsolete(flatGuidelines[this.page.fileSlug]) ) { if (process.env.WCAG_VERBOSE) { - const since = technique.obsoleteSince!.split("").join("."); + const since = resolveDecimalVersion(technique.obsoleteSince!); console.warn( `linkTechniques in ${this.page.inputPath}: ` + `skipping obsolete technique ${id} (as of ${since})` diff --git a/techniques/index.html b/techniques/index.html index 2f6f6245dc..59fbe73e1f 100644 --- a/techniques/index.html +++ b/techniques/index.html @@ -4,7 +4,7 @@ - All WCAG 2.2 Techniques | WAI | W3C + All WCAG {{ versionDecimal }} Techniques | WAI | W3C @@ -34,7 +34,7 @@
    -

    Techniques for WCAG 2.2

    +

    Techniques for WCAG {{ versionDecimal }}

diff --git a/acknowledgements/ag-wg-active.html b/acknowledgements/ag-wg-active.html index 19cc2fd7ea..5fe6963c27 100644 --- a/acknowledgements/ag-wg-active.html +++ b/acknowledgements/ag-wg-active.html @@ -1,5 +1,5 @@
-

Participants of the AG WG active in the development of this document:

+

Participants of the AG WG active in the development of this document:

  • Jake Abma (Invited Expert)
  • Shadi Abou-Zahra (Amazon)
  • diff --git a/acknowledgements/funders.html b/acknowledgements/funders.html index 7d1b5f9402..29e6be2e67 100644 --- a/acknowledgements/funders.html +++ b/acknowledgements/funders.html @@ -1,4 +1,4 @@
    -

    Enabling funders

    +

    Enabling funders

    This publication has been funded in part with U.S. Federal funds from the Health and Human Services, National Institute on Disability, Independent Living, and Rehabilitation Research (NIDILRR), initially under contract number ED-OSE-10-C-0067, then under contract number HHSP23301500054C, and now under HHS75P00120P00168. The content of this publication does not necessarily reflect the views or policies of the U.S. Department of Health and Human Services or the U.S. Department of Education, nor does mention of trade names, commercial products, or organizations imply endorsement by the U.S. Government.

    From c0f95d975eea28bcba4d40bc0760126e647c8f8d Mon Sep 17 00:00:00 2001 From: "Kenneth G. Franqueiro" Date: Mon, 30 Sep 2024 13:57:13 -0400 Subject: [PATCH 14/24] Update npm dependencies for security fixes --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1135bac189..3f9e41028a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "license": "W3C", "dependencies": { "@11ty/eleventy": "^3.0.0", - "axios": "^1.7.2", + "axios": "^1.7.7", "cheerio": "^1.0.0", "glob": "^10.3.16", "gray-matter": "^4.0.3", @@ -807,9 +807,9 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "node_modules/axios": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz", - "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==", + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", + "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", diff --git a/package.json b/package.json index 75dc4278b7..156a3283e7 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "license": "W3C", "dependencies": { "@11ty/eleventy": "^3.0.0", - "axios": "^1.7.2", + "axios": "^1.7.7", "cheerio": "^1.0.0", "glob": "^10.3.16", "gray-matter": "^4.0.3", From 7c421236205da31030eb304be2ef0faece6a3bcb Mon Sep 17 00:00:00 2001 From: "Kenneth G. Franqueiro" Date: Mon, 30 Sep 2024 16:36:02 -0400 Subject: [PATCH 15/24] 2.1 support: Pin ACT mappings to requested version --- 11ty/guidelines.ts | 19 ++++++++++++++----- eleventy.config.ts | 7 ++++++- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/11ty/guidelines.ts b/11ty/guidelines.ts index e033c0964e..7eb46f29dd 100644 --- a/11ty/guidelines.ts +++ b/11ty/guidelines.ts @@ -6,7 +6,7 @@ import { readFile } from "fs/promises"; import { basename } from "path"; import { flattenDomFromFile, load } from "./cheerio"; -import { generateId } from "./common"; +import { generateId, resolveDecimalVersion } from "./common"; export type WcagVersion = "20" | "21" | "22"; export function assertIsWcagVersion(v: string): asserts v is WcagVersion { @@ -29,10 +29,9 @@ type ActMapping = { "act-rules": ActRule[]; }; -/** Data used for test-rules sections, from act-mapping.json */ -export const actRules = ( - JSON.parse(await readFile("guidelines/act-mapping.json", "utf8")) as ActMapping -)["act-rules"]; +/** Retrieves act-mapping.json data for test-rules sections. */ +export const getActRules = async () => + (JSON.parse(await readFile("guidelines/act-mapping.json", "utf8")) as ActMapping)["act-rules"]; /** * Flattened object hash, mapping each WCAG 2 SC slug to the earliest WCAG version it applies to. @@ -281,6 +280,16 @@ export const getAcknowledgementsForVersion = async (version: WcagVersion) => { return subsections; }; +/** + * Retrieves act-mapping.json data for test-rules sections, pinned to an earlier WCAG version. + */ +export const getActRulesForVersion = async (version: WcagVersion) => + ( + await axios.get( + `https://raw.githubusercontent.com/w3c/wcag/refs/heads/WCAG-${resolveDecimalVersion(version)}/guidelines/act-mapping.json` + ) + ).data["act-rules"]; + /** * Retrieves and processes a pinned WCAG version using published guidelines. */ diff --git a/eleventy.config.ts b/eleventy.config.ts index 8639dcc805..f992e96d5d 100644 --- a/eleventy.config.ts +++ b/eleventy.config.ts @@ -6,8 +6,9 @@ import { copyFile } from "fs/promises"; import { CustomLiquid } from "11ty/CustomLiquid"; import { resolveDecimalVersion } from "11ty/common"; import { - actRules, assertIsWcagVersion, + getActRules, + getActRulesForVersion, getFlatGuidelines, getPrinciples, getPrinciplesForVersion, @@ -47,6 +48,10 @@ const isTechniqueObsolete = (technique: Technique | undefined) => const isGuidelineObsolete = (guideline: Principle | Guideline | SuccessCriterion | undefined) => guideline?.type === "SC" && guideline.level === ""; +const actRules = process.env.WCAG_VERSION + ? await getActRulesForVersion(version) + : await getActRules(); + /** Tree of Principles/Guidelines/SC across all versions (including later than selected) */ const allPrinciples = await getPrinciples(); /** Flattened Principles/Guidelines/SC across all versions (including later than selected) */ From 1b58864eadf6a4b9757da3ae7bd610abf8184994 Mon Sep 17 00:00:00 2001 From: "Kenneth G. Franqueiro" Date: Mon, 30 Sep 2024 17:59:12 -0400 Subject: [PATCH 16/24] Remove paragraph from target-size-enhanced that duplicates template --- understanding/21/target-size-enhanced.html | 1 - 1 file changed, 1 deletion(-) diff --git a/understanding/21/target-size-enhanced.html b/understanding/21/target-size-enhanced.html index 6690225d7c..5084184159 100644 --- a/understanding/21/target-size-enhanced.html +++ b/understanding/21/target-size-enhanced.html @@ -84,7 +84,6 @@

    Resources

Techniques

-

Each numbered item in this section represents a technique or combination of techniques that the WCAG Working Group deems sufficient for meeting this Success Criterion. However, it is not necessary to use these particular techniques. For information on using other techniques, see Understanding Techniques for WCAG Success Criteria, particularly the "Other Techniques" section.

Sufficient

From 7cd97a87c74cd2575533f22460b2bd85c9fb2315 Mon Sep 17 00:00:00 2001 From: "Kenneth G. Franqueiro" Date: Tue, 1 Oct 2024 12:12:10 -0400 Subject: [PATCH 17/24] Update Intro and Understanding Techniques to reference specific version --- understanding/intro.html | 22 ++++++------ understanding/understanding-techniques.html | 38 ++++++++++----------- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/understanding/intro.html b/understanding/intro.html index f4c29f8a8d..abdc83175e 100644 --- a/understanding/intro.html +++ b/understanding/intro.html @@ -1,17 +1,17 @@ - Introduction to Understanding WCAG 2 + Introduction to Understanding WCAG {{ versionDecimal }} -

Introduction to Understanding WCAG 2

-

Understanding WCAG 2 is an essential guide to understanding and using "Web Content Accessibility Guidelines 2". Although the normative definition and requirements for WCAG 2 can all be found in the WCAG 2 document itself, the concepts and provisions may be new to some people. Understanding WCAG 2 provides a non-normative extended commentary on each guideline and each success criterion to help readers better understand the intent and how the guidelines and success criteria work together. It also provides examples of techniques or combinations of techniques that the Working Group has identified as being sufficient to meet each success criterion. Links are then provided to write-ups for each of the techniques.

+

Introduction to Understanding WCAG {{ versionDecimal }}

+

Understanding WCAG {{ versionDecimal }} is an essential guide to understanding and using "Web Content Accessibility Guidelines {{ versionDecimal }}". Although the normative definition and requirements for WCAG {{ versionDecimal }} can all be found in the WCAG {{ versionDecimal }} document itself, the concepts and provisions may be new to some people. Understanding WCAG {{ versionDecimal }} provides a non-normative extended commentary on each guideline and each success criterion to help readers better understand the intent and how the guidelines and success criteria work together. It also provides examples of techniques or combinations of techniques that the Working Group has identified as being sufficient to meet each success criterion. Links are then provided to write-ups for each of the techniques.

This is not an introductory document. It is a detailed technical description of the guidelines and their success criteria. See Web Content Accessibility Guidelines (WCAG) Overview for an introduction to WCAG, supporting technical documents, and educational material.

-

Understanding WCAG 2 is organized by guideline. There is an Understanding Guideline X.X section for each guideline. The intent and any advisory techniques that are related to the guideline but not specifically related to any of its success criteria are listed there as well.

+

Understanding WCAG {{ versionDecimal }} is organized by guideline. There is an Understanding Guideline X.X section for each guideline. The intent and any advisory techniques that are related to the guideline but not specifically related to any of its success criteria are listed there as well.

The Understanding Guidelines X.X section is then followed by an Understanding success criterion X.X.X section for each success criterion of that guideline. These sections each contain:

  • -

    The success criterion as it appears in WCAG 2

    +

    The success criterion as it appears in WCAG {{ versionDecimal }}

  • Intent of the success criterion

    @@ -35,11 +35,11 @@

    Introduction to Understanding WCAG 2

    Additional advisory techniques that go beyond what is required to meet the success criterion but can be used to make some or all types of content more accessible. Use of advisory techniques does not impact the level of conformance claimed.

  • -

    Key terms for this success criterion (taken from the WCAG 2 Glossary)

    +

    Key terms for this success criterion (taken from the WCAG {{ versionDecimal }} Glossary)

-

Links are provided from each Guideline in WCAG 2 directly to each Understanding Guideline X.X in this document. Similarly, there is a link from each success criterion in WCAG 2 to the Understanding Success Criterion X.X.X section in this document.

-

For information about individual techniques, follow the links throughout this document to the techniques of interest in the Techniques for WCAG 2 document.

+

Links are provided from each Guideline in WCAG {{ versionDecimal }} directly to each Understanding Guideline X.X in this document. Similarly, there is a link from each success criterion in WCAG {{ versionDecimal }} to the Understanding Success Criterion X.X.X section in this document.

+

For information about individual techniques, follow the links throughout this document to the techniques of interest in the Techniques for WCAG {{ versionDecimal }} document.

For links to information on different disabilities and assistive technologies see Disabilities on Wikipedia.

Understanding the Four Principles of Accessibility

@@ -79,18 +79,18 @@

Understanding the Four Principles of Accessibility

If any of these are not true, users with disabilities will not be able to use the Web.

-

Under each of the principles are guidelines and success criteria that help to address these principles for people with disabilities. There are many general usability guidelines that make content more usable by all people, including those with disabilities. However, in WCAG 2, we only include those guidelines that address problems particular to people with disabilities. This includes issues that block access or interfere with access to the Web more severely for people with disabilities.

+

Under each of the principles are guidelines and success criteria that help to address these principles for people with disabilities. There are many general usability guidelines that make content more usable by all people, including those with disabilities. However, in WCAG {{ versionDecimal }}, we only include those guidelines that address problems particular to people with disabilities. This includes issues that block access or interfere with access to the Web more severely for people with disabilities.

Layers of Guidance

The Guidelines

-

Under each principle there is a list of guidelines that address the principle. There are a total of 13 guidelines. A convenient list of just the guidelines can be found in the WCAG 2 table of contents. One of the key objectives of the guidelines is to ensure that content is directly accessible to as many people as possible, and capable of being re-presented in different forms to match different peoples' sensory, physical and cognitive abilities.

+

Under each principle there is a list of guidelines that address the principle. There are a total of 13 guidelines. A convenient list of just the guidelines can be found in the WCAG {{ versionDecimal }} table of contents. One of the key objectives of the guidelines is to ensure that content is directly accessible to as many people as possible, and capable of being re-presented in different forms to match different peoples' sensory, physical and cognitive abilities.

Success Criteria

Under each guideline, there are success criteria that describe specifically what must be achieved in order to conform to this standard. They are similar to the "checkpoints" in WCAG 1.0. Each success criterion is written as a statement that will be either true or false when specific Web content is tested against it. The success criteria are written to be technology neutral.

-

All WCAG 2 success criteria are written as testable criteria for objectively determining if content satisfies the success criteria. While some of the testing can be automated using software evaluation programs, others require human testers for part or all of the test.

+

All WCAG {{ versionDecimal }} success criteria are written as testable criteria for objectively determining if content satisfies the success criteria. While some of the testing can be automated using software evaluation programs, others require human testers for part or all of the test.

Although content may satisfy the success criteria, the content may not always be usable by people with a wide variety of disabilities. Professional reviews utilizing recognized qualitative heuristics are important in achieving accessibility for some audiences. In addition, usability testing is recommended. Usability testing aims to determine how well people can use the content for its intended purpose.

The content should be tested by those who understand how people with different types of disabilities use the Web. It is recommended that users with disabilities be included in test groups when performing human testing.

Each success criterion for a guideline has a link to the section of the How to Meet document that provides:

diff --git a/understanding/understanding-techniques.html b/understanding/understanding-techniques.html index 9332ed5fee..c4b3322563 100644 --- a/understanding/understanding-techniques.html +++ b/understanding/understanding-techniques.html @@ -1,13 +1,13 @@ - Understanding Techniques for WCAG 2 Success Criteria + Understanding Techniques for WCAG {{ versionDecimal }} Success Criteria -

Understanding Techniques for WCAG 2 Success Criteria

-

WCAG 2 guidelines and success criteria are designed to be broadly applicable to current and future web technologies, including dynamic applications, mobile, digital television, etc. They are stable and do not change.

-

Specific guidance for authors and evaluators on meeting the WCAG 2 success criteria is provided in techniques, which include code examples, resources, and tests. W3C's Techniques for WCAG 2 document is updated periodically, about twice per year, to cover more current best practices and changes in technologies and tools.

-

The three types of guidance in Techniques for WCAG 2 are explained below:

+

Understanding Techniques for WCAG {{ versionDecimal }} Success Criteria

+

WCAG {{ versionDecimal }} guidelines and success criteria are designed to be broadly applicable to current and future web technologies, including dynamic applications, mobile, digital television, etc. They are stable and do not change.

+

Specific guidance for authors and evaluators on meeting the WCAG {{ versionDecimal }} success criteria is provided in techniques, which include code examples, resources, and tests. W3C's Techniques for WCAG {{ versionDecimal }} document is updated periodically, about twice per year, to cover more current best practices and changes in technologies and tools.

+

The three types of guidance in Techniques for WCAG {{ versionDecimal }} are explained below:

  • Sufficient techniques
  • Advisory techniques
  • @@ -24,14 +24,14 @@

    Understanding Techniques for WCAG 2 Success Criteria

    Understanding Conformance provides related information, including on understanding accessibility support.

    Techniques are Informative

    -

    Techniques are informative — that means they are not required. The basis for determining conformance to WCAG 2 are the success criteria from the WCAG 2 standard — not the techniques.

    -

    Note 1: W3C cautions against requiring W3C's sufficient techniques. The only thing that should be required is meeting the WCAG 2 success criteria. To learn more, see:

    +

    Techniques are informative — that means they are not required. The basis for determining conformance to WCAG {{ versionDecimal }} are the success criteria from the WCAG {{ versionDecimal }} standard — not the techniques.

    +

    Note 1: W3C cautions against requiring W3C's sufficient techniques. The only thing that should be required is meeting the WCAG {{ versionDecimal }} success criteria. To learn more, see:

    -

    Note 2: Techniques for WCAG 2 uses the words "must" and "should" only to clarify guidance within the techniques, not to convey requirements for WCAG 2.

    +

    Note 2: Techniques for WCAG {{ versionDecimal }} uses the words "must" and "should" only to clarify guidance within the techniques, not to convey requirements for WCAG {{ versionDecimal }}.

    Sufficient Techniques

    @@ -44,7 +44,7 @@

    Sufficient Techniques

    From an evaluator's perspective: If web content implements the sufficient techniques for a given criterion correctly and it is accessibility-supported for the content's users, it conforms to that success criterion. (The converse is not true; if content does not implement these sufficient techniques, it does not necessarily fail the success criteria, as explained in Testing Techniques below.)

-

There may be other ways to meet success criteria besides the sufficient techniques in W3C's Techniques for WCAG 2 document, as explained in Other Techniques below. (See also Techniques are Informative above.)

+

There may be other ways to meet success criteria besides the sufficient techniques in W3C's Techniques for WCAG {{ versionDecimal }} document, as explained in Other Techniques below. (See also Techniques are Informative above.)

Numbered Lists, "AND"

The W3C-documented sufficient techniques are provided in a numbered list where each list item provides a technique or combination of techniques that can be used to meet the success criterion. Where there are multiple techniques on a numbered list item connected by "AND" then all of the techniques must be used to be sufficient. For example, Sufficient Techniques for 1.3.1 has: "G115: Using semantic elements to mark up structure AND H49: Using semantic markup to mark emphasized or special text (HTML)".

@@ -72,35 +72,35 @@

Failures

Authors to know what to avoid,

  • -

    Evaluators to use for checking if content does not meet WCAG 2 success criteria.

    +

    Evaluators to use for checking if content does not meet WCAG {{ versionDecimal }} success criteria.

  • -

    Content that has a failure does not meet WCAG 2 success criteria, unless an alternate version is provided without the failure.

    +

    Content that has a failure does not meet WCAG {{ versionDecimal }} success criteria, unless an alternate version is provided without the failure.

    If anyone identifies a situation where a documented failure is not correct, please report the situation as a WCAG 2 comment so that it can be corrected or deleted as appropriate.

    General and Technology-specific Techniques

    General techniques describe basic practices that apply to all technologies. Technology-specific techniques apply to a specific technology.

    Some success criteria do not have technology-specific techniques and are covered only with general techniques. Therefore, both the general techniques and the relevant technology-specific techniques should be considered.

    -

    Publication of techniques for a specific technology does not imply that the technology can be used in all situations to create content that meets WCAG 2 success criteria and conformance requirements. Developers need to be aware of the limitations of specific technologies and provide content in a way that is accessible to people with disabilities.

    +

    Publication of techniques for a specific technology does not imply that the technology can be used in all situations to create content that meets WCAG {{ versionDecimal }} success criteria and conformance requirements. Developers need to be aware of the limitations of specific technologies and provide content in a way that is accessible to people with disabilities.

    Other Techniques

    -

    In addition to the techniques in W3C's Techniques for WCAG 2 document, there are other ways to meet WCAG 2 success criteria. W3C's techniques are not comprehensive and may not cover newer technologies and situations.

    -

    Web content does not have to use W3C's published techniques in order to conform to WCAG 2. (See also Techniques are Informative above.)

    -

    Content authors can develop different techniques. For example, an author could develop a technique for HTML5, WAI-ARIA, or other new technology. Other organizations may develop sets of techniques to meet WCAG 2 success criteria.

    +

    In addition to the techniques in W3C's Techniques for WCAG {{ versionDecimal }} document, there are other ways to meet WCAG {{ versionDecimal }} success criteria. W3C's techniques are not comprehensive and may not cover newer technologies and situations.

    +

    Web content does not have to use W3C's published techniques in order to conform to WCAG {{ versionDecimal }}. (See also Techniques are Informative above.)

    +

    Content authors can develop different techniques. For example, an author could develop a technique for HTML5, WAI-ARIA, or other new technology. Other organizations may develop sets of techniques to meet WCAG {{ versionDecimal }} success criteria.

    Any techniques can be sufficient if:

    Submitting Techniques

    -

    The WCAG Working Group encourages people to submit new techniques so that they can be considered for inclusion in updates of the Techniques for WCAG 2 document. Please submit techniques for consideration to the WCAG repository on GitHub.

    +

    The WCAG Working Group encourages people to submit new techniques so that they can be considered for inclusion in updates of the Techniques for WCAG {{ versionDecimal }} document. Please submit techniques for consideration to the WCAG repository on GitHub.

    @@ -138,10 +138,10 @@

    Support Notes Change Over Time

    Using the Techniques

    -

    Techniques for WCAG 2 is not intended to be used as a stand-alone document. Instead, it is expected that content authors will usually use How to Meet WCAG {{ versionDecimal }}: A customizable quick reference to read the WCAG 2 success criteria, and follow links from there to specific topics in Understanding WCAG 2 and to specific techniques.

    +

    Techniques for WCAG {{ versionDecimal }} is not intended to be used as a stand-alone document. Instead, it is expected that content authors will usually use How to Meet WCAG {{ versionDecimal }}: A customizable quick reference to read the WCAG 2 success criteria, and follow links from there to specific topics in Understanding WCAG 2 and to specific techniques.

    Alternatives must meet success criteria

    -

    Some techniques describe how to provide alternate ways for users to get content. For example, G73: Providing a long description in another location... mentions a transcript as an alternative for an audio file. Some alternatives must also conform to WCAG 2. For example, the transcript itself must meet all relevant success criteria.

    +

    Some techniques describe how to provide alternate ways for users to get content. For example, mentions a transcript as an alternative for an audio file. Some alternatives must also conform to WCAG 2. For example, the transcript itself must meet all relevant success criteria.

    Example Code

    From 8c5535d1e0f3db26a78efecc148f92cc09c6f5eb Mon Sep 17 00:00:00 2001 From: "Kenneth G. Franqueiro" Date: Tue, 1 Oct 2024 13:24:09 -0400 Subject: [PATCH 18/24] Re-simplify title logic for other understanding docs --- 11ty/CustomLiquid.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/11ty/CustomLiquid.ts b/11ty/CustomLiquid.ts index c603a975b6..536ddfe9ef 100644 --- a/11ty/CustomLiquid.ts +++ b/11ty/CustomLiquid.ts @@ -395,12 +395,9 @@ export class CustomLiquid extends Liquid { `Understanding ${type} ${scope.guideline.num}: ${scope.guideline.name}${titleSuffix}` ); } else { - // Update WCAG version in title and top-level heading in other understanding docs - const wcagPattern = /WCAG 2(?:\.\d)?( |$)/; - const wcagReplacement = `WCAG ${scope.versionDecimal}$1`; - $title.text($title.text().replace(wcagPattern, wcagReplacement) + titleSuffix); - const $h1 = $("h1"); - $h1.text($h1.text().replace(wcagPattern, wcagReplacement)); + $title.text( + $title.text().replace(/WCAG 2( |$)/, `WCAG ${scope.versionDecimal}$1`) + titleSuffix + ); } // Remove Techniques section from obsolete SCs (e.g. Parsing in 2.2) From 81d5d1ac6f39fe32ab2257d15d7bc16fe44aa04c Mon Sep 17 00:00:00 2001 From: "Kenneth G. Franqueiro" Date: Mon, 7 Oct 2024 12:48:07 -0400 Subject: [PATCH 19/24] Revert "2.1 support: Pin ACT mappings to requested version" This reverts commit 89e759363bd0c26c26a0754f3a3c472564a4d9bd. After discussion with Daniel who conferred with ACT planning team, the version of act-mapping.json currently on the WCAG-2.1 branch is indeed very out of date and not something to preserve. --- 11ty/guidelines.ts | 19 +++++-------------- eleventy.config.ts | 7 +------ 2 files changed, 6 insertions(+), 20 deletions(-) diff --git a/11ty/guidelines.ts b/11ty/guidelines.ts index 7eb46f29dd..e033c0964e 100644 --- a/11ty/guidelines.ts +++ b/11ty/guidelines.ts @@ -6,7 +6,7 @@ import { readFile } from "fs/promises"; import { basename } from "path"; import { flattenDomFromFile, load } from "./cheerio"; -import { generateId, resolveDecimalVersion } from "./common"; +import { generateId } from "./common"; export type WcagVersion = "20" | "21" | "22"; export function assertIsWcagVersion(v: string): asserts v is WcagVersion { @@ -29,9 +29,10 @@ type ActMapping = { "act-rules": ActRule[]; }; -/** Retrieves act-mapping.json data for test-rules sections. */ -export const getActRules = async () => - (JSON.parse(await readFile("guidelines/act-mapping.json", "utf8")) as ActMapping)["act-rules"]; +/** Data used for test-rules sections, from act-mapping.json */ +export const actRules = ( + JSON.parse(await readFile("guidelines/act-mapping.json", "utf8")) as ActMapping +)["act-rules"]; /** * Flattened object hash, mapping each WCAG 2 SC slug to the earliest WCAG version it applies to. @@ -280,16 +281,6 @@ export const getAcknowledgementsForVersion = async (version: WcagVersion) => { return subsections; }; -/** - * Retrieves act-mapping.json data for test-rules sections, pinned to an earlier WCAG version. - */ -export const getActRulesForVersion = async (version: WcagVersion) => - ( - await axios.get( - `https://raw.githubusercontent.com/w3c/wcag/refs/heads/WCAG-${resolveDecimalVersion(version)}/guidelines/act-mapping.json` - ) - ).data["act-rules"]; - /** * Retrieves and processes a pinned WCAG version using published guidelines. */ diff --git a/eleventy.config.ts b/eleventy.config.ts index f992e96d5d..8639dcc805 100644 --- a/eleventy.config.ts +++ b/eleventy.config.ts @@ -6,9 +6,8 @@ import { copyFile } from "fs/promises"; import { CustomLiquid } from "11ty/CustomLiquid"; import { resolveDecimalVersion } from "11ty/common"; import { + actRules, assertIsWcagVersion, - getActRules, - getActRulesForVersion, getFlatGuidelines, getPrinciples, getPrinciplesForVersion, @@ -48,10 +47,6 @@ const isTechniqueObsolete = (technique: Technique | undefined) => const isGuidelineObsolete = (guideline: Principle | Guideline | SuccessCriterion | undefined) => guideline?.type === "SC" && guideline.level === ""; -const actRules = process.env.WCAG_VERSION - ? await getActRulesForVersion(version) - : await getActRules(); - /** Tree of Principles/Guidelines/SC across all versions (including later than selected) */ const allPrinciples = await getPrinciples(); /** Flattened Principles/Guidelines/SC across all versions (including later than selected) */ From 8b6e9af2e632fbbb89f2efd83c581bb2da558da3 Mon Sep 17 00:00:00 2001 From: "Kenneth G. Franqueiro" Date: Wed, 9 Oct 2024 14:33:00 -0400 Subject: [PATCH 20/24] Move techniques changelog to version-specific files --- _includes/techniques/changelog/21.html | 35 +++++++++++++++++++++++++ _includes/techniques/changelog/22.html | 35 +++++++++++++++++++++++++ techniques/index.html | 36 +------------------------- 3 files changed, 71 insertions(+), 35 deletions(-) create mode 100644 _includes/techniques/changelog/21.html create mode 100644 _includes/techniques/changelog/22.html diff --git a/_includes/techniques/changelog/21.html b/_includes/techniques/changelog/21.html new file mode 100644 index 0000000000..3323b54657 --- /dev/null +++ b/_includes/techniques/changelog/21.html @@ -0,0 +1,35 @@ +
      +
    1. : Removed F87 Failure of Success Criterion 1.3.1 due to inserting non-decorative content by using :before and :after pseudo-elements and the 'content' property in CSS
    2. +
    3. : Removed Flash techniques.
    4. +
    5. : Added {{ "F105" | linkTechniques }}
    6. +
    7. : Added {{ "F106" | linkTechniques }}
    8. +
    9. : Added {{ "F104" | linkTechniques }}
    10. +
    11. : Added {{ "F103" | linkTechniques }}
    12. +
    13. : Added {{ "F102" | linkTechniques }}
    14. +
    15. : Added {{ "G216" | linkTechniques }}
    16. +
    17. : Added {{ "G215" | linkTechniques }}
    18. +
    19. : Added {{ "F100" | linkTechniques }}
    20. +
    21. : Added {{ "G214" | linkTechniques }}
    22. +
    23. : Added {{ "C40" | linkTechniques }}
    24. +
    25. : Added {{ "F99" | linkTechniques }}
    26. +
    27. : Added {{ "SCR39" | linkTechniques }}
    28. +
    29. : Added {{ "G212" | linkTechniques }}
    30. +
    31. : Added {{ "F98" | linkTechniques }}
    32. +
    33. : Added {{ "G213" | linkTechniques }}
    34. +
    35. : Added {{ "ARIA24" | linkTechniques }}
    36. +
    37. : Added {{ "F97" | linkTechniques }}
    38. +
    39. : Removed {{ "F52" | linkTechniques }} from SC 3.2.1 (still attached to SC 3.2.5)
    40. +
    41. : Added {{ "G209" | linkTechniques }}
    42. +
    43. : Added {{ "C39" | linkTechniques }}
    44. +
    45. : Added {{ "G207" | linkTechniques }}
    46. +
    47. : Added {{ "C38" | linkTechniques }}
    48. +
    49. : Added {{ "F95" | linkTechniques }}
    50. +
    51. : Added {{ "C34" | linkTechniques }}
    52. +
    53. : Added {{ "C36" | linkTechniques }}
    54. +
    55. : Added {{ "C37" | linkTechniques }}
    56. +
    57. : Added {{ "G207" | linkTechniques }}
    58. +
    59. : Added {{ "F95" | linkTechniques }}
    60. +
    61. : Added {{ "F96" | linkTechniques }}
    62. +
    63. : Added {{ "C38" | linkTechniques }}
    64. +
    65. : Added {{ "G207" | linkTechniques }}
    66. +
    diff --git a/_includes/techniques/changelog/22.html b/_includes/techniques/changelog/22.html new file mode 100644 index 0000000000..3323b54657 --- /dev/null +++ b/_includes/techniques/changelog/22.html @@ -0,0 +1,35 @@ +
      +
    1. : Removed F87 Failure of Success Criterion 1.3.1 due to inserting non-decorative content by using :before and :after pseudo-elements and the 'content' property in CSS
    2. +
    3. : Removed Flash techniques.
    4. +
    5. : Added {{ "F105" | linkTechniques }}
    6. +
    7. : Added {{ "F106" | linkTechniques }}
    8. +
    9. : Added {{ "F104" | linkTechniques }}
    10. +
    11. : Added {{ "F103" | linkTechniques }}
    12. +
    13. : Added {{ "F102" | linkTechniques }}
    14. +
    15. : Added {{ "G216" | linkTechniques }}
    16. +
    17. : Added {{ "G215" | linkTechniques }}
    18. +
    19. : Added {{ "F100" | linkTechniques }}
    20. +
    21. : Added {{ "G214" | linkTechniques }}
    22. +
    23. : Added {{ "C40" | linkTechniques }}
    24. +
    25. : Added {{ "F99" | linkTechniques }}
    26. +
    27. : Added {{ "SCR39" | linkTechniques }}
    28. +
    29. : Added {{ "G212" | linkTechniques }}
    30. +
    31. : Added {{ "F98" | linkTechniques }}
    32. +
    33. : Added {{ "G213" | linkTechniques }}
    34. +
    35. : Added {{ "ARIA24" | linkTechniques }}
    36. +
    37. : Added {{ "F97" | linkTechniques }}
    38. +
    39. : Removed {{ "F52" | linkTechniques }} from SC 3.2.1 (still attached to SC 3.2.5)
    40. +
    41. : Added {{ "G209" | linkTechniques }}
    42. +
    43. : Added {{ "C39" | linkTechniques }}
    44. +
    45. : Added {{ "G207" | linkTechniques }}
    46. +
    47. : Added {{ "C38" | linkTechniques }}
    48. +
    49. : Added {{ "F95" | linkTechniques }}
    50. +
    51. : Added {{ "C34" | linkTechniques }}
    52. +
    53. : Added {{ "C36" | linkTechniques }}
    54. +
    55. : Added {{ "C37" | linkTechniques }}
    56. +
    57. : Added {{ "G207" | linkTechniques }}
    58. +
    59. : Added {{ "F95" | linkTechniques }}
    60. +
    61. : Added {{ "F96" | linkTechniques }}
    62. +
    63. : Added {{ "C38" | linkTechniques }}
    64. +
    65. : Added {{ "G207" | linkTechniques }}
    66. +
    diff --git a/techniques/index.html b/techniques/index.html index 59fbe73e1f..ecdb524b35 100644 --- a/techniques/index.html +++ b/techniques/index.html @@ -47,41 +47,7 @@

    Techniques for WCAG {{ versionDecimal }}

    Change Log

    A list of new, removed or significantly updated techniques:

    -
      -
    1. : Removed F87 Failure of Success Criterion 1.3.1 due to inserting non-decorative content by using :before and :after pseudo-elements and the 'content' property in CSS
    2. -
    3. : Removed Flash techniques.
    4. -
    5. : Added Failure of Success Criterion 2.5.1 due to providing functionality via a path-based gesture without simple pointer alternative
    6. -
    7. : Added Failure due to inability to deactivate motion actuation
    8. -
    9. : Added Failure of Success Criterion 1.4.12 due to clipped or overlapped content when text spacing is adjusted
    10. -
    11. : Added Failure of Success Criterion 4.1.3 due to providing status messages that cannot be programmatically determined through role or properties
    12. -
    13. : Added Failure of Success Criterion 1.4.10 due to content disappearing and not being available when content has reflowed
    14. -
    15. : Added Providing single point activation for a control slider
    16. -
    17. : Added Providing controls to achieve the same result as path based or multipoint gestures
    18. -
    19. : Added Failure of Success Criterion 1.3.4 due to showing a message asking to reorient device
    20. -
    21. : Added Using a control to allow access to content in different orientations which is otherwise restricted
    22. -
    23. : Added Creating a two-color focus indicator to ensure sufficient contrast
    24. -
    25. : Added Failure of Success Criterion 2.1.4 due to implementing character key shortcuts that cannot be turned off or remapped
    26. -
    27. : Added Making content on focus or hover hoverable, dismissible, and persistent
    28. -
    29. : Added Using native controls to ensure functionality is triggered on the up-event
    30. -
    31. : Added Failure of Success Criterion 2.5.6 due to interactions being limited to touch-only on touchscreen devices
    32. -
    33. : Added Provide conventional controls and an application setting for motion activated input
    34. -
    35. : Added Semantically identifying a font icon with role="img"
    36. -
    37. : Added Failure due to locking the orientation to landscape or portrait view
    38. -
    39. : Removed F52 from SC 3.2.1 (still attached to SC 3.2.5)
    40. -
    41. : Added Provide sufficient contrast at the boundaries between adjoining colors
    42. -
    43. : Added Using the CSS reduce-motion query to prevent motion
    44. -
    45. : Added Ensuring that a contrast ratio of 3:1 is provided for icons
    46. -
    47. : Added Using CSS width, max-width and flexbox to fit labels and inputs
    48. -
    49. : Added F95 of 1.4.13 due to content shown on hover not being hoverable
    50. -
    51. : Added C34 Using media queries to un-fixing sticky headers / footers
    52. -
    53. : Added C36 Allowing for text spacing override
    54. -
    55. : Added C37 Using CSS max-width and height to fit images
    56. -
    57. : Added G207 Ensuring that drag-and-drop actions can be cancelled
    58. -
    59. : Added F95 Failure of Success Criterion 1.4.13 due to content shown on hover not being hoverable
    60. -
    61. : Added F96 Failure of Success Criterion 2.5.3 due to "accessible name" not containing the visible label text
    62. -
    63. : Added C38 Using CSS width, max-width and flexbox to fit labels and inputs
    64. -
    65. : Added G207 Ensuring that a contrast ratio of 3:1 is provided for icons
    66. -
    + {% include "techniques/changelog/{{ version }}.html" %}

    For a more detailed view of recent changes to the informative documents see the github updates.

    ${$el.html()}

    `); }); - + // Add header to example sections in Key Terms (aside) and Conformance (div) $("aside.example, div.example").each((_, el) => { const $el = $(el); From 312c92a10ea421004f4362a39fa9b9eda17d0832 Mon Sep 17 00:00:00 2001 From: "Kenneth G. Franqueiro" Date: Wed, 9 Oct 2024 16:45:37 -0400 Subject: [PATCH 23/24] Fix Cheerio 1.0.0 type errors --- 11ty/CustomLiquid.ts | 9 ++++----- 11ty/cheerio.ts | 3 +++ 11ty/guidelines.ts | 6 +++--- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/11ty/CustomLiquid.ts b/11ty/CustomLiquid.ts index 72f5357ceb..6b1a4394b6 100644 --- a/11ty/CustomLiquid.ts +++ b/11ty/CustomLiquid.ts @@ -1,4 +1,3 @@ -import type { Cheerio, Element } from "cheerio"; import { Liquid, type Template } from "liquidjs"; import type { RenderOptions } from "liquidjs/dist/liquid-options"; import compact from "lodash-es/compact"; @@ -8,7 +7,7 @@ import { basename } from "path"; import type { GlobalData } from "eleventy.config"; -import { flattenDom, load } from "./cheerio"; +import { flattenDom, load, type CheerioAnyNode } from "./cheerio"; import { generateId } from "./common"; import { getAcknowledgementsForVersion, getTermsMap } from "./guidelines"; import { resolveTechniqueIdFromHref, understandingToTechniqueLinkSelector } from "./techniques"; @@ -61,7 +60,7 @@ const normalizeTocLabel = (label: string) => * expand to a link with the full technique ID and title. * @param $el a $()-wrapped link element */ -function expandTechniqueLink($el: Cheerio) { +function expandTechniqueLink($el: CheerioAnyNode) { const href = $el.attr("href"); if (!href) throw new Error("expandTechniqueLink: non-link element encountered"); const id = resolveTechniqueIdFromHref(href); @@ -407,7 +406,7 @@ export class CustomLiquid extends Liquid { // Process defined terms within #render, // where we have access to global data and the about box's HTML const $termLinks = $(termLinkSelector); - const extractTermName = ($el: Cheerio) => { + const extractTermName = ($el: CheerioAnyNode) => { const name = $el .text() .toLowerCase() @@ -432,7 +431,7 @@ export class CustomLiquid extends Liquid { }); } else if (scope.isUnderstanding) { const $termsList = $("section#key-terms dl"); - const extractTermNames = ($links: Cheerio) => + const extractTermNames = ($links: CheerioAnyNode) => compact(uniq($links.toArray().map((el) => extractTermName($(el))))); if ($termLinks.length) { diff --git a/11ty/cheerio.ts b/11ty/cheerio.ts index f1292be14b..72f3c65008 100644 --- a/11ty/cheerio.ts +++ b/11ty/cheerio.ts @@ -5,6 +5,9 @@ import { dirname, resolve } from "path"; export { load } from "cheerio"; +/** Superset of the type returned by any Cheerio $() call. */ +export type CheerioAnyNode = ReturnType>; + /** Convenience function that combines readFile and load. */ export const loadFromFile = async ( inputPath: string, diff --git a/11ty/guidelines.ts b/11ty/guidelines.ts index e033c0964e..b4501461bb 100644 --- a/11ty/guidelines.ts +++ b/11ty/guidelines.ts @@ -1,11 +1,11 @@ import axios from "axios"; -import type { Cheerio, CheerioAPI, Element } from "cheerio"; +import type { CheerioAPI } from "cheerio"; import { glob } from "glob"; import { readFile } from "fs/promises"; import { basename } from "path"; -import { flattenDomFromFile, load } from "./cheerio"; +import { flattenDomFromFile, load, type CheerioAnyNode } from "./cheerio"; import { generateId } from "./common"; export type WcagVersion = "20" | "21" | "22"; @@ -107,7 +107,7 @@ const contentIgnores = [ * Returns HTML content used for Understanding guideline/SC boxes. * @param $el Cheerio element of the full section from flattened guidelines/index.html */ -const getContentHtml = ($el: Cheerio) => { +const getContentHtml = ($el: CheerioAnyNode) => { // Load HTML into a new instance, remove elements we don't want, then return the remainder const $ = load($el.html()!, null, false); $(contentIgnores.join(", ")).remove(); From 895e0b1ff78952f7b8aa1bcbf5aa35d51e0c581d Mon Sep 17 00:00:00 2001 From: "Kenneth G. Franqueiro" Date: Fri, 8 Nov 2024 17:00:07 -0500 Subject: [PATCH 24/24] 2.1 support: Prune future-version SCs from technique titles --- 11ty/techniques.ts | 33 ++++++++++++++++++++++++++++++--- eleventy.config.ts | 2 +- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/11ty/techniques.ts b/11ty/techniques.ts index 94820e7784..fcfd5dd1f6 100644 --- a/11ty/techniques.ts +++ b/11ty/techniques.ts @@ -199,7 +199,7 @@ export interface Technique extends TechniqueFrontMatter { * Used to generate index table of contents. * (Functionally equivalent to "techniques-list" target in build.xml) */ -export async function getTechniquesByTechnology() { +export async function getTechniquesByTechnology(guidelines: FlatGuidelinesMap) { const paths = await glob("*/*.html", { cwd: "techniques" }); const techniques = technologies.reduce( (map, technology) => ({ @@ -208,6 +208,9 @@ export async function getTechniquesByTechnology() { }), {} as Record ); + const scNumbers = Object.values(guidelines) + .filter((entry): entry is SuccessCriterion => entry.type === "SC") + .map(({ num }) => num); // Check directory data files (we don't have direct access to 11ty's data cascade here) const technologyData: Partial> = {}; @@ -237,13 +240,37 @@ export async function getTechniquesByTechnology() { if (!h1Match || !h1Match[1]) throw new Error(`No h1 found in techniques/${path}`); const $h1 = load(h1Match[1], null, false); - const title = $h1.text(); + let title = $h1.text(); + let titleHtml = $h1.html(); + if (process.env.WCAG_VERSION) { + // Check for invalid SC references for the WCAG version being built + const multiScPattern = /(?:\d\.\d+\.\d+(,?) )+and \d\.\d+\.\d+/; + if (multiScPattern.test(title)) { + const scPattern = /\d\.\d+\.\d+/g; + const criteria: typeof scNumbers = []; + let match; + while ((match = scPattern.exec(title))) + criteria.push(match[0] as `${number}.${number}.${number}`); + const filteredCriteria = criteria.filter((sc) => scNumbers.includes(sc)); + if (filteredCriteria.length) { + const finalSeparator = + filteredCriteria.length > 2 && multiScPattern.exec(title)?.[1] ? "," : ""; + const replacement = `${filteredCriteria.slice(0, -1).join(", ")}${finalSeparator} and ${ + filteredCriteria[filteredCriteria.length - 1] + }`; + title = title.replace(multiScPattern, replacement); + titleHtml = titleHtml.replace(multiScPattern, replacement); + } + // If all SCs were filtered out, do nothing - should be pruned when associations are checked + } + } + techniques[technology].push({ ...data, // Include front-matter id: basename(filename, ".html"), technology, title, - titleHtml: $h1.html(), + titleHtml, truncatedTitle: title.replace(/\s*\n[\s\S]*\n\s*/, " … "), }); } diff --git a/eleventy.config.ts b/eleventy.config.ts index 8639dcc805..829759c7f2 100644 --- a/eleventy.config.ts +++ b/eleventy.config.ts @@ -64,7 +64,7 @@ for (const [key, value] of Object.entries(allFlatGuidelines)) { if (value.version > version) futureGuidelines[key] = value; } -const techniques = await getTechniquesByTechnology(); +const techniques = await getTechniquesByTechnology(flatGuidelines); const flatTechniques = getFlatTechniques(techniques); /** Maps technique IDs to SCs found in target version */