add a11y css to project when not in prod mode

Summary: Ref T3078

Reviewers: kuba-orlik

Maniphest Tasks: T3078

Differential Revision: https://hub.sealcode.org/D1687
This commit is contained in:
Kuba Orlik 2026-03-26 18:55:10 +01:00
parent 021783e2d3
commit ee2368c686
6 changed files with 34 additions and 2 deletions

11
package-lock.json generated
View File

@ -26,6 +26,7 @@
"@types/leaflet": "^1.9.8", "@types/leaflet": "^1.9.8",
"@types/object-hash": "^3.0.6", "@types/object-hash": "^3.0.6",
"@types/simplemde": "^1.11.11", "@types/simplemde": "^1.11.11",
"a11y.css": "^5.3.0",
"c8": "^10.1.3", "c8": "^10.1.3",
"colord": "^2.9.3", "colord": "^2.9.3",
"dotenv": "^16.4.5", "dotenv": "^16.4.5",
@ -2478,6 +2479,11 @@
"dev": true, "dev": true,
"peer": true "peer": true
}, },
"node_modules/a11y.css": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/a11y.css/-/a11y.css-5.3.0.tgz",
"integrity": "sha512-WBEnwAT3NHyJFN0ejedhc4Q0U4yX2fNWmoee8Ai6TWIICjrocTNugXm8XFuIC8dI8goaIoqxnSd4PlnljJ8dxw=="
},
"node_modules/abbrev": { "node_modules/abbrev": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz",
@ -16967,6 +16973,11 @@
"dev": true, "dev": true,
"peer": true "peer": true
}, },
"a11y.css": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/a11y.css/-/a11y.css-5.3.0.tgz",
"integrity": "sha512-WBEnwAT3NHyJFN0ejedhc4Q0U4yX2fNWmoee8Ai6TWIICjrocTNugXm8XFuIC8dI8goaIoqxnSd4PlnljJ8dxw=="
},
"abbrev": { "abbrev": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz",

View File

@ -82,6 +82,10 @@
{ {
"from": "node_modules/@sealcode/sealcodemirror/mode/javascript/javascript.js", "from": "node_modules/@sealcode/sealcodemirror/mode/javascript/javascript.js",
"to": "dist/codemirror-javascript-mode.js" "to": "dist/codemirror-javascript-mode.js"
},
{
"from": "node_modules/a11y.css/css",
"to": "dist/a11y.css"
} }
] ]
}, },
@ -105,6 +109,7 @@
"@types/leaflet": "^1.9.8", "@types/leaflet": "^1.9.8",
"@types/object-hash": "^3.0.6", "@types/object-hash": "^3.0.6",
"@types/simplemde": "^1.11.11", "@types/simplemde": "^1.11.11",
"a11y.css": "^5.3.0",
"c8": "^10.1.3", "c8": "^10.1.3",
"colord": "^2.9.3", "colord": "^2.9.3",
"dotenv": "^16.4.5", "dotenv": "^16.4.5",

View File

@ -0,0 +1,11 @@
/* we properly embed charset metadata, but Turbo changes the order of elements in <head> and this rule gives a false positive */
head :first-child:not([charset]) {
&,
&::after {
display: none !important;
}
}
head :first-child:not([charset]) ~ link:last-of-type::before {
display: none !important;
}

View File

@ -48,3 +48,4 @@ export const UPLOADS_FS_DIR =
process.env.UPLOADS_FS_DIR || locreq.resolve("uploaded_files"); process.env.UPLOADS_FS_DIR || locreq.resolve("uploaded_files");
export const MEILISEARCH_MASTER_KEY = process.env.MEILISEARCH_MASTER_KEY || "qwerty"; export const MEILISEARCH_MASTER_KEY = process.env.MEILISEARCH_MASTER_KEY || "qwerty";
export const MEILISEARCH_HOST = process.env.MEILISEARCH_HOST || "http://localhost:1098"; export const MEILISEARCH_HOST = process.env.MEILISEARCH_HOST || "http://localhost:1098";
export const SCAN_A11Y = false;

View File

@ -3,6 +3,7 @@ import type { Readable } from "stream";
import type { Context } from "koa"; import type { Context } from "koa";
import type { HTMLOptions } from "@sealcode/sealgen"; import type { HTMLOptions } from "@sealcode/sealgen";
import { htmlEscape } from "escape-goat"; import { htmlEscape } from "escape-goat";
import { SCAN_A11Y } from "./config.js";
export const start_timestamp = Date.now(); export const start_timestamp = Date.now();
@ -24,7 +25,8 @@ export function defaultHead({
description: string; description: string;
}): JSX.Element | Readable { }): JSX.Element | Readable {
const origin = ctx.URL.origin; const origin = ctx.URL.origin;
return tempstream /* HTML */ `<title>${title}</title> return tempstream /* HTML */ `<meta charset="utf-8" /> <title>${title}</title>
${SCAN_A11Y ? `<link rel="stylesheet" href="/dist/a11y.css/a11y-en.css">` : ""}
<link rel="icon" type="image/svg+xml" href="/favicon.svg" /> <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="${htmlEscape(description)}" /> <meta name="description" content="${htmlEscape(description)}" />

View File

@ -40,6 +40,8 @@ export default function html({
controllers.push("refresh-on-ts-changes"); controllers.push("refresh-on-ts-changes");
} }
const classes = (htmlOptions?.bodyClasses || []).join(" ");
return tempstreamAsync /* HTML */ ` <!DOCTYPE html> return tempstreamAsync /* HTML */ ` <!DOCTYPE html>
<html <html
lang="${htmlOptions.language || DEFAULT_HTML_LANG}" lang="${htmlOptions.language || DEFAULT_HTML_LANG}"
@ -58,7 +60,7 @@ export default function html({
</head> </head>
<body <body
data-controller="${controllers.join(" ")}" data-controller="${controllers.join(" ")}"
class="${(htmlOptions?.bodyClasses || []).join(" ")}" ${classes ? `class="${classes}"` : ""}
> >
${!hideNavigation ? (htmlOptions?.navbar || default_navbar)(ctx) : ""} ${!hideNavigation ? (htmlOptions?.navbar || default_navbar)(ctx) : ""}
${body} ${body}