Skip to content

chrome C8a: the explanation as a bottom sheet on a phone - #567

Merged
sf624 merged 6 commits into
mainfrom
chrome-c8a
Nov 1, 2026
Merged

sf624 merged 6 commits into
mainfrom
chrome-c8a

Conversation

@sf624

@sf624 sf624 commented Nov 1, 2026

Copy link
Copy Markdown
Owner

Step C8a of docs/design/chrome-redesign.md ("The sheet"). C8b (the keyboard and the peek's summary row) follows; no release between them.

What users see

  • On a phone, and on a touch screen without hover held upright up to 1024 px, the explanation is a bottom sheet over the editor: peek (its row alone), half (at first), full (the app bar slides away).
  • One 48 px header row at every height: Explanation Ln 8, Problems ⓧ2 ⚠4 ⓘ6 (name "Problems: 2 errors, 4 warnings, 6 caveats"; ✓ when none), Reference / Ref, and the view pill (C7's component).
  • The whole header is the grabber: a tap around the tabs or on the chosen tab cycles peek → half → full; another tab on a folded sheet opens it; a drag settles by position and speed; the grabber button (before the tabs) takes ↑ ↓ Home End Enter. One-time hint at the first full sheet.
  • Problems is a tab (the page's list, moved into the sheet); choosing a problem goes to its line at half. Toasts sit above the sheet. The editor ends at the sheet's top; the cursor's line stays in view. overscroll-behavior: contain. Reduced motion: no animation.
  • Read | Edit in the strip above the editor (every touch screen) replaces the top bar's keyboard button; the strip is Read | Edit, name, chip, ⋯ (44 px). Open/Save are in the menu on the sheet.

The rule (src/ui/sheet.ts, unit-tested in test/sheet.test.ts)

snapHeights (peek = row + edge + safe area; half = 55% of the window; full = up to the strip), settle (≥ 0.5 px/ms: next height that way; else nearest to where 150 ms of speed carries it), tapped, keyed, sheetForms (tools under icons → no tools → "Ref" → no line → highest level only), problemCounts.

Plan changes (recorded in the C8a Done note)

  • Layout class bottom-sheet (.sheet is the menu/settings dialogs). Upright = screen.orientation, so the keyboard cannot turn the sheet off. A phone in desktop-site mode gets no sheet.
  • The grabber button lets presses through (pointer-events: none); the header takes taps and drags; the chosen tab is the 44 px tap target. The hit-area check now skips pass-through controls (test/support/hit-areas.ts) and measures the phone at all three heights.
  • Collapse/Copy are on the row only where it has room (tablet); on a phone, Copy as Markdown is in the menu's Share group (#menu-copy-md, shown only then).
  • The old snug/noline/icons/compact steps and per-language widths were the prototype's; main never had them.
  • At full, a toast stays inside the window (over the tabs row at most).

Real-device checks for this PR (section 8)

3 (drag, no pull-to-refresh), 4 (thumb taps on grabber/tabs/pill), 5 (app bar at full), 6 (safe areas, Safari's bottom bar), 9 (chip sheet and settings at full), 10 (130% text), 11 (landscape, iPad upright/turned), 13 (reduced motion), 14 (VoiceOver/TalkBack: sheet, tabs with full counts, chip). Checks 1, 2, 7 wait for C8b.

Tests

  • New: test/sheet.test.ts (snap table, heights, keys, forms), test/web-sheet.test.ts (row boxes at every height and tab at 300–430 px en/ja with CJK fonts and 130% text; touch drags slow/fast/from a tab; taps; keys and focus; hint with/without storage; Problems tab; cursor line and toast; reduced motion).
  • Updated: web-phone (layouts incl. tablet upright/turned/keyboard, Read | Edit), web-hit-areas, web-kind, web-menu, web-resize/web-problems (divider and strip now in a narrow touch window), and .cm-content focus instead of a click where its middle lies under the sheet.
  • npm test locally: 10520 passing. tsc root and -p web, stylelint, snippets check, level-css check.

Screenshots: 390 and 360, en and ja, each height (local, measured).

🤖 Generated with Claude Code

@github-actions

github-actions Bot commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

Web preview removed: the pull request is closed.

sf624 added a commit that referenced this pull request Nov 1, 2026
…ng, the new height said once, the focus home at full)

- fitsRow (web/layout.ts): a form fits when no item runs past the row's end
  padding; scrollWidth alone let the pill run up to 10 px into the padding,
  under the edge's swipe strip (390 px en, 360 px ja). One check for the
  sheet's row, the desktop panel header and the editor's header (C5).
- Tests: a tap at the inner edge of the row's last control finds it, and the
  row's boxes keep 12 px from the edges; the desktop header never runs into
  its end padding.
- The grabber's new height is said once, in the page's live region; its name
  waits while it has the focus.
- focusHome gives the grabber while the sheet is full (the editor is inert).
- overscroll-behavior on the pane did nothing: removed, and the real guards
  tested. The hit-area check leaves out the grabber only.
- Docs: Collapse and Copy as Markdown on a phone (details file), decision 11's
  note pending the maintainer's agreement.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
sf624 added a commit that referenced this pull request Nov 1, 2026
…alue at once; one source for the editor's and the explanation's stops)

- The grabber is a slider (role="slider", vertical, values 0 to 2 with the
  height in words), named once: its value is set at once, never stale while
  it has the focus. Enter cycles in `keyed`. Chromium's accessibility tree,
  as CDP shows it, has the number but an empty valuetext (also for a native
  range input), so the live region still says a new height, once.
- paneStops (web/main.ts): where the editor's and the explanation's stops
  are while they are inert (the settings sheet side by side; the grabber at
  full for the editor, folded for the explanation). focusHome and F6 read it;
  the sheet's `folded` and `covers` also drive `inert`.
- Tests: the slider in the accessibility tree at every change; F6 at full,
  folded and half.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
sf624 and others added 6 commits November 1, 2026 11:25
On a phone, and on a touch screen held upright up to 1024 px, the
explanation is a sheet over the editor at three heights (peek, half,
full). Its header is one 48 px row: the tabs Explanation (with "Ln 8"),
Problems (counts by level) and Reference, and the view pill. The header
is the grabber: a tap cycles the heights, a drag settles by position and
speed (src/ui/sheet.ts), the grabber button takes the arrow keys. At
full the app bar slides away. Read | Edit moves into the strip above the
editor. Problems is a tab; toasts sit above the sheet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ng, the new height said once, the focus home at full)

- fitsRow (web/layout.ts): a form fits when no item runs past the row's end
  padding; scrollWidth alone let the pill run up to 10 px into the padding,
  under the edge's swipe strip (390 px en, 360 px ja). One check for the
  sheet's row, the desktop panel header and the editor's header (C5).
- Tests: a tap at the inner edge of the row's last control finds it, and the
  row's boxes keep 12 px from the edges; the desktop header never runs into
  its end padding.
- The grabber's new height is said once, in the page's live region; its name
  waits while it has the focus.
- focusHome gives the grabber while the sheet is full (the editor is inert).
- overscroll-behavior on the pane did nothing: removed, and the real guards
  tested. The hit-area check leaves out the grabber only.
- Docs: Collapse and Copy as Markdown on a phone (details file), decision 11's
  note pending the maintainer's agreement.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…alue at once; one source for the editor's and the explanation's stops)

- The grabber is a slider (role="slider", vertical, values 0 to 2 with the
  height in words), named once: its value is set at once, never stale while
  it has the focus. Enter cycles in `keyed`. Chromium's accessibility tree,
  as CDP shows it, has the number but an empty valuetext (also for a native
  range input), so the live region still says a new height, once.
- paneStops (web/main.ts): where the editor's and the explanation's stops
  are while they are inert (the settings sheet side by side; the grabber at
  full for the editor, folded for the explanation). focusHome and F6 read it;
  the sheet's `folded` and `covers` also drive `inert`.
- Tests: the slider in the accessibility tree at every change; F6 at full,
  folded and half.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…as not the focus

A screen reader says the focused slider's new value (aria-valuetext) itself:
with the live region too, each key was said twice. A tap or a drag from
elsewhere is still said once; a resize says nothing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…line goes; decision 11 on a phone)

- sheetForms: after "Ref", the Problems tab keeps its highest level only,
  and only then the line goes. With CI's fonts and the sample, the row keeps
  "Ln 8" from 360 to 430 px in English and Japanese. The tab's name keeps
  every count.
- The Problems-tab test is back at 390 px in English (44 px to spare).
- Decision 9 has the new order, decision 11 the agreed phone form
  (2026-11-01).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@sf624
sf624 merged commit de731f7 into main Nov 1, 2026
21 checks passed
sf624 added a commit that referenced this pull request Nov 1, 2026
…ng, the new height said once, the focus home at full)

- fitsRow (web/layout.ts): a form fits when no item runs past the row's end
  padding; scrollWidth alone let the pill run up to 10 px into the padding,
  under the edge's swipe strip (390 px en, 360 px ja). One check for the
  sheet's row, the desktop panel header and the editor's header (C5).
- Tests: a tap at the inner edge of the row's last control finds it, and the
  row's boxes keep 12 px from the edges; the desktop header never runs into
  its end padding.
- The grabber's new height is said once, in the page's live region; its name
  waits while it has the focus.
- focusHome gives the grabber while the sheet is full (the editor is inert).
- overscroll-behavior on the pane did nothing: removed, and the real guards
  tested. The hit-area check leaves out the grabber only.
- Docs: Collapse and Copy as Markdown on a phone (details file), decision 11's
  note pending the maintainer's agreement.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
sf624 added a commit that referenced this pull request Nov 1, 2026
…alue at once; one source for the editor's and the explanation's stops)

- The grabber is a slider (role="slider", vertical, values 0 to 2 with the
  height in words), named once: its value is set at once, never stale while
  it has the focus. Enter cycles in `keyed`. Chromium's accessibility tree,
  as CDP shows it, has the number but an empty valuetext (also for a native
  range input), so the live region still says a new height, once.
- paneStops (web/main.ts): where the editor's and the explanation's stops
  are while they are inert (the settings sheet side by side; the grabber at
  full for the editor, folded for the explanation). focusHome and F6 read it;
  the sheet's `folded` and `covers` also drive `inert`.
- Tests: the slider in the accessibility tree at every change; F6 at full,
  folded and half.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@sf624
sf624 deleted the chrome-c8a branch November 1, 2026 02:47

This branch was successfully deployed

1 active deployment
github-pages — 0a0cf3a6 Deployed Nov 1, 2026 by sf624 via Deploy to GitHub Pages #1994
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