【更新日 : 】
ViteでTailwind CSSを利用する(+最適化した独自styleが混在可能か検証)
- Category:
- 開発環境
ViteでTailwind CSSを導入したサンプルです。
Tailwind CSSの最適化(リセットCSSの削除、変数の削減、独自拡張)や、最適化した独自styleを混在させることができるのか検証しました。
Tailwind CSSとは
ユーティリティファーストで考案されたオープンソースのCSS フレームワークです。
flex
pt-4
text-center
といったようにCSSのclass名と設定されるstyleが一意のため、プロジェクト毎に依存しないコードを書くことができます。
一方でclass設定が多くなりHTMLの可読性が下がったりHTMLが肥大化するなどデメリットもあるため、利用する場合には実装要件に合わせてよく検討する必要があるかもしれません。
Viteのベース構築
本記事は公式ドキュメントに沿ってPostCSSのTailwindプラグインを利用します。
ベース環境のインストールは完了していることが前提ですので基本の構築は以下の記事を参照してください。
npmパッケージ tailwindcss の設定
Vite環境にインストール
npm install -D tailwindcss
※ autoprefixer が未インストールの場合は追加することを推奨します。
npm install -D autoprefixer
postcss.config.cjsへ呼び出しコードを追記
postcss.config.cjs
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
拡張子を.jsにする場合
postcss.config.js とする場合はエラー回避のため module.exports = {
を export default {
に書き換えてください。
postcss.config.js
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
package.json内に記載されていますが、Viteは "type": "module"
の形式です。"type": "module"
を設定すると.jsファイルはES6モジュールとして扱うようになりますが、ES6モジュールではrequire、module、exportsなどの識別子が利用できなくなります。(代わりにimportとexportが利用できます。)
“type”: “module”設定でも従来のコードを利用したい場合は、JSファイルをCommonjsモジュールとして扱うように拡張子をcjsに変更します。(”type”: “module”に関係なくES6モジュールとして扱いたいJSファイルがある場合は拡張子をmjsにします。)
本記事ではベースの構築記事でpostcss.configの拡張子を.cjsにしているためそれに合わせています。
tailwind.config.jsの作成
tailwind.config.js
を作成し以下の初期設定を記述してください。
tailwind.config.js
module.exports = {
content: [ //監視するファイルの設定
"./src/*.{js,html}",
"./src/**/*.{js,html}",
],
theme: {
extend: {},
},
plugins: [],
}
監視するファイルの設定では{}
を利用すると複数の拡張子を同時設定できます。
詳しくは以下をご参照ください。
SCSSにTailwind CSSのstyleを追加
style.scssにTailwind CSSのstyleを読み込むコードを追加します。
style.scss
@tailwind base;
@tailwind components;
@tailwind utilities;
ここまでで最低限の設定は完了したため、プロジェクトを開始してclassを追加するとstyleが有効化されるようになります。
<p class="text-3xl font-bold underline">
Hello world!
</p>
Tailwind CSSの利便性を上げる
VS Codeの拡張機能
VS Code上でclass名のオートコンプリートなど便利な機能が追加されるTailwind CSS IntelliSenseや、Prettierでclassを自動的に並べ替えなどしてくれるPrettier pluginなど、公式から便利な拡張機能が配信されています。詳細は以下をご参照ください。
ブラウザの拡張機能
Tailwind Cheat Sheet ExtensionはChromeの拡張機能です。ブラウザ上でプロパティ名などからTailwind CSSのclass名を検索できるので、公式のチートシートを見に行く手間を省くことができます。
リセットCSSを無効化する
Tailwind CSSは単体でも利用できるようにデフォルトでリセットCSSが設定されていますが、独自のリセットCSSを利用したい場合は corePlugins.preflight: false
とすると無効化することができます。
@tailwind base; にはリセット以外にも Tailwind CSS で利用するCSS変数が含まれるため削除は非推奨になっています。
tailwind.config.js
module.exports = {
content: [],
corePlugins: {
//リセットCSSを無効化
preflight: false,
},
theme: {
extend: {},
},
plugins: [],
}
preflightの詳細は以下をご参照ください。
利用していない不要なCSS変数を削除する
Tailwind CSSを利用しビルドすると、利用していないstyleのCSS変数も大量に出力されるのが確認できます。不要なものはcorePluginsで無効化しておくと変数の出力を止めることができます。
以下は全てのCSS変数を無効化する例ですが、フロント側で利用したい場合には当然記述を削除する必要がありますので、設定する際にはプロジェクトにおいて利用していないstyleを精査して十分注意するようにしてください。
tailwind.config.js
module.exports = {
content: [],
corePlugins: {
//リセットCSSを無効化
preflight: false,
//変数を生成する全てのstyleを無効化
borderSpacing: false,
backdropBlur: false,
backdropBrightness: false,
backdropContrast: false,
backdropFilter: false,
backdropGrayscale: false,
backdropHueRotate: false,
backdropInvert: false,
backdropOpacity: false,
backdropSaturate: false,
backdropSepia: false,
boxShadow: false,
filter: false,
fontVariantNumeric: false,
gradientColorStops: false,
ringOffsetWidth:false,
ringWidth:false,
scrollSnapType: false,
touchAction: false,
transform: false,
},
theme: {
extend: {},
},
plugins: [],
}
無効化する際に渡す文字列のバリエーションなどは以下で参照できます。
:hoverにメディアクエリ(@media (hover: hover) and (pointer: fine))を追加する
future.hoverOnlyWhenSupported
を有効化すると、ホバー系のclassを設定した際にメディアクエリ(@media (hover: hover) and (pointer: fine))を設定できます。
モバイル端末ではhover効果を無効化したい場合などに有効な設定です。
Tailwind v3.1以降で利用できる設定です。
tailwind.config.js
module.exports = {
future: {
hoverOnlyWhenSupported: true,
},
}
メディアクエリのブレイクポイントを変更する
theme.screens
でブレイクポイントの変更や拡張ができます。
tailwind.config.js
module.exports = {
theme: {
screens: {
'sm': '640px',
// => @media (min-width: 640px) { ... }
'md': '768px',
// => @media (min-width: 768px) { ... }
'lg': '1024px',
// => @media (min-width: 1024px) { ... }
'xl': '1280px',
// => @media (min-width: 1280px) { ... }
'2xl': '1536px',
// => @media (min-width: 1536px) { ... }
}
}
}
既存の内包そのままで設定を追加したい場合は以下のような省略記法もあります。
tailwind.config.js
const defaultTheme = require('tailwindcss/defaultTheme')
module.exports = {
theme: {
screens: {
'xs': '475px',
...defaultTheme.screens,
},
},
plugins: [],
}
複雑なブレイクポイントの設定
固定範囲ブレークポイント
tailwind.config.js
module.exports = {
theme: {
screens: {
'sm': {'min': '640px', 'max': '767px'},
// => @media (min-width: 640px and max-width: 767px) { ... }
'md': {'min': '768px', 'max': '1023px'},
// => @media (min-width: 768px and max-width: 1023px) { ... }
'lg': {'min': '1024px', 'max': '1279px'},
// => @media (min-width: 1024px and max-width: 1279px) { ... }
'xl': {'min': '1280px', 'max': '1535px'},
// => @media (min-width: 1280px and max-width: 1535px) { ... }
'2xl': {'min': '1536px'},
// => @media (min-width: 1536px) { ... }
},
}
}
複数範囲のブレークポイント
tailwind.config.js
module.exports = {
theme: {
screens: {
'sm': '500px',
'md': [
{'min': '668px', 'max': '767px'},
{'min': '868px'}
],
'lg': '1100px',
'xl': '1400px',
}
}
}
詳細は以下をご参照ください。
色のカスタマイズ
デフォルトパレットのカスタマイズ
theme.colors
で色のカスタマイズが可能です。
tailwind.config.js
module.exports = {
theme: {
colors: {
primary: '#5c6ac4',
secondary: '#ecc94b',
transparent: 'transparent',
current: 'currentColor',
'white': '#ffffff',
'tahiti': {
100: '#cffafe',
200: '#a5f3fc',
300: '#67e8f9',
400: '#22d3ee',
500: '#06b6d4',
600: '#0891b2',
700: '#0e7490',
800: '#155e75',
900: '#164e63',
light: '#67e8f9',
DEFAULT: '#06b6d4',
dark: '#0e7490',
},
// ...
},
},
}
色を追加する
新しい色を追加したい場合は、theme.extend.colors
に追記します。
tailwind.config.js
module.exports = {
theme: {
extend: {
colors: {
brown: {
50: '#fdf8f6',
100: '#f2e8e5',
200: '#eaddd7',
300: '#e0cec7',
400: '#d2bab0',
500: '#bfa094',
600: '#a18072',
700: '#977669',
800: '#846358',
900: '#43302b',
},
}
},
},
}
詳細は以下をご参照ください。
余白のカスタマイズ
デフォルト余白のカスタマイズ
theme.spacing
でカスタマイズ可能です。
tailwind.config.js
module.exports = {
theme: {
spacing: {
sm: '8px',
md: '12px',
lg: '16px',
xl: '24px',
'1': '8px',
'2': '12px',
'3': '16px',
'4': '24px',
'5': '32px',
'6': '48px',
}
}
}
余白を追加する
デフォルトで用意されていない設定を追加したい場合は、theme.extend.spacing
に追記します。
tailwind.config.js
module.exports = {
theme: {
extend: {
spacing: {
'13': '3.25rem',
'15': '3.75rem',
'128': '32rem',
'144': '36rem',
}
}
}
}
数値をそのままpxに変換する
theme.spacing
内で関数を実行し機能拡張することも可能です。
以下の例は1〜300の余白の数値をそのままpxに変換します。(例:mt-1 → margin-top: 1px;)
tailwind.config.js
module.exports = {
theme: {
extend: {
spacing: (() => {
let spacing = {};
for (let i = 1; i <= 300; i++) {
// プロパティ名と値を動的に設定します
spacing[i] = `${i}px`;
}
return spacing;
})(),
}
}
}
独自関数を使ったtheme.extendでの拡張
直上の「数値をそのままpxに変換する」の項で掲載した方法ですが、各設定項目には独自関数の返り値を渡すことで同じような記述を自動化することも可能です。
以下の記事ではpx値のclass名を記載するとCSS上ではremに変換する方法(例:text-16ptr で font-size:1rem;)が掲載されています。
以下にいくつかカスタマイズ例を記載します。
tailwind.config.js
module.exports = {
theme: {
extend: {
/*
leading-1 → line-height: 1;
leading-12 → line-height: 1.2;
→2桁の場合は小数点以下1桁になる
leading-145 → line-height: 1.45;
→3桁の場合は小数点以下2桁になる
*/
lineHeight: () => {
let values = {};
for (let i = 1; i <= 300; i++) {
if (i > 10 && i < 100) {
values[i] = i / 10;
} else if (i >= 100) {
values[i] = i / 100;
} else {
values[i] = i;
}
}
return values;
},
/*
tracking-1 → letter-spacing: 0.001em;
〜
tracking-999 → letter-spacing: 0.999em;
*/
letterSpacing: () => {
let spacing = {};
for (let i = 1; i <= 999; i++) {
spacing[i] = (i / 1000).toFixed(3) + 'em';
console.log((i / 1000).toFixed(3) + 'em');
}
return spacing;
},
//z-1〜z-9までのz-indexを生成
zIndex: (() => {
let zIndex = {};
for (let i = 1; i <= 9; i++) {
// プロパティ名と値を動的に設定します
zIndex[i] = i;
}
return zIndex;
})(),
}
}
}
独自のユーティリティclassを追加する
Tailwind CSSが元々内包しているプラグイン addUtilities()
を利用すると独自のユーティリティclassを追加できます。
addUtilities()で追加したclassはTailwind CSS IntelliSense によるオートコンプリートが効くので便利です。
tailwind.config.js
const plugin = require('tailwindcss/plugin')
module.exports = {
plugins: [
plugin(function({ addUtilities }) {
addUtilities({
'.flex-center': {
'display': 'flex',
'justify-content': 'center',
'align-items': 'center'
},
'.flex-between': {
'display': 'flex',
'justify-content': 'space-between',
},
})
})
]
}
詳細は以下をご参照ください。
独自のコンポーネントclassを追加する
Tailwind CSSが元々内包しているプラグイン addComponents()
を利用すると独自のユーティリティclassを追加できます。
addComponents()で追加したclassはTailwind CSS IntelliSense によるオートコンプリートが効くので便利です。
tailwind.config.js
const plugin = require('tailwindcss/plugin')
module.exports = {
plugins: [
plugin(function({ addComponents }) {
addComponents({
'.btn': {
padding: '.5rem 1rem',
borderRadius: '.25rem',
fontWeight: '600',
},
'.btn-blue': {
backgroundColor: '#3490dc',
color: '#fff',
'&:hover': {
backgroundColor: '#2779bd'
},
},
})
})
]
}
詳細は以下をご参照ください。
addUtilities()、addComponents()でのstyleの書き方
CSS-in-JS 構文を利用でき、SCSSファイルに近い書き方でネストできます。
addComponents({
'.card': {
backgroundColor: '#fff',
borderRadius: '.25rem',
boxShadow: '0 2px 4px rgba(0,0,0,0.2)',
'&:hover': {
boxShadow: '0 10px 15px rgba(0,0,0,0.2)',
},
'@media (min-width: 500px)': {
borderRadius: '.5rem',
}
}
})
詳細は以下をご参照ください。
【検証】独自styleをPurgeCSSで除外しつつTailwind CSSも利用する
ここで言う独自styleとは直上の .test のようにTailwind CSS以外の方法で記述している独自styleを指しています。addUtilities()やaddComponents()でのstyle追加はTailwind CSSのPurge対象です。
現行のTailwind CSSは以前のバージョンから仕様変更があり、PurgeCSSの内蔵しておらず独自の方法でTailwind CSSで出力されるstyleのみ最適化するようになったため、Tailwind CSS以外の方法で追加した独自styleはPurgeの対象外になっています。
動的に追加されるclassを許容するにはsafelistを利用し工夫する必要があるため設定がやや複雑化します。細かい調整ができないプロジェクトではできる限りPurgeCSSの利用は控えた方が良いかもしれません。
postcss-purgecssをインストールし挙動を検証
インストール
npm install -D @fullhuman/postcss-purgecss
postcss.config.cjs に追記し有効化
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
'@fullhuman/postcss-purgecss': {
content: ['./src/**/*.html','./src/js/**/*.js'],
safelist: []
},
},
}
contentの設定のみでファイルをビルドすると、PurgeCSSが利用しているTailwind CSSのclassも削除してしまいます。
Tailwind CSSのclass名は [&_li:after]:content-['|']
のような普通では利用しない特殊記号も用いているのでclass名と認識してくれないのが原因です。
ある程度の回避策としてPurgeCSSのdefaultExtractorオプションでセレクタの範囲を調整すると緩和が可能です。
動的に追加されるclass名と連動している場合は、safelistと組み合わせる必要があります。
(直下の項目に詳細を記載します。)
postcss.config.cjs
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
'@fullhuman/postcss-purgecss': {
content: ['./src/**/*.html','./src/js/**/*.js'],
defaultExtractor: (content) => {
/*
動的に追加されるclass名と連動したclass名は
該当classをsafelistに追加する必要がある
*/
// class属性内の全ての文字列をマッチ
const classAttrMatches = content.match(/class="([^"]*)"/g) || [];
// マッチした文字列から `class=""` を取り除き、空白で分割してクラス名の配列を作成
const cleanedMatches = classAttrMatches.flatMap(match => match.replace(/class="|"/g, '').split(/\s+/));
// 特殊な文字を含むクラス名を抽出
const specialCharMatches = content.match(/[^<>"`\s]*[^<>"`\s:]/g) || [];
return cleanedMatches.concat(specialCharMatches);
},
safelist: []
},
},
}
動的に追加されるclassを許容するにはsafelistを利用し工夫する必要がある
以下のような動的に追加されるclassを想定してstyleを追記した場合、上記正規表現のみではclassが削除されてしまいます。
<p class="hidden [&.is-active]:block">is-activeが追加されると表示</p>
このような場合はsafelistに is-active
を追加し、動的に追加されるclassを予め許可しておく必要があります。
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
'@fullhuman/postcss-purgecss': {
content: ['./src/**/*.html','./src/js/**/*.js'],
defaultExtractor: (content) => {
//省略
},
safelist: ['is-active']
},
},
}
【検証】SCSS内でTailwind CSSを独自styleの途中に挿入できるか
柔軟性の高い結論のみ参照したい場合は、末尾の項目 【解決案】scssファイルの読み込みをhtmlからjsに変更し、PostCSSのタスク実行を調整する をご参照ください。
通常Tailwind CSSはCSSの末尾に挿入されますが、独自styleの間にTailwind CSSを挿入できるのかを検証しました。(後ろに独自styleを挿入できた方が何らかの理由でstyleを上書きする際に都合が良いなどの理由があります。)
外部ファイルを読み込む @use
はファイル上部に書く必要があるので、途中で @tailwind
を挿入するとエラーが発生します。
読み込みエラーになる例
@use "_base";
@tailwind base;
@tailwind components;
@tailwind utilities;
@use "_page"; /* ここでエラーが出る */
これの回避方法として @tailwind
を外部ファイル化し読み込ませるとエラーは出ませんが、メディアクエリ内のstyleはビルド時にcssの末尾に出力されるため、位置が保持されるのはメディアクエリなしのstyleのみです。
_tailwind.scss
@tailwind base;
@tailwind components;
@tailwind utilities;
外部ファイル化した@tailwindをテスト
@use "_base";
/*
外部ファイル化するとエラーは出ないが
md:xxxなどメディアクエリ内のstyleはcssの末尾に出力される
*/
@use "_tailwind";
@use "_page";
ビルド結果(@tailwind のメディアクエリのstyleは末尾へ出力される)
/* _base.scssに書いたstyle */
.base {}
/* @tailwind メディアクエリなしのstyle */
.text-center {}
/* _page.scssに書いたstyle */
.page {}
/* @tailwind メディアクエリがあるstyle */
@media (min-width: 768px){
.md\:text-right{text-align:right}
}
postcss-sort-media-queriesでソートしてみる
postcss-sort-media-queriesはメディアクエリをソートして1つにまとめてくれるPostCSSのプラグインです。
末尾に出力されてしまうメディアクエリのstyleも含めソートするとどうなるか検証しました。
インストール
npm install -D postcss-sort-media-queries
postcss.config.cjs に追記し有効化
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
'postcss-sort-media-queries': {},
'@fullhuman/postcss-purgecss': {
//省略
},
},
}
以下のような .test
で .md:text-right
を上書きできるかテストしてみます。
表示テストhtml
<p class="md:text-right test">.testでTailwind CSSのstyleを上書きしたい</p>
表示テスト.scss
/* md:text-rightを出力する */
@use "_tailwind";
.test {
@media (min-width: 768px) {
text-align: center;
}
}
以下のような結果になりました。.md:text-right
が .test
よりも後に出力されるため、このままでは自作classによる上書きは詳細度を上げる必要があります。
ビルド結果
@media (min-width: 768px) {
.test {
text-align: center;
}
.md\:text-right {
text-align: right;
}
}
代案:自作のメディアクエリを調整しTailwind CSSのメディアクエリとは分離させる
SCSSファイルのインポートの方法やpackage.jsonを調整できる場合は、直下の項目【解決案】scssファイルの読み込みをhtmlからjsに変更し、PostCSSのタスク実行を調整する をご参照ください。
Tailwind CSSのメディアクエリとは異なる書き方であれば、ソートしても分離されるので意図した挙動にすることができました。
表示テスト.scss
/* md:text-rightを出力する */
@use "_tailwind";
.test {
@media not screen and (min-width: 768px) {
text-align: center;
}
}
ビルド結果(Tailwind CSSより下にソートされる)
@media (min-width: 768px) {
.md\:text-right {
text-align: right;
}
}
@media only screen and (min-width: 768px) {
.test {
text-align: center;
}
}
【解決案】scssファイルの読み込みをhtmlからjsに変更し、PostCSSのタスク実行を調整する
ViteでSCSSファイルを読み込む方法は2種類あり、これまでの執筆記事ではなるべく元のHTMLに近づけるためhtmlファイル内で読み込む方法を採用していましたが、Tailwindの出力位置をコントロールする場合はjsファイルでインポートする方が柔軟性がありました。
また、ソートや圧縮のタスクはViteでCSSファイルをビルドした後に実行するよう調整すると設定が効かない問題をクリアできます。
SCSSの読み込み方法を調整
HTML側の記述を削除し、全ページに共通して読み込ませているJavaScriptファイル内でインポートします。
index.html CSSの読み込みを削除(before)
<!DOCTYPE html>
<html lang="ja">
<head>
〜省略〜
<!-- ↓共通して読み込んでいるCSSを削除 -->
<link rel="stylesheet" href="./scss/style.scss">
<script type="module" src="./js/main.js"></script>
</head>
<body>
〜省略〜
</body>
</html>
index.html CSSの読み込みを削除(after)
<!DOCTYPE html>
<html lang="ja">
<head>
〜省略〜
<script type="module" src="./js/main.js"></script>
</head>
<body>
〜省略〜
</body>
</html>
共通して読み込ませている main.js でCSSをインポート
//Tailwind CSSのstyleよりも上に出力したいものは先にインポート
import '../scss/_reset.scss';
import '../scss/_tailwind.scss';
//Tailwind CSSのstyleよりも下に出力したいものは後でインポート
import '../scss/style.scss';
//main.jsのコード
ソートや圧縮などビルドの最後で実行したいタスクを無効化
postcss.config.cjsに記載しているとVite側で自動的に実行されてしまうため、コメントアウトなどで並び替えや圧縮のタスクを無効化します。
postcss-csso はcssを圧縮するプラグインです。以降複数タスクを追加するパターンの例として追記しています。
postcss.config.cjs
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
// 'postcss-sort-media-queries': {},
// 'postcss-csso': {},
'@fullhuman/postcss-purgecss': {
//省略
},
},
}
Viteのビルドが終わった後にPostCSSのコードを実行するため npm-run-all と postcss-cli をインストールします。
build_cssを&&でつないでそのまま実行するとエラーが出ることがあるため、エラー対策としてnpm-run-allを利用します。
npm install -D npm-run-all postcss-cli
本記事と環境をあわせる場合 cssoのインストール(任意)
npm install -D postcss-csso
package.jsonを編集し、build_cssのコマンドを追記します。
Viteのbuildコマンドが終了した後に、PostCSSの残タスクを実行できるよう "build"
にもコマンドを追記します。
package.json
{
〜省略〜
"scripts": {
"build_css": "postcss dist/assets/css/*.css --use postcss-sort-media-queries --use postcss-csso --replace --no-map",
"build": "vite build && npm-run-all build_css",
},
〜省略〜
}
--use プラグイン名
を追記していくことで実行タスクを追加できます。--プラグイン名-オプション名 値
でプラグインのオプション設定が可能です。
PostCSS CLIの詳細は以下をご参照ください。
Tailwind CSSの書き方を勉強する
上記の検証過程で少し構文を記載しましたが、Tailwind CSSはそれ単体で一通り実装ができるように非常に細かいカスタマイズが可能になっています。
簡単に内容を知りたい方は以下の記事などを参照すると良いかもしれません。
細かくどのような設定があるか参照したい場合は、公式ドキュメントのCore Concepts項目辺りを一読すると良いでしょう。
関連リンク
- 【詳細版】Viteでコーダーのコーディング環境(HTML(ejsライク:ハンドルバー化)・Sass・JS)を作る
- 【簡易版】Viteでコーダーのコーディング環境(HTML(ejsライク:ハンドルバー化)・Sass・JS)を作る
- ViteでビルドしたCSS内の日本語がUnicodeエスケープシーケンス変換されないようにする
- ViteでUnoCSSを利用する