Skip to content
CTCO
Go back

Blog Refresh 2.0 – Improvements (May 2025)

Published:  at  07:00 PM
5 min read

Table of contents

Open Table of contents

Introduction

Ship, observe, iterate – repeat.

What are long weekends for if not to tinker with your blog site. 48 hours, ~200 changed files, and the occasional VS Code GitHub Copilot chat session later, here’s what landed.


Performance & Build Optimisations

AreaChangeBenefit
BundlerSwitched to the official Tailwind integration and dropped the Vite-only pluginCold-start ↓ ~0.3 s
Third-party JSAdded Partytown to off-load analytics to a web-workerMain thread stays unblocked
HTML at edgePost-build now runs Jampack to inline critical CSS, minify HTML and pre-render lazy imagesTTFB ↓ ≈ 12 %
// astro.config.ts – excerpt
import tailwind from "@astrojs/tailwind";
import react from "@astrojs/react";
import partytown from "@astrojs/partytown";

export default defineConfig({
  integrations: [
    tailwind({ applyBaseStyles: false }),
    react(),
    partytown({ config: { forward: ["dataLayer.push"] } }),
  ],
});

Content, Media & Branding


Visual Enrichment

Because great content deserves great presentation, I focused on two highly-visible upgrades:

FeatureWhat changedWhy it matters
Author avatarsEvery head-shot in /assets/images/author/ is auto-discovered at build-time via import.meta.glob and rendered as a responsive grid on the About page.Puts faces to names and gives the site an editorial feel.
Featured-card bannersA lightweight <Card> component now pulls a hero image for every post: I first look for an explicit ogImage, then gracefully fall back to a file in /assets/featured/ that matches the slug.Cards now resemble magazine covers rather than plain text links.

Author avatars – zero-config

// layouts/AboutLayout.astro – excerpt
const authorImageModules = import.meta.glob(
  '../assets/images/author/*.{jpg,jpeg,png,webp}',
  { eager: true, as: 'url' }
);
const authorImages = Object.values(authorImageModules) as string[];

The images drop into a CSS grid that expands or collapses with contributor count; max-width: 110px keeps every avatar uniformly cropped into a neat row of circles.

// components/Card.tsx – excerpt
export default function Card({ href, frontmatter }: Props) {
  const { title, ogImage } = frontmatter;
  const hero =
    typeof ogImage === "string"
      ? ogImage
      : ogImage?.src ?? `/assets/featured/${slugifyStr(title)}.png`;

  return (
    <article class="card">
      <a href={href}>
        <img src={hero} alt="" loading="lazy" />
        {/* title, date, reading-time, etc. */}
      </a>
    </article>
  );
}

To guarantee the fallback points at a real file, the build now generates an asset map of everything in /assets/featured/; missing images fail the build instead of 404-ing after deploy.

Copilot confession: Even this matching logic came from a Copilot Chat suggestion – and, yes, the AI reminded me to add loading="lazy" for better Core Web Vitals!


Developer-Experience Upgrades


Automation & CI/CD

The Azure Static Web Apps workflow slimmed from ~70 lines to 44:

name: CTCO.blog – Build & Deploy

on:
  pull_request:
    types: [closed]
    branches: [main]
  workflow_dispatch:

jobs:
  build_and_deploy_job:
    if: >-
      github.event_name == 'workflow_dispatch' ||
      (github.event_name == 'pull_request' && github.event.pull_request.merged)
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v3
        with:
          submodules: true

      - name: Build & Deploy
        uses: Azure/static-web-apps-deploy@v1
        with:
          azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN }}
          action: upload
          app_location: "/"
          output_location: "/dist"

Bonus: the job automatically closes preview environments once a PR merges.


Accessibility, SEO & Social


Conclusion

What started with caching tweaks; finished with a near-full-stack refactor and a shiny commit workflow that reminds me to write tidy messages before every push.



Previous Post
Building and Running Local Language Models in C# – Quickstart Edition
Next Post
From Coder to Leader: Reflections and a Look Ahead