Skip to content

ブログを Astro 5 と Tailwind v4 にアップデートしてみた

目次

🎧 音声概要 (※ NotebookLM で生成されており、誤りを含む場合があります)

ふと思い立ったので Astro v3 で構築されていたこのブログを v5.7 にアップデートしつつ、ついでに Tailwind も v4 系にアップデートしました。

Astro 自体のアップデートよりも、もはや Tailwind のバージョン変更にやや手こずったのでその記録です。

Astro を v5 にアップデート

Astro のアップデートは、公式のアップグレードガイド [1] が用意されているのでそちらに従います。

基本的には、以下のコマンドを実行することで Astro 本体とその周辺パッケージをいい感じにアップデートしてくれます:

pnpm dlx @astrojs/upgrade

このブログは小さなプロジェクトなので、特に破壊的な影響を受けることはありませんでした。 場合によっては公式のガイド [1] に従って変更を行う必要があります。

Tailwind を v4 にアップデート

次に Tailwind を v4 にアップデートしていきます。 Tailwind v3 と v4 で仕様が変わった箇所がいくつかある [2] ので、それらをざっくりでも把握したうえで作業に臨むことをおすすめします。

このブログを Tailwind v4 に対応させる上で必要になった作業は以下の通りです:

  • Tailwind のバージョンアップデート
  • Astro の Tailwind integration の設定を変更
  • Tailwind の設定ファイルの記述変更

以下それぞれ説明します。

Tailwind のバージョンアップデート

公式のアップグレードガイド [2] に記載されている下記コマンドを実行します:

pnpm dlx @tailwindcss/upgrade

Tailwind v3 では tailwindcssPostCSS のプラグインとして扱われていましたが、v4 では PostCSS プラグインが @tailwindcss/postcss に内在する形に変わった [3] ため、それに対応する設定変更が必要です。postcss.config.mjs が以下のように変更されていることを確認してください:

postcss.config.mjs:

export default {
  plugins: {
    "@tailwindcss/postcss": {}
  }
};

Astro の Tailwind integration の設定を変更

Astro v3 には、Tailwind を良い感じに利用するための integration @astrojs/tailwind [4] がありました。しかしながら、Astro v5 ではこちらの integration は非推奨となっています。

Tailwind v4 を利用する場合は代わりに、Vite の plugin として設定することが推奨されています。詳しくは Astro 公式の Tailwind 設定に関するドキュメント [5] を参照してください。

本ブログの更新作業にあたっては、astro.config.mjs を以下のように更新しました。

更新前:

import tailwind from "@astrojs/tailwind";
// ...
export default defineConfig({
  // ...
  integrations: [sitemap(), tailwind()],
});

更新後:

import tailwindcss from "@tailwindcss/vite";
// ...
export default defineConfig({
  // ...
  integrations: [sitemap()],
  vite: {
    plugins: [tailwindcss()]
  },
});

Tailwind の設定ファイルの記述変更

Tailwind の version を上げた後に build を試みると、自分のケースではエラーが発生しました。 これは v3 と v4 の仕様の違い [2] によるものであり、該当箇所の記述を変更する必要があります。

今回は、以下の点を変更する必要がありました。

  • global.css 内で theme() が使えない
  • Tailwind の Typography プラグイン [6] の設定変更

原則として、Tailwind v4 では tailwind.config.cjs のような config ファイルは不要となり、global.css のような CSS ファイル内にすべて記述するようになります。

そこで、tailwind.config.cjs に対して以下のような変更を加えます:

  • theme で extend しているテーマを削除し、global.css に移す
    • ただし、typography のカスタマイズ設定は tailwind.config.cjs 側に残す [7]
  • darkMode の設定を global.css に移す

以下に、tailwind.config.cjsglobal.css の変更前と変更後をそれぞれ記載します。

tailwind.config.cjs (変更前):

module.exports = {
  content: ["./src/**/*.astro"],
  darkMode: "class",
  theme: {
    extend: {
      fontFamily: {
        body: ["Open Sans", "sans-serif"],
        heading: ["Open Sans", "sans-serif"],
      },
      
      // ... 他の拡張設定
      
      typography: () => ({
        // tailwind typography のカスタマイズ設定
      }),
    },
  },
  plugins: [require("@tailwindcss/typography")],
};

tailwind.config.cjs (変更後):

module.exports = {
  theme: {
    extend: {
      typography: () => ({
        // tailwind typography のカスタマイズ設定
      }),
    },
  },
};

global.css (変更前):

@tailwind base;
@tailwind components;
@tailwind utilities;

@layer base {
  /* 省略 */
}

@layer components {
  /* 省略 */
}

global.css (変更後):

@import "tailwindcss";

/* darkMode の設定 */
@custom-variant dark (&:where(.dark, .dark *));

/* typography plugin の import */
@plugin "@tailwindcss/typography";

/* config file の読み込み */
@config "../../tailwind.config.cjs";


/* theme.extend で指定していたテーマの記述 */
@theme {
  /* Font families */
  --font-body: "Open Sans", sans-serif;
  --font-heading: "Open Sans", sans-serif;

  /* color など他の設定も同様に記述する */
}

@layer base {
  /* 省略 */
}

@layer utilities {
  /* 省略 */
}

はまったポイント: Prism の依存関係

これで晴れて移行完了、と思いきや、build した際に require is not defined というエラーが発生しました。

調べてみると Prism の依存関係に起因するエラーであり、同様の issue が立てられていました。issue 内の解決策を参考に、prismjs を別途インストールしたうえで、astro.config.mjs を下記のように更新することで解決しました。

import tailwindcss from "@tailwindcss/vite";
// ...
export default defineConfig({
  // ...
  integrations: [sitemap()],
  vite: {
    plugins: [tailwindcss()],
    ssr: {
      external: ["prismjs"],
    },
  },
});

ついでに dependencies もガーデニング

以上で Astro や Tailwind の移行作業は終わりですが、ついでに他の依存関係も更新しておきます。 --latest オプションを付けることで package.json の内容も更新されます。

pnpm update --latest

最後に正常に動作することを確認したら DONE です。

余談: AI でアップデート作業は楽になったのか

AI のおかけで、だいぶこのような移行作業に対する腰の重さは個人的に軽くなった気がしています。 他方で、今回のバージョン更新のような、比較的新しい情報を用いて作業を行う場合はハルシネーションを起こす可能性も非常に高く、注意が必要だと感じました。

下記に AI の使い方に関するふりかえりをざっくりまとめます:

  • 良かった点
    • コンテキストを与え、スコープを絞った作業は AI にもできる
      • 例: tailwind.config.cjs のテーマ拡張を、Tailwind v4 の @theme ディレクティブの書き方に変更させるのはワークした
    • Tailwind のちょっとした疑問など、気になった点は都度 AI に質問して理解しながら作業を進められる
      • 例: Tailwind の各ディレクティブの働きなど
  • いまいちだった点
    • 情報の量とカットオフの観点から、Web 検索を行えるモデルが前提になる
      • いまや Web 検索は標準機能になりつつあるので問題ないっちゃない
    • 「移行に必要な作業は?」というざっくりした質問には正確に答えられない
      • そもそも質問のスコープが大きすぎるので当然ではある、質問の仕方だいじ
    • そして、バージョン移行作業などはその要諦を自分自身が理解できていない状態で始めるがゆえに情報の正しさを判断できないので、結局ソースを丁寧に見る必要がある
    • 今回のように Astro と Tailwind の統合など、複数の登場人物が絡むとハルシネーションを起こしやすい
    • 参照すべきドキュメントなどコンテキストを明示的に渡さないと、ハルシネーションを起こしやすい
      • 例: Tailwind v4 について聞いても平気で v3 の情報を基に回答することがある

実装にあたっては ChatGPT (o3, 4.1) や Gemini (2.5 Pro)、Claude 3.7、Perplexity などいろいろ使いながら進めました。所感としては、以下のようになります。

  • スコープの大きい質問をするなら o3
  • Web で調べたら分かるだろうな、とあたりがつくものは Perplexity
  • コーディングは GPT-4.1 / Gemini 2.5 Pro / Sonnet 3.7 を適宜使い分ける

自分のプロンプトエンジニアリング力不足によるところもあると思うので、精進せねば...という感じです。

以上、ここまでお読みいただきありがとうございました。

参考


[7]
本ブログでは typography プラグインに関していくつかのカスタマイズ設定を行っており、その設定だけ tailwind.config.cjs に残しています。本来であればすべて global.css 側に移行したかったのですが、公式の Adding custom color themes セクションでも tailwind config から import する手法が取られており、それを踏襲しています。