Skip to content

Interaction helpers, sticky Milo header, and the /milo experience - #8

Merged
Vilos92 merged 24 commits into
mainfrom
milo-header
Jul 27, 2026
Merged

Vilos92 merged 24 commits into
mainfrom
milo-header

Conversation

@Vilos92

@Vilos92 Vilos92 commented Jul 26, 2026

Copy link
Copy Markdown
Owner

Milestone-by-milestone branch for the sticky header + expandable Milo work. Each milestone landed as one commit; a multi-agent self-review then ran over the whole branch and its confirmed findings were fixed in two follow-up commits.

Milestones

  • M1 — Interaction helpers (07486cb): hover() gates every hover state behind @media (hover: hover); tapExtension() grows coarse-pointer hit boxes with padding + negative margin (no layout shift). Swept all six previously-unguarded hover sites. Breadcrumbs intentionally keep minHeight (their list is an overflow-x scroll container, which would clip an extended hit area).
  • M2 — Animation perf (b4b4527): Milo skips his ~8 per-frame setAttribute writes when the pose is unchanged, so an idle Milo paints nothing.
  • M3 — Reduced motion + tap gaze (a48c23b): prefers-reduced-motion renders a static portrait (and reacts live to preference flips); pointerdown/isPrimary tap-to-look.
  • M4 — Sticky header (b5a8181, 3393ff8, 1d25b4f): slim header (wordmark left, Milo centered, Blog/Tools right) that slides in past an 8rem sentinel via IntersectionObserver and stays until you return to top; on narrow screens the wordmark collapses to "Home" so the centered Milo always fits.
  • M5 — /milo/ page (13035ab): fullscreen responsive Milo (min(92vw, 82dvh), cannot clip), safe-area-aware close button with Escape support, touch-action: none stage for drag-following.
  • M6 — The morph (c921b07): native cross-document view transitions (@view-transition + shared view-transition-name) so the header Milo scales up into /milo/; reduced-motion gated; Firefox falls back to an instant navigation.

Post-review fixes

  • acfeab8 drops the touch-event scroll-follow path (browsers deliver touchmove during scroll too inconsistently); taps and touch-action: none drags cover touch reliably.
  • d8e8de7 hardens the /milo close flow (referrer origin check requires a real / delimiter; Escape is one-shot per page view), restores the 44px coarse-pointer tap minimum (0.8rem vertical extensions), and idles a hidden header Milo on a 300ms checkVisibility recheck instead of a per-frame rAF loop.
  • b04e929 deduplicates the hover-gated globalStyle shape (hoverRule()), extracts a shared iconButton base for the studio copy button and the /milo close button, and moves the reveal sentinel inside Header.astro.

Known trade-off: the header Milo's view-transition-name is gated on data-stuck="true". On a non-BFCache back-navigation the restored page is captured before the IntersectionObserver runs, so the return morph degrades to a plain crossfade. The alternative (ungated) would morph the giant Milo into an invisible hidden-header corner, which is worse. BFCache restores — the common case — morph correctly.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Introduced a sticky Milo site header with Blog and Tools navigation.
    • Added a dedicated full-screen Milo page with improved close/back handling.
    • Enabled smoother view transitions for same-origin navigation.
  • Accessibility
    • Honors reduced-motion settings by disabling view-transition animations and Milo motion.
    • Improved pointer/touch targets for interactive elements.
  • Style Improvements
    • Standardized hover, focus-visible, dark-mode, and high-contrast link/button interactions across the UI.
  • Other
    • Improved Milo animation stability and pointer interaction responsiveness.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 26, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 43931440-5c3c-4098-a42c-ccd9367de23b

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The PR adds a sticky site header, a standalone Milo page, view-transition and reduced-motion support, improved Milo animation cleanup and pointer handling, and shared hover and touch-target styling helpers.

Changes

Site experience updates

Layer / File(s) Summary
Shared interaction styling
src/styles/interaction.ts, src/styles/icon-button.css.ts, src/styles/breakpoints.ts, src/styles/{global,breadcrumbs,footer}.css.ts, src/pages/tools/_3d-to-svg/studio.css.ts
Adds shared hover and coarse-pointer touch-target helpers, an icon button style, and applies them to global links, breadcrumbs, footer links, and Studio buttons.
Site header and navigation
src/components/{BaseHead,BaseLayout,Header}.astro, src/styles/header.css.ts
Adds the header, responsive wordmark and navigation, sentinel-based sticky behavior, named Milo transition styling, and global reduced-motion view-transition rules.
Standalone Milo page
src/pages/milo.astro, src/styles/milo-page.css.ts
Adds a full-viewport Milo page with constrained stage styling, safe-area close control, and guarded back-or-root navigation.
Milo animation lifecycle and input
src/components/milo/animate.ts
Adds reduced-motion handling, explicit cleanup, delayed hidden-state checks, pose-write caching, primary-pointer handling, pointerdown support, and tracker disposal.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Visitor
  participant Header
  participant MiloPage
  participant MiloAnimation
  Visitor->>Header: Select Milo navigation
  Header->>MiloPage: Navigate to /milo/
  MiloPage->>MiloAnimation: Render and start Milo
  MiloAnimation->>MiloAnimation: Track visibility and pointer input
  Visitor->>MiloPage: Close or press Escape
  MiloPage->>Header: Navigate back or assign /
Loading

Possibly related PRs

Poem

A bunny hops where Milo gleams,
Through sticky bars and animated dreams.
Hover gently, tap targets grow,
Reduced motion makes the soft winds slow.
Back through the doorway, home we go!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main changes: interaction helpers, a sticky Milo header, and the new /milo experience.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch milo-header

Comment @coderabbitai help to get the list of available commands.

Vilos92 and others added 10 commits July 26, 2026 16:23
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The /milo referrer check now requires a real same-origin path so lookalike
hosts cannot pass, Escape is one-shot per page view, tap extensions grow to
0.8rem vertical to clear the 44px minimum the old minHeight guaranteed, and
a hidden header Milo idles on a slow recheck instead of a per-frame rAF.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
hoverRule() now carries the hover-gated globalStyle shape used at four
sites, both square icon buttons compose one shared base, and the header's
reveal sentinel rides inside Header.astro instead of leaning on BaseLayout.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Vilos92

Vilos92 commented Jul 27, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai full review

Context for the review: this branch adds, in milestone commits — (1) mobile-safe CSS interaction helpers (hover()/hoverRule() gate all hover states behind @media (hover: hover); tapExtension() grows coarse-pointer hit boxes via padding + negative margin), (2) performance and input work in the Milo mascot animation (src/components/milo/animate.ts: pose dirty-check so idle frames write no DOM, prefers-reduced-motion gating with live preference flips, pointerdown/isPrimary tap-to-look, checkVisibility-based idling when hidden), (3) a sticky header (Header.astro + sentinel + IntersectionObserver, revealed past 8rem, Milo absolutely centered with a wordmark that collapses to "Home" under 480px), (4) a fullscreen /milo/ page with a touch-action: none stage and a safe-area-aware close button, and (5) cross-document view transitions morphing the header Milo into the /milo/ Milo.

Known, deliberate trade-offs (please don't re-flag): the header Milo's view-transition-name is gated on data-stuck="true", so non-BFCache back-navigations degrade to a plain crossfade — the ungated alternative morphs into an invisible hidden header, which is worse; breadcrumbs keep the minHeight tap-target approach because their scroll container would clip negative-margin hit areas; the site-wide @view-transition { navigation: auto; } crossfade on all internal navigations is intentional.

A multi-agent self-review already ran and its confirmed findings were fixed in d8e8de7/b04e929 (referrer-origin delimiter on the /milo close flow, one-shot Escape, 44px tap-target restoration, hidden-Milo rAF idling, hover/icon-button dedup). Most valuable areas for fresh eyes: the PID-driven animation loop's edge cases, the IntersectionObserver/visibility interplay in the header reveal, vanilla-extract composition/cascade order (iconButton base vs consumer sizing), and anything accessibility-related on the /milo page.

@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown

@Vilos92 I’ll perform a fresh full review of #8, focusing on the animation loop, header visibility/observer lifecycle, style composition, and /milo/ accessibility while respecting the documented trade-offs.

✅ Action performed

Full review finished.

@Vilos92
Vilos92 marked this pull request as ready for review July 27, 2026 00:08

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/components/milo/animate.ts (1)

140-149: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Document side-effecting functions with @sideEffect.

startMilos and the new lifecycle/input closures mutate DOM state, schedule work, or manage listeners. Add @sideEffect to their JSDoc/comments.

As per coding guidelines, use @sideEffect on non-pure functions (including exports and closures) while keeping pure functions untagged.

Also applies to: 195-246, 302-356

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/milo/animate.ts` around lines 140 - 149, Add `@sideEffect`
documentation to the startMilos function and the side-effecting lifecycle/input
closures in the referenced sections, including DOM mutation, scheduling, and
listener management functions. Keep pure helper functions untagged and preserve
the existing behavior.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/styles/interaction.ts`:
- Line 9: Export the SelectorRule type, or replace it with an existing public
vanilla-extract type, so the exported hover API no longer exposes a private
type. Update the related definitions and hover usage in this section
consistently while preserving the existing selector typing.

---

Nitpick comments:
In `@src/components/milo/animate.ts`:
- Around line 140-149: Add `@sideEffect` documentation to the startMilos function
and the side-effecting lifecycle/input closures in the referenced sections,
including DOM mutation, scheduling, and listener management functions. Keep pure
helper functions untagged and preserve the existing behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 13ef142c-48e0-4a9d-91e5-532eb9622d70

📥 Commits

Reviewing files that changed from the base of the PR and between 6d96510 and b04e929.

📒 Files selected for processing (14)
  • src/components/BaseHead.astro
  • src/components/BaseLayout.astro
  • src/components/Header.astro
  • src/components/milo/animate.ts
  • src/pages/milo.astro
  • src/pages/tools/_3d-to-svg/studio.css.ts
  • src/styles/breadcrumbs.css.ts
  • src/styles/breakpoints.ts
  • src/styles/footer.css.ts
  • src/styles/global.css.ts
  • src/styles/header.css.ts
  • src/styles/icon-button.css.ts
  • src/styles/interaction.ts
  • src/styles/milo-page.css.ts

Comment thread src/styles/interaction.ts Outdated
Vilos92 and others added 3 commits July 26, 2026 18:01
Mobile browsers leave the visual viewport offset below the layout viewport
while their toolbar collapses mid-scroll, which slid the fixed header's top
out of sight until the toolbar returned. Reproduced with touch-gesture
probes (visualViewport.offsetTop held at 16px during downward flings);
tracking that offset in `top` keeps the whole bar on screen.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Exports the SelectorRule type the hover helper references, splits the
frame loop into plan/advance/rest helpers so every function clears the
complexity threshold, tags startMilos per the @sideeffect convention,
and sweeps new comments free of em dashes and prose semicolons.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Vilos92

Vilos92 commented Jul 27, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai full review

Context for this second pass — everything from your first review is resolved, and five commits landed after it:

  • 99d98f8 pins the sticky header's top to visualViewport.offsetTop, fixing a real bug where mobile browsers' collapsing toolbars left the fixed header's top slice off screen during downward touch scrolls (reproduced and verified with touch-gesture probes measuring the visual viewport offset).
  • 7df8a42 scopes the cross-document crossfade to the Milo morph: an inline pageswap/pagereveal gate in BaseHead.astro skips the transition on any navigation where neither side is /milo/. The outgoing page is the primary gate; the incoming page checks via the Navigation API where available.
  • eca7fde resolves your critical finding (exported SelectorRule, fallow green), applies your @sideEffect nitpick to the named mutators, and decomposes the frame loop (planFrame/advance/rest/applyPoseIfChanged in animate.ts) to clear fallow's complexity thresholds.
  • Earlier, d8e8de7/b04e929 hardened the /milo close flow, restored 44px tap targets, and deduplicated hover/icon-button recipes.

Same deliberate trade-offs as before (please don't re-flag): the data-stuck-gated view-transition-name, breadcrumbs keeping minHeight tap targets, and @sideEffect applying to named argument-mutating functions but not closures, matching the existing codebase precedent.

Most valuable focus for this pass: the restructured frame loop in animate.ts (any behavior drift from the decomposition), the pageswap/pagereveal gate's edge cases (back/forward navigations, browsers without the Navigation API), and the interplay between the visual-viewport pinning and the header's reveal transition.

@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '=== Changed files and focused commit diffs ==='
git diff --stat HEAD~5..HEAD
for commit in 99d98f8 7df8a42 eca7fde; do
  printf '\n=== %s ===\n' "$commit"
  git show --format=fuller --find-renames --find-copies --stat "$commit"
  git show --format= --find-renames --find-copies -- \
    src/components/milo/animate.ts \
    src/components/BaseHead.astro \
    src/components/Header.astro \
    src/styles/header.css.ts \
    "$commit" 2>/dev/null || true
done

printf '%s\n' '=== Focused file outlines ==='
for file in \
  src/components/milo/animate.ts \
  src/components/BaseHead.astro \
  src/components/Header.astro \
  src/styles/header.css.ts \
  src/pages/milo.astro \
  src/styles/milo-page.css.ts; do
  if [ -f "$file" ]; then
    printf '\n--- %s ---\n' "$file"
    ast-grep outline "$file" --items all || true
  fi
done

printf '%s\n' '=== Focused source ==='
for file in \
  src/components/milo/animate.ts \
  src/components/BaseHead.astro \
  src/components/Header.astro \
  src/styles/header.css.ts \
  src/pages/milo.astro \
  src/styles/milo-page.css.ts; do
  if [ -f "$file" ]; then
    printf '\n--- %s ---\n' "$file"
    cat -n "$file"
  fi
done

printf '%s\n' '=== Related lifecycle and test references ==='
rg -n -C 3 --glob '!node_modules/**' \
  'startMilos\s*\(|data-milo-started|visualViewport|pageswap|pagereveal|view-transition|IntersectionObserver|visibilitychange|Navigation API|navigation\.activation' \
  src test tests 2>/dev/null || true

printf '%s\n' '=== Project scripts/configuration ==='
for file in package.json astro.config.mjs astro.config.ts vite.config.ts; do
  [ -f "$file" ] && { printf '\n--- %s ---\n' "$file"; cat -n "$file"; }
done

Length of output: 96709


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '=== Correct focused diffs ==='
for commit in 99d98f8 7df8a42 eca7fde; do
  printf '\n=== %s ===\n' "$commit"
  git show --format= --find-renames --find-copies "$commit" -- \
    src/components/milo/animate.ts \
    src/components/BaseHead.astro \
    src/components/Header.astro \
    src/styles/header.css.ts \
    src/components/milo/Milo.astro \
    src/components/BaseLayout.astro \
    src/pages/milo.astro
done

printf '%s\n' '=== Relevant commit ancestry ==='
git log --oneline --decorate -12
git merge-base HEAD origin/main 2>/dev/null || true

printf '%s\n' '=== Header and component call sites ==='
for file in src/components/BaseLayout.astro src/components/milo/Milo.astro; do
  if [ -f "$file" ]; then
    printf '\n--- %s ---\n' "$file"
    cat -n "$file"
  fi
done

printf '%s\n' '=== Tests and test outlines ==='
fd -a -e ts -e tsx . src | rg '\.test\.(ts|tsx)$' || true
for file in $(fd -a -e ts -e tsx . src | rg '\.test\.(ts|tsx)$' || true); do
  printf '\n--- %s ---\n' "$file"
  ast-grep outline "$file" --items all || true
  cat -n "$file"
done

printf '%s\n' '=== Exact transition and viewport references ==='
rg -n -C 8 --glob '!node_modules/**' \
  '(pageswap|pagereveal|skipTransition|visualViewport|offsetTop|data-site-header|data-header-sentinel)' \
  src

Length of output: 36811


🤖 web_search executed:

MDN PageSwapEvent activation PageRevealEvent viewTransition skipTransition NavigationActivation entry from URL cross-document view transitions

Length of output: 14590


@Vilos92 Full second-pass review complete. I found one actionable issue:

  • Medium — initialize the visual-viewport alignment immediately. In src/components/Header.astro, Lines 59-66 register resize and scroll handlers but never invoke align() initially. If visualViewport.offsetTop is already nonzero when the document is initialized or restored from BFCache, the header retains its CSS top: 0 until a later visual-viewport event occurs—reintroducing the layout-viewport misalignment this change is intended to solve. Call align() once after its declaration, before registering or immediately after registering the listeners.

I also checked the restructured Milo frame loop and the cross-document transition gate. The rest/sleep/step paths preserve the intended cleanup, hidden-idling, timing reset, and frame scheduling behavior. The outgoing pageswap and incoming pagereveal checks use the appropriate activation sources, including the intended Navigation API fallback behavior. No additional findings in those areas.

✅ Action performed

Full review finished.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Vilos92

Vilos92 commented Jul 27, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai The startup-alignment finding is fixed in 0c9017ealign() now runs once right after the listeners register, covering bfcache restores that arrive with a nonzero visual-viewport offset. That was the last open item from both review passes.

@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

@Vilos92 Confirmed—calling align() during initialization closes the BFCache/startup gap identified in the review. Thanks for addressing the final open item.

🐇 ✅

Vilos92 and others added 3 commits July 26, 2026 18:24
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/components/Header.astro (1)

58-66: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider a CSS variable instead of the inline top write.

header.style.top bypasses the normal cascade for a value that's otherwise fully defined in header.css.ts (top: 0). A createVar() + fallbackVar() default in header.css.ts, updated here via setElementVar(header, offsetVar, ...), would keep the static rule in the stylesheet and let only the runtime value live in the script, per this repo's convention for runtime-varying styles.

As per coding guidelines: "Use createVar plus setElementVar for runtime-varying values; keep the static rule in .css.ts and move only the runtime value" and "Treat imperative element.style as a last resort for styling, and use it only when data-* variants or CSS variables do not fit."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/Header.astro` around lines 58 - 66, Replace the direct
header.style.top assignment in the visualViewport alignment handler with the
repository’s CSS-variable pattern: define an offset variable and fallbackVar
default in header.css.ts while retaining the static top rule, then use
setElementVar(header, offsetVar, ...) in Header.astro for runtime viewport
offsets.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/Header.astro`:
- Around line 58-66: Update the visualViewport setup in Header.astro to invoke
the existing align function immediately after defining it and before registering
the resize and scroll listeners, so the header reflects the current offsetTop
even when no viewport event fires initially.

---

Nitpick comments:
In `@src/components/Header.astro`:
- Around line 58-66: Replace the direct header.style.top assignment in the
visualViewport alignment handler with the repository’s CSS-variable pattern:
define an offset variable and fallbackVar default in header.css.ts while
retaining the static top rule, then use setElementVar(header, offsetVar, ...) in
Header.astro for runtime viewport offsets.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: ea34f016-f79c-4248-a18f-19f9217af3cf

📥 Commits

Reviewing files that changed from the base of the PR and between 6d96510 and eca7fde.

📒 Files selected for processing (14)
  • src/components/BaseHead.astro
  • src/components/BaseLayout.astro
  • src/components/Header.astro
  • src/components/milo/animate.ts
  • src/pages/milo.astro
  • src/pages/tools/_3d-to-svg/studio.css.ts
  • src/styles/breadcrumbs.css.ts
  • src/styles/breakpoints.ts
  • src/styles/footer.css.ts
  • src/styles/global.css.ts
  • src/styles/header.css.ts
  • src/styles/icon-button.css.ts
  • src/styles/interaction.ts
  • src/styles/milo-page.css.ts

Comment on lines +58 to +66
const {visualViewport} = window;
if (visualViewport) {
const align = () => {
header.style.top = `${visualViewport.offsetTop}px`;
};

visualViewport.addEventListener('resize', align, {passive: true});
visualViewport.addEventListener('scroll', align, {passive: true});
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Call align() once before relying on the resize/scroll events.

align() is only invoked reactively. If visualViewport.offsetTop is already non-zero when this script runs (e.g. BFCache restoration mid-gesture, or the toolbar already partially collapsed on load), the header stays at its CSS default top: 0 until the next visual-viewport event fires.

🛠️ Proposed fix
     const {visualViewport} = window;
     if (visualViewport) {
       const align = () => {
         header.style.top = `${visualViewport.offsetTop}px`;
       };
 
+      align();
       visualViewport.addEventListener('resize', align, {passive: true});
       visualViewport.addEventListener('scroll', align, {passive: true});
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const {visualViewport} = window;
if (visualViewport) {
const align = () => {
header.style.top = `${visualViewport.offsetTop}px`;
};
visualViewport.addEventListener('resize', align, {passive: true});
visualViewport.addEventListener('scroll', align, {passive: true});
}
const {visualViewport} = window;
if (visualViewport) {
const align = () => {
header.style.top = `${visualViewport.offsetTop}px`;
};
align();
visualViewport.addEventListener('resize', align, {passive: true});
visualViewport.addEventListener('scroll', align, {passive: true});
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/Header.astro` around lines 58 - 66, Update the visualViewport
setup in Header.astro to invoke the existing align function immediately after
defining it and before registering the resize and scroll listeners, so the
header reflects the current offsetTop even when no viewport event fires
initially.

Vilos92 and others added 2 commits July 26, 2026 18:36
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Vilos92 and others added 4 commits July 26, 2026 18:44
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Vilos92
Vilos92 merged commit 31aafdb into main Jul 27, 2026
6 checks passed
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