Skip to content

docs(website): theme a11y/contrast and git-dated sitemap lastmod (#590 P3s) - #614

Merged
ScriptedAlchemy merged 6 commits into
mainfrom
docs/590-p3-followups
Sep 5, 2026
Merged

docs(website): theme a11y/contrast and git-dated sitemap lastmod (#590 P3s)#614
ScriptedAlchemy merged 6 commits into
mainfrom
docs/590-p3-followups

Conversation

@ScriptedAlchemy

@ScriptedAlchemy ScriptedAlchemy commented Sep 5, 2026

Copy link
Copy Markdown
Owner

Third PR for #590 (after #599 and #610): the two remaining P3s the audit measured that were worth doing rather than deferring — theme accessibility/contrast and sitemap lastmod. Website (private package) and the docs workflow only, so skip-changeset.

Theme a11y and contrast (website/theme/index.tsx, website/styles/index.css)

Audit findings at 1440×900 on Rspress 2.0.21 defaults, and what changed:

Finding Before After
Appearance switch is a click-only div with no role/name, not focusable Named export SwitchAppearance replaces the default site-wide (Nav, NavScreen, NavHamburger import it from @rspress/core/theme): role="button", tabIndex=0, translated aria-label, aria-pressed after mount (hydration-safe), Enter/Space toggle, brand focus ring. Stays a div because NavHamburger mounts it inside its own <button>.
GitHub social link has no accessible name SocialLinks wrapper sets aria-label from the link's host (github.com) on icon-only anchors.
No skip link top slot renders "Skip to main content" / "跳至主要内容" as the first focusable element, targeting a zero-size #ab-content marker placed by beforeDocContent (doc pages) and the custom HomeLayout; hidden on 404/custom/blank pages.
Home page has no <main> / <h1> Custom HomeLayout wraps hero + features in <main> (footer stays outside to keep contentinfo). The hero title stays a div: an <h1> would need a HomeHero fork.
Prev/next 12 px labels (--rp-c-text-3) 1.85:1 light / 3.19:1 dark 5.18:1 / 5.37:1 (also lifts the home footer, last-updated line, code-block icons)
Active-nav brand on white #0d8f80 3.99:1 #0b8072 4.83:1 — same hue and saturation, lightness 31 % → 27 %; theme-color meta follows
Shiki light string token #31a94d 3.04:1 #26843c 4.72:1; the light comment (2.06:1 → 4.74:1), light parameter (2.30:1 → 4.66:1) and dark comment (3.84:1 → 5.02:1) tokens failed the same way and were fixed alongside

Every ratio is WCAG 2.x, computed from the declared colours over the theme backgrounds; the values are in the CSS comments next to each token.

Sitemap lastmod (website/plugins/sitemap-lastmod.ts, .github/workflows/docs.yml)

@rspress/plugin-sitemap dates every <url> with the source file's mtime — in CI that is checkout time for authored pages and build time for generated ones, so every deploy told crawlers every page had changed, and the plugin has no option to turn it off. sitemapLastmod wraps pluginSitemap (Rspress runs afterBuild hooks in parallel, so a second plugin cannot order itself after the write) and rewrites <lastmod> to the last commit touching the page's source: the .mdx/.md for authored pages, packages/agent-bundle/src for /api/**, src/adapters/capabilities for hosts/events/notices, docs/diagnostics.md for the diagnostics page. On a shallow clone or when git is unavailable the element is omitted rather than wrong. docs.yml checks out full history bloblessly (fetch-depth: 0, filter: blob:none: commits and trees only, which is all git log -- <path> needs; the pack is 159 MiB with blobs).

Local gate (pnpm docs:site:build) green; sitemap has 1,948 URLs with dates ranging from 2026-09-03 to today per page.

Self-review

Reviewer: gpt-5.6-sol-medium (implementing lanes: claude-fable-5-1-thinking-max for the theme, gpt-5.6-sol-medium for the first sitemap draft, claude-fable-5-1-thinking-max for the plugin composition, CI change and integration). Two independent Sol runs on the pre-fix diff (ad0e259):

  1. Nested interactive content: the focusable role="button" switch also renders inside NavHamburger's <button> (shown at 769–1280 px). Fixed (9d0d846): after mount the switch detects closest('button') and renders as the default theme's plain click target there; the standalone copy keeps role/name/tab stop. Verified headless: at 1024 px the nested copy has no role/tabindex after hydration, at 1440 px Tab reaches the switch and Enter toggles the theme.
  2. 404 page had no <main> and no skip target. Fixed: NotFoundLayout wraps the default in <main> with the skip target; 404.html now has landmark, target and link.
  3. String token failed on highlighted lines (4.20:1 on the {n}/[!code highlight] background). Fixed: #227a37 — 5.37:1 on white, 4.78:1 highlighted.
  4. Prev/next hover: the theme's opacity: .7 on the hovered card dropped the label to ~2.8:1, and the dark token was 4.11:1 on the mute background. Fixed: hover keeps full opacity (the background change remains as feedback); dark --rp-c-text-3 at 65 % → 7.08:1 / 5.08:1.
  5. No automated tests for sitemap-lastmod.ts. Dismissed as a unit test, addressed as a gate assertion: the unit pool is packages/**/tests/**/*.test.ts and website/ has no harness (none of its plugins or scripts are unit-tested); check-built-links.mjs now asserts every <lastmod> parses, is not in the future, and that the values are not all identical when there are more than ten URLs — the exact regression this change removes — and was verified to fail on a doctored sitemap.

No findings on hydration (aria-pressed after mount), override reachability, skip targets on doc/API pages, desktop width rules, sitemap route mapping, blobless history, or the checkout assumptions of other steps. Second pass after the fixes: one residual, dismissed: at 769–1280 px the visible switch is the copy inside NavHamburger's <button>, which now renders without a role or tab stop, and the hamburger's own Enter/Space only opens the menu — so a keyboard user at those widths still cannot toggle appearance. That is the default theme's behaviour at those widths (the switch was a click-only div there before and after this PR), the fix is a fork of NavHamburger that renders its popover outside the trigger, and the site's acceptance viewport is 1440 × 900 desktop (AGENTS.md). Recorded on #590 as deferred / upstream-worthy. All contrast ratios were recomputed independently (5.37, 4.79, 7.10, 5.09 before 8-bit rounding); the sitemap assertion's one theoretical false positive (a single commit touching all 75 authored sources with no other history) was judged acceptable for this site.

Deslop

cursor-grok-4.6-high-fast, 8 edits — ran after this PR had merged (the owner's rule arrived at 08:39), so the comment trims land in #622; no behaviour change.

Refs #590.

…link, skip link, home <main>, contrast tokens (#590)

Theme-level fixes for the P3 "theme a11y / contrast" audit items, measured
at 1440×900 on Rspress 2.0.21 defaults. Only website/theme/index.tsx and
website/styles/index.css change.

- SwitchAppearance: re-exported as div[role=button] with tabIndex, a
  translated aria-label, aria-pressed (set after mount to avoid a hydration
  mismatch) and Enter/Space handling. Kept a div because NavHamburger mounts
  it inside its own <button>. Brand focus ring declared in CSS.
- SocialLinks: wrapper sets aria-label (hostname) on icon-only anchors.
- Skip link in the `top` slot targeting a zero-size #ab-content marker in
  `beforeDocContent` and in the custom HomeLayout; hidden on 404/custom/blank.
- HomeLayout: hero + features wrapped in <main> (Markdown render for
  llms.txt keeps the default). Hero title stays a div; an <h1> would need
  a HomeHero fork.
- --rp-c-text-3: 1.85:1 → 5.18:1 light, 3.19:1 → 5.37:1 dark (prev/next
  labels, home footer, last-updated).
- --rp-c-brand light: #0d8f80 3.99:1 → #0b8072 4.83:1 (same hue/saturation).
- Shiki light string #31a94d 3.04:1 → #26843c 4.72:1; light comment
  2.06:1 → 4.74:1, light parameter 2.30:1 → 4.66:1, dark comment
  3.84:1 → 5.02:1.
@ScriptedAlchemy ScriptedAlchemy added the skip-changeset PR changes a publishable package but ships no observable change; changeset not required label Sep 5, 2026
@changeset-bot

changeset-bot Bot commented Sep 5, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 9d0d846

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@pkg-pr-new

pkg-pr-new Bot commented Sep 5, 2026

Copy link
Copy Markdown
npm i https://pkg.pr.new/ScriptedAlchemy/agent-bundle@614
npm i https://pkg.pr.new/ScriptedAlchemy/agent-bundle/create-agent-bundle@614
npm i https://pkg.pr.new/ScriptedAlchemy/agent-bundle/rsc-markdown-stream@614
npm i https://pkg.pr.new/ScriptedAlchemy/agent-bundle/@agent-bundle/runtime@614

commit: 9d0d846

…main> + skip target, string/tertiary tokens pass on highlighted and hover backgrounds, sitemap lastmod asserted post-build
@ScriptedAlchemy
ScriptedAlchemy marked this pull request as ready for review September 5, 2026 07:56
@ScriptedAlchemy
ScriptedAlchemy merged commit 41883c5 into main Sep 5, 2026
16 checks passed
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 5, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-05T07:59:51.831041Z 9d0d846 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

ScriptedAlchemy added a commit that referenced this pull request Sep 5, 2026
… deslop) (#622)

* docs(website): trim comments that restated the a11y/contrast code (#614 deslop)

* review(#622): keep the class-name contract and live-DOM rationale
ScriptedAlchemy added a commit that referenced this pull request Sep 5, 2026
# Conflicts:
#	website/docs/en/reference/targets-artifacts.mdx
#	website/docs/zh/reference/targets-artifacts.mdx
@ScriptedAlchemy
ScriptedAlchemy deleted the docs/590-p3-followups branch September 5, 2026 09:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

skip-changeset PR changes a publishable package but ships no observable change; changeset not required

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant