feat: pre-render site to static HTML at build time (SSG)#42
Closed
feat: pre-render site to static HTML at build time (SSG)#42
Conversation
Subject Line: What Happened:
Sync quip background colors across all three prism faces (top, bottom, front underline) using a parallel quipBgs array and faceBg state that updates on rotate-back. Fix hero typewriter animation on mobile by reserving full name width upfront and positioning cursor absolutely to prevent line-break jank. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Break up the 525-line Header into quip data, magnetic tilt hook, prism flip hook, and a thin orchestration shell. Use semantic <header> and <nav> elements. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Neither hook is imported anywhere — deleting to reduce clutter. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Expose getMousePosition() instead of live-bound let exports to prevent silent breakage from destructured imports. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Caveat was loaded but never referenced — removes an unnecessary network request on every page load. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Vite serves static/ as root, so the runtime path should be /fonts/SimpleCakes.ttf, not /static/fonts/SimpleCakes.ttf. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Install and configure ESLint 9 (flat config) with typescript-eslint, react-hooks, react-refresh, and eslint-config-prettier. Add Prettier with project-matching style. Install Storybook 10 with react-vite framework and addon suite. Add typecheck, lint, format, and storybook scripts. Remove package-lock.json to standardize on pnpm (resolves #27). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move ref assignment in useActiveSection into useEffect to satisfy react-hooks/refs rule. Remove unnecessary regex escape in xray.ts. Apply Prettier formatting to Hero.tsx, confetti.ts, and main.tsx. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace npm ci with pnpm install --frozen-lockfile using pnpm/action-setup@v4. Add format:check, lint, and typecheck steps before the build step. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Configure Storybook with @tailwindcss/vite plugin via viteFinal so Tailwind classes render in stories. Add dark mode toolbar toggle via decorator that toggles .dark class on <html>. Create Header stories: Default, WithScrollSections (mock scroll targets), and DarkMode. Include vitest addon integration for story-based testing. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The component was a 9-line wrapper around a single <span>. Inline the element directly and delete CursiveRob.tsx. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace 9 standalone gradient classes and keyframes (~207 lines) with a shared base rule and CSS custom properties (~68 lines). Each variant now only sets --prism-stops, --prism-size, --prism-speed, and --prism-timing. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Guard useState initializers in useTheme and useTypewriter to return safe defaults when window is undefined (SSR). Add suppressHydrationWarning to Hero's typewriter span since server renders full text while client starts empty for animation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add entry-server.tsx that exports a render() function using renderToString for static HTML generation. Switch main.tsx from createRoot to hydrateRoot so the client hydrates pre-rendered markup. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add scripts/prerender.js that uses Vite's ssrLoadModule to render the app to static HTML and inject it into the build output. Update build script to run prerender after vite build. Add scripts/ to eslint ignores. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add dark class to <html> as default and inject an inline script that reads localStorage/matchMedia before first paint to set the correct theme class, preventing a flash of unstyled content. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
✅ Deploy Preview for symphonious-blancmange-7aafce ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
d4b27c5 to
a897e35
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
renderToString, so crawlers see real content instead of an empty<div id="root"></div>createRoottohydrateRootso the browser hydrates the pre-rendered markup into an interactive SPAChanges
New files
src/entry-server.tsx— SSR render function exportingrender()viarenderToStringscripts/prerender.js— post-build script using Vite'sssrLoadModuleto render the app and inject HTML into the build outputModified files
src/main.tsx—createRoot().render()→hydrateRoot()src/hooks/useTheme.ts— guarduseStateinitializer for SSR (typeof window === 'undefined'→"dark")src/hooks/useTypewriter.ts— render full text on server for SEO, empty string on client for animationsrc/components/Hero.tsx—suppressHydrationWarningon typewriter span (SSR has full text, client starts empty)index.html— adddarkclass default + inline FOUC-prevention scriptpackage.json— build script adds&& node scripts/prerender.jseslint.config.js— addscripts/to ignores (Node.js build tooling)Test plan
pnpm run build— tsc + vite build + prerender all passpnpm run typecheck— passespnpm vitest run— 3 tests passpnpm run lint— 0 errorspnpm run format:check— all files formattedpublic/index.html—<div id="root">contains rendered HTML ("Robert Blakey", nav, sections)pnpm run preview— visual check that hydration works, typewriter animates, theme toggle worksCloses #39
🤖 Generated with Claude Code
Summary by cubic
Pre-render the site to static HTML at build so crawlers see real content, then hydrate on the client for an interactive SPA. Also prevents dark-to-light FOUC on first paint with a before-paint theme script.
New Features
Bug Fixes
Written for commit a897e35. Summary will update on new commits.