Azure Static Web Apps Are Now Generally Available
I wrote recently about moving my blog from Wordpress to an Azure Static Site.
Azure Static Web Apps have now moved out of Preview and are now …
Read MoreI’ve standardized design, upgraded components, added client-side search, and configured browser caching for this site over the last few weeks. I’ve also adopted a workflow that helps me capture ideas, test changes, and get posts published more easily.
I’m not a web developer, I’m a database nerd. I’m don’t want to be great at CSS. I got this all done with a personal Cursor subscription and an obsessive urge to tag and tidy all my old posts. AI tooling these days makes static websites easier than ever to set up, design, and improve.
If you’re open to AI tooling and want to create a website (or upgrade one you already have), a static site is a great bet.

KendraLittle.com is a static site built with Hugo, a static site generator. I write posts in Markdown, Hugo builds them into HTML files, and Netlify hosts and deploys the site. There’s no database, no server-side code, and no Content Management System, just files in a free GitHub repository and Netlify.
I currently pay for a Netlify subscription and a Cursor subscription. It’s a hobby for me, I want some fun things, so those costs are worth it to me. (Netlify has a good free tier, but occasionally I’m lucky enough to exceed its bandwidth costs. And I feel like I get good value for what I pay for.)
This setup works well for a technical blog. It’s fast, secure, and easy to maintain.
And I have never missed WordPress.
I upgraded the major frontend components to bring the site up to date:
The Hugo upgrade here was a big deal. There were some important fixes between those two versions, and I’d struggled to do this upgrade on my own before. I knew it was important to complete this before making more changes.
These upgrades required template changes. Bootstrap 5 changed class names (navbar-toggle → navbar-toggler, data-toggle → data-bs-toggle). Hugo 0.152.2 deprecated some template functions, so I updated those too. Cursor helped identify all the places that needed changes, which made the upgrade process manageable.
It wasn’t bad this time at all, especially with the help of…
Every time I open a pull request or push a change to a branch with a pull request, Netlify creates a preview deployment. I can see how the site looks and behaves before merging to main. This is especially valuable for CSS changes: I can test button heights, spacing, and mobile layouts in a real deployment without affecting the live site.
It’s basically a staging environment that’s automatically created for every pull request, and you can access it on different devices. (This post is not sponsored by Netlify, but this database person sure loves a staging site. They include this in the free plan, by the way.)
I’m currently using Windows for my personal nerdery, so I have a PowerShell script that runs Hugo with specific flags for local development:
--disableFastRender to ensure CSS changes are visible immediately--noHTTPCache to prevent browser caching issues--buildFuture to preview future-dated postsThe script also syncs CSS files before starting the server and optionally builds the search index. These customizations make local development smoother: I can see CSS changes right away– at least usually. Admittedly, there are some times when I still have to do a full build of the site to see changes and I don’t fully understand why, but it’s not yet been a big enough problem to dive into.
Here’s the Hugo command the script runs:
hugo server --port 1313 --bind 127.0.0.1 --poll 700ms --disableFastRender --noHTTPCache --buildFuture
The --poll flag helps with reliable file watching on Windows, and --buildFuture lets me preview posts scheduled for future dates.
Before, buttons and form fields had wildly inconsistent heights, padding, and styling. I had a hard time fixing it.
I used Cursor to find all button definitions and help standardize them. This cut down on the repetitive work, and chatting with the robot helped me figure out how to troubleshoot visual issues in Firefox somewhat efficiently (well, sometimes).
I standardized everything to a 38px height:
It ain’t perfect, but it’s a lot better.
I added mobile optimizations for screens under 768px:
These changes make the site easier to use on phones and tablets.
I added Pagefind for client-side search. It indexes the built HTML files and provides search entirely in the browser, no backend needed. Here’s my new search page, and there’s a search form in the footer, too.
How it works:
Why this works for static sites:
I’m now using GitHub Issues to track bugs and planned blog posts.
The most important thing about this is that I can quickly create a GitHub issue on my phone for a blog post idea, wherever I am when I think about it. Or I can screenshot a bug on the site and create an issue for that, then forget it for the moment.
Years ago, when I was a part of Brent Ozar Unlimited, I had a big old list of blog post ideas in a shared GitHub repo with the team that I could pull from anytime I was in the mood to write. I loved it. It feels good to be getting back to that, even if it’s just me.
I created git hooks that automatically sync GitHub issues to local markdown files:
The hooks run a Python script that fetches issue data from the GitHub API and saves it to issues/issue-{number}.md. This lets me reference issues locally without opening GitHub in a browser.
I’m sure there are extensions and various other ways to do this, but I like markdown files, OK? My cursorrules file knows where my issues are, so I can also use prompts like, “new branch to write post in issue 29 to publish on Jan 10,” and it can get my workflow started and have an idea what basic things to put in the front matter of the new file it creates for me.
I may switch this to a different mechanism than a git hook. Git is complex enough. But it’s good enough for now.
--- markers. It contains information like the post title, date, categories, and tags. Hugo uses this metadata to generate the site structure and HTML.I spent a good chunk of time cleaning up categories and tags across all my old blog posts. I had inconsistent taxonomy: tons of posts used “sql” as a category, many posts were missing tags entirely.
What I standardized:
query-performance, indexing, database-administration) and moved old posts to the right categories. I also added azure-sql as a new category for cloud database content.query-store not Query Store or query_store).This was not something that Cursor made super easy. I have 580+ posts at this point, and I didn’t feel like building a whole sentiment/content analysis machine. A few attempts to use python scripts to generate the tags didn’t go well, the tags were often ridiculous. Cursor doesn’t love going through that many posts manually, either.
I worked through a fair amount of this while cooking things for Thanksgiving and doing other things. Not letting perfect be the enemy of good helped get me through it, and I’m pretty happy with where we landed.
Hugo supports future-dated posts. I can set a post’s date in the future, and Hugo won’t publish it until that date arrives.
How it works:
Here’s an example of front matter for a scheduled post:
---
title: "My Future Post"
date: 2026-01-10
draft: false
---
This is a simple way to scheduling posts in advance. The static site generator (Hugo) handles the scheduling logic, and automated builds handle the publishing.
Netlify automatically builds and deploys the site when I push to the main branch. It also runs scheduled builds twice daily.
How scheduled builds work:
netlify/functions/) triggers at set times using a cron scheduleSetting up the build trigger: I created a Netlify scheduled function that uses the @netlify/functions package’s schedule helper. The function is configured with a cron expression in netlify.toml and calls a build hook URL that Netlify generates. When you POST to that URL, it triggers a new deployment.
Here’s the scheduled function code:
import fetch from 'node-fetch'
import { schedule } from '@netlify/functions'
const BUILD_HOOK = 'https://api.netlify.com/build_hooks/YOUR_HOOK_ID'
// Runs twice daily: 11 AM UTC (6 AM EST) and 1 AM UTC (8 PM EST)
const handler = schedule('0 11,1 * * *', async () => {
const response = await fetch(BUILD_HOOK, {
method: 'POST'
})
return {
statusCode: 200,
body: JSON.stringify({ message: 'Deploy triggered successfully' })
}
})
export { handler }
The cron expression '0 11,1 * * *' runs at 11 AM UTC and 1 AM UTC daily. You configure this in netlify.toml:
[functions.scheduled-deploy]
schedule = "0 11,1 * * *"
I’ve configured cache headers in netlify.toml to improve performance. Static assets (CSS, JavaScript, images, fonts) are cached for 1 year with the immutable flag: these files have versioned filenames from Hugo’s asset pipeline, so aggressive caching is safe. HTML files are cached for 1 hour with must-revalidate, which balances performance with the need to show updated content.
Here’s the cache header configuration in netlify.toml:
# Static assets - cache for 1 year (with versioning in filename)
[[headers]]
for = "/*.css"
[headers.values]
Cache-Control = "public, max-age=31536000, immutable"
[[headers]]
for = "/*.js"
[headers.values]
Cache-Control = "public, max-age=31536000, immutable"
[[headers]]
for = "/images/*"
[headers.values]
Cache-Control = "public, max-age=31536000, immutable"
# HTML files - cache for 1 hour with revalidation
[[headers]]
for = "/*.html"
[headers.values]
Cache-Control = "public, max-age=3600, must-revalidate"
The site also uses a service worker for offline support and client-side caching. Initially, it was using “Cache First” for images even during local development, which meant updated images wouldn’t show up in preview until I cleared the browser cache. I updated the service worker to detect when it’s running on localhost and use “Network First” for images in development, so image updates appear immediately. In production, it still uses “Cache First” for better performance.
Here’s what I’m currently using and how to get started:
Core components:
Getting started:
brew install hugo (Mac) or choco install hugo (Windows) or download from hugo.iogit clone <your-repo-url> && cd <repo-name> && hugo new site ..gitignore file with public/ and .hugo_build.lock (Hugo generates these, don’t commit them)git submodule add https://github.com/devcows/hugo-universal-theme themes/universalconfig.toml with theme = "universal" (or your chosen theme name)hugo new blog/my-first-post.mdhugo server (view at http://localhost:1313)git add . && git commit -m "Initial Hugo site" && git pushThe Hugo documentation is excellent, and the Universal theme has good examples to get you started. Add Pagefind for search after you have content, and customize from there.
Jekyll is another popular static site generator, and it’s the default for GitHub Pages. The main difference is that Jekyll is Ruby-based while Hugo is Go-based. Jekyll has a larger plugin ecosystem, while Hugo uses Go templates. Jekyll has big fans and a huge community. I’ve experimented with it recently on a new site and I really like it.
For now, I’m resisting the urge to give this site a Jekyll makeover, but if it still seems like a fun idea in a couple of months I may give it a shot.
Copyright (c) 2025, Catalyze SQL, LLC; all rights reserved. Opinions expressed on this site are solely those of Kendra Little of Catalyze SQL, LLC. Content policy: Short excerpts of blog posts (3 sentences) may be republished, but longer excerpts and artwork cannot be shared without explicit permission.