From 9e1c5bc020ad370be33845e54b986b3ab7cdfaf3 Mon Sep 17 00:00:00 2001 From: Daniel Naab Date: Wed, 29 Apr 2026 02:27:55 +0000 Subject: [PATCH 01/11] docs: mobile navigation design spec Addresses viewport overflow on iPhone-width screens by introducing a popover-based hamburger menu below 36rem breakpoint. --- .../2026-04-28-mobile-navigation-design.md | 168 ++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 docs/superpowers/specs/2026-04-28-mobile-navigation-design.md diff --git a/docs/superpowers/specs/2026-04-28-mobile-navigation-design.md b/docs/superpowers/specs/2026-04-28-mobile-navigation-design.md new file mode 100644 index 0000000..1073b53 --- /dev/null +++ b/docs/superpowers/specs/2026-04-28-mobile-navigation-design.md @@ -0,0 +1,168 @@ +# Mobile Navigation Design + +## Problem + +On iPhone-sized viewports (~375px), the site header's horizontal nav (Work, Commitment, About, GitHub) plus the "Flexion Labs" brand exceed the viewport width, causing: + +1. Page width overflows the viewport on first load (requires zoom-out to fit). +2. Nav items overlap the brand text. +3. "Flexion Labs" wraps to two lines. + +Root cause: the header is a flex row with `justify-content: space-between` and no collapse behavior. No media queries handle narrow viewports. + +## Solution + +A responsive hamburger menu using the HTML `popover` API at narrow viewports (below 36rem / 576px). Zero JavaScript required. + +## Breakpoint + +- **Below 36rem:** Hamburger button visible, nav links inside a popover card. +- **Above 36rem:** Current horizontal nav bar, unchanged. + +This uses a `@media` query (not a container query) since the header width tracks the viewport, not a container. + +## HTML Structure + +```tsx + +``` + +Key structural decisions: + +- The ` + + + ) +} +``` + +- [ ] **Step 2: Run tests to verify no build breakage** + +Run: `bun test --filter smoke` +Expected: PASS — the build still produces valid HTML pages. + +- [ ] **Step 3: Commit** + +```bash +git add src/design/components/header/index.tsx +git commit -m "feat(header): add hamburger button and popover wrapper for mobile nav" +``` + +--- + +### Task 2: Add Mobile/Desktop Media Queries to Layout CSS + +**Files:** +- Modify: `src/design/layout.css` + +- [ ] **Step 1: Add responsive nav rules to layout.css** + +At the end of the `@layer layout { ... }` block (before the closing `}`), add: + +```css + /* ---- Responsive navigation ---- */ + @media (min-width: 36rem) { + .mobile-nav-toggle { + display: none; + } + #mobile-nav { + display: contents; + } + } + @media (max-width: 36rem) { + .site-header nav ul { + flex-direction: column; + gap: var(--space-3); + } + #mobile-nav { + position: absolute; + inset-block-start: 100%; + inset-inline-end: var(--space-5); + margin: 0; + padding: var(--space-4); + background: var(--color-surface); + border: 1px solid var(--color-surface-alt); + border-radius: var(--radius-md); + box-shadow: var(--shadow-card); + } + } +``` + +Note: The `position: absolute` on `#mobile-nav` positions the popover card relative to the header. The `.site-header` needs `position: relative` added so the popover anchors correctly. + +- [ ] **Step 2: Add position relative to .site-header** + +In `layout.css`, inside the existing `.site-header` rule block, add `position: relative;`: + +Change: +```css + .site-header { + inline-size: var(--measure-wide); + margin-inline: auto; + padding: var(--space-4) var(--space-5); + display: flex; + align-items: center; + justify-content: space-between; + gap: var(--space-5); + border-block-end: 1px solid var(--color-surface-alt); + } +``` + +To: +```css + .site-header { + position: relative; + inline-size: var(--measure-wide); + margin-inline: auto; + padding: var(--space-4) var(--space-5); + display: flex; + align-items: center; + justify-content: space-between; + gap: var(--space-5); + border-block-end: 1px solid var(--color-surface-alt); + } +``` + +- [ ] **Step 3: Run tests** + +Run: `bun test --filter smoke` +Expected: PASS + +- [ ] **Step 4: Commit** + +```bash +git add src/design/layout.css +git commit -m "feat(layout): add responsive nav media queries at 36rem breakpoint" +``` + +--- + +### Task 3: Style the Hamburger Button + +**Files:** +- Modify: `src/design/components/header/styles.css` + +- [ ] **Step 1: Add mobile-nav-toggle styles** + +Append to `src/design/components/header/styles.css`: + +```css +.mobile-nav-toggle { + display: inline-flex; + align-items: center; + justify-content: center; + background: none; + border: none; + padding: var(--space-2); + cursor: pointer; + color: var(--color-ink); + border-radius: var(--radius-sm); +} +.mobile-nav-toggle:focus-visible { + outline: 2px solid var(--color-focus-ring); + outline-offset: 2px; +} +``` + +Note: The `display: none` at desktop is handled by the media query in `layout.css`. These styles define appearance when visible. + +- [ ] **Step 2: Run tests** + +Run: `bun test --filter smoke` +Expected: PASS + +- [ ] **Step 3: Commit** + +```bash +git add src/design/components/header/styles.css +git commit -m "feat(header): style hamburger menu toggle button" +``` + +--- + +### Task 4: Visual Verification + +**Files:** None (testing only) + +- [ ] **Step 1: Build the site** + +Run: `bun run build` + +- [ ] **Step 2: Serve and verify in browser** + +Run: `bunx serve dist` (or equivalent static server) + +Verify at mobile width (375px in DevTools): +1. Hamburger button appears, nav links are hidden. +2. Tapping hamburger opens popover card with stacked links. +3. Pressing Escape or clicking outside closes the popover. +4. "Flexion Labs" brand stays on one line. +5. Page does not overflow the viewport horizontally. + +Verify at desktop width (>576px): +1. Hamburger button is hidden. +2. Nav links display horizontally as before. +3. No visual regression from the popover wrapper. + +- [ ] **Step 3: Run full test suite** + +Run: `bun test` +Expected: 67 pass (the a11y test may still fail due to needing a fresh build — that's pre-existing). + +- [ ] **Step 4: Commit any fixes if needed, then final commit** + +If all looks good: +```bash +git log --oneline worktree-mobile-menu ^main +``` + +Expected: 3 commits (header HTML, layout CSS, button styles). From 3b255a85b7e226cafdeb35035ed0144e1dec6197 Mon Sep 17 00:00:00 2001 From: Daniel Naab Date: Wed, 29 Apr 2026 02:59:00 +0000 Subject: [PATCH 03/11] feat(header): add hamburger button and popover wrapper for mobile nav --- src/design/components/header/index.tsx | 45 +++++++++++++++++--------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/src/design/components/header/index.tsx b/src/design/components/header/index.tsx index 944326c..dd80a5e 100644 --- a/src/design/components/header/index.tsx +++ b/src/design/components/header/index.tsx @@ -15,23 +15,36 @@ export function Header({ config }: { config: SiteConfig }) { /> Flexion Labs + ) From bd5389d796c7d26479d7097505725c040e8299a7 Mon Sep 17 00:00:00 2001 From: Daniel Naab Date: Wed, 29 Apr 2026 03:00:11 +0000 Subject: [PATCH 04/11] feat(layout): add responsive nav media queries at 36rem breakpoint --- src/design/layout.css | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/design/layout.css b/src/design/layout.css index 56c2a67..ac1f2e9 100644 --- a/src/design/layout.css +++ b/src/design/layout.css @@ -19,6 +19,7 @@ gap: var(--space-7); } .site-header { + position: relative; inline-size: var(--measure-wide); margin-inline: auto; padding: var(--space-4) var(--space-5); @@ -93,4 +94,30 @@ grid-column: 2; } } + /* ---- Responsive navigation ---- */ + @media (min-width: 36rem) { + .mobile-nav-toggle { + display: none; + } + #mobile-nav { + display: contents; + } + } + @media (max-width: 36rem) { + .site-header nav ul { + flex-direction: column; + gap: var(--space-3); + } + #mobile-nav { + position: absolute; + inset-block-start: 100%; + inset-inline-end: var(--space-5); + margin: 0; + padding: var(--space-4); + background: var(--color-surface); + border: 1px solid var(--color-surface-alt); + border-radius: var(--radius-md); + box-shadow: var(--shadow-card); + } + } } From 3e675a2a558b8d106b757e50ac91fdf90b6f3597 Mon Sep 17 00:00:00 2001 From: Daniel Naab Date: Wed, 29 Apr 2026 03:00:55 +0000 Subject: [PATCH 05/11] feat(header): style hamburger menu toggle button --- src/design/components/header/styles.css | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/design/components/header/styles.css b/src/design/components/header/styles.css index 0a8d093..c4e08c0 100644 --- a/src/design/components/header/styles.css +++ b/src/design/components/header/styles.css @@ -12,3 +12,18 @@ inline-size: auto; display: block; } +.mobile-nav-toggle { + display: inline-flex; + align-items: center; + justify-content: center; + background: none; + border: none; + padding: var(--space-2); + cursor: pointer; + color: var(--color-ink); + border-radius: var(--radius-sm); +} +.mobile-nav-toggle:focus-visible { + outline: 2px solid var(--color-focus-ring); + outline-offset: 2px; +} From 0600fe0da7aba3375cfe881b908ed1af7699aaab Mon Sep 17 00:00:00 2001 From: Daniel Naab Date: Wed, 29 Apr 2026 03:01:56 +0000 Subject: [PATCH 06/11] fix(header): use explicit popover="auto" instead of boolean attribute Hono JSX renders bare `popover` as `popover="true"` which relies on the invalid-value-default behavior. Explicit "auto" is clearer. --- src/design/components/header/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/design/components/header/index.tsx b/src/design/components/header/index.tsx index dd80a5e..d4156f2 100644 --- a/src/design/components/header/index.tsx +++ b/src/design/components/header/index.tsx @@ -27,7 +27,7 @@ export function Header({ config }: { config: SiteConfig }) {