本文へ移動
aapp-hacks
← 記事一覧へ
著者
app-hacks
公開日

Tailwind CSS / Astro / Markdown

Tailwind CSS Typographyで実現するMarkdown記事の美しいスタイリング

Tailwind CSS 4とTypographyプラグインを使い、AstroのMarkdown本文を読みやすく整える方法を解説する。

Markdownから生成されるHTMLは、通常のコンポーネントと性質が異なる。本文中のh2pulpreへ一つずつユーティリティクラスを付けることはできないからである。Tailwind CSS Typographyは、親要素にproseクラスを置くだけで、その内側にある文章要素へ一貫したタイポグラフィを適用する。

このプロジェクトはTailwind CSS 4を使用している。Astroとの接続には@astrojs/tailwindではなく、@tailwindcss/viteを使う。前者はTailwind CSS 3向けの旧方式であり、新規構成へ持ち込まない。

必要なパッケージと設定

Astroの公式CLIでTailwindを追加すると、Tailwind本体とViteプラグインが導入される。Typographyは別途インストールする。

npx astro add tailwind
npm install @tailwindcss/typography

Astro設定ではViteプラグインとして登録される。Tailwind専用のAstro integrationを追加する必要はない。

// astro.config.mjs
import { defineConfig } from 'astro/config';
import tailwindcss from '@tailwindcss/vite';

export default defineConfig({
  vite: {
    plugins: [tailwindcss()],
  },
});

グローバルCSSではTailwind本体とTypographyプラグインを読み込む。Tailwind CSS 4ではCSSファーストの設定が基本になるため、プラグインを使うためだけにtailwind.config.jsを作る必要はない。

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

@theme {
  --color-ink: #17212b;
  --color-muted: #667582;
  --color-accent: #0f83d8;
}

このCSSを共通レイアウトの先頭でimportすれば、すべてのページからユーティリティとproseを利用できる。レイアウトから読み込むことで、記事ごとに同じCSSをimportする重複も避けられる。

Markdown本文へproseを適用する

Content Collectionsから取得した記事はrender()でコンポーネントに変換する。Contentの外側にproseを置き、横幅を明示的に解除するのが扱いやすい。

---
import { render } from 'astro:content';

const { post } = Astro.props;
const { Content } = await render(post);
---

<article class="mx-auto max-w-3xl rounded-2xl bg-white px-6 py-10">
  <div
    class="prose prose-slate max-w-none
      prose-headings:font-bold
      prose-a:text-sky-700 prose-a:no-underline
      hover:prose-a:underline"
  >
    <Content />
  </div>
</article>

Typographyのproseには既定の最大幅がある。外側のarticleですでに本文幅を制御している場合、内側ではmax-w-noneを指定し、二重の幅制限を避ける。本文幅は文字サイズだけでなく、一行が長くなりすぎないことにも影響するため、ページ全体ではmax-w-3xl程度の上限を持たせる。

CSS変数で本文色を調整する

細かな色や行間は、Typographyが公開するCSS変数を上書きすると整理しやすい。見出しやコードごとに長いvariantを並べるより、記事用クラスへまとめた方がレイアウト側を読める状態に保てる。

@layer components {
  .article-prose {
    --tw-prose-body: #33414d;
    --tw-prose-headings: #17212b;
    --tw-prose-links: #0968ad;
    --tw-prose-code: #17212b;
    --tw-prose-pre-code: #e6edf3;
    --tw-prose-pre-bg: #15222e;
    --tw-prose-quote-borders: #69b8ed;
    font-size: 1.0625rem;
    line-height: 1.95;
  }

  .article-prose :where(h2, h3) {
    scroll-margin-top: 6rem;
    letter-spacing: -0.025em;
  }
}

本文色を真っ黒にすると長文ではコントラストが強く感じられるため、本文は濃いグレー、見出しはより濃い色に分ける。リンク色は周辺テキストとの差を確保し、hoverだけに依存せず通常時からリンクと分かる表現にする。下線を消す場合も色差とフォーカス表示を残す必要がある。

コードブロックとインラインコードを分ける

AstroのMarkdownコードフェンスはシンタックスハイライト済みのprecodeへ変換される。コードブロックは長い行を折り返すより、横スクロールを許可した方がインデントと行構造を保ちやすい。

.article-prose :where(pre) {
  overflow-x: auto;
  border: 1px solid #263848;
  border-radius: 0.75rem;
  box-shadow: 0 10px 30px rgb(23 33 43 / 8%);
}

.article-prose :where(code):not(:where(pre code)) {
  border: 1px solid #d9e2e9;
  border-radius: 0.35rem;
  background: #eef3f6;
  padding: 0.12em 0.36em;
  font-size: 0.88em;
  font-weight: 600;
}

.article-prose :where(code):not(:where(pre code))::before,
.article-prose :where(code):not(:where(pre code))::after {
  content: none;
}

インラインコードでは背景と境界を使い、本文から識別できるようにする。Typographyが装飾用の引用符を疑似要素で加える場合があるため、明示的に消して実際のコード文字列と混同させない。

引用は注意書きとして多用せず、本当に別の発言や補足を示す場面に限定する。背景色と左境界を薄く付け、斜体を解除すると日本語本文でも読みやすい。見出しにはscroll-margin-topを設定しておけば、アンカー移動時に固定ヘッダーへ隠れにくい。

Typographyは完成済みテーマではなく、Markdownの要素間隔と基本色をまとめる土台である。外側の本文幅、通常テキストの行間、コードブロックのスクロール、リンクの識別性を同時に調整することで、記事全体を一つの読書面として設計できる。