From b43d265e5c0f5661989cb4bde6663bb348237484 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 28 Oct 2026 16:26:12 -0700 Subject: [PATCH] fix(site): text contrast in dark mode and code highlighting A contrast scan of every page in light and dark mode found text under 4.5:1: in dark mode the breadcrumb, footer and all code blocks (the light highlight theme was used on a dark background, down to 1.18:1), and in light mode the orange, green and red highlight colours on the code background (3.28:1 to 4.3:1). Code blocks now use GitHub's dark highlight theme in dark mode, the four short colours are replaced with GitHub's own shades of the same hue, and the breadcrumb and footer get dark-mode colours. A re-scan of all 223 pages finds nothing under 4.5:1 in either mode. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01PmRBrUTuhzYnQBFxUCN6Vr --- html/styles/custom.css | 7 ++ html/styles/highlight.css | 130 ++++++++++++++++++++++++++++++++++++-- scripts/build-html.js | 25 +++++++- 3 files changed, 154 insertions(+), 8 deletions(-) diff --git a/html/styles/custom.css b/html/styles/custom.css index 22a18a5a..94adef20 100644 --- a/html/styles/custom.css +++ b/html/styles/custom.css @@ -193,6 +193,8 @@ } @media (prefers-color-scheme: dark) { + .breadcrumb { color: #9198a1; } + .breadcrumb a { color: #58a6ff; } .site-header { background: #0d1117; border-bottom-color: #30363d; } .search-input { background: #161b22; border-color: #30363d; color: #e6edf3; } .search-input:focus { border-color: #58a6ff; box-shadow: 0 0 0 3px rgba(88,166,255,0.3); background: #0d1117; } @@ -847,6 +849,11 @@ textarea:focus-visible { text-decoration: underline; } +@media (prefers-color-scheme: dark) { + .site-footer { color: #9198a1; border-top-color: #30363d; } + .site-footer a { color: #58a6ff; } +} + /* Reduced motion support */ @media (prefers-reduced-motion: reduce) { *, diff --git a/html/styles/highlight.css b/html/styles/highlight.css index 1001da62..b68bcdd9 100644 --- a/html/styles/highlight.css +++ b/html/styles/highlight.css @@ -28,7 +28,7 @@ code.hljs { .hljs-type, .hljs-variable.language_ { /* prettylights-syntax-keyword */ - color: #d73a49 + color: #cf222e } .hljs-title, .hljs-title.class_, @@ -59,7 +59,7 @@ code.hljs { .hljs-built_in, .hljs-symbol { /* prettylights-syntax-variable */ - color: #e36209 + color: #953800 } .hljs-comment, .hljs-code, @@ -72,7 +72,7 @@ code.hljs { .hljs-selector-tag, .hljs-selector-pseudo { /* prettylights-syntax-entity-tag */ - color: #22863a + color: #116329 } .hljs-subst { /* prettylights-syntax-storage-modifier-import */ @@ -99,7 +99,7 @@ code.hljs { } .hljs-addition { /* prettylights-syntax-markup-inserted */ - color: #22863a; + color: #116329; background-color: #f0fff4 } .hljs-deletion { @@ -115,4 +115,124 @@ code.hljs { .hljs-tag { /* purposely ignored */ -} \ No newline at end of file +} +@media (prefers-color-scheme: dark) { +pre code.hljs { + display: block; + overflow-x: auto; + padding: 1em +} +code.hljs { + padding: 3px 5px +} +/*! + Theme: GitHub Dark + Description: Dark theme as seen on github.com + Author: github.com + Maintainer: @Hirse + Updated: 2021-05-15 + + Outdated base version: https://github.com/primer/github-syntax-dark + Current colors taken from GitHub's CSS +*/ +.hljs { + color: #c9d1d9; + background: #0d1117 +} +.hljs-doctag, +.hljs-keyword, +.hljs-meta .hljs-keyword, +.hljs-template-tag, +.hljs-template-variable, +.hljs-type, +.hljs-variable.language_ { + /* prettylights-syntax-keyword */ + color: #ff7b72 +} +.hljs-title, +.hljs-title.class_, +.hljs-title.class_.inherited__, +.hljs-title.function_ { + /* prettylights-syntax-entity */ + color: #d2a8ff +} +.hljs-attr, +.hljs-attribute, +.hljs-literal, +.hljs-meta, +.hljs-number, +.hljs-operator, +.hljs-variable, +.hljs-selector-attr, +.hljs-selector-class, +.hljs-selector-id { + /* prettylights-syntax-constant */ + color: #79c0ff +} +.hljs-regexp, +.hljs-string, +.hljs-meta .hljs-string { + /* prettylights-syntax-string */ + color: #a5d6ff +} +.hljs-built_in, +.hljs-symbol { + /* prettylights-syntax-variable */ + color: #ffa657 +} +.hljs-comment, +.hljs-code, +.hljs-formula { + /* prettylights-syntax-comment */ + color: #8b949e +} +.hljs-name, +.hljs-quote, +.hljs-selector-tag, +.hljs-selector-pseudo { + /* prettylights-syntax-entity-tag */ + color: #7ee787 +} +.hljs-subst { + /* prettylights-syntax-storage-modifier-import */ + color: #c9d1d9 +} +.hljs-section { + /* prettylights-syntax-markup-heading */ + color: #58a6ff; + font-weight: bold +} +.hljs-bullet { + /* prettylights-syntax-markup-list */ + color: #f2cc60 +} +.hljs-emphasis { + /* prettylights-syntax-markup-italic */ + color: #c9d1d9; + font-style: italic +} +.hljs-strong { + /* prettylights-syntax-markup-bold */ + color: #c9d1d9; + font-weight: bold +} +.hljs-addition { + /* prettylights-syntax-markup-inserted */ + color: #aff5b4; + background-color: #033a16 +} +.hljs-deletion { + /* prettylights-syntax-markup-deleted */ + color: #ffdcd7; + background-color: #67060c +} +.hljs-char.escape_, +.hljs-link, +.hljs-params, +.hljs-property, +.hljs-punctuation, +.hljs-tag { + /* purposely ignored */ + +} +} diff --git a/scripts/build-html.js b/scripts/build-html.js index e05c608f..4eaeb52a 100644 --- a/scripts/build-html.js +++ b/scripts/build-html.js @@ -987,9 +987,21 @@ function setupStyles(outputDir) { const githubCss = require.resolve('github-markdown-css'); fs.copyFileSync(githubCss, path.join(stylesDir, 'github-markdown.css')); - // Copy highlight.js CSS (GitHub theme) - const highlightCss = path.join(require.resolve('highlight.js'), '../../styles/github.css'); - fs.copyFileSync(highlightCss, path.join(stylesDir, 'highlight.css')); + // highlight.js CSS: the GitHub light theme, plus GitHub dark for dark mode. + // A few stock colours fall short of WCAG 1.4.3 (4.5:1) on the code block + // background (#f6f8fa light, #151b23 dark), so they are replaced with + // GitHub's own darker or lighter shades of the same hue. + const stylesRoot = path.join(require.resolve('highlight.js'), '../../styles'); + const lightCss = fs.readFileSync(path.join(stylesRoot, 'github.css'), 'utf8') + .replace(/color: #e36209/g, 'color: #953800') + .replace(/color: #22863a/g, 'color: #116329') + .replace(/color: #d73a49/g, 'color: #cf222e'); + const darkCss = fs.readFileSync(path.join(stylesRoot, 'github-dark.css'), 'utf8') + .replace(/color: #1f6feb/g, 'color: #58a6ff'); + fs.writeFileSync( + path.join(stylesDir, 'highlight.css'), + `${lightCss}\n@media (prefers-color-scheme: dark) {\n${darkCss}\n}\n` + ); // Create custom CSS for additional accessibility and styling const customCss = ` @@ -1187,6 +1199,8 @@ function setupStyles(outputDir) { } @media (prefers-color-scheme: dark) { + .breadcrumb { color: #9198a1; } + .breadcrumb a { color: #58a6ff; } .site-header { background: #0d1117; border-bottom-color: #30363d; } .search-input { background: #161b22; border-color: #30363d; color: #e6edf3; } .search-input:focus { border-color: #58a6ff; box-shadow: 0 0 0 3px rgba(88,166,255,0.3); background: #0d1117; } @@ -1841,6 +1855,11 @@ textarea:focus-visible { text-decoration: underline; } +@media (prefers-color-scheme: dark) { + .site-footer { color: #9198a1; border-top-color: #30363d; } + .site-footer a { color: #58a6ff; } +} + /* Reduced motion support */ @media (prefers-reduced-motion: reduce) { *,