Repository navigation
Make the sidebar, drawer and command palette keyboard-safe, and prefetch chapters on intent - #11
Conversation
JSX trims each line of a text block and joins the lines with one space.
The Chinese copy is wrapped across source lines throughout the course, so
it rendered with spaces between Chinese characters and around full-width
punctuation: 1,572 of them in 36 files.
- 1,548 line breaks are joined by a script that walks the TypeScript
syntax tree and touches only JSX text and JSX attribute strings, never
code samples, template literals or comments. Breaks next to Latin
letters, digits, expressions, "——" or operators keep their space.
- 24 more spaces go: 20 after full-width punctuation, 3 between Chinese
text and a tag whose text is Chinese, and one {" "} between a
full-width mark and Chinese text.
- test/zh-copy-spacing.test.ts, the check DataData and AlgoAlgo use,
scans every TSX file and fails on any new stray space; CLAUDE.md states
the rule.
Apart from the one {" "}, the diff changes nothing but whitespace. The
text every edited file renders differs from master only by deleted
spaces, each next to Chinese text, so the English copy is unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…on intent - The sidebar is inert whenever it is off screen (closed drawer, collapsed on desktop), so its links are no longer invisible tab stops. An open drawer makes the page behind it inert, locks scrolling, takes focus, and closes on Escape or the scrim with focus back on the menu button, which now has aria-controls and aria-expanded. - Add a "Skip to content" link before the sidebar that focuses the page's <main>. - The command palette is a modal combobox: aria-modal, Tab stays in the search box, the results are a listbox announced through aria-activedescendant and scrolled into view, the page behind does not scroll, an IME confirmation is not taken as Enter, and closing returns focus to the opener. Its search field is 16px so iOS does not zoom. - Chapter and brand links prefetch only on hover or focus instead of on every page load. - Show Ctrl K outside Apple platforms, name the theme button by what it will do, give the chapter state dots role="img", count tasks and quizzes in the singular and plural, and replace the gaming term in the Chinese progress line. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 751c09f24f
ℹ️ 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".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| return () => { | ||
| html.style.overflow = previousOverflow; |
There was a problem hiding this comment.
Coordinate scroll locks when closing stacked overlays
On a viewport at or below 960px, open the drawer, press Ctrl/Cmd-K, then press Escape. Both the palette's global Escape listener and the drawer listener close their overlay in the same update. The palette captured overflow: hidden from the drawer, and because its cleanup runs after the earlier Sidebar component's cleanup, this assignment restores hidden after the drawer restored the original value, leaving the page permanently unscrollable after both overlays disappear. Use a shared/reference-counted scroll lock or ensure Escape dismisses only the topmost overlay.
Useful? React with 👍 / 👎.
| <a className="skip-link" href="#main" onClick={skipToContent}> | ||
| {L({ en: "Skip to content", zh: "跳到正文" })} | ||
| </a> |
There was a problem hiding this comment.
Keep the skip link inside the drawer focus boundary
When the narrow-screen drawer is open, only .shell-main is made inert, while this newly added skip link is a focusable sibling outside both .shell-main and the drawer. Tabbing forward from the drawer's last control therefore wraps to the skip link, so focus escapes the modal and activating the link attempts to focus inert page content. Make the skip link inert while the drawer is open or include it in a complete focus trap.
Useful? React with 👍 / 👎.
Branches from #9 (commit
2148ad8), so it can be merged before or after it, and independently of #10.Summary
Fixes from the 2026-10-09 audit (
04-TSer-代码与体验.md):aria-labelon a role-less<span>.Changes
app/sidebar.tsx<aside id="sidebar">isinertwhenever it is off screen..shell-mainis inert,<html>does not scroll, and the first link takes focus. Escape or the scrim closes the drawer and focuses the menu button.<main>.prefetch={false}and prefetch on hover or focus.role="img". The progress line handles singular and plural, and the Chinese line now reads 学完 N/12 章.app/toolbar.tsxid="sidebar-toggle",aria-controls="sidebar"andaria-expanded, and uses the same breakpoint hook as the sidebar.app/command-palette.tsxaria-modal="true". Tab stays in the search box.aria-activedescendant, and the results are alistboxofoptions, scrolled into view as the selection moves.app/globals.css:.skip-linkstyles next to the sidebar rules, and.cmdk-inputat 16px.test/shell-keyboard.test.tsx.Verification
npx tsc --noEmit --incremental false: clean.npm test: 7 files, 48 tests pass.npm run build: succeeds.next start:<main>..shell-mainis inert and<html>overflow is hidden. Escape closes it and focuses#sidebar-toggle, and a scrim click does the same.🤖 Generated with Claude Code