This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Personal site for Joel G Goodman (joelgoodman.co) — a blog/portfolio built with Eleventy v3, hosted on Bunny CDN (Object Storage), with build-time image optimization via @11ty/eleventy-img.
Content is "letters" (newsletter posts) in letters/ as Markdown. Static pages are root-level .njk files. timeline.html is a standalone data visualization page with its own inline styles/scripts.
npm start (runs Sass + PostCSS + Rollup + Eleventy in parallel with live reload)npm run build (sets ELEVENTY_ENV=production, enables CSS purging/inlining, HTML minification, and image optimization)npm run deploy (builds then uploads _site/ to Bunny Object Storage)npm run debug:start (dev with Eleventy debug logging)Individual watchers: npm run watch:sass, npm run watch:js, npm run watch:eleventy
_includes/assets/scss/) → compiled to _includes/assets/css/jgg.css_includes/assets/js/jgg.js → _site/assets/js/jgg.bundle.js (IIFE format)_site/@11ty/eleventy-img transforms <img> tags into <picture> elements with AVIF/WebP/JPEG at 300/600/900/auto widthseleventy.config.js:purge-and-inline-css: PurgeCSS strips unused CSS, then inlines it into <head> (replaces <!-- INLINE CSS--> comment in components/head.njk)htmlmin: minifies all HTML outputjsmin async Nunjucks filter (Terser)_site/ to Bunny Object Storage via scripts/upload-to-bunny.jsIn development, CSS loads as an external stylesheet; in production, all CSS and JS are inlined for zero render-blocking requests.
. / Output: _site/ / Includes: _includes/ / Data: _data/letter → layouts/letter.njkletter — all files matching ./letters/eleventy-plugin-embed-everything, @11ty/eleventy-plugin-rss, @11ty/eleventy-img (transform plugin)readableDate, yearDate, htmlDateString, readingTime, cssmin, jsminyt — YouTube privacy-enhanced embed from URL_includes/assets/; images from assets/img/; data from assets/data/assets/img/ (flat structure, no subdirectories)<img src="/assets/img/filename.jpg"> — eleventy-img automatically generates responsive <picture> elements with AVIF, WebP, and JPEG variants<picture>)cover: { image: filename.jpg, alt: description }layouts/base.njk — main layout (includes nav, menu, social, now-playing widget, inline JS)layouts/letter.njk — extends base for newsletter postscomponents/ — reusable Nunjucks partials (head, nav, menu, social, now-playing, webmentions, callout)timeline.html — standalone listening autobiography page (own CSS/JS, uses D3.js, fetches live Last.fm stats)SCSS uses @use module syntax. Design tokens are CSS custom properties defined in _includes/assets/scss/_config.scss:
Spacing (8px grid-aligned): --space-xs (8px) through --space-3xl (80px), plus fluid --space-section-h and --space-section-v using clamp(). Legacy --gutter-sm/md/lg aliases remain.
Typography: Fluid sizing via clamp() on body and all headings — no breakpoint steps. Fonts: Gantari (body, variable weight), Instrument (headings, serif). Both self-hosted in _includes/assets/fonts/.
Colors: Light default with dark mode via prefers-color-scheme: dark. Dark mode uses charcoal-blue palette (#1c1e26 bg, #f0ead8 text, #c8c0b0 body text).
Transitions: --t-fast (0.2s), --t-base (0.3s), --t-slow (0.6s), plus --t-timing (275ms) and --t-effect (cubic-bezier).
Media queries: Mobile-first via @include mq(sm/md/lg/xl/huge) mixin in _helpers.scss.
Timeline page has its own design system (dark-only, amber accent, uses Bunny.net font mirror for Instrument Serif and JetBrains Mono, self-hosted Gantari).
_data/metadata.json — site metadata, author info, feed config_data/site.js — exports environment (production/development)jgg.config.js — directory path definitionsscripts/upload-to-bunny.js.env with BUNNY_STORAGE_ZONE (zone name) and BUNNY_API_KEY (storage zone password from FTP & API Access)timeline.html for privacy-friendly Google Fonts alternativeletters/ as .md files with frontmatter_drafts/ (excluded via .eleventyignore).draft are also excluded from buildsNode 22 (pinned in .nvmrc)