Laravel MixでTailwindとSassをビルドする設定ハンズオン
概要
Laravel Mix は webpack を薄くラップしたアセットビルドツールで、webpack.mix.js に数行書くだけで JS/CSS のトランスパイル・バンドル・ハッシュ付与ができます。本記事では、1つのプロジェクトで Tailwind CSS(ユーティリティ)と Sass(独自コンポーネントの構造的なスタイル)を併用 し、両者を1本の CSS にビルドするところまでをハンズオン形式で進めます。
「Tailwind を入れたら Sass の @use が効かない」「@apply が未定義だと怒られる」「本番で未使用クラスが落ちない」といった、併用時にハマりやすいポイントを順に潰していきます。
なお、Laravel Mix は現在メンテナンスモードで、新規プロジェクトは Vite が公式デフォルトです。ただし既存案件の多くは今も Mix で動いており、保守・改修の文脈で設定を触る機会は依然多いため、本記事はその「既存 Mix プロジェクトを触る」想定としています。
環境
- Laravel 9 系(
laravel-mix同梱の既存プロジェクトを想定) - Node.js 18 LTS / npm 9
- laravel-mix
^6.0 - tailwindcss
^3.x - sass / sass-loader
バージョンは記事執筆時点のものです。Tailwind v3 系と Mix v6 系の組み合わせを前提としています(v4 系は設定方式が変わるため本記事の対象外)。
仕組み:なぜ併用でハマるのか
Laravel Mix における CSS の処理パイプラインは、ざっくり次の順序です。
.scss → sass-loader(Sass→CSS) → postcss-loader(Tailwind / Autoprefixer)→ 出力
最重要ポイントは Tailwind が PostCSS プラグインとして動く ことです。つまり Sass のコンパイルが終わった後の CSS に対して Tailwind が走ります。@tailwind base; のようなディレクティブは Sass にとってはただの通過点で、Sass はこれを素通しし、後段の PostCSS で Tailwind が展開します。
したがって Mix の設定で PostCSS のプラグイン配列に tailwindcss を明示的に渡す 必要があります。これを忘れると @tailwind が展開されません。@apply が落ちるのも同じ理屈で、@apply は Tailwind(PostCSS 段)が解決するため、Sass 段では素通しされます。
手順
1. パッケージの導入
npm install -D tailwindcss@^3 postcss autoprefixer npm install -D sass sass-loader
laravel-mix 本体はプロジェクトに既にある前提です。無ければ npm install -D laravel-mix も追加します。
2. Tailwind の設定ファイルを生成
npx tailwindcss init
生成された tailwind.config.js の content に、クラスを使うテンプレートのパスを列挙します。ここが未使用クラス削除(purge)の要です。
// tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'./resources/**/*.blade.php',
'./resources/**/*.js',
'./resources/**/*.vue',
],
theme: {
extend: {},
},
plugins: [],
};
3. エントリ CSS を用意する
resources/sass/app.scss を作り、Tailwind のディレクティブと自前の Sass を同居させます。
// resources/sass/app.scss
// Tailwind のレイヤー(PostCSS 段で展開される)
@tailwind base;
@tailwind components;
@tailwind utilities;
// ここから下は自前の Sass
@use 'variables' as v;
@use 'components/card';
body {
font-family: v.$font-base;
}
// resources/sass/_variables.scss $font-base: 'Helvetica Neue', Arial, sans-serif; $color-primary: #2563eb;
// resources/sass/components/_card.scss
.card {
// @apply は Tailwind(後段)が解決する。Sass 段では素通し。
@apply rounded-lg shadow p-4;
// Sass ならではのネスト・変数も併用できる
&__title {
font-weight: 700;
}
}
@use は Sass モジュールシステムで、古い @import の後継です。変数は名前空間(ここでは v.)経由で参照します。
4. webpack.mix.js を設定する
ここが本記事の核心です。.sass() で Sass をコンパイルし、.options() の postCss に Tailwind プラグインを明示的に渡します。
// webpack.mix.js
const mix = require('laravel-mix');
mix
.js('resources/js/app.js', 'public/js')
.sass('resources/sass/app.scss', 'public/css')
.options({
postCss: [
require('tailwindcss'),
require('autoprefixer'),
],
});
if (mix.inProduction()) {
mix.version(); // ハッシュ付与でキャッシュバスティング
}
.sass() の第1引数が .scss でも問題ありません(sass-loader が拡張子で判定)。postCss に require('tailwindcss') を渡すことで、Sass コンパイル後の CSS に Tailwind が適用されます。
postcss.config.js を別ファイルで置く方法もありますが、Mix の .options({ postCss: [...] }) と二重に設定するとどちらが効くか分かりにくくなります。どちらか一方に寄せるのが事故を防ぐコツです。本記事では Mix 側に寄せています。
5. ビルドスクリプト
package.json の scripts は Mix 標準のままで動きます。
{
"scripts": {
"dev": "mix",
"watch": "mix watch",
"prod": "mix --production"
}
}
6. Blade から読み込む
{{-- resources/views/layouts/app.blade.php --}}
<link rel="stylesheet" href="{{ mix('css/app.css') }}">
<script src="{{ mix('js/app.js') }}" defer></script>
mix() ヘルパーは mix.version() が付けたハッシュを mix-manifest.json から引いてくれるので、本番のキャッシュバスティングが自動で効きます。
ビルドを実行する
# 開発(非圧縮) npm run dev # ファイル監視 npm run watch # 本番(圧縮 + 未使用クラス削除 + ハッシュ付与) npm run prod
本番ビルドでは Tailwind v3 の JIT が content に列挙したファイルを走査し、実際に使われているクラスだけを出力します。開発ビルドでは全ユーティリティが含まれ CSS が大きくなりますが、これは想定どおりで、--production で削られます。
確認方法
1. Tailwind が展開されているか
ビルド後の public/css/app.css にユーティリティ由来の定義が含まれるか確認します。
grep -c "border-radius" public/css/app.css
0 なら PostCSS に Tailwind が渡っていません(webpack.mix.js の postCss を見直す)。
2. @apply が解決されているか
.card の定義に border-radius / box-shadow / padding が展開されていれば成功です。@apply rounded-lg shadow p-4; が文字列のまま残っていたら失敗です。
3. 本番で未使用クラスが落ちているか
npm run dev と npm run prod で public/css/app.css のサイズを比較します。prod で桁違いに小さくなっていれば purge が効いています。
4. ハッシュ付与
npm run prod 後に public/mix-manifest.json を開き、app.css にハッシュが付いていれば OK です。
注意点
contentのパス漏れに注意。 Blade だけ書いて JS/Vue を書き忘れると、JS 側で動的に付けるクラスが本番で消えます。動的クラス名は文字列連結('bg-' + color)で組むと purge に検出されないため、完全なクラス名を書くかsafelistを使います。@applyに自作 Sass の変数は渡せません。@applyは Tailwind のクラス名しか受け付けません。Sass 変数を使いたい箇所は通常の CSS プロパティで書きます。- Node のバージョン差でビルドが落ちることがあります。 Mix v6 + sass-loader の組み合わせは Node のメジャーバージョンに敏感です。
.nvmrcを置いてチームで固定するのが安全です。 - 新規なら Vite を検討。 本記事は既存 Mix 資産の保守前提です。新規開発なら公式デフォルトの Vite のほうが設定が素直で、Tailwind 公式ドキュメントも Vite 前提が増えています。
まとめ
Laravel Mix で Tailwind と Sass を併用する勘所は、たった1つに集約されます。
- Tailwind は PostCSS プラグイン。 Sass コンパイルの「後段」で動くので、
webpack.mix.jsの.options({ postCss: [require('tailwindcss'), ...] })に明示的に渡す。 @tailwind/@applyは Sass にとって素通し対象で、評価は Tailwind 段。だから.scssに混在させて問題ない。- 本番の肥大化対策は
tailwind.config.jsのcontent指定が全て。動的クラス名は safelist で救う。
この3点を押さえれば、ユーティリティファーストの速さと、Sass の構造的な書きやすさを1本のパイプラインで両立できます。
