Skip to content

feat(mobile): search-first header, replacing the second toolbar row - #945

Open
shukiv wants to merge 4 commits into
bulwarkmail:mainfrom
shukiv:feat/mobile-search-first-header
Open

feat(mobile): search-first header, replacing the second toolbar row#945
shukiv wants to merge 4 commits into
bulwarkmail:mainfrom
shukiv:feat/mobile-search-first-header

Conversation

@shukiv

@shukiv shukiv commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Problem

On phones the mail list sits under two stacked 56px bars: MobileHeader (menu + mailbox name) and a second row holding select-all, the search box, the filter toggle and refresh. That is 112px of chrome before the first message, and the title largely repeats what the folder drawer already shows.

There is also no indication of which account you are reading until you open the drawer — easy to confuse with several accounts on a small screen.

Change

The header becomes: menu · search field · account avatar. Tapping the search field presents the existing second row full-screen.

Nothing about search is rebuilt — the full-screen panel is the same block, so SearchBox, SearchSuggestions, the search-history store and SearchChips all come along as they are. Select-all and refresh remain reachable inside it, and the list already supports long-press selection (hooks/use-long-press.ts) and pull-to-refresh.

Tablet and desktop render exactly as before; the row is only relocated at isMobile.

Why a new AccountSwitcher variant

rail flies its popover out sideways, which is right for a narrow vertical sidebar and wrong at the top of the screen: anchored to a trigger at the inline-end edge it runs off the viewport. In RTL that edge is the left one, so the menu opened past the left border and could not be reached.

The new header variant drops the menu below the trigger and pins it by its trailing edge, with max-width: calc(100vw - 16px) so it cannot exceed the viewport in either direction. rail and expanded are untouched.

Direction

The bar is a flex row, so it mirrors on its own — the avatar lands top-right in LTR and top-left in RTL, always opposite the menu button. The back arrow in the full-screen panel is mirrored with rtl:-scale-x-100.

Notes

  • Translation keys are addressed through the root translator with full paths (sidebar.search_placeholder_hint, sidebar.mobile.go_back), per fix(i18n): resolve cross-namespace keys via root translator (../ never worked) #688.
  • No new strings, no new dependencies.
  • tsc --noEmit clean. Test suite shows the same 4 failures as main at the time of writing (incomplete zh-TW locale, three auth-store logout tests) — unrelated to this change.
  • Running on a production instance.

The rail variant flies its popover out sideways, which is right for a
narrow vertical sidebar but wrong at the top of the screen: anchored to
a trigger at the inline-end edge it runs straight off the viewport. In
RTL that edge is the left one, so the menu opened past the left border
and was unreachable.

Add a "header" variant that drops the menu below the trigger and pins it
by its trailing edge, with a max-width so it can never exceed the
viewport. rail and expanded are untouched.
Phones stacked two 56px bars: the mailbox title, then a row holding
select-all, the search box, the filter toggle and refresh. That is 112px
of chrome before the first message, and the title merely repeats what
the drawer already shows.

The header now carries the account avatar and a tappable search field,
and the second row is presented full-screen when that field is tapped —
so the same SearchBox, suggestions, history and filter chips appear,
none of it rebuilt. Select-all and refresh remain in that panel, and the
list already supports long-press selection and pull-to-refresh.

Tablet and desktop are unchanged; the row is only relocated on phones.
The full-screen panel is fixed inset-0, so it sat on top of the very
list it filters: submitting a query worked, updated the list underneath,
and left the reader staring at an empty panel.

Close it on submit, on picking a contact suggestion, and on clear, so
the results it just produced are what you land on. The header field then
shows the active query rather than the placeholder, so a filtered list
does not look unexplained, and tapping it reopens the panel to refine.

SearchBox gains an autoFocus prop so the panel opens straight to the
keyboard instead of needing a second tap.
Once a search runs the header field shows the active query, but the only
way back to the full list was to open the panel and clear it there.

Put a clear button at the trailing end of the field, shown only while a
query is applied. The field is now a container rather than one button,
since nesting a button inside a button is invalid markup — the label
area and the clear control are siblings.
@shukiv
shukiv force-pushed the feat/mobile-search-first-header branch from de7285a to 4279c37 Compare September 1, 2026 15:44
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