What You'll Learn at This Station
HAP's Discovery: Customizing a template taught me that small changes add up to big transformations. I didn't need to rewrite everything—just replace colors, add fonts, and create one custom shortcode. Here are the three insights that made my blog feel truly mine:
CSS Custom Properties Power
I created hap-colors.css with all my colors as custom properties in :root. Changing the whole color scheme? Just update those values! Everything that references them changes automatically. Prof. Teeters called this "single source of truth."
Shortcodes Save Repetition
Instead of typing long Cloudinary URLs everywhere, I created a hapPose shortcode. Now I just write {% hapPose "laptop", "HAP at computer", 150 %} and it outputs the full image tag. Reusable, consistent, and SO much easier!
Deploy = Upload HTML
No server setup. No database configuration. No PHP installation. I connected my GitHub repo to Netlify, told it to run "npm run build" and publish the _site folder. My blog was live in under 5 minutes!
HAP's Confession:
- I forgot that I can't edit files in
_sitebecause it looks like the pages change but then those edits get overwritten by Eleventy. - I tried to customize the Image Transform Plugin to skip my Cloudinary images. First attempt: a config option that doesn't exist. Second attempt: an HTML attribute that got ignored. Final solution: just comment out the plugin! Sometimes removing a feature is the right answer.
- I forgot to add
_siteto.gitignoreand committed 47MB of generated HTML files to my repository. Prof. Teeters noticed when she reviewed my PR. "That folder rebuilds every time," she said gently. "It shouldn't be in Git." Oops. - I pushed to main without testing the build locally first. Netlify build failed because I had a typo in my shortcode. Always run
npm run buildbefore pushing! Now I test locally every single time.
The Customization Journey
HAP's Approach:
Prof. Teeters mapped out a safe path through customization: start with colors (lowest risk), then fonts, then custom features, then complex changes. Each step built on the previous one, and I tested after every change. Here's the exact journey I followed:
Step 1: Color System (Safest Change)
I created a new file: css/hap-colors.css
The magic is CSS custom properties. Instead of scattering color values everywhere, I defined them once:
:root {
--warm-orange: hsl(32, 76%, 37%);
--peach-background: hsl(32, 41%, 90%);
--cream-white: hsl(32, 100%, 97%);
--dark-brown: hsl(28, 45%, 16%);
--teal-accent: hsl(168, 28%, 54%);
--teal-darker: hsl(168, 34%, 30%);
}
Then in index.css, I replaced the template's colors with my custom properties:
body {
background-color: var(--peach-background);
color: var(--dark-brown);
}
a {
color: var(--teal-darker);
}
One file controls everything! Want a darker orange? Change 37% to 33% and the whole site updates.
Step 2: Typography (Medium Risk)
Variable fonts are amazing! Instead of loading separate files for each weight:
- Nunito-Regular.woff2
- Nunito-Bold.woff2
- Nunito-Italic.woff2
- (and nine more!)
I loaded ONE file that contains ALL weights:
@font-face {
font-family: 'Nunito';
src: url('/fonts/Nunito/Nunito-VariableFont_wght.woff2') format('woff2');
font-weight: 200 1000;
font-display: swap;
}
Now I can use font-weight: 400, font-weight: 700, or even font-weight: 623—any value I want! The browser interpolates between weights automatically.
Step 3: Custom Shortcode (Complex but Powerful)
I was copying the same Cloudinary URL pattern over and over:
<img src="https://res.cloudinary.com/cynthia-teeters/image/upload/f_auto,q_auto,w_150,c_limit/v1759495998/hap-laptop_xiewar.jpg"
alt="HAP at laptop" width="150" height="150" loading="lazy">
Prof. Teeters said, "That's what shortcodes are for! Let me help you write and test one."
// HAP pose shortcode for Cloudinary images
eleventyConfig.addShortcode("hapPose", (poseName, altText, width = 150) => {
const url = cloudinary.getHapPoseUrl(poseName, width);
return `<img src="${url}" alt="${altText}" width="${width}" height="${width}" loading="lazy" decoding="async">`;
});
Now in my Markdown I just write:
{% hapPose "laptop", "HAP at his laptop", 400 %}
One line instead of five! And if I ever need to change the URL pattern, I change it in ONE place.
The Image Plugin Problem
HAP's Frustration:
Not everything went smoothly. The Image Transform Plugin that comes with eleventy-base-blog kept downloading my Cloudinary images and creating local copies. That defeats the whole point of using an image CDN! Here's how I solved it:
🟠 The Problem:
Eleventy's Image Transform Plugin is designed to optimize local images—it creates multiple sizes, converts to WebP/AVIF, and serves the best format. Great for local images!
But my images are already on Cloudinary, which does all that optimization automatically. The plugin was downloading my optimized images and re-optimizing them locally. Wasted bandwidth, slower builds, unnecessary files, and not as much flexibility.
Failed Attempts
Attempt 1 — FAILED
I searched for a config option to exclude external URLs:
// Doesn't exist!
remoteImageURLs: (url) =>
url.includes('cloudinary')
? false : true
Attempt 2 — FAILED
I tried adding an HTML attribute to skip processing:
<img src="..."
eleventy:ignore>
Plugin processed it anyway!
I was getting frustrated. Grace Hopper suggested, "Maybe check if the plugin is even necessary for your use case?"
The Solution — SUCCESS!
Grace was right. I don't HAVE any local images. Every image on my blog comes from Cloudinary. So why do I need an image optimization plugin?
// import { eleventyImageTransformPlugin } from "@11ty/eleventy-img";
// Disabled - using Cloudinary CDN only
// Image optimization: DISABLED
// All images served via Cloudinary CDN (already optimized)
// No local images need processing
// If local images needed in future, re-enable here
// eleventyConfig.addPlugin(eleventyImageTransformPlugin, {...});
That's it. No plugin, no problem.
🟠 Prof. Teeters' Wisdom:
"Sometimes the best solution is removing features you don't need. The template includes the Image Transform Plugin because MOST blogs have local images. YOUR blog, HAP, doesn't. Different needs, different solutions."
I felt silly for not thinking of that sooner. But now I know: always ask "Do I actually need this?" before trying to configure around it.
Deployment to Netlify
HAP's Triumph:
With my customizations complete and tested locally, it was time to put my blog on the internet. I'd heard horror stories about server configuration and deployment pipelines. Turns out, static site deployment is wonderfully simple:
Pre-Deployment Checklist
Add _site to .gitignore
Don't commit generated files!
Test the build locally
Run npm run build
Check for errors
Review the build output carefully
Review _site folder
See what will actually be deployed
Update README.md
Make sure it's current and complete
Commit to GitHub
Push all source files
Netlify Setup
1. Create Account
Go to netlify.com (free tier is plenty!)
2. Add New Site
Click "Add new site" → "Import an existing project"
3. Connect GitHub
Authorize Netlify to access your repositories
4. Select Repository
Choose your blog repo from the list
Build command: npm run build
Publish directory: _site
That's it! Netlify clones your repo, runs the build command, and puts the _site folder on their CDN.
The Magic of Continuous Deployment
Every time I push to the main branch of my repo:
- Netlify detects the change
- Runs
npm run buildautomatically - Deploys the new version
- Old version stays available for instant rollback
Pull requests get preview deployments—a temporary URL to test changes before merging. Prof. Teeters and I sometimes use this to review my blog posts before they go live!
🟠 HAP's Triumph:
My blog as a multi-page site was live at a real URL in under 5 minutes. No servers to configure. No databases to manage. No FTP uploads. Just push to GitHub and Netlify handles the rest.
I refreshed the page probably 50 times that first day, just to see MY blog on the actual internet. With MY colors. And MY name. And my HAP selfies!
🎓 Customization & Deployment Quick Reference
Colors
Create css/hap-colors.css — Use hsl() format, define in :root
Fonts
Create css/hap-fonts.css — Variable fonts = one file for all weights
Shortcodes
Edit eleventy.config.js — addShortcode() for reusable components
Images
Comment out Image Transform if using CDN, or configure for local images
Test Build
Run npm run build — Always test locally before pushing!
Deploy
Netlify: Build command npm run build, Publish directory _site