Personal Website V5
March 12, 2026 · View on GitHub
Landing page for photocat.blue. The site presents two personas — human (programming/career) and furry (photography/fandom) — and lets visitors pick which side they want. Fifth version of the personal site.
What's in here
index.html main page
404.html custom error page
style.css flexbox layout, CSS custom properties for theming
script.js hover splits, theme switching, language detection, animations
assets/
languages.json translations (en, zh-TW, zh-CN)
img/ favicons, profile photo
devscript/
screenshot.js puppeteer script — generates the og:image at build time
favicon-convert.mjs svg → png favicon conversion
Config files: eslint.config.js, .stylelintrc.json. No framework, no bundler. Just HTML, CSS, and vanilla JS.
Run locally
npx http-server
Or just open index.html directly in a browser — it works without a server.
Lint
npm run lint
That runs eslint, htmlhint, and stylelint in sequence. Or individually:
npx eslint .
npx htmlhint index.html
npx stylelint style.css
Build
npm run build
Minifies HTML, CSS, and JS (terser, clean-css, html-minifier-terser) into dist/. The deploy workflow runs this automatically on push to main.
Deploy
GitHub Actions handles it. On push to main:
- Spins up a local http-server
- Takes a 1200×600 screenshot with Puppeteer (used for og:image)
- Runs
npm run build - Pushes
dist/to GitHub Pages
Features worth noting
Hover split — on desktop, hovering the left third expands the human side; the right third expands the furry side. CSS transitions handle the animation.
Themes — light, dark, and system. Preference is saved to localStorage. Built with CSS custom properties, so switching is a single class change on the root.
Language detection — reads navigator.language and maps it to one of three translations (en, zh-TW, zh-CN) stored in assets/languages.json. Falls back to English.
Accessibility — skip-to-content link, keyboard navigation through the theme dropdown, focus rings, 44px minimum tap targets on mobile, prefers-reduced-motion respected throughout.