Polymathic Blog
Digital transformation, higher education, innovation, technology, professional skills, management, and strategy
2 min read
technology

From 11ty to Hugo: simplifying my blog stack

After running this blog on various platforms since 2007—WordPress, Jekyll, 11ty—I’ve landed on Hugo. The migration wasn’t driven by dissatisfaction with 11ty, which served me well. It was driven by a growing unease with npm.

The npm problem

Every time I ran npm install, I watched dozens of packages download. Dependencies of dependencies of dependencies. Each one a potential supply-chain attack vector. The event-stream incident in 2018 wasn’t an anomaly—it was a preview of an ongoing vulnerability in how we build software.

For a personal blog, this felt absurd. I was pulling in hundreds of megabytes of node_modules to generate static HTML files. The attack surface was wildly disproportionate to the task.

Why Hugo?

Hugo is a single binary. No runtime dependencies. No package manager. You download it, and it works.

It’s also remarkably fast. My site has over 1,400 posts accumulated across nearly two decades of writing. Hugo builds the entire thing in under two seconds. 11ty was fast too, but Hugo’s Go-based architecture handles scale effortlessly.

The real draw, though, was Hugo Pipes. Modern Hugo (extended edition) includes Dart Sass compilation built-in. No PostCSS. No Tailwind build step. No npm. Just Sass files that Hugo processes natively during the build.

The migration path

The content migration was straightforward—both 11ty and Hugo use Markdown with YAML frontmatter. A few find-and-replace operations handled the differences:

  • permalink: became url:
  • Layout references changed format
  • Some shortcode syntax differed

The theme required more work. I’d been using Tailwind CSS, which depends on npm. I rebuilt the entire stylesheet in native Sass, reimplementing the utility classes I actually used. The result (I’m calling it “poly5”) is about 800 lines of Sass that compile to clean CSS via Hugo Pipes.

The base template now looks like this:

{{ $sass := resources.Get "sass/main.scss" }}
{{ $styles := $sass | toCSS | minify | fingerprint }}
<link rel="stylesheet" href="{{ $styles.RelPermalink }}">

That’s it. No build tooling. No npm scripts. Hugo handles everything.

Deployment

I’m hosting on Cloudflare Pages, which detects Hugo automatically. The build configuration is minimal:

Build command: hugo --gc --minify
Output directory: public
Environment: HUGO_VERSION = 0.152.2

No npm install step. Build times dropped significantly, and I no longer wonder what code is executing during my deployments.

What I gave up

Tailwind’s utility-first approach is genuinely productive for rapid prototyping. Writing equivalent Sass required more upfront thought about structure. But for a blog that changes design rarely, this tradeoff made sense.

I also lost 11ty’s JavaScript-based flexibility. Hugo’s templating is powerful but more constrained. For my use case—rendering Markdown to HTML with consistent layouts—this constraint hasn’t mattered.

The result

The blog looks identical to visitors. Under the hood, it’s dramatically simpler:

  • Zero npm dependencies (down from 67 packages)
  • Single binary toolchain (Hugo extended)
  • Native Sass compilation (no PostCSS/Tailwind)
  • Faster builds (~1.5 seconds for 1,400+ pages)
  • Smaller attack surface

Software has a tendency toward accidental complexity. Every dependency is a decision to trust someone else’s code—and everyone whose code they depend on. For a personal blog, I’ve decided that trust should be minimal.

Hugo with native Sass isn’t the right choice for every project. But for static content sites where simplicity and security matter more than ecosystem breadth, it’s been the right choice for me.

Related Posts

25 Nov 2025

The 90% of Education We’ve Never Tried

the 90% of education we’ve never tried

For decades, we’ve been operating with just 10% of education’s true potential—not by choice, but by …