This is my personal website, built with Hugo. It's a static site that lives at kwon.nyc.
Stack
- Hugo - static site generator
- Netlify - hosting
- Node.js - Build-time data fetching scripts
Setup
# Clone the repo git clone https://github.com/rjkwon/blog.git # Install dependencies npm install # Run local dev server hugo server
Homepage sections (media logs)
Currently reading
Displays one book I'm currently reading, tracked at literal.club. Uses the literal.club API.
- Template:
layouts/partials/literal.html - Updates: Fetched directly at build time using Hugo's
resources.GetRemote
Recently watched
Displays recent movie watches, tracked at Letterboxd. Letterboxd doesn't give access to their API for personal projects, so this approach uses a script to fetch the RSS feed for my data and convert it to JSON, then Hugo uses the data file to generate the section on my homepage.
- Script:
scripts/fetch_letterboxd.js - Data:
assets/letterboxd.json - Template:
layouts/partials/letterboxd.html - Updates: Runs automatically at build time
Links of interest ("Meanwhile, on the internet...")
Displays 9 recent links of interest, tracked in this Google Sheet. Uses a Google Apps Script endpoint
- Script:
assets/js/links.js - Data: Google Sheet
- Updates: Google Apps Script endpoint, fetches at page load
Content sections
/notes
Notes (blog posts) written in Markdown.
- Content:
content/notes/ - Template:
layouts/_default/single.html
/about
My bio and other stuff about me
- Content:
content/about/ - Template:
layouts/about/single.html
/colophon
How this site is built, less technical than this README.
- Content:
content/colophon/ - Template:
layouts/colophon/single.html
Microsites and special features
/bike
Microsite about my bike. I "manually" traced each bike part from a photo to create an SVG with each part as a group, then animated it using GSAP/ScrollTrigger.
- Content:
content/bike/(just the blurb) - Layout:
layouts/bike/ - CSS:
static/css/bike.css - Animation: GSAP + ScrollTrigger (loaded via CDN in
layouts/bike/baseof.html) - Related notes: auto-lists any post from /notes tagged
bikevia{{ range .Site.Taxonomies.tags.bike }}insingle.html - SVG: created in Figma (source) and optimized with svgo. If I edit the source SVG, re-run the following:
npx svgo@3 bike.svg -o bike.svg --config svgo.config.js --pretty --indent 4 --final-newline
/cans
An ongoing stream of photos of craft beer cans with neat designs. Originals are processed locally with ImageMagick (resized to 1500px, GPS stripped), then a script generates per-can info into a JSON file that Hugo uses to create the page. At build time, Hugo resizes each photo again (640x853, WebP, q75) for display and caches the result in resources/_gen/images/cans/ — commit those generated files along with new photos so Netlify doesn't have to reprocess the whole set on every deploy.
TODO
- Add alt text and captions
- Content/images:
content/cans/(JPEGs live alongside_index.mdas page resources) - Data:
data/cans.json - Scripts:
scripts/process_can_photos.sh- processes original photos, outputs intocontent/cans/(run locally)scripts/generate_cans_json.js- generates JSON from photos incontent/cans/(runs as part ofnpm run build)
- Template:
layouts/cans/list.html
/dad-hats
My dad's hats.
TODO
-
Add alt text
-
Consider displaying captions
-
Finish writing descriptions and consider displaying them
- Content:
content/dad-hats/(just a blurb about why I made this) - Data:
data/hats.json, with the following keys per hat: —image: image urlcaption: image caption (usually just whatever text is on the hat)description: a personal note about the hat and its significancealt: alt textbg_color(not in use): custom background color, used for a different version of this pagetext_color(not in use): custom text color, used for a different version of this page
- Images:
static/hats/ - Layout:
layouts/hats/
- Content:
/december-adventure-2025
Microsite for December Adventure, a challenge stewarded by eli_oat, with daily updates for the month of December.
- Content:
content/december-adventure-2025/ - Layout:
layouts/decadv/
/internet
A collection of articles centered around how personal websites can reclaim the web. Each entry has a Markdown file with front matter (used to be stored in a single JSON file but switched to individual .md files in Aug 2026).
- Content:
content/internet/ - Template:
layouts/internet/list.html - CSS:
themes/kwon-theme/assets/css/extended/internet.css - RSS feed:
themes/kwon-theme/layouts/internet/rss.xml
curl detector
Bare bones, CLI version of the site, via curl. Uses a Netlify edge function. Inspired by elle.
- Edge function:
netlify/edge-functions/curl-detector.js - Config: Mapped to
/innetlify.toml
Directory structure
├── assets/
│ ├── css/ # Additional stylesheets
│ ├── js/ # JavaScript (links.js)
│ └── *.json # Generated data (letterboxd.json)
├── content/
│ ├── about/
│ ├── blank/ # A blank page, just for fun
│ ├── cans/
│ ├── colophon/
│ ├── december-adventure-2025/
│ ├── notes/ # Blog posts
│ └── ...
├── data/
│ ├── cans.json
│ ├── hats.json
│ └── links.json
├── netlify/
│ └── edge-functions/ # curl-detector.js
├── scripts/
│ ├── fetch_letterboxd.js
│ └── generate_cans_json.js
├── static/
│ ├── decadv/ # December Adventure image files
│ ├── hats/ # dad-hats image files
│ └── images/
├── themes/
│ └── kwon-theme/
│ ├── assets/css/ # main stylesheets
│ └── layouts/ # Hugo templates
├── hugo.toml # Hugo config
├── netlify.toml # Netlify config
└── package.json # npm scripts
Build & deployment
npm run build
This runs:
npm run prebuild- Fetches Letterboxd and generates cans JSONhugo --gc- Builds the static site
Pushes to main trigger a Netlify build automatically. Hugo version and build settings are in netlify.toml.