はじめに
私が所属するユニークビジョン株式会社ではフロントエンドは TailwindCSS を使って開発しています。
ある日のコードレビュー
私がフロントエンドのコードレビューをしていた時でした。
私(お、なんか見慣れない Tailwind のクラスが使われているな!max-w-max
?調べてみるか。)
私(なるほど、確かにこの書き方でも問題なさそうだ。でもちょっと気になるのでコメントしておこう。)
こんな書き方があるんですね。でもあまり一般的ではない珍しい書き方だと思うので、
flex
に入れて子要素のサイズまで縮ませる方が読みやすいと思います。
私「よし、送信、と。」
~~ しばらくして ~~
レビュイー「レビューありがとうございました。指摘のとおり、どちらの書き方でも動作することは分かりました。」
私「お!よかったです。では変更をお願いします!」
レビュイー「でも、『珍しいかどうか』は分かりませんでした。」
レビュイー「フロントエンドを普段あまり触らない私にはどちらも同じくらい『珍しい』と思いました。」
レビュイー「どうやったら 『珍しいかどうか』が分かるんですか? 」
私「・・・」
私「経験・・・とか?」
全クラスの使用回数をカウントしてみる
困りました。経験こそ全てだ! みたいなことは絶対に言いたくないので、どのクラスをどれくらい使っているかを調べることにしました。
次のような手順で行いました。
実際のスクリプトと使い方は以下に置いておきました。
対象
今回の分析対象は下記です。
ただし、公式ドキュメントには説明のために Tailwind CSS のクラス名が頻出しています。
今回はアプリケーション部分にフォーカスしたかったので、src/components だけをカウントの対象としました。
(ちなみに説明部分の多くは src/pages/blog に置かれていました。)
集計結果
tailwind.com
クラス名 | 出現回数 |
---|---|
flex | 204 |
items-center | 106 |
relative | 103 |
flex-none | 93 |
absolute | 79 |
sr-only | 75 |
text-sm | 68 |
justify-center | 60 |
font-medium | 53 |
w-full | 51 |
hidden | 50 |
続きを表示
クラス名 | 出現回数 |
---|---|
rounded-full | 49 |
font-semibold | 47 |
leading-6 | 45 |
flex-auto | 44 |
rounded-lg | 39 |
px-4 | 35 |
overflow-hidden | 31 |
bg-white | 30 |
mx-auto | 26 |
border | 26 |
text-xs | 25 |
text-white | 25 |
border-b | 25 |
py-2 | 23 |
px-3 | 23 |
z-10 | 22 |
inline-flex | 21 |
bottom-0 | 21 |
pointer-events-none | 20 |
font-mono | 20 |
rounded-md | 19 |
grid | 19 |
max-w-7xl | 18 |
inset-0 | 18 |
top-0 | 17 |
space-x-4 | 17 |
left-0 | 17 |
py-1 | 16 |
mt-10 | 16 |
flex-col | 16 |
duration-500 | 16 |
shadow-xl | 15 |
px-6 | 15 |
p-6 | 15 |
overflow-visible | 15 |
h-6 | 15 |
cursor-pointer | 15 |
p-4 | 14 |
leading-5 | 14 |
transition | 13 |
h-8 | 13 |
border-t | 13 |
uppercase | 12 |
text-black | 12 |
rounded-xl | 12 |
mt-4 | 12 |
inset-x-0 | 12 |
flex-wrap | 12 |
w-6 | 11 |
transform | 11 |
text-lg | 11 |
mt-2 | 11 |
ml-3 | 11 |
h-10 | 11 |
block | 11 |
text-base | 10 |
space-x-2 | 10 |
shadow | 10 |
opacity-0 | 10 |
items-baseline | 10 |
h-full | 10 |
h-5 | 10 |
border-r | 10 |
shadow-sm | 9 |
overflow-auto | 9 |
mt-6 | 9 |
items-start | 9 |
w-8 | 8 |
truncate | 8 |
top-full | 8 |
text-right | 8 |
shadow-lg | 8 |
px-2 | 8 |
opacity-100 | 8 |
object-cover | 8 |
ml-2 | 8 |
justify-end | 8 |
h-12 | 8 |
col-span-2 | 8 |
sticky | 7 |
scale-100 | 7 |
rounded | 7 |
pb-6 | 7 |
pb-4 | 7 |
min-h-0 | 7 |
mb-6 | 7 |
justify-between | 7 |
flex-1 | 7 |
-mx-px | 7 |
-mt-6 | 7 |
w-auto | 6 |
w-5 | 6 |
transition-colors | 6 |
text-pink-500 | 6 |
text-indigo-600 | 6 |
text-indigo-500 | 6 |
text-center | 6 |
space-x-3 | 6 |
select-none | 6 |
row-start-1 | 6 |
rounded-tl | 6 |
py-6 | 6 |
py-3 | 6 |
pr-4 | 6 |
pb-1 | 6 |
p-2 | 6 |
my-auto | 6 |
my-6 | 6 |
mr-3 | 6 |
min-w-0 | 6 |
mb-4 | 6 |
mb-2 | 6 |
h-1 | 6 |
font-bold | 6 |
col-start-2 | 6 |
bg-teal-400 | 6 |
z-20 | 5 |
w-4 | 5 |
w-10 | 5 |
w-1/2 | 5 |
text-gray-900 | 5 |
text-2xl | 5 |
right-0 | 5 |
py-4 | 5 |
pt-6 | 5 |
pt-2 | 5 |
pt-10 | 5 |
pl-4 | 5 |
mt-1 | 5 |
max-w-none | 5 |
inset-y-0 | 5 |
h-px | 5 |
h-4 | 5 |
duration-300 | 5 |
border-l | 5 |
bg-top | 5 |
w-64 | 4 |
w-1 | 4 |
visible | 4 |
tracking-wider | 4 |
tracking-tight | 4 |
text-3xl | 4 |
space-y-6 | 4 |
scale-0 | 4 |
ring-inset | 4 |
pl-2 | 4 |
mr-2 | 4 |
ml-auto | 4 |
min-w-full | 4 |
md: | 4 |
grid-cols-5 | 4 |
grid-cols-1 | 4 |
fixed | 4 |
border-2 | 4 |
bg-blue-500 | 4 |
-mx-4 | 4 |
-mt-px | 4 |
-mb-px | 4 |
whitespace-pre | 3 |
w-px | 3 |
w-48 | 3 |
w-2/5 | 3 |
translate-y-0 | 3 |
transition-transform | 3 |
transition-opacity | 3 |
space-y-8 | 3 |
space-y-2 | 3 |
space-y-1 | 3 |
space-x-1 | 3 |
sm: | 3 |
shadow-md | 3 |
scale-95 | 3 |
row-end-3 | 3 |
pt-0 | 3 |
pr-2 | 3 |
pointer-events-auto | 3 |
pb-8 | 3 |
pb-2 | 3 |
p-8 | 3 |
p-3 | 3 |
p-0 | 3 |
mt-3 | 3 |
mt-16 | 3 |
ml-4 | 3 |
lg: | 3 |
leading-none | 3 |
inline-block | 3 |
hover | 3 |
grid-cols-2 | 3 |
gap-8 | 3 |
gap-6 | 3 |
from-white | 3 |
font-sans | 3 |
font-extrabold | 3 |
focus | 3 |
ease-out | 3 |
ease-in | 3 |
divide-y | 3 |
border-transparent | 3 |
bg-current | 3 |
bg-black | 3 |
appearance-none | 3 |
-mx-2 | 3 |
-ml-2 | 3 |
z-50 | 2 |
whitespace-pre-wrap | 2 |
w-3 | 2 |
w-12 | 2 |
tracking-wide | 2 |
text-xl | 2 |
text-teal-400 | 2 |
text-pink-400 | 2 |
text-gray-700 | 2 |
text-gray-500 | 2 |
text-4xl | 2 |
space-y-4 | 2 |
space-y-3 | 2 |
row-start-2 | 2 |
row-end-2 | 2 |
rounded-tr-xl | 2 |
rounded-tr | 2 |
rounded-tl-xl | 2 |
rounded-sm | 2 |
py-5 | 2 |
px-5 | 2 |
px-1 | 2 |
pt-5 | 2 |
pt-4 | 2 |
pr-3 | 2 |
pl-6 | 2 |
pl-10 | 2 |
pl-0 | 2 |
p-5 | 2 |
origin-bottom | 2 |
order-1 | 2 |
opacity-50 | 2 |
mx-4 | 2 |
mx-2 | 2 |
mt-8 | 2 |
mt-5 | 2 |
mt-20 | 2 |
mr-4 | 2 |
mr-1 | 2 |
ml-6 | 2 |
min-h-full | 2 |
mb-8 | 2 |
mb-5 | 2 |
mb-3 | 2 |
mb-10 | 2 |
mb-1 | 2 |
max-w-3xl | 2 |
m-0 | 2 |
left-full | 2 |
leading-4 | 2 |
inset-px | 2 |
h-3 | 2 |
grid-rows-2 | 2 |
grid-cols-3 | 2 |
gap-4 | 2 |
gap-2 | 2 |
font-serif | 2 |
font-normal | 2 |
flex-row-reverse | 2 |
duration-200 | 2 |
duration-100 | 2 |
delay-500 | 2 |
container | 2 |
col-start-1 | 2 |
col-span-3 | 2 |
col-end-4 | 2 |
bottom-full | 2 |
bg-transparent | 2 |
bg-repeat-x | 2 |
bg-red-500 | 2 |
bg-no-repeat | 2 |
bg-left-bottom | 2 |
bg-indigo-600 | 2 |
bg-indigo-50 | 2 |
bg-gradient-to-t | 2 |
bg-gradient-to-b | 2 |
align-top | 2 |
-translate-x-1/2 | 2 |
-my-1 | 2 |
-mt-2 | 2 |
-mr-4 | 2 |
-ml-10 | 2 |
-bottom-1 | 2 |
z-40 | 1 |
xl: | 1 |
w-56 | 1 |
w-40 | 1 |
w-4/5 | 1 |
w-32 | 1 |
w-24 | 1 |
w-16 | 1 |
w-1/5 | 1 |
w-0 | 1 |
underline | 1 |
translate-y-4 | 1 |
translate-y-2 | 1 |
transition-all | 1 |
tracking-normal | 1 |
top-px | 1 |
top-1 | 1 |
text-yellow-400 | 1 |
text-pink-600 | 1 |
text-pink-300 | 1 |
text-left | 1 |
text-indigo-400 | 1 |
text-indigo-300 | 1 |
text-green-900 | 1 |
text-gray-400 | 1 |
text-blue-600 | 1 |
text-blue-500 | 1 |
text-blue-300 | 1 |
space-y-10 | 1 |
space-y-0 | 1 |
space-x-8 | 1 |
space-x-10 | 1 |
space-x-0 | 1 |
shadow-2xl | 1 |
row-span-2 | 1 |
row-end-4 | 1 |
row-end-1 | 1 |
rounded-t-xl | 1 |
rounded-b-xl | 1 |
ring-white | 1 |
pt-8 | 1 |
pt-32 | 1 |
pr-8 | 1 |
pl-8 | 1 |
pl-12 | 1 |
pb-16 | 1 |
pb-0 | 1 |
origin-right | 1 |
origin-left | 1 |
order-3 | 1 |
order-2 | 1 |
opacity-25 | 1 |
my-5 | 1 |
mx-3 | 1 |
mt-auto | 1 |
mt-12 | 1 |
mr-8 | 1 |
ml-1 | 1 |
mb-16 | 1 |
mb-12 | 1 |
max-w-xs | 1 |
max-w-xl | 1 |
max-w-screen-xl | 1 |
max-w-full | 1 |
lowercase | 1 |
left-1 | 1 |
italic | 1 |
invisible | 1 |
inset-y-px | 1 |
inline-grid | 1 |
inline | 1 |
h-auto | 1 |
h-24 | 1 |
h-16 | 1 |
gap-16 | 1 |
font-black | 1 |
flex-shrink | 1 |
flex-col-reverse | 1 |
col-start-6 | 1 |
col-start-3 | 1 |
col-span-7 | 1 |
col-span-5 | 1 |
border-dashed | 1 |
border-collapse | 1 |
border-black | 1 |
border-0 | 1 |
bg-teal-900 | 1 |
bg-teal-800 | 1 |
bg-teal-700 | 1 |
bg-teal-600 | 1 |
bg-teal-500 | 1 |
bg-teal-300 | 1 |
bg-teal-200 | 1 |
bg-teal-100 | 1 |
bg-scroll | 1 |
bg-red-400 | 1 |
bg-pink-50 | 1 |
bg-local | 1 |
bg-green-600 | 1 |
bg-gray-50 | 1 |
bg-gray-200 | 1 |
bg-gradient-to-r | 1 |
bg-fixed | 1 |
bg-clip-text | 1 |
bg-clip-padding | 1 |
bg-clip-content | 1 |
bg-clip-border | 1 |
bg-blue-50 | 1 |
align-baseline | 1 |
-my-2 | 1 |
-mx-3 | 1 |
-mt-4 | 1 |
-mt-10 | 1 |
-mr-px | 1 |
-mr-2 | 1 |
-mr-1 | 1 |
-ml-px | 1 |
-ml-6 | 1 |
-ml-4 | 1 |
-ml-16 | 1 |
-mb-8 | 1 |
-mb-3 | 1 |
-mb-1 | 1 |
-m-8 | 1 |
(0 件は省略しています。)
ユニークビジョンのプロダクト
クラス名 | 出現回数 |
---|---|
flex | 110 |
w-full | 71 |
flex-row | 67 |
text-center | 47 |
items-center | 44 |
w-32 | 35 |
flex-col | 33 |
h-full | 30 |
flex-shrink-0 | 27 |
justify-center | 26 |
text-right | 24 |
w-64 | 21 |
font-bold | 20 |
続きを表示
クラス名 | 出現回数 |
---|---|
justify-between | 18 |
space-y-3 | 17 |
flex-grow | 17 |
w-48 | 15 |
space-x-3 | 13 |
p-10 | 11 |
w-24 | 10 |
min-h-0 | 10 |
text-white | 8 |
p-5 | 8 |
space-x-5 | 7 |
p-3 | 7 |
cursor-pointer | 7 |
bg-white | 7 |
text-lg | 6 |
p-16 | 6 |
whitespace-pre-wrap | 5 |
w-56 | 5 |
space-y-2 | 5 |
rounded | 5 |
px-3 | 5 |
justify-end | 5 |
contents | 5 |
break-words | 5 |
w-20 | 4 |
w-2/3 | 4 |
w-12 | 4 |
text-sm | 4 |
space-y-5 | 4 |
space-x-1 | 4 |
relative | 4 |
py-1 | 4 |
px-5 | 4 |
mb-2 | 4 |
block | 4 |
whitespace-no-wrap | 3 |
text-xl | 3 |
text-3xl | 3 |
space-y-6 | 3 |
space-x-2 | 3 |
mx-5 | 3 |
items-start | 3 |
border | 3 |
w-40 | 2 |
top-0 | 2 |
static | 2 |
space-y-1 | 2 |
shadow | 2 |
self-end | 2 |
self-center | 2 |
rounded-full | 2 |
py-5 | 2 |
py-2 | 2 |
px-2 | 2 |
pl-2 | 2 |
p-2 | 2 |
overflow-auto | 2 |
min-w-0 | 2 |
mb-5 | 2 |
leading-none | 2 |
h-56 | 2 |
flex-wrap | 2 |
divide-y-2 | 2 |
break-all | 2 |
border-white | 2 |
absolute | 2 |
w-4/5 | 1 |
w-4 | 1 |
w-16 | 1 |
w-10 | 1 |
w-1/3 | 1 |
transform | 1 |
text-xs | 1 |
text-left | 1 |
sticky | 1 |
space-y-10 | 1 |
space-x-4 | 1 |
space-x-10 | 1 |
self-start | 1 |
select-none | 1 |
rounded-r-none | 1 |
rounded-r | 1 |
rounded-lg | 1 |
rounded-l-none | 1 |
right-0 | 1 |
py-3 | 1 |
px-4 | 1 |
pointer-events-none | 1 |
pointer-events-auto | 1 |
p-px | 1 |
p-1 | 1 |
overflow-y-scroll | 1 |
overflow-y-auto | 1 |
mr-2 | 1 |
max-w-full | 1 |
m-auto | 1 |
inset-0 | 1 |
inline-flex | 1 |
h-4 | 1 |
h-12 | 1 |
fixed | 1 |
box-content | 1 |
border-t-0 | 1 |
border-t | 1 |
border-r-0 | 1 |
border-opacity-50 | 1 |
border-l | 1 |
border-b-0 | 1 |
border-b | 1 |
border-2 | 1 |
(0 件は省略しています。)
分析
概ね予想通りの結果
tailwindcss.com での出現回数上位のクラスはほとんどは我々が良く使っているものでした。
すなわち、我々の TailwindCSS の使用方法は Tailwind CSS 公式の使用方法から大きく逸脱していないということが分かりました。
そして、冒頭に登場した max-w-max
は 0 という結果だったので、「珍しい」という直感は間違っていなかったようです。
tailwindcss.com の方で出現回数が多かったクラス
一方、少なからず見慣れないクラス名もいくつか上位に入っていました。
sr-only
screen reader only という言葉の略称のようです。画面には表示しませんが、音声読み上げ機能などでは読み上げられる、アクセシビリティのためのスタイルとのことです。
hidden
hidden
は CSS の display: none;
です。
tailwindcss.com では、下記のようにレスポンシブ対応のために使われていることが多かったです。
<div class="hidden md:block">...</div>
ユニークビジョンのプロダクトは PC ブラウザ向けのアプリケーションなので、あまり使っていません。
flex-none
flex-none
は CSS の flex: none;
です。子要素を伸縮させず、子要素自身の大きさを優先するプロパティです。
後述しますが、我々は代わりに flex-shrink-0
を多用しているので、使う機会があまりなかったようです。
我々のプロダクトの方が出現回数が多かったクラス
逆に、我々のプロダクトでは多用していたのに、tailwindcss.com ではほとんど使われていなかった例もありました。
flex-row
flex-row
は CSS の flex-direction: row;
です。なんと tailwindcss.com では 0 回でした。
flex
のデフォルトは flex-row
なので、書く必要が無いためだと思われます。
我々は、flex
が row
か col
なのかを明示的に書くという方針 だったので、flex flex-row
のような書き方をよくしていました。
結果、この差が生まれてしまったようです。
flex-shrink-0
flex-shrink-0
は CSS の flex-shrink: 0;
です。flex コンテナ内の要素を縮ませないようにするためのプロパティです。前述したとおり、flex-none
でも同等の効果を得られます。flex-shrink-0
は flex の子要素一つ一つに付けるのに対し、flex-none
は親要素だけに付与すれば OK です。場合によって使い分けることで記述量を減らせそうです。
まとめ
CSS は同じスタイルでも、様々な表現方法があります。余計なプロパティを書いても表示結果が変わらないことが多いです。
プロジェクトが小さいうちは良いのですが、大きくなるにつれ、複雑な CSS がスタイルバグを生み出したり、コードを読む速度を低下させたりするなど、生産性の低下を招きかねません。
CSS をシンプルに保つためには、可能な限り珍しくない(=よく使われている)ものを使う方がベターです。
今回、Tailwind CSS の全クラスの出現回数をカウントすることで、 使おうとしているクラスが「珍しいかどうか」を知ることができるようになりました。
珍しい Tailwind CSS のクラスを使おうとしているときには、代替手段を探してみることをおすすめします。
代替手段が見つからない場合は、そのクラスを使いましょう。
また、一般的な使い方と自分たちの使い方を比較することで、新しいテクニックやアンチパターンを発見できることが分かりました。
あなたのプロジェクトでも是非試してみてください。
最後に
フロントエンド周りのことをよく呟いています。よければフォローお願いします。
ユニークビジョンという会社のエンジニアです。
— シライシ@ユニークビジョン (@punksy2) October 3, 2022
フロントエンド開発と品質向上に取り組んでいるので、そのあたり興味ある方はフォローしていただけると嬉しいです。
品質の高いサービスを提供し続ける会社であるために/品質向上チーム https://t.co/vI7YsKL6QW