Skip to content

fix(web): wrap markdown tables in an accessible scroll region - #4605

Merged
bpamiri merged 1 commit into
developfrom
peter/table-scroll-region
Oct 19, 2026
Merged

bpamiri merged 1 commit into
developfrom
peter/table-scroll-region

Conversation

@bpamiri

@bpamiri bpamiri commented Oct 15, 2026

Copy link
Copy Markdown
Collaborator

Summary

.prose table used display: block; overflow-x: auto so wide tables scroll on phones. But display: block on a <table> drops its implicit table role in Safari + VoiceOver. This replaces that stopgap with the accessible scrollable-region pattern:

  • New shared plugin, @wheels-dev/ui/markdown/rehype-table-scroll.mjs: wraps each table in <div class="table-scroll" role="region" aria-label="Table" tabindex="0">. It has no dependencies, plain hast recursion like rehype-base-prefix.mjs, and never wraps a table twice.
  • Blog: wired into markdown.rehypePlugins. Astro's GFM and Shiki defaults are unchanged.
  • Packages: wired into the site's own unified() README renderer in src/lib/markdown.ts, after rehype-sanitize so the region attributes survive. That page uses set:html, so an astro.config entry wouldn't reach it.
  • CSS: the .prose table rule is gone; tables get native display back. .table-scroll owns the scroll and shows a :focus-visible outline in --color-brand.

Unchanged:

  • landing /ai: its hand-written tables already sit in a scrolling .table-wrap;
  • the Starlight sites (guides, api): they don't use .prose.

Fixes #4575

Testing

  • Plugin unit tests: node --test web/packages/ui/src/markdown/rehype-table-scroll.test.mjs, 5/5. Covers a top-level table, a nested table, siblings untouched, no double wrap, and several tables.
  • Packages: pnpm --filter ./sites/packages test, 19/19, including a new case for the wrapper around a GFM table.
  • Built HTML (blog + packages): every table is wrapped exactly once, including a post with three tables, and code highlighting is intact.
  • Phone width (390px): a wide blog table scrolls inside its region and adds nothing to the page width. /ai is visually unchanged at 390px and 1280px.
  • Keyboard: Tab reaches the region and shows the 2px brand outline.
  • Not tested: Safari + VoiceOver.
  • Visual-regression baselines: none change; no baseline page renders a markdown table.

🤖 Generated with Claude Code

`.prose table` used display: block and overflow-x: auto so wide tables
scroll on phones, but display: block drops the table's implicit role in
Safari + VoiceOver. A shared rehype plugin now wraps each markdown table
in <div class="table-scroll" role="region" aria-label="Table"
tabindex="0">, which owns the horizontal scroll, and the table keeps its
native display.

The plugin is wired into the blog's Astro markdown pipeline and into the
packages site's own unified() README renderer (after rehype-sanitize, so
the region attributes survive). The landing /ai page already wraps its
hand-written tables and is unchanged; Starlight sites don't use .prose.

Fixes #4575

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Peter Amiri <peter@alurium.com>
@github-actions github-actions Bot added dependencies Pull requests that update a dependency file javascript Pull requests that update javascript code labels Oct 15, 2026
@bpamiri
bpamiri merged commit 6e4915d into develop Oct 19, 2026
22 checks passed
@bpamiri
bpamiri deleted the peter/table-scroll-region branch October 19, 2026 19:24
bpamiri added a commit that referenced this pull request Oct 19, 2026
Starlight styles markdown tables with display: block (in @layer
starlight.content), which drops the table role in Safari + VoiceOver.
The guides site now runs the shared rehype-table-scroll plugin (#4605),
which wraps each table in an accessible scroll region, and an unlayered
.sl-markdown-content .table-scroll > table rule restores native display.

The API site does not use the plugin: Astro runs markdown rehype plugins
before rehype-raw, so it would double-wrap the generated parameter
tables, which already sit in .wd-params-table with display: table. Its
one plain table, the versions list in docs/index.md, is wrapped by hand.

Inline code on Starlight pages also gets overflow-wrap: anywhere,
matching the blog.

Fixes #4608

Signed-off-by: Peter Amiri <peter@alurium.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file javascript Pull requests that update javascript code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

a11y: wrap markdown tables in a scroll container (rehype) instead of display:block

1 participant