CLAUDE.md
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
What This Is
Shaoyi (Sean) Zheng’s academic personal website built with Jekyll using the Academic Pages template (forked from Minimal Mistakes). Hosted on GitHub Pages at https://www.shaoyizheng.me (custom domain via CNAME).
Branching
dev— the live branch. GitHub Pages builds the site fromdev(verify withgh api repos/OWNER/REPO/pages), so pushing todevdeploys to https://www.shaoyizheng.me. All work happens here.master— GitHub’s default branch, but not what Pages serves. It holds an older state of the upstream template and is not part of the deploy path. Do not mergedevinto it expecting a deploy.
Check a deploy with gh api repos/OWNER/REPO/pages/builds --jq '.[0]'.
Development Commands
# Install dependencies
bundle install
# Serve locally (auto-rebuilds on Markdown/HTML changes; restart for _config.yml changes)
bundle exec jekyll serve -l -H localhost
# Site available at localhost:4000
# Docker alternative
docker compose up
# Site available at localhost:4000
# Build JS assets (rarely needed)
npm run build:js
Architecture
The site uses only the publications collection (defined in _config.yml under collections:). Other template collections (talks, teaching, portfolio) are not active in navigation.
Active content (everything else in _pages/ is built but not linked from nav):
_pages/about.md— homepage (permalink/),layout: home, research intro_pages/cv.html— CV page (education, projects, industry experience, skills)_pages/publications.html— publications list, grouped by year, each row linking straight to arXiv_pages/interests.html— Interests page with photography carousel_publications/— academic papers; see “Adding a New Publication” below_data/navigation.yml— top nav (Publications, CV, Interests)_data/photography.yml— photo entries with EXIF metadata (camera, location, date, focal, ISO, shutter); rendered via_includes/photo-carousel.html
Key configuration:
_config.yml— site metadata, author sidebar info, collection definitions, plugins.site_themepicks the SCSS theme family (“default” or “air”);color_modepicks light or dark (see Color system)._config_docker.yml— overridesurlto empty for local Docker useCNAME— custom domainwww.shaoyizheng.me
Theme & layout:
_layouts/— HTML templates (default.htmlbase,single.html,archive.html,home.html, etc.)_includes/— reusable HTML partials. Custom override slots:head/custom.html,footer/custom.html(see Custom Features)._sass/— SCSS stylesheets. Custom partials include_design-system.scss,_home.scss,_global-theme.scss.
Utilities:
markdown_generator/— Python scripts and Jupyter notebooks to batch-generate publication/talk Markdown from TSV/BibTeXfiles/— static assets (PDFs, etc.), accessible at/files/filenameimages/— site images.images/photography/holds the carousel photos referenced by_data/photography.yml.
Custom Features (beyond upstream Academic Pages)
The template has been extended with several custom client-side features. Touch these carefully — they interact across _includes/head/custom.html, _includes/footer/custom.html, assets/js/, and _sass/.
Color system (pure CSS — no JavaScript):
- Greyscale base with a single accent hue,
#A7C1D9(hsl 209 40% 75%). The former 19-color auto-cycling palette, its click-to-change button, andassets/js/color-picker.jshave all been removed. - All colors are CSS custom properties declared in two places:
_sass/layout/_home.scss(--home-*) and_sass/layout/_global-theme.scss(--global-*,--gt-*). Each file defines a:rootblock (light) and anhtml[data-theme="dark"]block (dark). - The accent is split in two, and this matters:
#A7C1D9scores only 1.9:1 on white (WCAG AA needs 4.5:1) but 10.6:1 on the near-black hero. So:--home-accentis text-safe —#365A7Din light mode (7.2:1 on white),#A7C1D9in dark. Use for anything carrying text.--home-accent-softis the raw tint, decorative only — section-title rules, year rules, glows, icon backgrounds. Never put text in it on a light background.--home-hero-accentis for the hero, which is near-black in both themes, so it stays#A7C1D9regardless.
- Light/dark toggle (moon/sun button in the masthead,
assets/js/theme-toggle.js): flipsdata-themeon<html>and stores the choice inlocalStorageundercolor-mode.color_modein_config.yml("light""dark") is only the default for visitors with no stored choice. It is separate fromsite_theme, which picks the SCSS partial family (default/air) — settingsite_theme: "dark"would break the@importinassets/css/main.scss.- The initial value is resolved by an inline script in
_includes/head/custom.html. It must stay synchronous and in<head>or the page flashes the config default before the stored choice applies. Do not move it to an external file.
- The hero follows the theme (white in light mode, near-black in dark), so
--home-hero-accentis text-safe in each mode rather than fixed. - Residual color from upstream that is not part of this system: Solarized syntax highlighting (
_sass/_syntax.scss) and social-brand icon colors.
Wave canvas (assets/js/waves.js):
- Custom
<a-waves>Web Component with Perlin-noise lines, mouse/touch interaction, IntersectionObserver to pause off-screen - Used as the homepage and inner-page hero background
- Style is driven by CSS custom properties, re-read via a
MutationObserverondata-themeso the canvas re-tunes when the theme flips:--wave-color,--wave-line-width,--wave-gap-scale(multiplies the base gap fromdata-lines; bigger = fewer lines). Light mode gets thin dense dark lines on white; dark mode gets thicker sparser white lines on black. - A
data-colorattribute on the element still overrides--wave-color. Changing--wave-gap-scalerebuilds the line geometry.
Homepage + inner-page JS:
assets/js/home.js— typing effect (cycling keywords) and.revealscroll animations; loaded only onlayout: homeassets/js/inner-pages.js— scroll-reveal + smooth scroll; loaded onlayout: cv | publications- Layout-conditional loading is wired in
_includes/footer/custom.html
Photography carousel:
_pages/interests.htmlrenders_includes/photo-carousel.html, which iterates_data/photography.yml- Carousel JS is inlined in
_includes/footer/custom.html(auto-advance, pause-on-hover); styles in_sass/layout/_home.scssunder.carousel__*and.photo-layout__* - To add a photo: drop the file in
images/photography/and append an entry to_data/photography.ymlwith the EXIF fields used by existing entries
Other head/footer customizations:
_includes/head/custom.html— Academicons CSS, favicon suite (SVG/PNG/manifest), and a tiny script that clears the retired color-cycle keys from returning visitors’ localStorage_includes/footer/custom.html— MathJax, Plotly, Mermaid, plus the JS loaders above
JS asset build: npm run build:js rebuilds the bundled assets/js/main.min.js from the upstream template’s source modules. Custom JS (waves.js, home.js, inner-pages.js) is loaded directly and is not part of that bundle — edits to those files take effect on Jekyll reload without npm run build:js.
Adding a New Publication
Publications have no individual pages — the collection is output: false in _config.yml, and each entry on /publications/ links straight to its arXiv page. arxiv is therefore required; without it the row renders as a dead link.
Create _publications/YYYY-MM-DD-slug.md following existing files:
---
title: "Paper Title"
collection: publications
category: conferences # or: manuscripts, books
excerpt: 'One-line summary.'
date: YYYY-MM-DD
venue: 'ICML 2026' # conference + year only, no page/volume info
year: "2026" # required — which year heading it files under
arxiv: "https://arxiv.org/abs/XXXX.XXXXX" # required — the click target
authors: "First Author, Second Author" # "Shaoyi Zheng" is auto-bolded + underlined
citation: 'Author(s). "Title." <i>Venue</i>.'
---
The list is grouped under year headings taken from year, not from date — a paper’s venue year can differ from its arXiv date (HilbertA was posted in 2025 but is ICML 2026). date still controls ordering within a group. venue should be just the conference and year.
Only title, venue, year, authors, and arxiv are rendered on /publications/. The body text, excerpt, citation, and tags are currently unused — kept for reference and for a possible future detail view.
