Blog Setup

I’ve tried:

  • Writing HTML & CSS from scratch: repetitive.
  • Using Jekyll (for GitHub pages): Annoying to set up and manage - ran into rough edges with Ruby.
  • Writing a homebrew blog that compiles Markdown: Nice for just markdown!
  • Extending my homebrew blog with MDX plus server- and client-side components: frustrating to manage the complexity.
  • Next.js with MDX: Took some effort to set up, static site generation broken out of the box, and broke when I tried upgrading (from 14 to 15). Their website even recommends against using it for a complex personal blog.

And finally, I’ve settled on:

  • Astro: Decent initial blog set up, with MDX (!), easy to add Tailwind, and static site generation just works!

Issues & Fixes

A few random issues I’ve run into so far, and how I fixed them.

Font preloaded but not used within a few seconds

I’m including the Inter font locally (global.css):

@font-face {
	font-family: 'Inter';
	src: url('/fonts/inter.ttf');
}

But I kept getting this error:

The resource http://localhost:8000/fonts/inter.ttf was preloaded using link preload but not used within a few seconds from the window’s load event. Please make sure it has an appropriate as value and it is preloaded intentionally.

My issue is that I wasn’t specifying format("truetype"):

@font-face {
	font-family: 'Inter';
	src: url('/fonts/inter.ttf') format("truetype");
}

Optimizing images

To use optimized images in your MDX posts, you have to move the images somewhere into /src/ (I’m using /src/content/images/), and then follow the Astro guide to dynamically import them.

Note that in Markdown, they must be relative links, not absolute:

  • Good: ![Bangkok's Chinatown](../images/languages.png)
  • Bad: ![Bangkok's Chinatown](/src/content/images/languages.png)

Generating Favicons

I drew a square SVG in Inkscape. You can use an SVG for your favicon (in <head>) with:

<link rel="icon" href="/favicon.svg" type="image/svg+xml" />

But some browsers might still look for a .ico, so I also created that with ImageMagick & icoutils (nix-shell -p imagemagick icoutils):

magick src/icons/my_icon.svg -resize 32 /tmp/favicon_32x32.png
magick src/icons/my_icon.svg -resize 64 /tmp/favicon_64x64.png
icotool -c -o public/favicon.ico /tmp/favicon_32x32.png /tmp/favicon_64x64.png

Ban generative AI scrapers

Or, at least, the ones with crawlers that respect robots.txt. Also, include your Astro sitemap. In pages/robots.txt.ts:

import type { APIRoute } from "astro";

const disallowedAgents = [
  // OpenAI training generative AI foundation models
  // https://platform.openai.com/docs/bots
  "GPTBot",

  // FB training AI models
  // https://developers.facebook.com/docs/sharing/webmasters/web-crawlers
  "meta-externalagent",

  // Google Gemini generative APIs
  // https://developers.google.com/search/docs/crawling-indexing/google-common-crawlers
  "Google-Extended",
];

export const GET: APIRoute = ({ site }) => {
  // prettier-ignore
  return new Response(
`User-agent: *
Allow: /

${disallowedAgents.map(a =>
`User-agent: ${a}
Disallow: /`).join('\n\n')}

Sitemap: ${new URL("sitemap-index.xml", site).href}`);
};

Example generation:

User-agent: *
Allow: /

User-agent: GPTBot
Disallow: /

User-agent: meta-externalagent
Disallow: /

User-agent: Google-Extended
Disallow: /

Sitemap: https://julian.guide/sitemap-index.xml