Skip to content

A Deep Dive Into My Blog's Technical Setup

A walkthrough of how this blog is built — what stack it runs on, the custom pieces I’ve layered on top, and how it deploys.

Core Technologies

The stack is small and deliberately boring:

The blog started from Sat Naing’s Astro Paper theme. Most of the customizations — the resume page with PDF download, the table of contents component, various UI tweaks — are layered on top of that base.

Theme and Styling

Custom theme rather than off-the-shelf, but built on top of Astro Paper’s foundation.

Light & Dark Mode

Tailwind Configuration

Theme-Aware Syntax Highlighting

Code blocks track the active theme. Astro uses Shiki for syntax highlighting, and toggle-theme.js swaps Shiki’s styles when the theme changes — a custom dark color map (githubDarkColorMap) is applied so dark-mode code stays readable.

// Conceptual snippet from toggle-theme.js related to Shiki
function updateShikiTheme(theme) {
  const shikiBlocks = document.querySelectorAll('pre.shiki');
  if (theme === 'dark') {
    // Apply dark theme styles, potentially by adding/removing classes
    // or directly manipulating style properties based on githubDarkColorMap
  } else {
    // Apply light theme styles
  }
}

Content Management & Structure

Astro’s content collections handle the posts.

Key Layouts

Custom Features

Dynamic Open Graph Image Generation

The result: every post gets a consistently branded social preview image without me touching anything.

// Simplified conceptual example from src/utils/generateOgImages.tsx
// (Actual implementation involves Astro's build hooks)

// import { satori } from 'satori';
// import { Resvg } from '@resvg/resvg-js';
// import { siteOgImage } from './siteOgImage'; // A React component

// async function generateImage() {
//   const svg = await satori(
//     siteOgImage({ title: 'My Awesome Blog Post' }),
//     {
//       width: 1200,
//       height: 630,
//       fonts: [/* font data */],
//     }
//   );
//   const resvg = new Resvg(svg);
//   const pngData = resvg.render();
//   const pngBuffer = pngData.asPng();
//   // fs.writeFileSync(..., pngBuffer);
// }

Interactive Resume with PDF Generation

Reusable Astro Components

The src/components/ directory has the standard set:

Helper Utilities

src/utils/ keeps the small stuff:

SEO and Performance

SEO

Performance

// Example snippet from astro.config.mjs (or .ts) for Vite options
// import { defineConfig } from 'astro/config';

// export default defineConfig({
//   vite: {
//     build: {
//       rollupOptions: {
//         output: {
//           manualChunks(id) {
//             if (id.includes('node_modules')) {
//               // Group vendor modules into a separate chunk
//               return 'vendor';
//             }
//           }
//         }
//       }
//     }
//   }
// });

Hosting and Deployment

The blog is hosted on Cloudflare Pages, with deploys driven by Git:

What this gets you in practice:

Development Experience

Step-by-Step Deployment Guide

If you want to deploy the same way, here’s the full path.

I. Prerequisites:

II. Setting Up Cloudflare Pages:

  1. Log in to Cloudflare and open the dashboard.
  2. Workers & Pages → Pages.
  3. Create a project and pick “Connect to Git”.
  4. Connect to GitHub:
    • Authorize Cloudflare. You can grant access to all repos or just one.
    • Pick the blog repo.
  5. Configure build settings:
    • Project name — becomes part of your *.pages.dev subdomain.
    • Production branch — usually main.
    • Framework preset — Astro (usually auto-detected).
    • Build commandnpm run build or astro build.
    • Build output directorydist/.
    • Root directory — leave default unless your project is in a subdir.
    • Environment variables — add anything your build or runtime needs (API keys, etc.). You can set different values for production and preview.
  6. Save and Deploy. First build takes a few minutes. Watch the logs in the dashboard.

III. Build and Deployment Process:

IV. Automatic Preview and Production Deployments:

V. Custom Domains (Optional):

  1. Pages project → Custom domains.
  2. Set up a custom domain.
    • If your domain is on Cloudflare DNS, it’s a CNAME record.
    • Otherwise, Cloudflare gives you the records to add at your registrar.
  3. Cloudflare manages the SSL/TLS certificate automatically.

VI. Troubleshooting:

Conclusion

That’s the setup. Astro for performance, Tailwind for styling, TypeScript for safety, and a small set of custom features to cover what I needed beyond the base theme. None of it is exotic — most of the value is in keeping the moving parts small and the build / deploy loop fast.

If you’re putting together something similar, the source is on GitHub — feel free to pull it apart.

Tags