Skip to content

fix(ui): mobile hero search centering, touch targets, and portal hardening - #298

Merged
BigSimmo merged 16 commits into
mainfrom
cursor/mobile-ui-hero-search-38c7
Jul 5, 2026
Merged

fix(ui): mobile hero search centering, touch targets, and portal hardening#298
BigSimmo merged 16 commits into
mainfrom
cursor/mobile-ui-hero-search-38c7

Conversation

@BigSimmo

@BigSimmoBigSimmo commented Jul 5, 2026

Copy link
Copy Markdown
Owner

Summary

Re-applies the mobile UI fixes from closed#271 onto current main. Separate from access/setup PRs (#274/#277/#287).

Mobile search layout

  • Phone mode-home empty states: heading → centered hero search → starter actions
  • Composer portal hardening + bottom-dock flash suppression
  • 44px touch targets on phone composer actions
  • Footer Evidence/Sources chips removed; scope via + menu
  • Favourites hero composer slot restored
  • /applications route restored

Verification

  • Cherry-pick onto main — clean (rebased)
  • CI verify — pass
  • CI ui-smoke — pass (97 tests)
  • CI Gitleaks — pass
  • Playwright geometry tests include Answer home at 390×820

After merge, redeploy to update psychiatry.tools mobile layout.

Open in WebOpen in Cursor

@supabase

supabaseBot commented Jul 5, 2026

Copy link
Copy Markdown

This pull request has been ignored for the connected project sjrfecxgysukkwxsowpy because there are no changes detected in supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@BigSimmo
BigSimmo marked this pull request as ready for review July 5, 2026 18:35
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@BigSimmo
BigSimmo enabled auto-merge July 5, 2026 18:37

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Applications route lacks search
    • Restored the local ToolSearch input on the standalone /applications route when no desktop composer slot is provided, so users can filter applications again while dashboard tools mode still uses the shared header composer.

You can send follow-ups to the cloud agent here.

Reviewed by Cursor Bugbot for commit 0479e8b. Configure here.

Comment threadsrc/components/applications-launcher-page.tsx Outdated
BigSimmoand others added 15 commits July 6, 2026 03:35
- Hide footer Evidence/Sources chips on phone hero composers; scope stays in + menu
- Suppress bottom-dock composer flash until hero portal slot is ready
- Increase composer action/send touch targets to 44px on phones
- Update Playwright tests for scope menu, Answer home geometry, and stress fallback
- Remove stray merge conflict markers from master-search-header, ui-stress, globals.css
- Restore standalone /applications page and remove redirect to /?mode=tools
- Re-export ApplicationsLauncherPage for the restored route
- Regenerate docs/site-map.md
…oute
The applications layout hides the shared header composer, but the launcher
was always rendering an empty hero composer slot instead of the local
ToolSearch input. Show ToolSearch when no desktopComposerSlotId is provided
so /applications can filter tools again without affecting dashboard tools mode.
@BigSimmo
BigSimmoforce-pushed the cursor/mobile-ui-hero-search-38c7 branch from 9713507 to 32eca9bCompareJuly 5, 2026 19:36
@BigSimmo
BigSimmo merged commit e64f1a2 into mainJul 5, 2026
5 checks passed
@BigSimmo
BigSimmo deleted the cursor/mobile-ui-hero-search-38c7 branch July 8, 2026 16:25
BigSimmo added a commit that referenced this pull request Aug 9, 2026
Four rows, all measured against origin/main 199b303 in this session:
- #295 (P2 task) the ErrorState enforcement check. GATES.md:106 still
reads "planned" and grep over scripts/ and eslint-rules/ returns zero
references to ErrorState, so the component exists but nothing requires
it. Deliberately not flipped to implemented.
- #296 (P3 task) adopting ErrorState at the three surfaces that genuinely
hand-roll the guard. Live-look change, downstream of the redesign.
- #297 (P3 issue) the safe-area exemption in the new raw-value ratchet,
which is by form rather than by absence of a literal.
- #298 (P2 issue) the three sites carried into M4 as guards that are not
guards, so the next reader does not convert them.
Written with npm run issues:add, never by hand.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
BigSimmo added a commit that referenced this pull request Aug 9, 2026
…1786)
* feat(design-system): build ErrorState, the gate with nothing behind it
GATES.md §3 lists the prohibition "Render '0 matches' after a failed
request" with the gate "ErrorState adoption + check", status planned.
Measured at origin/main 199b303, ErrorState existed nowhere in src or
tests — only in COMPONENTS.md, GATES.md and SPEC.md. This builds it.
The invariant is clinical, not cosmetic. A search that failed has no count
to report, so reporting zero is a false statement about the corpus: on the
services page "0 matches" asserts there are no crisis services when the
search never ran, and on favourites it reads as "you have saved nothing"
rather than "we could not load them". COMPONENTS.md:322 draws the same line
from the other side — "no result count is available" is not a MissingValue.
The component therefore takes no count and no children. There is no prop
through which a number can arrive, and the generated dtsPropsFor entry now
records that as the published API. The one remaining route, a caller writing
a count into title or body, is covered by a development-time tripwire that
matches a figure against a counted noun ("0 matches", "no results") so an
error code or a duration does not trip it. It warns and never throws: on the
one screen already reporting a failure, a thrown error is a blank page.
Requirements came from the surfaces that hand-roll this guard today. Three
do, and their comments state the rule outright:
search-results-header-band.tsx:210 ("no number may reach the DOM"),
services-navigator-page.tsx:634 ("a blocked registry must not reach the band
as '0 matches'") and favourites-command-library-page.tsx:1182. They are
correct, just not shared; converting them is a live-look change and
deliberately not here.
Three further sites carried into this task as hand-rolled guards are not
that, measured at this HEAD, and are recorded so the next reader does not
convert them: differentials-home.tsx:716,729 renders "0 matches"/"No
matches" when sourcesChecked is true, i.e. a legitimate zero after a search
that SUCCEEDED; specifiers-home-page.tsx:211 is a comment about not showing
a stale zero above real catalogue results, and lives in
src/components/specifiers/, not clinical-dashboard/; document-search-results
gates on recordStatus for loading, not for a failed count.
Registered per gates 11 and 12: source, design-sync export, preview, prop
contract, publication test entry, behavioural DOM tests, adoption-contract
family, and both generators regenerated (54 components, 59 roots).
Uses the shared floatingControl recipe rather than a hand-rolled control, so
the tap floor, focus ring and forced-colors border come from one owner. Adds
zero arbitrary padding/gap/radius/line-height, so the ratchet landed in the
previous commit is unaffected and the two stay separately revertible.
Scope: ErrorState only. OfflineState, PermissionDeniedState, NotFoundState
and UnavailableState share the pattern but have no gate pointing at them.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat(design-system): ratchet raw gap literals, the family #1780 left uncovered
#1780 landed rawPaddingLiterals, rawRadiusLiterals and rawLineHeightLiterals
for #262 part 3. Gap was the one remaining spacing surface a hand-picked
value could hide in: gap-[9px] and `gap: 18px` were counted by no ratchet at
all. This adds rawGapLiterals on that commit's own predicate and wiring.
Measured against origin/main ef9bb51: 34 sites across 9 files — 21 Tailwind
utilities, every one under src/components/therapy-compass/, plus 13 CSS
declarations in globals.css that a utility-only scan misses. Covering both
spellings is the same reason #1780 counts both: otherwise a literal escapes by
moving from a class into globals.css.
Kept as its own metric rather than folded into rawPaddingLiterals so the
therapy-compass cleanup can be paid down and re-pinned independently of the
padding debt, which is spread across fifteen unrelated files.
Reuses RAW_LITERAL_VALUE unchanged, so a value containing a CSS function
(env(, clamp(, max(, calc() stays a sanctioned computed form and is exempt.
Also corrects the §3 prohibition row, which #1780 left reading
"implemented-partial (colour/shadow/tap literals only)" and which named none
of the metrics it had just shipped. A row that understates shipped work is
what sends the next session to rebuild it — this change was itself started as
a duplicate of #262 part 3 for exactly that reason.
Mutation-verified in both halves, each naming the metric and the path:
a gap-[3px] utility gives "rawGapLiterals increased from 34 to 35" plus
"at src/components/ui/missing-value.tsx increased from 0 to 1"; a
`gap: 19px` declaration gives the same total plus "at src/app/globals.css
increased from 13 to 14".
Baseline diff is additive only. legacyShadowAliases measures 218 against its
pinned 220 on main; that slack is left exactly as found.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* docs(issues): capture the ErrorState and duplicate-work follow-ups
Four rows (#298-#301), added with npm run issues:add. Ids 295-297 were
claimed by main while this branch was open, which is ledger #156's
read-modify-write race behaving exactly as recorded.
- #298 (P2 task) the ErrorState enforcement check. GATES.md still reads
"planned" for the 0-matches prohibition and nothing in scripts/ or
eslint-rules/ references ErrorState, so the component exists but is not
required. Deliberately not flipped to implemented.
- #299 (P3 task) adopting ErrorState at the three surfaces that genuinely
hand-roll the guard. Live-look change, downstream of the redesign.
- #300 (P2 issue) three sites miscarried into M4 as guards that are not,
so the next reader does not convert them. differentials-home renders its
zero after a search that SUCCEEDED.
- #301 (P3 issue) two sessions built #262 part 3 in parallel because the
§3 row understated what had shipped. Proposes asserting that every
baseline metric key appears in GATES.md.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(design-system): restrict the ErrorState copy tripwire to development
Codex review on #1786 (P2). The doc comment described the tripwire as
development-only, but the emitter only silenced NODE_ENV === "test", so a
production caller supplying count-bearing title/body copy had the full
caller-provided string written to console.warn. On a clinical surface that
copy can quote the query — "0 results for <query>" — which turns a copy
defect into a disclosure risk. Nobody reads a production browser console
for design-system warnings, so the emit is now development-only and an
unset NODE_ENV is treated as production: fail quiet.
The gate is an exported predicate rather than an inline comparison because
an inline comparison is untestable here. Vite statically replaces
process.env.NODE_ENV inside src/ modules, so under Vitest the check
compiles to `"test" === "development"` and no stubEnv can move it. The
review asked for a production console-spy check; written that way it would
have passed while proving nothing, staying silent for the wrong reason and
continuing to pass even if the guard were deleted. shouldEmitErrorStateDiagnostic
is asserted directly instead — development true; production, test and unset
false — with the console spy kept alongside as the weaker check that catches
an emitter which warns unconditionally.
Verified: typecheck 0 errors, lint 0, format:check 0,
check:design-system-contract 0, Tests 70 passed (70) across
error-state.dom and design-sync-visual-exports.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
BigSimmo pushed a commit that referenced this pull request Aug 12, 2026
Archived #298: GATES.md:126 now records the '0 matches after a failed request'
rule as implemented-blocking (11 Aug 2026) via errorStateCountProps and
failedStateResultCounts at baseline 0, so the row's 'nothing enforces it'
premise no longer holds. Adoption stays open under #299.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017paT42ZVMf8jaLtkjFxdy5
Sign up for freeto 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.

2 participants

@BigSimmo@cursoragent