Skip to content

Commit

Permalink
Merge branch 'main' into so-332-labels-instructions
Browse files Browse the repository at this point in the history
  • Loading branch information
scottaohara authored Dec 16, 2024
2 parents 13221f2 + 4635e4c commit ee93e2e
Show file tree
Hide file tree
Showing 23 changed files with 273 additions and 334 deletions.
17 changes: 10 additions & 7 deletions .github/workflows/11ty-publish.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,13 @@ jobs:
curl https://labs.w3.org/spec-generator/?type=respec"&"url=https://raw.githack.com/$GITHUB_REPOSITORY/main/requirements/22/index.html -o _site/requirements/22/index.html -f --retry 3
curl https://labs.w3.org/spec-generator/?type=respec"&"url=https://raw.githack.com/$GITHUB_REPOSITORY/main/conformance-challenges/index.html -o _site/conformance-challenges/index.html -f --retry 3
- name: Push
working-directory: _site
run: |
git config user.email [email protected]
git config user.name w3cgruntbot
git add -A .
git commit -m ":robot: Deploy to GitHub Pages: $GITHUB_SHA from branch $GITHUB_REF"
git push origin gh-pages
uses: stefanzweifel/git-auto-commit-action@v5
with:
repository: _site
branch: gh-pages
commit_user_name: w3cgruntbot
commit_user_email: [email protected]
commit_author: "w3cgruntbot <[email protected]>"
commit_message: ":robot: Deploy to GitHub Pages: ${{ github.sha }} from branch ${{ github.ref }}"
skip_fetch: true
skip_checkout: true
26 changes: 17 additions & 9 deletions 11ty/CustomLiquid.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Liquid, type Template } from "liquidjs";
import type { RenderOptions } from "liquidjs/dist/liquid-options";
import type { LiquidOptions, RenderOptions } from "liquidjs/dist/liquid-options";
import compact from "lodash-es/compact";
import uniq from "lodash-es/uniq";

Expand All @@ -10,7 +10,7 @@ import type { GlobalData } from "eleventy.config";
import { biblioPattern, getBiblio } from "./biblio";
import { flattenDom, load, type CheerioAnyNode } from "./cheerio";
import { generateId } from "./common";
import { getAcknowledgementsForVersion, getTermsMap } from "./guidelines";
import { getAcknowledgementsForVersion, type TermsMap } from "./guidelines";
import { resolveTechniqueIdFromHref, understandingToTechniqueLinkSelector } from "./techniques";
import { techniqueToUnderstandingLinkSelector } from "./understanding";

Expand All @@ -22,7 +22,6 @@ const techniquesPattern = /\btechniques\//;
const understandingPattern = /\bunderstanding\//;

const biblio = await getBiblio();
const termsMap = await getTermsMap();
const termLinkSelector = "a:not([href])";

/** Generates {% include "foo.html" %} directives from 1 or more basenames */
Expand Down Expand Up @@ -72,6 +71,10 @@ function expandTechniqueLink($el: CheerioAnyNode) {

const stripHtmlComments = (html: string) => html.replace(/<!--[\s\S]*?-->/g, "");

interface CustomLiquidOptions extends LiquidOptions {
termsMap: TermsMap;
}

// Dev note: Eleventy doesn't expose typings for its template engines for us to neatly extend.
// Fortunately, it passes both the content string and the file path through to Liquid#parse:
// https://github.com/11ty/eleventy/blob/9c3a7619/src/Engines/Liquid.js#L253
Expand All @@ -84,6 +87,11 @@ const stripHtmlComments = (html: string) => html.replace(/<!--[\s\S]*?-->/g, "")
* - generating/expanding sections with auto-generated content
*/
export class CustomLiquid extends Liquid {
termsMap: TermsMap;
constructor(options: CustomLiquidOptions) {
super(options);
this.termsMap = options.termsMap;
}
public parse(html: string, filepath?: string) {
// Filter out Liquid calls for computed data and includes themselves
if (filepath && !filepath.includes("_includes/") && isHtmlFileContent(html)) {
Expand Down Expand Up @@ -300,7 +308,7 @@ export class CustomLiquid extends Liquid {
public async render(templates: Template[], scope: GlobalData, options?: RenderOptions) {
// html contains markup after Liquid tags/includes have been processed
const html = (await super.render(templates, scope, options)).toString();
if (!isHtmlFileContent(html) || !scope) return html;
if (!isHtmlFileContent(html) || !scope || scope.page.url === false) return html;

const $ = load(html);

Expand Down Expand Up @@ -414,7 +422,7 @@ export class CustomLiquid extends Liquid {
.toLowerCase()
.trim()
.replace(/\s*\n+\s*/, " ");
const term = termsMap[name];
const term = this.termsMap[name];
if (!term) {
console.warn(`${scope.page.inputPath}: Term not found: ${name}`);
return;
Expand All @@ -428,7 +436,7 @@ export class CustomLiquid extends Liquid {
const $el = $(el);
const termName = extractTermName($el);
$el
.attr("href", `${scope.guidelinesUrl}#${termName ? termsMap[termName].trId : ""}`)
.attr("href", `${scope.guidelinesUrl}#${termName ? this.termsMap[termName].trId : ""}`)
.attr("target", "terms");
});
} else if (scope.isUnderstanding) {
Expand All @@ -442,7 +450,7 @@ export class CustomLiquid extends Liquid {
// since terms may reference other terms in their own definitions.
// Each iteration may append to termNames.
for (let i = 0; i < termNames.length; i++) {
const term = termsMap[termNames[i]];
const term = this.termsMap[termNames[i]];
if (!term) continue; // This will already warn via extractTermNames

const $definition = load(term.definition);
Expand All @@ -459,7 +467,7 @@ export class CustomLiquid extends Liquid {
return 0;
});
for (const name of termNames) {
const term = termsMap[name]; // Already verified existence in the earlier loop
const term = this.termsMap[name]; // Already verified existence in the earlier loop
$termsList.append(
`<dt id="${term.id}">${term.name}</dt>` +
`<dd><definition>${term.definition}</definition></dd>`
Expand All @@ -469,7 +477,7 @@ export class CustomLiquid extends Liquid {
// Iterate over non-href links once more in now-expanded document to add hrefs
$(termLinkSelector).each((_, el) => {
const name = extractTermName($(el));
el.attribs.href = `#${name ? termsMap[name].id : ""}`;
el.attribs.href = `#${name ? this.termsMap[name].id : ""}`;
});
} else {
// No terms: remove skeleton that was placed in #parse
Expand Down
14 changes: 9 additions & 5 deletions 11ty/biblio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { readFile } from "fs/promises";
import { glob } from "glob";
import uniq from "lodash-es/uniq";

import { wrapAxiosRequest } from "./common";

export const biblioPattern = /\[\[\??([\w-]+)\]\]/g;

/** Compiles URLs from local biblio + specref for linking in Understanding documents. */
Expand All @@ -12,24 +14,26 @@ export async function getBiblio() {
.replace(/^respecConfig\.localBiblio\s*=\s*/, "(")
.replace("};", "})")
);

const refs: string[] = [];
for (const path of await glob(["guidelines/**/*.html", "understanding/*/*.html"])) {
const content = await readFile(path, "utf8");
let match;
while ((match = biblioPattern.exec(content))) if (!localBiblio[match[1]]) refs.push(match[1]);
}
const uniqueRefs = uniq(refs);

const response = await axios.get(`https://api.specref.org/bibrefs?refs=${uniqueRefs.join(",")}`);

const response = await wrapAxiosRequest(
axios.get(`https://api.specref.org/bibrefs?refs=${uniqueRefs.join(",")}`)
);
const fullBiblio = {
...response.data,
...localBiblio,
};

const resolvedRefs = Object.keys(fullBiblio);
const unresolvedRefs = uniqueRefs.filter((ref) => !resolvedRefs.includes(ref));
if (unresolvedRefs.length) console.warn(`Unresolved biblio refs: ${unresolvedRefs.join(", ")}`);

return fullBiblio;
}
22 changes: 22 additions & 0 deletions 11ty/common.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
/** @fileoverview Common functions used by multiple parts of the build process */

import { AxiosError, type AxiosResponse } from "axios";

import type { Guideline, Principle, SuccessCriterion } from "./guidelines";

/** Generates an ID for heading permalinks. Equivalent to wcag:generate-id in base.xslt. */
Expand Down Expand Up @@ -28,3 +30,23 @@ export function wcagSort(
}
return 0;
}

/**
* Handles HTTP error responses from Axios requests in local dev;
* re-throws error during builds to fail loudly.
* This should only be used for non-critical requests that can tolerate null data
* without major side effects.
*/
export const wrapAxiosRequest = <T, D>(promise: Promise<AxiosResponse<T, D>>) =>
promise.catch((error) => {
if (!(error instanceof AxiosError) || !error.response || !error.request) throw error;
const { response, request } = error;
console.warn(
`AxiosError: status ${response.status} received from ${
request.protocol + "//" + request.host
}${request.path || ""}`
);

if (process.env.ELEVENTY_RUN_MODE === "build") throw error;
else return { data: null };
});
30 changes: 17 additions & 13 deletions 11ty/guidelines.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,14 +197,17 @@ interface Term {
/** id of dfn in TR, which matches original id in terms file */
trId: string;
}
export type TermsMap = Record<string, Term>;

/**
* Resolves term definitions from guidelines/index.html organized for lookup by name;
* comparable to the term elements in wcag.xml from the guidelines-xml Ant task.
*/
export async function getTermsMap() {
const $ = await flattenDomFromFile("guidelines/index.html");
const terms: Record<string, Term> = {};
export async function getTermsMap(version?: WcagVersion) {
const $ = version
? await loadRemoteGuidelines(version)
: await flattenDomFromFile("guidelines/index.html");
const terms: TermsMap = {};

$("dfn").each((_, el) => {
const $el = $(el);
Expand Down Expand Up @@ -240,24 +243,25 @@ const loadRemoteGuidelines = async (version: WcagVersion) => {
);

// 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();
$("a.internalDFN").removeAttr("class data-link-type id href title");
$("[role='note'] .marker").remove();
$("[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) => {
// Un-process bibliography references, to be processed by CustomLiquid
$("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, ""));
$el.replaceWith(`[${$el.find("a.bibref").html()}]`);
});

// Remove generated IDs and markers from examples
$(".example[id]").removeAttr("id");
$(".example .marker:has(.self-link)").remove();

// Remove extra markup from headings so they can be parsed for names
$("bdi").remove();

// Remove abbr elements which exist only in TR, not in informative docs
$("#acknowledgements li abbr").each((_, abbrEl) => {
$("#acknowledgements li abbr, #glossary abbr").each((_, abbrEl) => {
$(abbrEl).replaceWith($(abbrEl).text());
});

Expand Down
2 changes: 1 addition & 1 deletion 11ty/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ interface EleventyPage {
outputPath: string;
rawInput: string;
templateSyntax: string;
url: string;
url: string | false; // (false when permalink is set to false for the page)
}

interface EleventyDirectories {
Expand Down
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ Content for WCAG 2.1 and later is organized according to the file structure belo

Where `{version}` is "20", content came from WCAG 2.0. "21" is used for content introduced in WCAG 2.1, "22" for WCAG 2.2, etc.

## Build System

The Techniques and Understanding documents are processed through a build system based on Eleventy and Liquid for templating and Cheerio for transformations.
More details, including instructions for previewing the output locally, can be found in the [build process README](11ty/README.md).

## Editing Draft Success Criteria

[Success Criteria Managers](https://www.w3.org/WAI/GL/wiki/SC_Managers_Phase1) will prepare candidate success criteria, ready for inclusion in the guidelines document. To prepare success criteria, follow these steps:
Expand Down Expand Up @@ -158,7 +163,7 @@ Once a technique branch and file is set up, populate the content and request rev

### Formatting Techniques

Techniques in the repository are plain HTML files with minimal formatting. For publication to the editors' draft and W3C location, techniques are formatted by a build process based on Eleventy for templating and Cheerio for transformations. More details, including instructions for previewing locally, can be found in the [build process README](11ty/README.md).
Techniques in the repository are plain HTML files with minimal formatting. For publication to the editors' draft and W3C location, techniques are formatted by the Eleventy build process described above in the [Build System section](#build-system).

The generator compiles the techniques together as a suite with formatting and navigation. It enforces certain structures, such as ordering top-level sections described above and standardizing headings. It attempts to process cross reference links to make sure the URIs work upon publication. One of the most substantial roles is to populate the Applicability section with references to the guidelines or success criteria to which the technique relates. The information for this comes from the Understanding documents. Proper use of the technique template is important to enable this functionality, and mal-formed techniques may cause the generator to fail.

Expand Down
4 changes: 4 additions & 0 deletions eleventy.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
getFlatGuidelines,
getPrinciples,
getPrinciplesForVersion,
getTermsMap,
scSlugOverrides,
type FlatGuidelinesMap,
type Guideline,
Expand Down Expand Up @@ -103,6 +104,8 @@ for (const [technology, list] of Object.entries(techniques)) {
const understandingDocs = await getUnderstandingDocs(version);
const understandingNav = await generateUnderstandingNavMap(principles, understandingDocs);

const termsMap = process.env.WCAG_VERSION ? await getTermsMap(version) : await getTermsMap();

// Declare static global data up-front so we can build typings from it
const globalData = {
version,
Expand Down Expand Up @@ -274,6 +277,7 @@ export default function (eleventyConfig: any) {
root: ["_includes", "."],
jsTruthy: true,
strictFilters: true,
termsMap,
})
);

Expand Down
2 changes: 1 addition & 1 deletion guidelines/guidelines.css
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
.change {
display: inline;
color: #fff;
background: #B50000;
background: #005A9C;
border-radius: 0.25em;
padding: 0.25em 0.4em;
margin: 0 0.25em 0 0;
Expand Down
26 changes: 13 additions & 13 deletions guidelines/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -792,19 +792,19 @@ <h2>Change Log</h2>
<li>2022-03-22: Added <a href="#focus-not-obscured-minimum">Focus Not Obscured (Minimum)</a>.</li>
<li>2022-05-30: Added <a href="#focus-not-obscured-enhanced">Focus Not Obscured (Enhanced)</a>.</li>
<li>2023-06-05: Added privacy and security sections within conformance.</li>
<li>2024-11-15: Republished WCAG 2.2, incorporating the following errata:
<ul>
<li>modified the definitions of <a>single pointer</a>, <a>used in an unusual or restricted way</a>, <a>motion animation</a>, and <a>programmatically determined</a></li>
<li>modified the formatting of definitions for <a>changes of context</a>, <a>general flash and red flash thresholds</a>, <a>cognitive function test</a>, and <a>structure</a></li>
<li>removed the defunct <q>encloses</q> definition</li>
<li>corrected typo in <a href="#input-purposes">input purposes</a> list</li>
<li>modified the formatting of Target Size (Minimum) and Accessible Authentication (Minimum)</li>
<li>modified the visual presentation for content identified as New</li>
<li>modified the language covering devices in the <a href="#abstract">Abstract</a></li>
<li>made editorial changes to improve consistent use of definitions in the success criteria</li>
<li>made editorial changes to improve consistent use of the terms <q>success criteria/criterion</q>, <q>web</q>, <q>website</q>, and <q>web page</q></li>
</ul>
</li>
<li>2024-12-1w: Republished WCAG 2.2, incorporating the following errata:
<ul>
<li>modified the definitions of <a>single pointer</a>, <a>used in an unusual or restricted way</a>, <a>motion animation</a>, and <a>programmatically determined</a></li>
<li>modified the formatting of definitions for <a>changes of context</a>, <a>general flash and red flash thresholds</a>, <a>cognitive function test</a>, and <a>structure</a></li>
<li>removed the defunct <q>encloses</q> definition</li>
<li>corrected typo in <a href="#input-purposes">input purposes</a> list</li>
<li>modified the formatting of Target Size (Minimum) and Accessible Authentication (Minimum)</li>
<li>modified the visual presentation for content identified as New</li>
<li>modified the language covering devices in the <a href="#abstract">Abstract</a></li>
<li>made editorial changes to improve consistent use of definitions in the success criteria</li>
<li>made editorial changes to improve consistent use of the terms <q>success criteria/criterion</q>, <q>web</q>, <q>website</q>, and <q>web page</q></li>
</ul>
</li>
</ul>
</section>
<section class="appendix informative section" id="acknowledgements">
Expand Down
2 changes: 1 addition & 1 deletion guidelines/input-purposes.html
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ <h2>Input Purposes for User Interface Components</h2>
<li><code class="language-html">address-level1</code> - The broadest administrative level in the address, i.e., the province within which the locality is found; for example, in the US, this would be the state; in Switzerland it would be the canton; in the UK, the post town</li>
<li><code class="language-html">country</code> - Country code</li>
<li><code class="language-html">country-name</code> - Country name</li>
<li><code class="language-html">postal-code</code> - Postal code, post code, ZIP code, CEDEX code (if CEDEX, append "CEDEX", and the <i lang="fr">dissement</i>, if relevant, to the <code class="language-html">address-level2</code> field)</li>
<li><code class="language-html">postal-code</code> - Postal code, post code, ZIP code, CEDEX code (if CEDEX, append "CEDEX", and the <i lang="fr">arrondissement</i>, if relevant, to the <code class="language-html">address-level2</code> field)</li>
<li><code class="language-html">cc-name</code> - Full name as given on the payment instrument</li>
<li><code class="language-html">cc-given-name</code> - Given name as given on the payment instrument (in some Western cultures, also known as the <i>first name</i>)</li>
<li><code class="language-html">cc-additional-name</code> - Additional names given on the payment instrument (in some Western cultures, also known as <i>middle names</i>, forenames other than the first name)</li>
Expand Down
Loading

0 comments on commit ee93e2e

Please sign in to comment.