Make mobile search a flyout (animate as well) and increase the hitbox of the search button / make it more easily identifiable as a button #177

Closed
opened 2026-08-27 02:21:39 +00:00 by Grandiras · 2 comments
Owner
No description provided.
Collaborator

🤖 On it — follow along in the action run; the reply will link the full session transcript.

📜 Live transcript — updating every ~5 minutes while the task runs.

🤖 On it — follow along in [the action run](https://git.grandiras.net/Grandiras/claude-bot/actions/runs/405); the reply will link the full session transcript. <!-- claude-run:comment --> 📜 [Live transcript](https://git.grandiras.net/Grandiras/claude-bot/src/branch/claude-logs/transcripts/Grandiras/Ponente/issue-177/run-405.md) — updating every ~5 minutes while the task runs.
Collaborator

Pushed claude/issue-177.

What changed

The compact search is a flyout now, not a full-screen mode. Below 960px, opening the box used to replace the whole page: the field at the top, the board gone, and most of the screen blank under eight rows of results. It now hangs under the header as a flyout, right-aligned to the window rather than to its trigger — what sits between the trigger and the window's edge is the two view links and the admin fold (~320px of them), so a 26rem panel anchored to the trigger's right edge starts at -146px in a 620px window, and off the other edge if it's anchored left. Anchored to the window it always fits, and the trigger is always inside its span, which is what .nav-admin-menu beside it already does.

On a phone it's a sheet standing on the bar — the shape of the two menus beside it, and docs/ui.md's sheet rule rather than an exception to it. It rises on the same sheetRise, casts the same upwards-only --shadow-sheet, dims the board above rather than replacing it, and leaves the magnifier lit underneath, so a second tap on the same spot is the way back out.

before: a full-screen takeover; after: a sheet on the bar with the board still visible

Two things inside that sheet are worth naming:

  • The field is at the bottom and the results grow upward above it. A sheet is anchored at its bottom edge, so with the field on top every result that arrives shoves it up — you type one letter and the box you're typing into jumps a couple of hundred pixels away from your thumb, mid-word. Only the grid rows swap; the markup still reads field-then-list, which is the order a screen reader follows.
  • The on-screen keyboard is measured, because no CSS describes it. The sheet is position: fixed to the layout viewport and a keyboard shrinks only the visual one — dvh, vh and env() all describe the layout viewport too — so the field would be drawn underneath the thing it's typed on, every time it opened. search-interop.js watches visualViewport and publishes --keyboard-inset; the sheet sits on max(--phone-nav-h, --keyboard-inset) and caps its height against the same number. Unset (no visualViewport, or the module failed to load) the fallback is 0px and the sheet is simply on the bar. Anything under 120px is read as zero, or the sheet would hop with the URL bar. It's also why the phone keeps "Close": with the keyboard up, the trigger is behind one.

And the trigger reads as a button. It was a bare glyph between a board name and a face in a pill — a label for the row rather than the third thing in it you can press. It takes .nav-menu-toggle's box (filled --bg-2, hairline border, pill), grows from 38×38 to 46×40 at tablet widths and from 44×44 to 52×44 on the phone's bar, and the mark goes 18px → 20px so it isn't adrift inside it. The one inside the field stays 16px.

the phone bar before and after: a bare magnifier, then a filled pill matching the account chip

Verified

  • dotnet build / dotnet test Ponente.sln — 448 passed, DocumentationTests included.
  • Playwright against a host-run stack: search.spec.js, mobile.spec.js, calendar*.spec.js, filter.spec.js, i18n, icons, prefs all green on a fresh database.
  • search.spec.js gained assertions for the new shapes: the trigger is filled rather than transparent; the sheet's bottom is the bar's top (±2px) with the board still visible above; its shadow has no positive y-offset; the list sits above the field; a second tap on the trigger closes it; and at 620px the flyout stays inside the window with its trigger under it — that last one pins the -146px bug directly.
  • Checked by hand at 390/620/800/959/1440px, in dark mode, and with --keyboard-inset forced to 320px to confirm the sheet lifts onto the keyboard and clamps its height.

Notes

  • No changelog entry. The search box itself is still in Changelog.Unreleased, so this changes a shape no member has seen; the existing bullet is still accurate. Adding one would describe a change from a state that never shipped.
  • docs/search.md ("The box") is rewritten for the three shapes, the anchoring arithmetic and the keyboard; docs/ui.md no longer says the search is the sheet that covers the bar; docs/frontend.md lists --keyboard-inset as search-interop.js's fourth job.
  • One test, boards.spec.js:165 ("the nav switches boards"), fails in this container at expectVisibleSoon('.board > .column') — but it fails identically on unmodified main, so it isn't from this change. I couldn't tell whether it's real or an artifact of running outside the compose stack (no podman/docker here, so the app ran host-side against a local Postgres), so I haven't filed it. The other 15 failures in the full run are the PDS and forge containers being absent.

⏱ 31m 34s · 112 turns · tokens: 187.1k in / 73.3k out (+14598.9k cache-read)
📜 Full session transcript
→ Opened PR: #178

Pushed **`claude/issue-177`**. ## What changed **The compact search is a flyout now, not a full-screen mode.** Below 960px, opening the box used to replace the whole page: the field at the top, the board gone, and most of the screen blank under eight rows of results. It now hangs under the header as a flyout, right-aligned to the window rather than to its trigger — what sits between the trigger and the window's edge is the two view links and the admin fold (~320px of them), so a 26rem panel anchored to the trigger's right edge starts at **-146px** in a 620px window, and off the other edge if it's anchored left. Anchored to the window it always fits, and the trigger is always inside its span, which is what `.nav-admin-menu` beside it already does. **On a phone it's a sheet standing on the bar** — the shape of the two menus beside it, and `docs/ui.md`'s sheet rule rather than an exception to it. It rises on the same `sheetRise`, casts the same upwards-only `--shadow-sheet`, dims the board above rather than replacing it, and leaves the magnifier lit underneath, so a second tap on the same spot is the way back out. ![before: a full-screen takeover; after: a sheet on the bar with the board still visible](https://git.grandiras.net/attachments/e74bab87-b191-4e20-8689-1c1770aff7c6) Two things inside that sheet are worth naming: - **The field is at the *bottom* and the results grow upward above it.** A sheet is anchored at its bottom edge, so with the field on top every result that arrives shoves it up — you type one letter and the box you're typing into jumps a couple of hundred pixels away from your thumb, mid-word. Only the grid rows swap; the markup still reads field-then-list, which is the order a screen reader follows. - **The on-screen keyboard is measured, because no CSS describes it.** The sheet is `position: fixed` to the *layout* viewport and a keyboard shrinks only the visual one — `dvh`, `vh` and `env()` all describe the layout viewport too — so the field would be drawn underneath the thing it's typed on, every time it opened. `search-interop.js` watches `visualViewport` and publishes `--keyboard-inset`; the sheet sits on `max(--phone-nav-h, --keyboard-inset)` and caps its height against the same number. Unset (no `visualViewport`, or the module failed to load) the fallback is `0px` and the sheet is simply on the bar. Anything under 120px is read as zero, or the sheet would hop with the URL bar. It's also why the phone keeps "Close": with the keyboard up, the trigger is behind one. **And the trigger reads as a button.** It was a bare glyph between a board name and a face in a pill — a label for the row rather than the third thing in it you can press. It takes `.nav-menu-toggle`'s box (filled `--bg-2`, hairline border, pill), grows from 38×38 to 46×40 at tablet widths and from 44×44 to **52×44** on the phone's bar, and the mark goes 18px → 20px so it isn't adrift inside it. The one inside the field stays 16px. ![the phone bar before and after: a bare magnifier, then a filled pill matching the account chip](https://git.grandiras.net/attachments/f29b3a2f-94fb-40fa-9d07-69f99f14b4cd) ## Verified - `dotnet build` / `dotnet test Ponente.sln` — 448 passed, `DocumentationTests` included. - Playwright against a host-run stack: `search.spec.js`, `mobile.spec.js`, `calendar*.spec.js`, `filter.spec.js`, `i18n`, `icons`, `prefs` all green on a fresh database. - `search.spec.js` gained assertions for the new shapes: the trigger is filled rather than transparent; the sheet's bottom is the bar's top (±2px) with the board still visible above; its shadow has no positive y-offset; the list sits above the field; a second tap on the trigger closes it; and at 620px the flyout stays inside the window with its trigger under it — that last one pins the -146px bug directly. - Checked by hand at 390/620/800/959/1440px, in dark mode, and with `--keyboard-inset` forced to 320px to confirm the sheet lifts onto the keyboard and clamps its height. ## Notes - **No changelog entry.** The search box itself is still in `Changelog.Unreleased`, so this changes a shape no member has seen; the existing bullet is still accurate. Adding one would describe a change from a state that never shipped. - `docs/search.md` ("The box") is rewritten for the three shapes, the anchoring arithmetic and the keyboard; `docs/ui.md` no longer says the search is the sheet that covers the bar; `docs/frontend.md` lists `--keyboard-inset` as `search-interop.js`'s fourth job. - One test, `boards.spec.js:165` ("the nav switches boards"), fails in this container at `expectVisibleSoon('.board > .column')` — but it fails identically on unmodified `main`, so it isn't from this change. I couldn't tell whether it's real or an artifact of running outside the compose stack (no podman/docker here, so the app ran host-side against a local Postgres), so I haven't filed it. The other 15 failures in the full run are the PDS and forge containers being absent. --- ⏱ 31m 34s · 112 turns · tokens: 187.1k in / 73.3k out (+14598.9k cache-read) 📜 [Full session transcript](https://git.grandiras.net/Grandiras/claude-bot/src/branch/claude-logs/transcripts/Grandiras/Ponente/issue-177/run-405.md) → Opened PR: https://git.grandiras.net/Grandiras/Ponente/pulls/178
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Grandiras/Ponente#177
No description provided.