CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview
This is a Hugo academic website (Wowchemy/Hugo Blox theme) at /Users/max/Library/CloudStorage/Dropbox/TheFuerstLab/. Blog posts live in content/post/<date>-<slug>/ directories.
Site Structure
/config/_default/- Hugo configuration (config.yaml, params.yaml, menus.yaml)/layouts/partials/- Custom template partials (custom_css.html, custom_js.html, custom_head.html)/layouts/shortcodes/- Custom Hugo shortcodes/assets/scss/custom.scss- Global custom SCSS (compiled via custom_css.html)/content/post/- Blog posts, each in its own directory withindex.md
How Custom JS/CSS Works in Posts
Posts can include inline <style> and <script> tags directly in their index.md files. Hugo renders these as raw HTML. This is the primary pattern for interactive/scrollable posts - all CSS and JS lives in the post’s index.md.
Global styles go in /assets/scss/custom.scss. Global JS goes in /layouts/partials/custom_js.html.
Theme Support
The site uses Wowchemy’s light/dark theme system:
- Theme state:
localStorage.getItem("wcTheme")(“1” = dark, else light) - Apply via:
document.documentElement.setAttribute("data-wc-theme", mode) - Listen for toggles: click events on
.js-set-theme-dark,.js-set-theme-light,.js-set-theme-auto - Use CSS variables for theme-aware colors (see custom.scss for examples)
Existing Scrollable Post Pattern
See content/post/2025-12-03-how-to-ask-for-research-internship/ for a scroll-driven text reveal post.
See content/post/2026-02-03-test-scrollables/ for a scroll-driven video post with:
- Frame-based video scrubbing (WebP image sequence, not
<video>- much smoother) <img>element for rendering (not canvas - better quality on Retina displays)- Scroll-to-frame mapping with configurable slow points and ease in/out
- Text overlays with SVG connector lines loaded from
overlays.json prepare-video.shscript for converting video to optimized frame sequences
Key Architecture Decisions
- Frames not video: Extracting video to individual WebP frames and swapping
<img>.srcgives smooth scroll-driven playback without the lag of video seeking. <img>not<canvas>: Native<img>elements render at full Retina quality. Canvas-based rendering looked blurry on HiDPI displays.- Full-viewport layout: Video container is
position: fixed; width: 100vw; height: 100vh. Image usesmin-width: 100vw; min-height: 100vhfor cover-style scaling. - Non-linear scroll mapping: A speed function with cosine curves creates smooth slow-down zones. Pre-computed lookup table with binary search for efficient frame lookup.
- Loading strategy: First frame loads and displays immediately. Scrolling is disabled (
body overflow: hidden) until all frames are loaded. Subtle progress indicator in bottom-left corner.
Performance Notes
- WebP frames are ~60% smaller than JPEG at comparable quality
- Frame skipping (every Nth frame) can halve data load with minimal visual impact
prepare-video.shauto-calculates skip factor to hit a target total size- FPS counter available for testing (can be removed for production)
- On MacBook Pro, expect 55-60 FPS with 144 frames at 3000x1722
overlays.json Format
{
"startFrame": 22,
"endFrame": 35,
"text": "Annotation text",
"targetX": 1560, // video pixel X coordinate
"targetY": 840, // video pixel Y coordinate
"position": "right", // left|right|top|bottom|top-left|top-right|bottom-left|bottom-right
"lineLength": 180, // pixels from target to text
"lineType": "solid", // solid|dashed|dotted
"lineWidth": 1,
"lineColor": "#ffffff",
"dotRadius": 12,
"dotStyle": "hollow", // filled|hollow
"dotStrokeWidth": 1.5 // for hollow dots
}
JS Configuration Variables (top of script in index.md)
SCROLL_LENGTH- Viewport heights to scroll through (higher = slower)TOTAL_FRAMES- Auto-updated by prepare-video.shSLOW_POINTS- Array of{ frame, strength, width }for smooth decelerationEASE_IN/EASE_OUT-{ frames, strength }for smooth start/end
Tools
ffmpegis available for video/image conversion- Use
-c:v libwebp(not default) when extracting WebP frames with ffmpeg, otherwise it creates animated WebP - Hugo dev server for local testing