eslint-plugin-tailwindcssを触ってみる
eslintの設定めんどくさいですよね😖
Next.jsではv11からeslintがビルドインになったので、前から気になっていたeslint-plugin-tailwindcss
を触ってみたいと思います。
なお、今回はnpx create-next-app --ts
したものにtailwindcss
を追加したものでやっていきます😎
公式はこちら
モチベーション
- 人によって順番が違うclassの並びを
自動で修正したい
- typoを検知したい
-
breaking change
などでclass名が無効の場合検知したい -
tailwind.confg.js
を解析してエラーを吐いてほしい
機能
- classNameを自動で並び替えてくれる
- tailwindのclass名や指定されたcss以外のclassだとエラーを出す
- 同一のプロパティが重複している場合エラーを出す
-
JIT
対応
こんな感じ🤔
index.tsxをtailwind仕様に書き換える
index.tsxは下記のリポジトリwith tailwindcss
から拝借
import Head from 'next/head'
import Image from "next/image"
export default function Home() {
return (
<div className="flex flex-col items-center justify-center min-h-screen py-2">
<Head>
<title>Create Next App</title>
<link rel="icon" href="/favicon.ico" />
</Head>
<main className="flex flex-col items-center justify-center w-full flex-1 px-20 text-center">
<h1 className="text-6xl font-bold">
Welcome to{' '}
<a className="text-blue-600" href="https://nextjs.org">
Next.js!
</a>
</h1>
<p className="mt-3 text-2xl">
Get started by editing{' '}
<code className="p-3 font-mono text-lg bg-gray-100 rounded-md">
pages/index.js
</code>
</p>
<div className="flex flex-wrap items-center justify-around max-w-4xl mt-6 sm:w-full">
<a
href="https://nextjs.org/docs"
className="p-6 mt-6 text-left border w-96 rounded-xl hover:text-blue-600 focus:text-blue-600"
>
<h3 className="text-2xl font-bold">Documentation →</h3>
<p className="mt-4 text-xl">
Find in-depth information about Next.js features and API.
</p>
</a>
<a
href="https://nextjs.org/learn"
className="p-6 mt-6 text-left border w-96 rounded-xl hover:text-blue-600 focus:text-blue-600"
>
<h3 className="text-2xl font-bold">Learn →</h3>
<p className="mt-4 text-xl">
Learn about Next.js in an interactive course with quizzes!
</p>
</a>
<a
href="https://github.com/vercel/next.js/tree/master/examples"
className="p-6 mt-6 text-left border w-96 rounded-xl hover:text-blue-600 focus:text-blue-600"
>
<h3 className="text-2xl font-bold">Examples →</h3>
<p className="mt-4 text-xl">
Discover and deploy boilerplate example Next.js projects.
</p>
</a>
<a
href="https://vercel.com/import?filter=next.js&utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
className="p-6 mt-6 text-left border w-96 rounded-xl hover:text-blue-600 focus:text-blue-600"
>
<h3 className="text-2xl font-bold">Deploy →</h3>
<p className="mt-4 text-xl">
Instantly deploy your Next.js site to a public URL with Vercel.
</p>
</a>
</div>
</main>
<footer className="flex items-center justify-center w-full h-24 border-t">
<a
className="flex items-center justify-center"
href="https://vercel.com?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
Powered by{' '}
<Image src="/vercel.svg" alt="Vercel Logo" width={72} height={16} className="h-4 ml-2" />
</a>
</footer>
</div>
)
}
セットアップ
eslint-plugin-tailwindcssをインストール
$ npm i -D eslint-plugin-tailwindcss
.eslintrcに追記
{
"extends": [
"next",
"next/core-web-vitals",
+ "plugin:tailwindcss/recommended"
],
+ "plugins": ["tailwindcss"]
}
とりあえず、リンターを走らせてみる
npm run lintを実行
$ npm run lint
> next-eslint-plugin-tailwindcss@0.1.0 lint
> next lint
info - Using webpack 5. Reason: Enabled by default https://nextjs.org/docs/messages/webpack5
./src/pages/index.tsx
6:10 Warning: Invalid Tailwind CSS classnames order tailwindcss/classnames-order
12:13 Warning: Invalid Tailwind CSS classnames order tailwindcss/classnames-order
27:14 Warning: Invalid Tailwind CSS classnames order tailwindcss/classnames-order
30:13 Warning: Invalid Tailwind CSS classnames order tailwindcss/classnames-order
40:13 Warning: Invalid Tailwind CSS classnames order tailwindcss/classnames-order
50:13 Warning: Invalid Tailwind CSS classnames order tailwindcss/classnames-order
60:13 Warning: Invalid Tailwind CSS classnames order tailwindcss/classnames-order
70:15 Warning: Invalid Tailwind CSS classnames order tailwindcss/classnames-order
72:11 Warning: Invalid Tailwind CSS classnames order tailwindcss/classnames-order
78:77 Warning: Invalid Tailwind CSS classnames order tailwindcss/classnames-order
めちゃくちゃ怒られる😖
自動修正させる
scriptsにfixを追記
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
+ "fix:lint": "next lint --fix"
},
npm run fix:lintを実行
$ npm run fix:lint
> next-eslint-plugin-tailwindcss@0.1.0 fix:lint
> next lint --fix
info - Using webpack 5. Reason: Enabled by default https://nextjs.org/docs/messages/webpack5
✔ No ESLint warnings or errors
import Head from 'next/head'
import Image from "next/image"
export default function Home() {
return (
- <div className="flex flex-col items-center justify-center min-h-screen py-2">
+ <div className="flex flex-col justify-center items-center py-2 min-h-screen">
<Head>
<title>Create Next App</title>
<link rel="icon" href="/favicon.ico" />
</Head>
- <main className="flex flex-col items-center justify-center w-full flex-1 px-20 text-center">
+ <main className="flex flex-col flex-1 justify-center items-center px-20 w-full text-center">
<h1 className="text-6xl font-bold">
Welcome to{' '}
<a className="text-blue-600" href="https://nextjs.org">
Next.js!
</a>
</h1>
<p className="mt-3 text-2xl">
Get started by editing{' '}
<code className="p-3 font-mono text-lg bg-gray-100 rounded-md">
pages/index.js
</code>
</p>
- <div className="flex flex-wrap items-center justify-around max-w-4xl mt-6 sm:w-full">
+ <div className="flex flex-wrap justify-around items-center mt-6 sm:w-full max-w-4xl">
<a
href="https://nextjs.org/docs"
- className="p-6 mt-6 text-left border w-96 rounded-xl hover:text-blue-600 focus:text-blue-600"
+ className="p-6 mt-6 w-96 text-left hover:text-blue-600 focus:text-blue-600 rounded-xl border"
>
<h3 className="text-2xl font-bold">Documentation →</h3>
<p className="mt-4 text-xl">
Find in-depth information about Next.js features and API.
</p>
</a>
<a
href="https://nextjs.org/learn"
- className="p-6 mt-6 text-left border w-96 rounded-xl hover:text-blue-600 focus:text-blue-600"
+ className="p-6 mt-6 w-96 text-left hover:text-blue-600 focus:text-blue-600 rounded-xl border"
>
<h3 className="text-2xl font-bold">Learn →</h3>
<p className="mt-4 text-xl">
Learn about Next.js in an interactive course with quizzes!
</p>
</a>
<a
href="https://github.com/vercel/next.js/tree/master/examples"
- className="p-6 mt-6 text-left border w-96 rounded-xl hover:text-blue-600 focus:text-blue-600"
+ className="p-6 mt-6 w-96 text-left hover:text-blue-600 focus:text-blue-600 rounded-xl border"
>
<h3 className="text-2xl font-bold">Examples →</h3>
<p className="mt-4 text-xl">
Discover and deploy boilerplate example Next.js projects.
</p>
</a>
<a
href="https://vercel.com/import?filter=next.js&utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
- className="p-6 mt-6 text-left border w-96 rounded-xl hover:text-blue-600 focus:text-blue-600"
+ className="p-6 mt-6 w-96 text-left hover:text-blue-600 focus:text-blue-600 rounded-xl border"
>
<h3 className="text-2xl font-bold">Deploy →</h3>
<p className="mt-4 text-xl">
Instantly deploy your Next.js site to a public URL with Vercel.
</p>
</a>
</div>
</main>
- <footer className="flex items-center justify-center w-full h-24 border-t">
+ <footer className="flex justify-center items-center w-full h-24 border-t">
<a
- className="flex items-center justify-center"
+ className="flex justify-center items-center"
href="https://vercel.com?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
Powered by{' '}
- <Image src="/vercel.svg" alt="Vercel Logo" width={72} height={16} className="h-4 ml-2" />
+ <Image src="/vercel.svg" alt="Vercel Logo" width={72} height={16} className="ml-2 h-4" />
</a>
</footer>
</div>
)
}
並び順がきれいに揃った!😺
typoしてみる
<h1 className="text-6x font-bold">
Welcome to{' '}
<a className="text-blue-600" href="https://nextjs.org">
Next.js!
</a>
</h1>
$ npm run lint
> next-eslint-plugin-tailwindcss@0.1.0 lint
> next lint
info - Using webpack 5. Reason: Enabled by default https://nextjs.org/docs/messages/webpack5
./src/pages/index.tsx
13:13 Warning: Invalid Tailwind CSS classnames order tailwindcss/classnames-order
13:13 Warning: Classname 'text-6x' is not a Tailwind CSS class! tailwindcss/no-custom-classname
Need to disable some ESLint rules? Learn more here: https://nextjs.org/docs/basic-features/eslint#disabling-rules
Warning: Classname 'text-6x' is not a Tailwind CSS class! tailwindcss/no-custom-classname
エラーが出た!
Breaking Changeをしてみる
1xではwhitespace-no-wrap
だったものが2xではwhitespace-nowrap
に変わったので試してみる
<h1 className="text-6x font-bold whitespace-no-wrap">
Welcome to{' '}
<a className="text-blue-600" href="https://nextjs.org">
Next.js!
</a>
</h1>
$ npm run lint
> next-eslint-plugin-tailwindcss@0.1.0 lint
> next lint
info - Using webpack 5. Reason: Enabled by default https://nextjs.org/docs/messages/webpack5
./src/pages/index.tsx
13:13 Warning: Classname 'whitespace-no-wrap' is not a Tailwind CSS class! tailwindcss/no-custom-classname
Need to disable some ESLint rules? Learn more here: https://nextjs.org/docs/basic-features/eslint#disabling-rules
こっちもWarning: Classname 'whitespace-no-wrap' is not a Tailwind CSS class! tailwindcss/no-custom-classname
と出た
重複を試してみる
<h1 className="text-5xl text-6xl font-bold">
Welcome to{' '}
<a className="text-blue-600" href="https://nextjs.org">
Next.js!
</a>
</h1>
$ npm run lint
> next-eslint-plugin-tailwindcss@0.1.0 lint
> next lint
info - Using webpack 5. Reason: Enabled by default https://nextjs.org/docs/messages/webpack5
./src/pages/index.tsx
13:13 Error: Classnames text-5xl, text-6xl are conflicting! tailwindcss/no-contradicting-classname
Need to disable some ESLint rules? Learn more here: https://nextjs.org/docs/basic-features/eslint#disabling-rules
Error: Classnames text-5xl, text-6xl are conflicting! tailwindcss/no-contradicting-classname
エラーが出た
JITで試してみる
module.exports = {
purge: [
'./src/**/*.tsx',
'./src/**/*.ts',
],
+ mode: 'jit',
darkMode: false, // or 'media' or 'class'
theme: {
extend: {},
},
variants: {
extend: {},
},
plugins: [],
}
<h1 className="text-[10rem] font-bold">
Welcome to{' '}
<a className="text-blue-600" href="https://nextjs.org">
Next.js!
</a>
</h1>
$ npm run lint
> next-eslint-plugin-tailwindcss@0.1.0 lint
> next lint
info - Using webpack 5. Reason: Enabled by default https://nextjs.org/docs/messages/webpack5
✔ No ESLint warnings or errors
ちゃんと対応してる😺
単位を間違えていて動かない場合
<h1 className="text-[10re] font-bold">
Welcome to{' '}
<a className="text-blue-600" href="https://nextjs.org">
Next.js!
</a>
</h1>
$ npm run lint
> next-eslint-plugin-tailwindcss@0.1.0 lint
> next lint
info - Using webpack 5. Reason: Enabled by default https://nextjs.org/docs/messages/webpack5
✔ No ESLint warnings or errors
エラーは出ないらしい
tailwindの命名に則らない形で記述する
<h1 className="text-size-[10rem] font-bold">
Welcome to{' '}
<a className="text-blue-600" href="https://nextjs.org">
Next.js!
</a>
</h1>
$ npm run lint
> next-eslint-plugin-tailwindcss@0.1.0 lint
> next lint
info - Using webpack 5. Reason: Enabled by default https://nextjs.org/docs/messages/webpack5
./src/pages/index.tsx
13:13 Warning: Invalid Tailwind CSS classnames order tailwindcss/classnames-order
13:13 Warning: Classname 'text-size-[10rem]' is not a Tailwind CSS class! tailwindcss/no-custom-classname
Need to disable some ESLint rules? Learn more here: https://nextjs.org/docs/basic-features/eslint#disabling-rules
Warning: Classname 'text-size-[10rem]' is not a Tailwind CSS class! tailwindcss/no-custom-classname
エラーが出る
tailwind.configを読んでくれるか試してみる
module.exports = {
purge: [
'./src/**/*.tsx',
'./src/**/*.ts',
],
darkMode: false, // or 'media' or 'class'
theme: {
extend: {
fontSize: {
+ '10xl': '10rem',
}
},
},
variants: {
extend: {},
},
plugins: [],
}
<h1 className="text-10xl font-bold">
Welcome to{' '}
<a className="text-blue-600" href="https://nextjs.org">
Next.js!
</a>
</h1>
$ npm run lint
> next-eslint-plugin-tailwindcss@0.1.0 lint
> next lint
info - Using webpack 5. Reason: Enabled by default https://nextjs.org/docs/messages/webpack5
✔ No ESLint warnings or errors
独自のutilityを追加してみる
+ const plugin = require('tailwindcss/plugin');
module.exports = {
purge: [
'./src/**/*.tsx',
'./src/**/*.ts',
],
darkMode: false, // or 'media' or 'class'
theme: {
extend: {},
},
variants: {
extend: {},
},
plugins: [
+ plugin(({ addUtilities }) => {
+ const newUtilities = {
+ '.skew-10deg': {
+ transform: 'skewY(-10deg)',
+ }
+ }
+ addUtilities(newUtilities)
+ })
]
}
<h1 className="text-6xl font-bold skew-10deg">
Welcome to{' '}
<a className="text-blue-600" href="https://nextjs.org">
Next.js!
</a>
</h1>
$ npm run lint
> next-eslint-plugin-tailwindcss@0.1.0 lint
> next lint
info - Using webpack 5. Reason: Enabled by default https://nextjs.org/docs/messages/webpack5
./src/pages/index.tsx
13:13 Warning: Classname 'skew-10deg' is not a Tailwind CSS class! tailwindcss/no-custom-classname
Need to disable some ESLint rules? Learn more here: https://nextjs.org/docs/basic-features/eslint#disabling-rules
へーエラー出るのか😂
設定間違えたかな🤔
エラー出ないようにする
ホワイトリストに正規表現でぶちこめばいいらしい
{
"extends": [
"next",
"next/core-web-vitals",
"plugin:tailwindcss/recommended"
],
"plugins": ["tailwindcss"],
"settings": {
"tailwindcss": {
+ "whitelist": ["skew\\-[1]?[\\d]deg"]
}
}
}
$ npm run lint
> next-eslint-plugin-tailwindcss@0.1.0 lint
> next lint
info - Using webpack 5. Reason: Enabled by default https://nextjs.org/docs/messages/webpack5
✔ No ESLint warnings or errors
エラーが消えた😺
正直めんどくさいので、.eslintrc
を.eslintrc.js
に変更して独自のutilityの値の配列を渡してあげれば自動で同期できそう
tailwindのfullConfig
からpluginsの値が取得できない?っぽい
ついでに自動同期
exports.newUtilities = {
'.skew-10deg': {
transform: 'skewY(-10deg)',
}
};
const plugin = require('tailwindcss/plugin');
const newUtilities = require('./newUtilities');
module.exports = {
purge: [
'./src/**/*.tsx',
'./src/**/*.ts',
],
darkMode: false, // or 'media' or 'class'
theme: {
extend: {},
},
variants: {
extend: {},
},
plugins: [
+ plugin(({ addUtilities }) => addUtilities(newUtilities))
]
};
+const {newUtilities} = require('./newUtilities');
module.exports = {
"extends": [
"next",
"next/core-web-vitals",
"plugin:tailwindcss/recommended"
],
"plugins": ["tailwindcss"],
"settings": {
"tailwindcss": {
+ "whitelist": Object.keys(newUtilities).map((n) => n.replace(/^\./,''))
}
}
};
global cssやCSS Modulesを試す
globals.css
html,
body {
padding: 0;
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
}
a {
color: inherit;
text-decoration: none;
}
* {
box-sizing: border-box;
}
+.h1 {
+ font-size: 10rem;
+}
<h1 className="h1 font-bold">
Welcome to{' '}
<a className="text-blue-600" href="https://nextjs.org">
Next.js!
</a>
</h1>
$ npm run lint
> next-eslint-plugin-tailwindcss@0.1.0 lint
> next lint
info - Using webpack 5. Reason: Enabled by default https://nextjs.org/docs/messages/webpack5
./src/pages/index.tsx
13:13 Warning: Invalid Tailwind CSS classnames order tailwindcss/classnames-order
Need to disable some ESLint rules? Learn more here: https://nextjs.org/docs/basic-features/eslint#disabling-rules
並び替えのエラーしか出ていないので、大丈夫みたい
CSS Modules
.h1 {
font-size: 10rem;
}
import styles from './Home.module.css';
<h1 className={`${styles.h1} font-bold`}>
Welcome to{' '}
<a className="text-blue-600" href="https://nextjs.org">
Next.js!
</a>
</h1>
$ npm run lint
> next-eslint-plugin-tailwindcss@0.1.0 lint
> next lint
info - Using webpack 5. Reason: Enabled by default https://nextjs.org/docs/messages/webpack5
✔ No ESLint warnings or errors
まとめ
- 治安が悪くなりがちなtailwindのclassがいい感じになりそう
- pluginsとjitには注意が必要そう。
個人的にはbreakpointsでソートされている方が好みなので、オプションのgroupByResponsive
をtrue
にしたいと思う。
今回使ったリポジトリ
Discussion