a-blog cms の develop テーマを Tailwind CSS v4 の CSS-first 構成に寄せた話

2026年06月29日 CMS #テーマ #Tailwindcss

a-blog cms の develop テーマでは、Vite と Tailwind CSS v4 を使って CSS と JavaScript をビルドしています。

今回見直したかったのは、Tailwind CSS v4 を使っているにもかかわらず tailwind.config.js が残っている点でした。Tailwind CSS v4 では、テーマトークンやユーティリティ、プラグインの読み込みを CSS 側で扱う CSS-first な構成が基本になります。もちろん JavaScript config が完全に使えなくなったわけではありませんが、必要がないなら CSS 側へ寄せた方が、設定の所在が分かりやすくなります。

この記事では、a-blog cms の develop テーマで tailwind.config.js を使わない構成へ変更した内容をまとめます。

変更前の構成

対象のテーマディレクトリは次の場所です。

web/themes/develop

Vite の設定では、Tailwind CSS v4 用の Vite プラグインを読み込んでいました。

import tailwindcss from '@tailwindcss/vite';

メイン CSS もすでに Tailwind v4 らしい書き方になっていました。

@import 'tailwindcss';
@import './_theme.css';
@import './editor.css';

@utility container {
  max-width: 64rem;
  padding-inline: 1rem;
  margin-inline: auto;
}

つまり、テーマ全体が古い Tailwind v3 構成だったわけではありません。

残っていた主な依存は、src/style/editor.css のこの部分でした。

@plugin "@tailwindcss/typography";

@config '../../tailwind.config.js';

@config は JavaScript の Tailwind config を CSS から読み込むためのディレクティブです。今回のテーマでは、tailwind.config.js の中に @tailwindcss/typography.prose 向けカスタマイズが大量に書かれていました。

なぜ tailwind.config.js が残っていたのか

理由は @tailwindcss/typography です。

このプラグインは .prose クラスに対して、記事本文や CMS から出力される HTML に読みやすいタイポグラフィスタイルを付けてくれます。develop テーマでは、a-blog cms のブロックエディターやカラムユニットに合わせて、次のような要素を .prose の中で細かく調整していました。

  • 画像、動画、figure、caption
  • テーブルと横スクロール
  • リンクボタン
  • ファイルブロック
  • 埋め込みカード
  • カラムレイアウト
  • 地図、ストリートビュー
  • 目次
  • a-blog cms のテーブル用クラス

Tailwind Typography の raw CSS カスタマイズは、現状では JavaScript config の theme.extend.typography に書く形が案内されています。そのため、Tailwind v4 に移行していても、この部分だけ tailwind.config.js が残りやすい状態でした。

ただし今回のカスタマイズは、Tailwind の theme API に深く依存しているというより、ほとんどが通常の CSS と CSS カスタムプロパティで表現できる内容でした。

そこで、tailwind.config.js の役割を CSS 側へ移すことにしました。

方針

今回の方針はシンプルです。

@tailwindcss/typography 自体は引き続き使います。ただし、プラグインの raw CSS カスタマイズを tailwind.config.js 経由で注入するのではなく、テーマ側の CSS として src/style/editor.css に書きます。

変更後は、editor.css がこのような構成になります。

/* 管理画面側では、preflight を読み込まないようにする */
@layer theme, base, components, utilities;

@import 'tailwindcss/theme.css' layer(theme);
@import 'tailwindcss/utilities.css' layer(utilities);
@import './_theme.css';

@plugin "@tailwindcss/typography";

@layer utilities {
  .prose :where(...):not(:where(.not-prose, .not-prose *)) {
    ...
  }
}

ポイントは、.prose の中にだけ効くようにスコープしていることです。

Typography プラグインは、.prose 内の要素に対して次のようなセレクタを生成します。

.prose :where(p):not(:where([class~='not-prose'], [class~='not-prose'] *)) {
  ...
}

今回追加した CSS でも同じ考え方を踏襲し、.not-prose の中には影響しないようにしています。

.prose :where([data-type='linkButton']):not(:where(.not-prose, .not-prose *)) {
  margin-top: 1.25em;
  margin-bottom: 1.25em;
}

これにより、Typography プラグインの基本スタイルはそのまま使いながら、a-blog cms 固有の HTML だけをテーマ側で上書きできます。

実際に変更したファイル

主に変更したのは次のファイルです。

src/style/editor.css
src/style/_theme.css
stylelint.config.js
tailwind.config.js

tailwind.config.js を削除

まず、tailwind.config.js は削除しました。

もともとこのファイルには、theme.extend.typography.DEFAULT.css として .prose 用の CSS-in-JS が書かれていました。そこにあった内容を、通常の CSS として src/style/editor.css に移しています。

JavaScript で書かれていた次のような値も、

color: theme('colors.gray.500')

Tailwind v4 の CSS 変数へ置き換えました。

color: var(--color-gray-500);

また、ブレイクポイント参照も JavaScript の theme() ではなく、CSS 側で直接扱う形にしました。

@media (width >= 48rem) {
  ...
}

.prose カスタマイズを editor.css に移動

src/style/editor.css には、Typography プラグインの読み込みに続けて、a-blog cms 向けの .prose カスタマイズを追加しました。

例えば、リンクボタンは次のように定義しています。

.prose :where([data-type='linkButton'] a, [data-type='fileBlock'][data-display-type='button'] a):not(
  :where(.not-prose, .not-prose *)
) {
  display: inline-flex;
  gap: 0.375em;
  align-items: center;
  padding: 0.5em 0.75em;
  font-size: var(--text-sm);
  font-weight: var(--font-weight-semibold);
  line-height: 1.3;
  color: var(--color-gray-900) !important;
  text-decoration: none !important;
  background-color: var(--color-indigo-50) !important;
  border: 1px solid var(--color-gray-200);
  border-radius: var(--radius-md);
  transition-property: opacity;
}

埋め込みカードも、CMS の出力クラスに合わせて .prose の中でスタイルしています。

.prose :where([class*='column-embed'] .acms-embed-link):not(:where(.not-prose, .not-prose *)) {
  display: block;
  padding: 0;
  overflow: hidden;
  font-weight: 400;
  color: inherit;
  text-decoration: none;
  background-color: var(--color-white);
  border: 1px solid var(--color-gray-200);
  border-radius: var(--radius-md);
  transition-property: opacity;
}

このように、tailwind.config.js の中に閉じ込められていた CMS 固有の見た目を、テーマの CSS として読める場所へ戻したのが今回の一番大きな変更です。

_theme.css の breakpoint 参照を v4 らしく修正

src/style/_theme.css では、もともと次のように書かれていました。

@media (width >= theme(breakpoint.sm)) {
  --unit-gap-x: 2em;
}

これを Tailwind v4 の CSS 変数トークンに合わせて、次のように変更しました。

@media (width >= theme(--breakpoint-sm)) {
  --unit-gap-x: 2em;
}

@theme で定義した値や Tailwind が提供するトークンは、CSS 側から theme(--breakpoint-sm) のように参照できます。

stylelint.config.js から @config の許可を削除

@config を使わない構成にしたので、Stylelint 側でも config at-rule の許可を外しました。

これにより、今後うっかり @config を戻した場合に lint で気づきやすくなります。

ignoreAtRules: [
  'theme',
  'source',
  'utility',
  'variant',
  'custom-variant',
  'plugin',
  'apply',
],

注意したところ

今回の移行では、単に tailwind.config.js を消すだけでは不十分です。

tailwind.config.js 経由で Typography プラグインに渡していた CSS は、プラグイン側で .prose :where(...):not(...) の形に変換されていました。そのため、CSS に移すときも同じスコープを保たないと、テーマ全体にスタイルが漏れる可能性があります。

特に CMS の本文エリアでは、not-prose を使って Typography の影響を受けない領域を作ることがあります。そのため、追加したルールは基本的に次の形に揃えています。

.prose :where(...):not(:where(.not-prose, .not-prose *)) {
  ...
}

また、a-blog cms が出力するクラスには js-edit_inplacecolumnIcon のように、Stylelint の kebab-case ルールに合わないものがあります。これは自分たちで命名している CSS クラスではなく、CMS 側の出力に合わせる必要があるため、その部分だけ selector-class-pattern を無効化しています。

/* stylelint-disable selector-class-pattern */
.prose :where(:first-child.js-edit_inplace > :first-child):not(:where(.not-prose, .not-prose *)) {
  margin-block-start: 0;
}
/* stylelint-enable selector-class-pattern */

確認したこと

変更後、次の確認を行いました。

npm run stylelint

Stylelint は成功しました。

npm run build

Vite の production build も成功しました。

ビルド後の CSS は次のように生成されています。

dist/assets/admin-Bp1MALJ0.css
dist/assets/bundle-IRhKuCPZ.css

また、ローカルサイトにもアクセスして確認しました。

https://tailwind.ddev.site/

トップページは HTTP 200 で応答し、HTML 側も新しいビルド済みファイルを参照していました。

<link rel="stylesheet" href="/themes/develop/dist/assets/bundle-IRhKuCPZ.css?...">
<script type="module" src="/themes/develop/dist/assets/bundle-CbAxGCho.js?..." async></script>

この変更で良くなったこと

今回の変更で、Tailwind CSS v4 の CSS-first 構成にかなり近づきました。

tailwind.config.js を見に行かなくても、テーマの見た目に関する設定は src/style 配下で追えるようになっています。

特に editor.css は、a-blog cms のブロックエディターや本文表示に関わる CSS なので、Typography のカスタマイズもそこにまとまっていた方が自然です。

変更前は次のような構成でした。

editor.css
  -> @plugin "@tailwindcss/typography"
  -> @config "../../tailwind.config.js"
       -> typography.DEFAULT.css

変更後は次のようになります。

editor.css
  -> @plugin "@tailwindcss/typography"
  -> @layer utilities に .prose カスタマイズを書く

設定の流れが短くなり、Tailwind v4 の書き方としても素直になりました。

まとめ

Tailwind CSS v4 では、できるだけ CSS 側に設定を寄せることで、テーマトークンやユーティリティ、プラグインの読み込みを一箇所で管理しやすくなります。

今回の develop テーマでは、tailwind.config.js が残っていた理由は主に @tailwindcss/typography の raw CSS カスタマイズでした。その内容を src/style/editor.css に移すことで、@config を使わずに同等のスタイルを維持できるようにしました。

結果として、tailwind.config.js なしで stylelintvite build が通り、ローカルサイトでも新しい CSS/JS が読み込まれる状態になっています。

Tailwind v4 への移行では、「JavaScript config を全部消せるか」よりも、「その config が本当に Tailwind の設定である必要があるか」を見直すのが大事だと感じました。今回のように、実態がテーマ固有の CSS であれば、CSS に戻してしまった方が読みやすく、保守もしやすくなります。


と、今回は Codex くんが教えてくれました。

修正した、ファイルは

  • develop/src/style/editor.css

  • develop/src/style/_theme.css

で、develop/stylelint.config.js から @config の許可を削除し、当初の目標であった tailwind.config.js を削除することができました。

修正したファイルをアップしておきます。

著者写真
この記事を書いた人
山本 一道 / 有限会社アップルップル 代表

名古屋のWeb制作会社 (有)アップルップル代表。HTMLファーストな国産CMS「a-blog cms」開発・販売・サポート / 名古屋のWeb制作者コミュニティ「WCAN」主催 / コワーキングスペース「ベースキャンプ名古屋」運営。Web制作の現場をより良くするための活動をしています。

@kazumich

関連記事

この記事のハッシュタグ #テーマ#Tailwindcss から関連する記事を表示しています。

エンジニアじゃなくても MAMP と Tailwind CLI を活用できるようにしてみよう
エンジニアじゃなくても MAMP と Tailwind CLI を活用できるようにしてみよう
lp@simple2016 2.7対応版を用意しました
lp@simple2016 2.7対応版を用意しました
ランディングページ用のテーマ lp@simple2016 を作りました
ランディングページ用のテーマ lp@simple2016 を作りました
a-blog cms 2.5 で3つのテーマが新しくなりました
a-blog cms 2.5 で3つのテーマが新しくなりました
broken_image No Image
site2014のテーマをマルチブログ化する際に大事なのはテーマの継承とモジュールIDのグローバル化
WQHD対応のブログテーマ vgrid@blog2014
WQHD対応のブログテーマ vgrid@blog2014