feat(mobile): 44px phone header, making the home button a true 44x44 target

The brand "C" got a 44px-wide hit box in the previous commit but was capped at
36px tall by the bar it sits in. The phone header is now 44px, so the one
control that gets you back to the home screen is square at the platform
minimum, and every other header control gains the same 8px.

Redefined as --header-height inside the phone media query rather than as a
literal, so the panels positioned off that token (file browser, project
insights, plan overlays) follow the bar instead of drifting 8px underneath it;
.app's top offset is derived from it for the same reason. The header also stops
top-aligning its children on phones: that read as centred in a 36px bar whose
contents were ~31px, and leaves a visible gap under everything at 44px.

Costs 8px of terminal height on a phone.

Verified on a real isolated instance at 390px: header 44px, button 44x44
spanning the bar, a touch tap at (4,41) - inside the new area, outside the old
one - reaches the home screen, tabs centred, and content still clears the fixed
header. Tablet (48px) and desktop are untouched.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Codeman maintainer
2026-08-09 18:34:35 +02:00
parent 4add38c4b1
commit 0aafabd28d
2 changed files with 47 additions and 24 deletions
+17 -4
View File
@@ -163,15 +163,28 @@ describe('Phone home button tap target (static guard)', () => {
return decls;
}
it('gives the brand button a 44px-wide hit area on phones', () => {
it('gives the brand button a 44x44 hit area on phones', () => {
const decls = phoneLogoDecls();
expect(decls.get('min-width'), 'the "C" home button needs an explicit 44px min-width on phones').toBe('44px');
// A bare inline span ignores width entirely — the box only exists once it
// stops being inline.
expect(decls.get('display')).toBe('inline-flex');
// Full header height on the short axis: the phone header is pinned to 36px
// (min/max-height) and clips overflow, so this is as tall as the target can
// get without growing the header and taking it off the terminal.
// The other axis is the header's, so the two have to be read together: the
// button is only 44 tall because the phone header is.
expect(decls.get('height')).toBe('var(--header-height)');
});
it('keeps the phone header at 44px, the height that makes that target square', () => {
// The bar was 36px. Shrinking it again silently takes 8px back off every
// header touch target, the home button included.
let phoneHeaderHeight: string | undefined;
postcss.parse(css).walkAtRules('media', (atRule) => {
if (!appliesToPhone(atRule.params)) return;
atRule.walkRules((rule) => {
if (rule.selector.trim() !== ':root') return;
rule.walkDecls('--header-height', (decl) => (phoneHeaderHeight = decl.value.trim()));
});
});
expect(phoneHeaderHeight, '--header-height must be redefined for phones in mobile.css').toBe('44px');
});
});