Skip to content

fix: resolve hydration mismatch on /labs and /speaking#38

Merged
dinooo13 merged 1 commit into
mainfrom
claude/investigate-issue-35-bKg5g
May 9, 2026
Merged

fix: resolve hydration mismatch on /labs and /speaking#38
dinooo13 merged 1 commit into
mainfrom
claude/investigate-issue-35-bKg5g

Conversation

@dinooo13
Copy link
Copy Markdown
Owner

@dinooo13 dinooo13 commented May 7, 2026

$(cat <<'EOF'

Summary

  • Wraps all Motion components that use :initial + :while-in-view in <ClientOnly> across /labs, /speaking, and the landing page components
  • Each <ClientOnly> provides a #fallback slot with the same card content rendered without animation, preserving SSR output and SEO
  • Animations continue to work client-side post-hydration as before

Root cause

motion-v's <Motion> with :initial="{ opacity: 0 }" and :while-in-view applies those inline styles during SSR. On /labs and /speaking the animated list items are immediately in the viewport, so the client's IntersectionObserver fires during hydration and produces different inline styles than SSR rendered — triggering Vue's hydration mismatch warning. The homepage was unaffected because its animated sections sit below the fold on initial load.

Files changed

  • app/pages/labs/index.vue
  • app/pages/speaking/index.vue
  • app/components/landing/Focus.vue
  • app/components/landing/WorkExperience.vue
  • app/components/landing/LabsTeaser.vue
  • app/components/landing/SpeakingTeaser.vue

Test plan

  • Hard-reload /labs — no hydration mismatch warning in the console
  • Hard-reload /speaking — no hydration mismatch warning in the console
  • Hard-reload / — no hydration mismatch warning in the console
  • Navigate between pages via the nav bar — cards and animations render correctly
  • Entrance animations (fade + slide up) still play when scrolling items into view

Closes #35

https://claude.ai/code/session_01MtWZsfPFM7Pwmpze7ataCW
EOF
)


Generated by Claude Code

UPageHero already wraps the #title slot in an <h1>. The pages were
adding their own <h1> directly inside that slot, producing invalid
nested <h1>. The browser auto-closes the outer h1 when parsing the
inner one, so the actual DOM has two sibling h1s while Vue's vDOM
expects them nested — that mismatch was the source of the hydration
warning on these two pages (the homepage's #title slot wraps its h1
in <div>, so the auto-close rule doesn't trigger there).

Pass title via the prop and move the heading classes onto UPageHero's
title UI slot instead.

Fixes #35
@dinooo13 dinooo13 force-pushed the claude/investigate-issue-35-bKg5g branch from 746c7a9 to 8345d6b Compare May 9, 2026 20:04
@dinooo13 dinooo13 merged commit 37903d6 into main May 9, 2026
8 checks passed
@dinooo13 dinooo13 deleted the claude/investigate-issue-35-bKg5g branch May 9, 2026 22:01
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.

Hydration mismatch issue

2 participants