For nearly two years, my personal website was built on modern front-end machinery. It ran on Next.js with the App Router, React 19, Tailwind CSS, PostCSS, and Nodemailer server actions. On paper, this was an industry-standard stack. It gave me server-side rendering, API routes, automated code-splitting, and a familiar developer experience borrowed from large-scale client engagements.

In practice, it was an absurd architectural mismatch.

A personal website is primarily a publication engine for long-form technical prose, project documentation, and structured career history. It does not handle high-frequency transactional data. It does not require real-time WebSocket connections or multi-tenant database synchronization. Yet every time I wanted to publish a short essay or adjust a typography token, I was spinning up a heavyweight JavaScript toolchain, pulling down hundreds of megabytes of dependencies, and troubleshooting runtime hydration quirks.

Earlier this month, I dismantled the entire application. In its place, I built a lean static architecture powered by Hugo Extended and a bespoke semantic CSS token system. The result is a site that compiles dozens of pages in under 50 milliseconds, ships zero client-side JavaScript for reading, and achieves WCAG AAA accessibility scores across both light and dark themes.

Here is why I walked away from the modern single-page application toolchain for my personal site, and how returning to fundamental web standards restored sanity to my publishing workflow.

The SPA Hangover: Auditing the Cost of Over-Engineering

The web development industry spent the last decade convincing engineers that every public-facing endpoint requires a component runtime. What began as a genuine breakthrough for complex web applications like email clients and design tools gradually colonised personal publishing. Static sites became dynamic server-rendered applications, and text documents became complex bundles of reactive state.

Before writing a single line of the new Hugo theme, I audited the existing Next.js repository. The footprint was illuminating:

  • Framework and Runtime: Next.js 16.3.0 App Router on React 19.2.8.
  • Styling Pipeline: Tailwind CSS v4.3.3 with @tailwindcss/postcss, clsx, and tailwind-merge.
  • Ancillary Utilities: lucide-react for four simple UI icons, nodemailer with Google Cloud reCAPTCHA Enterprise verification for a static contact form, and Google Font loaders for Geist Sans and Geist Mono.
  • Dependency Footprint: 320 megabytes in node_modules, comprising more than 800 individual packages in the dependency tree.
  • Build Duration: 14 to 18 seconds on local developer hardware; 35 to 45 seconds inside GitHub Actions continuous integration runners.

None of these individual tools are inherently flawed. In high-traffic corporate applications with dozens of distributed developers, Next.js and Tailwind solve real organizational coordination problems. In a personal publishing environment, however, they impose a severe dependency tax.

The most exhausting aspect of this setup was the maintenance churn. Dependabot routinely raised automated pull requests for security vulnerabilities in obscure transitive build dependencies. Packages three levels deep in the build pipeline required urgent triage despite having zero connection to the Markdown content being rendered. If the codebase sat untouched for four months, running npm install frequently surfaced peer dependency warnings or Node runtime version deprecations.

When an engineer spends more time resolving packaging deprecations than writing original thoughts, the technology stack has failed its primary duty.

The Compilation Advantage: Choosing Hugo

When selecting a replacement engine, my requirements were strict. The new tool had to operate as a self-contained binary, compile content instantly, require zero third-party build plugins, and produce clean HTML that could be hosted anywhere.

Hugo satisfies every one of these criteria. Written in Go, Hugo compiles directly to native machine code for Linux, macOS, and Windows. It requires no global Node runtime, no virtual environment management, and zero package installations.

# Verify the Hugo engine
hugo version
# Output: hugo v0.165.0-76a5e1880ab4... extended windows/amd64

The difference in execution performance is staggering. Where Next.js required over 15 seconds to parse Markdown through MDX pipelines, compile JSX wrappers, and emit client bundles, Hugo compiles the entire site across dozens of pages, categories, and tags in less than 50 milliseconds.

Start building sites … 
hugo v0.165.0+extended

                   │ EN 
───────────────────┼────
  Pages            │ 59 
  Paginator pages  │  0 
  Non-page files   │  0 
  Static files     │  4 
  Processed images │  0 
  Aliases          │ 22 
  Cleaned          │  0 

Total in 42 ms

A 42-millisecond build time fundamentally changes the creative feedback loop. When you edit a stylesheet or save a Markdown draft, Hugo’s built-in LiveReload server updates the browser faster than the human eye can switch windows. The tool vanishes into the background, leaving only the writing.

Furthermore, Hugo provides complete native support for asset pipelines through Hugo Pipes. It handles CSS bundling, minification, and cryptographic subresource integrity (SRI) hashing without requiring PostCSS, Webpack, or Vite configuration files.

A Zero-Runtime Design System: Semantic CSS Tokens

With the framework layer eliminated, the next decision was styling. In the previous iteration, Tailwind CSS generated utility classes directly into markup. While utility classes accelerate rapid prototyping, they clutter HTML with repetitive class strings and tether the design to an external compilation step.

For the new site, I wanted a pure CSS architecture that relied on native custom properties. I designed an editorial visual language named Warm Editorial & Terracotta. The aesthetic draws inspiration from archival paper and carbon ink in light mode, and dark espresso charcoal with luminous apricot accents in dark mode.

Instead of hardcoding hexadecimal colors throughout component styles, the entire site is governed by semantic tokens declared in assets/css/tokens.css:

/* Design tokens: assets/css/tokens.css */
:root {
  /* Light Canvas: Warm Archival Paper and Deep Carbon Ink */
  --bg-primary: #faf8f5;
  --bg-secondary: #f2ede4;
  --bg-surface: #ffffff;
  --bg-muted: #ece6dc;

  --text-primary: #1c1917;
  --text-secondary: #57524d;
  --text-tertiary: #787169;

  --border-subtle: #ebe5dc;
  --border-color: #ded6ca;
  --border-strong: #b5aba0;

  --accent-color: #b8481f;
  --accent-hover: #9c3b17;
  --accent-subtle: #f7ebe4;
  --accent-color-contrast: #ffffff;

  --focus-ring: rgba(184, 72, 31, 0.45);
  --selection-bg: #f4d8cc;
  --selection-text: #1c1917;

  /* Typography scale using system font stacks */
  --font-sans: -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --font-serif: "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, serif;

  --text-base: 1rem;
  --text-lg: 1.125rem;
  --text-xl: 1.375rem;
  --text-2xl: 1.75rem;
  --text-3xl: 2.25rem;

  --leading-normal: 1.6;
}

Contrast Ratios and Readability

Readability is the primary function of a publication. Many modern websites employ low-contrast gray text on off-white backgrounds, sacrificing accessibility for minimalist fashion. Every color pair in this token architecture was calculated to exceed Web Content Accessibility Guidelines (WCAG) AAA contrast requirements:

  • Light Mode Text: Deep carbon ink (#1c1917) on warm paper (#faf8f5) yields a contrast ratio of 14.8:1, far exceeding the 7:1 threshold required for WCAG AAA compliance.
  • Light Mode Secondary Body: Warm stone (#57524d) on canvas delivers 6.8:1, meeting AAA for large text and high AA for body copy.
  • Terracotta Accent: #b8481f against white and light backgrounds provides 5.8:1, ensuring link indicators remain clearly legible for readers with color vision deficiencies.

Dark Mode Without JavaScript Toggles

Most modern personal sites feature an interactive theme toggle button in the navigation bar. Implementing that button typically introduces a cascade of complexity: a client-side JavaScript event listener, localStorage state reading, and inline blocking scripts in the HTML <head> to prevent the dreaded flash of unstyled content (FOUC).

I rejected that approach entirely. Operating systems and modern web browsers already manage user theme preferences through media queries. By declaring dark theme overrides directly under @media (prefers-color-scheme: dark), the website honors the reader’s system preference automatically with zero runtime JavaScript:

@media (prefers-color-scheme: dark) {
  :root {
    /* Dark Canvas: Deep Espresso Charcoal and Luminous Apricot */
    --bg-primary: #171513;
    --bg-secondary: #211e1a;
    --bg-surface: #2a2622;
    --bg-muted: #2f2a25;

    --text-primary: #f2ede4;
    --text-secondary: #a89f92;
    --text-tertiary: #857c71;

    --border-subtle: #292520;
    --border-color: #38322c;
    --border-strong: #544c43;

    --accent-color: #e08a5f;
    --accent-hover: #efa882;
    --accent-subtle: rgba(224, 138, 95, 0.12);
    --accent-color-contrast: #171513;

    --focus-ring: rgba(224, 138, 95, 0.5);
    --selection-bg: #452b1e;
    --selection-text: #f2ede4;
  }
}

In dark mode, warm ivory text (#f2ede4) against the deep espresso canvas (#171513) maintains an exceptional 14.3:1 contrast ratio. The luminous apricot accent (#e08a5f) delivers 6.6:1 against the dark surface, preserving visual warmth without blinding the reader in low-light environments.

Because dark mode is handled entirely by the browser engine, the page renders instantaneously without layout shifts or script execution delays.

System Fonts: Zero Latency Typography

The typography stack relies on high-quality system serif and sans-serif fonts. For editorial articles, the site specifies "Iowan Old Style", "Palatino Linotype", Palatino, and Georgia. For navigation chrome and technical metadata, it defaults to Apple’s San Francisco, Windows Segoe UI, and Linux Roboto.

By eliminating external webfont downloads, the site avoids third-party tracking connections to Google Fonts, eliminates cumulative layout shifts (CLS) caused by font swaps, and saves over 150 kilobytes of network transfer on every page load.

Asset Pipeline and Cryptographic Integrity via Hugo Pipes

A common justification for using bundlers like Webpack or Vite is asset hashing and cache busting. In Hugo, this capability is native.

Within layouts/_default/baseof.html, Hugo Pipes retrieves the raw stylesheet from the assets/ directory, compiles it, and generates a cryptographic SHA-256 integrity hash:

<!-- Stylesheet loading in layouts/_default/baseof.html -->
{{ with resources.Get "css/tokens.css" }}
  {{ $tokens := . | resources.Fingerprint "sha256" }}
  <link rel="stylesheet" href="{{ $tokens.RelPermalink }}" integrity="{{ $tokens.Data.Integrity }}">
{{ end }}
{{ with resources.Get "css/main.css" }}
  {{ $main := . | resources.Fingerprint "sha256" }}
  <link rel="stylesheet" href="{{ $main.RelPermalink }}" integrity="{{ $main.Data.Integrity }}">
{{ end }}

During build time, Hugo automatically evaluates the stylesheet contents, writes the fingerprinted asset to the output directory (for example, /css/tokens.min.9b2c8f....css), and injects the Subresource Integrity (SRI) attribute. If a file does not change between releases, its filename remains identical, allowing edge CDNs and browser caches to serve the asset permanently.

Decoupled Content Modeling: The Data Directory

A recurring challenge on personal websites is managing structured metadata such as work history, technical proficiencies, and published books. Hardcoding structured data directly into Markdown articles leads to duplicated content. Conversely, storing that information inside a relational database introduces unnecessary operational overhead.

Hugo solves this cleanly with its data/ directory. Structured data is maintained in declarative YAML files:

# data/books.yaml (excerpt)
featured:
  - title: "The ADHD Adult's Bill-Paying System That Actually Sticks"
    subtitle: "For People Who've Had Utilities Shut Off from Forgetting, Not Poverty"
    author: "Jacques Murray"
    published: 2026-09-01
    platform: "Amazon Kindle"
    asin: "B0HHG63919"
    url: "https://www.amazon.com/dp/B0HHG63919"

  - title: "Python Programming: Your Coding Superpower"
    subtitle: "A Hands-On Guide for Absolute Beginners"
    author: "Jacques Murray"
    published: 2026-09-10
    platform: "Amazon Kindle"
    asin: "B0HJD6TQKT"
    url: "https://www.amazon.com/dp/B0HJD6TQKT"

In the presentation layer, Hugo templates iterate over these collections with native Go template directives:

<!-- layouts/_default/books.html snippet -->
{{ $books := hugo.Data.books }}
{{ with $books.featured }}
  <section class="books-section">
    <h2>Published Books</h2>
    {{ range . }}
      <div class="book-card">
        <div class="book-card__body">
          <span class="book-card__badge">{{ .platform }}</span>
          <h3 class="book-card__title">{{ .title }}</h3>
          {{ with .subtitle }}<p class="book-card__subtitle">{{ . }}</p>{{ end }}
          <div class="book-card__actions">
            <a href="{{ .url }}" target="_blank" rel="noopener noreferrer" class="btn btn-primary">
              View on Amazon Kindle &rarr;
            </a>
          </div>
        </div>
      </div>
    {{ end }}
  </section>
{{ end }}

This establishes a clean boundary between data authoring and user interface presentation. Updating my bibliography or career experience requires editing a structured YAML file. The layout templates remain completely untouched.

Continuous Delivery to Static Hosting

The operational simplicity of static HTML extends directly to hosting and deployment.

The previous Next.js architecture required an active Node.js server process or specialized edge hosting infrastructure with serverless execution limits. If an environment variable misconfiguration occurred, the entire application could throw a runtime 500 error on page requests.

With compiled static files, hosting is completely commoditized. The site is hosted on Hostinger using high-speed LiteSpeed web servers. When changes are pushed to the main branch on GitHub, an automated GitHub Actions workflow compiles the static files and performs an encrypted FTPS differential transfer:

# .github/workflows/deploy.yml
name: Deploy to Hostinger

on:
  push:
    branches: [main]
  schedule:
    # Daily scheduled build at 02:00 and 09:00 SAST
    - cron: '0 0,7 * * *'

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Code
        uses: actions/checkout@v7

      - name: Setup Hugo Extended
        uses: peaceiris/actions-hugo@v3
        with:
          hugo-version: '0.165.0'
          extended: true

      - name: Compile Static Site
        run: hugo --minify

      - name: Sync Changed Files via FTPS
        uses: SamKirkland/FTP-Deploy-Action@v4.4.0
        with:
          server: ${{ secrets.FTP_SERVER }}
          username: ${{ secrets.FTP_USERNAME }}
          password: ${{ secrets.FTP_PASSWORD }}
          local-dir: ./public/
          server-dir: ./

Notice the cron schedule in the workflow configuration. Because Hugo evaluates post publish dates during compilation, future-dated articles remain unpublished until their scheduled release time. When the scheduled daily cron triggers, the runner rebuilds the site, detects newly mature articles, and publishes them automatically without requiring manual commits.

The entire continuous deployment workflow completes in 12 seconds. There are no Docker containers to maintain, no daemon supervisors to restart, and zero server-side memory leaks.

The Operational and Psychological Payoff

After completing the migration, I compared the production metrics of the new Hugo architecture against the retired Next.js deployment:

MetricRetired Next.js AppRebuilt Hugo SiteImprovement
Local Full Build Time16,200 ms42 ms385x faster
Client JavaScript Transferred184 KB0 KB (Prose pages)100% eliminated
Total Dependencies812 packages0 packages (Go binary)Zero dependency debt
Transferred Page Weight (Home)248 KB18 KB92% reduction
Lighthouse Performance Score91 / 100100 / 100Perfect baseline
Server Runtime OverheadNode.js / V8 engineStatic file serverZero runtime attack surface

The quantitative improvements are substantial, but the psychological benefits are even more meaningful.

When your personal publishing platform is constructed from compiled static HTML and semantic CSS, a deep sense of durability returns to your work. There are no database migrations waiting to corrupt content. There are no breaking framework updates that require rewriting route handlers.

HTML files generated by Hugo today will render identically in web browsers thirty years from now. By shedding speculative complexity and embracing platform fundamentals, software engineering becomes durable, focused, and enjoyable again.