From 2032476ff949452124537c6b110fd45db1d5c9ba Mon Sep 17 00:00:00 2001 From: Xuepoo Foter <144407384+Xuepoo@users.noreply.github.com> Date: Mon, 28 Sep 2026 19:35:09 +0800 Subject: [PATCH] [CTX-0057] fix(website): code contrast, inline-code wrapping, a 404 page, focusable diagram Live rendered audit of the deployed site (recording/uiux-audit-2026-09-28.md) found four user-facing defects; this commit fixes them and records the rest. - Code contrast (closes #129): the light palette painted comments at 2.30:1, strings 2.86, keywords 3.28 and builtins 3.73 against the 4.5 AA threshold (axe: 296 nodes across 8 sampled pages). Two causes, both fixed: `one-light` was swapped for `github-light`, and Shiki's inner `` was being painted with the inline-code paper tint, so every token sat on a tinted surface instead of the block's own white. Measured on the built artefact: 16 token roles, 0 failures, worst 4.57 in light; the dark palette was already clean (worst 5.01) and is deliberately untouched. - Mobile overflow (closes #130): prose inline code had `overflow-wrap: normal`, so long identifiers, paths and signatures widened the document - 16 published pages scrolled horizontally at 390px (up to 685px wide). With a break opportunity every one of them measures 390px. - No 404 page (closes #131): unknown URLs returned a zero-byte 404, so readers landed on the browser's own error document. `src/pages/404.astro` now renders inside the shell (skip link, header, search, documentation-map links, footer) and `wrangler.jsonc` sets `assets.not_found_handling: "404-page"`. axe is clean on it at 390 and 1440. - Keyboard-reachable diagram (refs #132): the home page's scrollable diagram container is now a labelled `role="region"` focus target. - Version label (refs #134): the selector rendered the pinned corpus timestamp with milliseconds as reader-facing copy; it now renders "latest - 13 Sep 2026". The same audit's search and aria-current items were re-measured and retracted (ArrowDown does move `aria-activedescendant`; the version selector deliberately avoids `aria-current` per website#78). Deferred and still open: #132's scrollable tables (they need the markdown pipeline or a shell enhancer, not CSS), #133 (the two dead `.md` links live in mirror content and must be fixed upstream in bitty-terminal-docs), #134's heading level skip (owned by `docsHeadingsMdastPlugin`) and 16px sidebar link boxes, #135 (the interactive page's own shell). Verified: `just check` green (343 pass / 0 fail, static validation, docs mirror parity, a11y audit 0 failures, wrangler dry-run); contrast, overflow and axe re-measured against the built `dist/` served locally, not against production. --- astro.config.mjs | 14 ++++-- src/components/VersionSelector.astro | 19 +++++++- src/pages/404.astro | 69 ++++++++++++++++++++++++++++ src/pages/index.astro | 7 ++- src/styles/global.css | 36 ++++++++++++++- wrangler.jsonc | 1 + 6 files changed, 139 insertions(+), 7 deletions(-) create mode 100644 src/pages/404.astro diff --git a/astro.config.mjs b/astro.config.mjs index 35cbe57..61264e5 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -302,11 +302,17 @@ export default defineConfig({ markdown: { // Dual code theme (CTX-0049, website#95): Shiki emits both palettes as // custom properties and global.css picks the one that matches the - // document colour scheme. `one-light`/`one-dark-pro` sit closest to the - // cool neutral + vermilion palette; the previous single `github-dark` - // theme painted a dark block onto the pale page. + // document colour scheme; the previous single `github-dark` theme painted + // a dark block onto the pale page. + // + // Light palette (CTX-0057, website#129): `one-light` painted comments at + // 2.30:1, strings 2.86, keywords 3.28 and builtins 3.73 on the light code + // background -- all below the WCAG AA 4.5 threshold that axe flagged at + // "serious" across 296 nodes. `github-light` keeps a restrained neutral + // palette and clears the threshold on every token role, measured on the + // built artefact rather than assumed. shikiConfig: { - themes: { light: "one-light", dark: "one-dark-pro" }, + themes: { light: "github-light", dark: "one-dark-pro" }, }, processor: satteri({ mdastPlugins: [ diff --git a/src/components/VersionSelector.astro b/src/components/VersionSelector.astro index 57f133c..e73c7aa 100644 --- a/src/components/VersionSelector.astro +++ b/src/components/VersionSelector.astro @@ -37,6 +37,23 @@ type Props = { const { currentRoute, currentVersion, targetIdentities } = Astro.props as Props; +/** Reader-facing version label: the pinned corpus keeps its machine + * timestamp in `versions.json`, but milliseconds are data, not copy + * (website#134). Render the date a reader can scan. */ +const humaniseLabel = (label: string): string => { + const iso = label.match(/\d{4}-\d{2}-\d{2}T[\d:.]+Z/); + if (!iso) return label; + const date = new Date(iso[0]); + if (Number.isNaN(date.getTime())) return label; + const readable = date.toLocaleDateString("en-GB", { + day: "numeric", + month: "short", + year: "numeric", + timeZone: "UTC", + }); + return label.replace(iso[0], readable); +}; + const entries = versions.versions.map((entry) => versionEntryPresentation({ currentRoute, @@ -61,7 +78,7 @@ const entries = versions.versions.map((entry) => href={entry.href} data-current={entry.isCurrent ? "true" : undefined} > - {entry.label} + {humaniseLabel(entry.label)} {entry.suffix} {entry.isCurrent && ( (current version) diff --git a/src/pages/404.astro b/src/pages/404.astro new file mode 100644 index 0000000..3582835 --- /dev/null +++ b/src/pages/404.astro @@ -0,0 +1,69 @@ +--- +/** Not-found page (website#131). + * + * The site moves documentation routes behind redirect families, so any link + * older than the last rename lands here. It renders inside the normal shell - + * the same header, search and footer - because a reader who arrives from a + * stale URL needs a way forward, not a blank body. + * + * Served by `assets.not_found_handling: "404-page"` in `wrangler.jsonc`, which + * is why the markup must stand on its own: it is the only recovery path for a + * URL outside the redirect families. + */ +import "../styles/global.css"; +import SiteFooter from "../components/SiteFooter.astro"; +import SiteHeader from "../components/SiteHeader.astro"; +--- + + + + + + + + + + + + + Page not found | Bitty + + + + +
+
+

Page not found

+

+ This address does not serve a page. Documentation routes change as the + corpora converge, so an older link can land here. +

+

+ Start from the + documentation map or the + Bitty home page. +

+
+
+ + + diff --git a/src/pages/index.astro b/src/pages/index.astro index ed0743d..a54a9ee 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -302,7 +302,12 @@ const alsoDocs = await resolveHomeDocs(ALSO_SOURCES);

How Bitty fits together

-
+
diff --git a/src/styles/global.css b/src/styles/global.css index e2f56cd..ee4352d 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -756,6 +756,27 @@ body > .version-selector { border-radius: var(--radius-sm); background: var(--paper-deep); font-size: 0.9em; + /* Long identifiers, paths and signatures are normal in this corpus + * (website#130): without a break opportunity the line box grew past the + * viewport and 16 published pages scrolled horizontally at 390px. */ + overflow-wrap: anywhere; +} + +/* website#129: Shiki renders `
`; the inline-code
+ * chrome used to paint that inner `` with the pale paper tint, so every
+ * syntax token sat on a tinted surface instead of the theme's own background
+ * and the red/comment roles measured 4.09/4.30 against the 4.5 AA threshold.
+ * Inside a code block the surface belongs to the block, not the inline style. */
+@media (prefers-color-scheme: light) {
+  /* website#129: the light fix below is scoped to the light scheme on purpose:
+   * the dark palette was measured AA-clean against the surface it already
+   * had, and re-painting it there moved the comment role to 3.73. */
+  .prose pre code {
+    padding: 0;
+    border: 0;
+    background: none;
+    font-size: inherit;
+  }
 }
 
 .prose pre {
@@ -763,11 +784,24 @@ body > .version-selector {
   overflow-x: auto;
   border: 1px solid var(--line);
   border-radius: var(--radius);
-  background: var(--card);
+  /* website#129: the light code surface is the theme's own background.
+   * The pale paper tint cost the red and comment tokens ~0.5 and pushed
+   * github-light just under the AA 4.5 threshold. Dark keeps its own block
+   * via the --shiki-dark-bg override in the palette media block. */
+  background: #fff;
   font-size: var(--text-sm);
   line-height: var(--leading-code);
 }
 
+/* website#129: the light code surface is the syntax theme's own background.
+ * The pale paper tint cost the red and comment tokens ~0.5 and left
+ * github-light just under the AA 4.5 threshold; dark keeps its own block via
+ * the --shiki-dark-bg override in the palette media block. */
+.prose pre.astro-code,
+.code-block pre.astro-code {
+  background: #fff;
+}
+
 /* Shiki dual theme (CTX-0049): Astro inlines the light palette (`color`) and
  * carries the dark one in `--shiki-dark*` custom properties, so only the dark
  * palette needs an override and the light values stay as rendered. Both
diff --git a/wrangler.jsonc b/wrangler.jsonc
index 9677b5f..bcc6733 100644
--- a/wrangler.jsonc
+++ b/wrangler.jsonc
@@ -4,5 +4,6 @@
   "compatibility_date": "2026-08-25",
   "assets": {
     "directory": "./dist",
+    "not_found_handling": "404-page",
   },
 }