Skip to content

fix(a11y): keyboard-accessible block reordering + tighter focus order#60

Merged
zhawtof merged 1 commit into
mainfrom
claude/silly-zhukovsky-c3d332
May 16, 2026
Merged

fix(a11y): keyboard-accessible block reordering + tighter focus order#60
zhawtof merged 1 commit into
mainfrom
claude/silly-zhukovsky-c3d332

Conversation

@zhawtof
Copy link
Copy Markdown
Contributor

@zhawtof zhawtof commented May 16, 2026

Summary

Makes the builder usable with the keyboard alone. The biggest gap was that block reordering required a mouse drag with no keyboard alternative; this also cleans up a handful of focus-order, ARIA, and landmark issues found during the audit.

Why

Block reordering via @dnd-kit's PointerSensor had no keyboard counterpart, and there were no Move Up / Move Down affordances either, so keyboard-only users literally could not change the order of blocks once they were on the surface. Several smaller issues compounded the problem:

  • Modal preview chrome (Cancel / Submit / X) rendered as real <button>s with no onClick, polluting the Tab order.
  • The per-row floating toolbar revealed only on hover, leaving the Edit / Duplicate / Delete buttons focusable but invisible.
  • The block-row Popover trigger is <div role="button">; Space scrolled the page instead of opening the editor.
  • The Toolbar's surface and theme pickers were Popovers with plain <button>s — no menu semantics, no arrow-key nav, didn't close on selection.
  • JSON drawer <textarea> had no accessible name.
  • No <main> landmark around the preview area.

What changed

  • block-row.tsx — Adds Move up / Move down buttons to the per-row floating toolbar (with edge-disabled states) and reveals the toolbar on group-focus-within so focused-but-invisible Tab stops can't happen. Adds an explicit Enter/Space handler on the row's role="button" div trigger that opens the popover without scrolling.
  • surface.tsx — Plumbs index / total / onReorder through to BlockRow. Demotes the fake Modal chrome (Close, Cancel, Submit) from <button> to styled <span aria-hidden> so they're invisible to assistive tech and not in the Tab order. Wraps the preview area in <main aria-label="Block preview">.
  • block-kitchen.tsx — Passes reorderBlock into Surface as onReorder.
  • toolbar.tsx — Replaces the ad-hoc Popover dropdowns with a small Menu helper that uses role="menu" + role="menuitemradio" + aria-checked and handles ArrowUp / ArrowDown / Home / End. Makes the Popovers controlled so they auto-close on selection (matching menuitemradio semantics) and return focus to the trigger.
  • json-drawer.tsx — Adds aria-label="Block Kit JSON" to the textarea.

Test plan

  • pnpm typecheck
  • pnpm lint
  • pnpm test — 211 / 211
  • Manual verification in the demo:
    • Move Up / Move Down reorder blocks; edges disable correctly
    • Floating row toolbar reveals when a row gets focus
    • Surface + Theme menus: ArrowDown / ArrowUp wrap / Home / End all work; menu closes on select and focus returns to the trigger
    • Pressing Space on a row opens the editor and does not scroll
    • Modal preview frame (light + dark) has no focusable Cancel / Submit / Close
    • JSON drawer textarea announces "Block Kit JSON"
    • <main aria-label="Block preview"> landmark exists
    • Mouse drag-and-drop still works

🤖 Generated with Claude Code

@cloudflare-workers-and-pages
Copy link
Copy Markdown
Contributor

cloudflare-workers-and-pages Bot commented May 16, 2026

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
block-kitchen f2c24bb Commit Preview URL

Branch Preview URL
May 16 2026, 05:31 PM

…s order

Adds keyboard-reachable Move up / Move down buttons on each block row, a
proper menu/menuitemradio pattern for the toolbar surface + theme
pickers (now closes on selection and returns focus to the trigger),
removes fake focusable chrome from the Modal preview, fixes
Space-scroll on the row Popover trigger, reveals the per-row toolbar
on focus-within so Tab-stops aren't invisible, adds an aria-label to
the JSON drawer textarea, and wraps the preview surface in a <main>
landmark.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@zhawtof zhawtof force-pushed the claude/silly-zhukovsky-c3d332 branch from cc1cbde to f2c24bb Compare May 16, 2026 17:31
@zhawtof zhawtof merged commit 01aa7e9 into main May 16, 2026
12 checks passed
@zhawtof zhawtof deleted the claude/silly-zhukovsky-c3d332 branch May 16, 2026 17:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant