Skip to content

Commit 67495c4

Browse files
authored
Merge pull request #12730 from quarto-dev/feature/axe-check
Feature/axe check
2 parents 23d0172 + c26e6f2 commit 67495c4

17 files changed

+359
-28
lines changed

configuration

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ export DATATABLES_CONFIG=bs5/jszip-3.10.1/dt-1.13.8/b-2.4.2/b-html5-2.4.2/b-prin
5454
export PDF_MAKE=0.2.7
5555

5656
# javascript search dependencies
57-
export AUTOCOMPLETE_JS=1.11.1
57+
export AUTOCOMPLETE_JS=1.19.1
5858
export FUSE_JS=6.6.2
5959
export ALGOLIA_SEARCH_JS=4.5.1
6060
export ALGOLIA_SEARCH_INSIGHTS_JS=2.0.3

src/core/temp-types.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66

77
export interface TempContext {
88
baseDir: string;
9+
createFileFromString: (
10+
content: string,
11+
options?: Deno.MakeTempOptions,
12+
) => string;
913
createFile: (options?: Deno.MakeTempOptions) => string;
1014
createDir: (options?: Deno.MakeTempOptions) => string;
1115
cleanup: () => void;

src/core/temp.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,13 @@ export function createTempContext(options?: Deno.MakeTempOptions): TempContext {
6666

6767
const tempContextCleanupHandlers: VoidFunction[] = [];
6868

69-
return {
69+
const result: TempContext = {
7070
baseDir: dir,
71+
createFileFromString: (content: string, options?: Deno.MakeTempOptions) => {
72+
const file = result.createFile(options);
73+
Deno.writeTextFileSync(file, content);
74+
return file;
75+
},
7176
createFile: (options?: Deno.MakeTempOptions) => {
7277
return Deno.makeTempFileSync({ ...options, dir });
7378
},
@@ -93,6 +98,7 @@ export function createTempContext(options?: Deno.MakeTempOptions): TempContext {
9398
tempContextCleanupHandlers.push(handler);
9499
},
95100
};
101+
return result;
96102
}
97103

98104
export function systemTempDir(name: string) {

src/format/html/format-html-axe.ts

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* format-html-axe.ts
3+
*
4+
* Copyright (C) 2020-2025 Posit Software, PBC
5+
*/
6+
7+
import { kIncludeInHeader } from "../../config/constants.ts";
8+
import { Format, FormatExtras } from "../../config/types.ts";
9+
import { TempContext } from "../../core/temp-types.ts";
10+
import { encodeBase64 } from "../../deno_ral/encoding.ts";
11+
12+
export function axeFormatDependencies(
13+
_format: Format,
14+
temp: TempContext,
15+
options?: unknown,
16+
): FormatExtras {
17+
if (!options) return {};
18+
19+
return {
20+
[kIncludeInHeader]: [
21+
temp.createFileFromString(
22+
`<script id="quarto-axe-checker-options" type="text/plain">${
23+
encodeBase64(JSON.stringify(options))
24+
}</script>`,
25+
),
26+
],
27+
html: {
28+
"sass-bundles": [
29+
{
30+
key: "axe",
31+
dependency: "bootstrap",
32+
user: [{
33+
uses: "",
34+
defaults: "",
35+
functions: "",
36+
mixins: "",
37+
rules: `
38+
body div.quarto-axe-report {
39+
position: fixed;
40+
bottom: 3rem;
41+
right: 3rem;
42+
padding: 1rem;
43+
border: 1px solid $body-color;
44+
}
45+
46+
.quarto-axe-violation-help { padding-left: 0.5rem; }
47+
.quarto-axe-violation-selector { padding-left: 1rem; }
48+
.quarto-axe-violation-target {
49+
padding: 0.5rem;
50+
color: $link-color;
51+
text-decoration: underline;
52+
cursor: pointer;
53+
}
54+
55+
.quarto-axe-hover-highlight {
56+
background-color: red;
57+
border: 1px solid $body-color;
58+
}`,
59+
}],
60+
},
61+
],
62+
},
63+
};
64+
}

src/format/html/format-html-shared.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ export const kComments = "comments";
5656
export const kHypothesis = "hypothesis";
5757
export const kUtterances = "utterances";
5858
export const kGiscus = "giscus";
59+
export const kAxe = "axe";
5960

6061
export const kGiscusRepoId = "repo-id";
6162
export const kGiscusCategoryId = "category-id";

src/format/html/format-html.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
*
66
* Copyright (C) 2020-2022 Posit Software, PBC
77
*/
8-
import { dirname, join, relative } from "../../deno_ral/path.ts";
8+
import { join, relative } from "../../deno_ral/path.ts";
99
import { warning } from "../../deno_ral/log.ts";
1010

1111
import * as ld from "../../core/lodash.ts";
@@ -68,6 +68,7 @@ import {
6868
clipboardDependency,
6969
createCodeCopyButton,
7070
kAnchorSections,
71+
kAxe,
7172
kBootstrapDependencyName,
7273
kCitationsHover,
7374
kCodeAnnotations,
@@ -116,8 +117,9 @@ import {
116117
import { kQuartoHtmlDependency } from "./format-html-constants.ts";
117118
import { registerWriterFormatHandler } from "../format-handlers.ts";
118119
import { brandSassFormatExtras } from "../../core/sass/brand.ts";
119-
import { ESBuildAnalysis, esbuildAnalyze } from "../../core/esbuild.ts";
120+
import { ESBuildAnalysis } from "../../core/esbuild.ts";
120121
import { assert } from "testing/asserts";
122+
import { axeFormatDependencies } from "./format-html-axe.ts";
121123

122124
let esbuildAnalysisCache: Record<string, ESBuildAnalysis> | undefined;
123125
export function esbuildCachedAnalysis(
@@ -245,6 +247,10 @@ export async function htmlFormatExtras(
245247
tippyOptions?: HtmlFormatTippyOptions,
246248
scssOptions?: HtmlFormatScssOptions,
247249
): Promise<FormatExtras> {
250+
const configurableExtras: FormatExtras[] = [
251+
axeFormatDependencies(format, temp, format.metadata[kAxe]),
252+
];
253+
248254
// note whether we are targeting bootstrap
249255
const bootstrap = formatHasBootstrap(format);
250256

@@ -645,7 +651,7 @@ export async function htmlFormatExtras(
645651
}
646652

647653
const metadata: Metadata = {};
648-
return {
654+
const result: FormatExtras = {
649655
[kIncludeInHeader]: includeInHeader,
650656
[kIncludeBeforeBody]: includeBeforeBody,
651657
[kIncludeAfterBody]: includeAfterBody,
@@ -657,6 +663,11 @@ export async function htmlFormatExtras(
657663
[kHtmlPostprocessors]: htmlPostProcessors,
658664
},
659665
};
666+
667+
return mergeConfigs(
668+
result,
669+
...configurableExtras,
670+
) as FormatExtras;
660671
}
661672

662673
const kFormatHasBootstrap = "has-bootstrap";

src/resources/editor/tools/vs-code.mjs

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23769,6 +23769,8 @@ var require_yaml_intelligence_resources = __commonJS({
2376923769
"Manuscript configuration",
2377023770
"internal-schema-hack",
2377123771
"List execution engines you want to give priority when determining\nwhich engine should render a notebook. If two engines have support for a\nnotebook, the one listed earlier will be chosen. Quarto\u2019s default order\nis \u2018knitr\u2019, \u2018jupyter\u2019, \u2018markdown\u2019, \u2018julia\u2019.",
23772+
"When defined, run axe-core accessibility tests on the document.",
23773+
"If set, output axe-core results on console. <code>json</code>:\nproduce structured output; <code>console</code>: print output to\njavascript console; <code>document</code>: produce a visual report of\nviolations in the document itself.",
2377223774
"Project configuration.",
2377323775
"Project type (<code>default</code>, <code>website</code>,\n<code>book</code>, or <code>manuscript</code>)",
2377423776
"Files to render (defaults to all files)",
@@ -24114,7 +24116,8 @@ var require_yaml_intelligence_resources = __commonJS({
2411424116
"Disambiguating year suffix in author-date styles (e.g.&nbsp;\u201Ca\u201D in \u201CDoe,\n1999a\u201D).",
2411524117
"Manuscript configuration",
2411624118
"internal-schema-hack",
24117-
"List execution engines you want to give priority when determining\nwhich engine should render a notebook. If two engines have support for a\nnotebook, the one listed earlier will be chosen. Quarto\u2019s default order\nis \u2018knitr\u2019, \u2018jupyter\u2019, \u2018markdown\u2019, \u2018julia\u2019."
24119+
"List execution engines you want to give priority when determining\nwhich engine should render a notebook. If two engines have support for a\nnotebook, the one listed earlier will be chosen. Quarto\u2019s default order\nis \u2018knitr\u2019, \u2018jupyter\u2019, \u2018markdown\u2019, \u2018julia\u2019.",
24120+
"Date format for the document"
2411824121
],
2411924122
"schema/external-schemas.yml": [
2412024123
{
@@ -24343,12 +24346,12 @@ var require_yaml_intelligence_resources = __commonJS({
2434324346
mermaid: "%%"
2434424347
},
2434524348
"handlers/mermaid/schema.yml": {
24346-
_internalId: 195861,
24349+
_internalId: 196444,
2434724350
type: "object",
2434824351
description: "be an object",
2434924352
properties: {
2435024353
"mermaid-format": {
24351-
_internalId: 195853,
24354+
_internalId: 196436,
2435224355
type: "enum",
2435324356
enum: [
2435424357
"png",
@@ -24364,7 +24367,7 @@ var require_yaml_intelligence_resources = __commonJS({
2436424367
exhaustiveCompletions: true
2436524368
},
2436624369
theme: {
24367-
_internalId: 195860,
24370+
_internalId: 196443,
2436824371
type: "anyOf",
2436924372
anyOf: [
2437024373
{
@@ -24404,7 +24407,32 @@ var require_yaml_intelligence_resources = __commonJS({
2440424407
"case-detection": true
2440524408
},
2440624409
$id: "handlers/mermaid"
24407-
}
24410+
},
24411+
"schema/document-a11y.yml": [
24412+
{
24413+
name: "axe",
24414+
schema: {
24415+
anyOf: [
24416+
"boolean",
24417+
{
24418+
object: {
24419+
properties: {
24420+
output: {
24421+
enum: [
24422+
"json",
24423+
"console",
24424+
"document"
24425+
],
24426+
description: "If set, output axe-core results on console. `json`: produce structured output; `console`: print output to javascript console; `document`: produce a visual report of violations in the document itself."
24427+
}
24428+
}
24429+
}
24430+
}
24431+
]
24432+
},
24433+
description: "When defined, run axe-core accessibility tests on the document."
24434+
}
24435+
]
2440824436
};
2440924437
}
2441024438
});

src/resources/editor/tools/yaml/web-worker.js

Lines changed: 33 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)