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
Area | Change | Benefit |
---|---|---|
Bundler | Switched to the official Tailwind integration and dropped the Vite-only plugin | Cold-start ↓ ~0.3 s |
Third-party JS | Added Partytown to off-load analytics to a web-worker | Main thread stays unblocked |
HTML at edge | Post-build now runs Jampack to inline critical CSS, minify HTML and pre-render lazy images | TTFB ↓ ≈ 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
-
Reading-time in front-matter – a tiny remark plugin now injects “3 min read” semantics automatically:
// src/utils/remark-reading-time.mjs import getReadingTime from "reading-time"; import { toString } from "mdast-util-to-string"; export function remarkReadingTime() { return (tree, { data }) => { const minutes = getReadingTime(toString(tree)); data.astro.frontmatter.readingTime = minutes.text; }; }
-
Dynamic Open Graph images –
generateOgImages.tsx
renders social cards on-the-fly with Satori/Resvg; no more hand-rolled PNGs. -
Static assets migrated to
/public
so Azure Static Web Pages can serve them straight from KV – goodbye 404 on cold builds.
Visual Enrichment
Because great content deserves great presentation, I focused on two highly-visible upgrades:
Feature | What changed | Why it matters |
---|---|---|
Author avatars | Every 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 banners | A 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.
Featured-card banners – automatic matching
// 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
-
ESLint for Astro and Prettier 3 config added; both run pre-commit via Husky + lint-staged:
# .husky/pre-commit #!/usr/bin/env sh . "$(dirname -- "$0")/_/husky.sh" npx lint-staged # formats & lints only the changed files
-
Commitizen + Conventional Commits – every merge now feeds the changelog generator automatically.
-
GitHub Copilot Chat – candidly, I leaned on it to stub
socialIcons.ts
, to convert old MDX import paths, and to sanity-check regexes. Even, on occasion, us more senior devs outsource the drudgery of repetitive or tiresome work now and then.
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
-
Inline-SVG social icons replace the old bundled assets, trimming 30 extra requests from the home page:
// socialIcons.ts – excerpt const socialIcons = { Twitter: `<svg …><path d="M22 4.01c-1 .49 …"/></svg>`, LinkedIn: `<svg …></svg>`, }; export default socialIcons;
-
Share-Links component now generates platform-specific URLs on the fly (WhatsApp, Telegram, Pinterest, Mail, …).
-
Slugification now uses
github-slugger
, fixing edge-cases likeC# 12 – what’s new?
.
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.