mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-02 13:39:41 +02:00
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:
+30
-20
@@ -350,30 +350,34 @@ html.mobile-init .file-browser-panel {
|
||||
Phone Breakpoint (<430px)
|
||||
============================================================================ */
|
||||
@media (max-width: 430px) {
|
||||
/* Phones get a 44px header, up from 36px. Every header control is a touch
|
||||
target and 44px is the floor for one; the brand "C" that gets you home is
|
||||
the one that matters most. Redefined as the TOKEN rather than a literal so
|
||||
the panels positioned off `var(--header-height)` (file browser, insights,
|
||||
plan overlays in styles.css) follow it instead of drifting 8px under the
|
||||
header. Costs 8px of terminal height on a phone. */
|
||||
:root {
|
||||
--header-height: 44px;
|
||||
}
|
||||
|
||||
/* Phone brand collapses to a single "C" home button: hide the wordmark,
|
||||
keep the tap target */
|
||||
.header-brand {
|
||||
padding-right: 0;
|
||||
margin-right: 0.1rem;
|
||||
border-right: none;
|
||||
/* styles.css sizes this to the FULL header height, so inside the header's
|
||||
0.15rem vertical padding it overflows and the last 2.4px are clipped
|
||||
(`overflow: hidden` on the phone header). Harmless while the brand was
|
||||
bare text; it would cut the bottom off the tap target's pressed state.
|
||||
Cancelling the padding for this one item is what makes the button fill
|
||||
the header edge to edge. `height: 100%` cannot do it: the header sets
|
||||
min/max-height rather than height, so the percentage has no definite
|
||||
containing block to resolve against and silently falls back to auto. */
|
||||
margin-block: -0.15rem;
|
||||
/* styles.css sizes this to the FULL header height, which is taller than the
|
||||
header's padding box; centred by the header's `align-items: center` above,
|
||||
that overflow is symmetric and the button lands flush with both edges.
|
||||
Do not "fix" it with `height: 100%`: the header sets min/max-height and no
|
||||
height, so the percentage has no definite containing block to resolve
|
||||
against and silently falls back to auto. */
|
||||
}
|
||||
|
||||
/* The "C" was a 0.85rem inline span — about a 12x13px hit area, far under the
|
||||
44px minimum, on the one control that gets you back to the home screen.
|
||||
It is now a real 44px-wide button spanning the full header height, with the
|
||||
glyph scaled to match. 44 is reachable on the horizontal axis only: the
|
||||
phone header is pinned to 36px tall (min-height/max-height below) and
|
||||
`overflow: hidden` clips anything taller, so a true 44x44 would mean growing
|
||||
the header and taking that height off the terminal. The negative margin
|
||||
It is now a real 44x44 button: 44px wide, and the full height of the phone
|
||||
header, which is itself 44px for exactly this reason. The negative margin
|
||||
spends the header's OWN left padding on the target instead of pushing the
|
||||
tab strip right. */
|
||||
.header-brand .logo {
|
||||
@@ -437,8 +441,12 @@ html.mobile-init .file-browser-panel {
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
min-height: 36px;
|
||||
max-height: 36px;
|
||||
min-height: var(--header-height);
|
||||
max-height: var(--header-height);
|
||||
/* styles.css top-aligns header children. That read as centred while the bar
|
||||
was 36px and its contents ~31px; in a 44px bar it leaves a visible gap
|
||||
under everything. */
|
||||
align-items: center;
|
||||
padding: 0.15rem 0.3rem;
|
||||
padding-left: calc(0.3rem + var(--safe-area-left));
|
||||
padding-right: calc(0.3rem + var(--safe-area-right));
|
||||
@@ -453,8 +461,8 @@ html.mobile-init .file-browser-panel {
|
||||
/* iOS safe area adjustment for fixed header - header extends into notch area */
|
||||
.ios-device .header {
|
||||
padding-top: calc(0.15rem + var(--safe-area-top));
|
||||
min-height: calc(36px + var(--safe-area-top));
|
||||
max-height: calc(36px + var(--safe-area-top));
|
||||
min-height: calc(var(--header-height) + var(--safe-area-top));
|
||||
max-height: calc(var(--header-height) + var(--safe-area-top));
|
||||
}
|
||||
|
||||
/* Push ALL content below fixed header (not just .main) so banners
|
||||
@@ -464,11 +472,13 @@ html.mobile-init .file-browser-panel {
|
||||
when keyboard is visible, and resetLayout() clears the inline
|
||||
style to re-expose this CSS value. */
|
||||
.app {
|
||||
padding-top: 42px;
|
||||
/* Header height plus its 1px border and a little slack. Derived from the
|
||||
token so the offset cannot fall out of step with the bar it clears. */
|
||||
padding-top: calc(var(--header-height) + 6px);
|
||||
}
|
||||
|
||||
.ios-device .app {
|
||||
padding-top: calc(42px + var(--safe-area-top));
|
||||
padding-top: calc(var(--header-height) + 6px + var(--safe-area-top));
|
||||
}
|
||||
|
||||
.main {
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user