コンポーネント指向時代のCSS: Tailwind CSSを選んだきっかけ
はじめに
私は普段Tailwind CSSを愛用していますが、そもそもの導入のきっかけの話になります。
結論から言ってしまうと「コンポーネント指向で設計していると、いつの間にかTailwind CSSの車輪の再発明をしてしまっていたから」です。
どういうことなのか、サンプルコードを交えてご紹介します。
コンポーネント指向とPRECSS
前提として「CSS設計完全ガイド」という書籍の影響で、当時はPRECSSというCSSの設計思想を利用していました。
まずは、このPRECSSを利用してコンポーネントを作ってみます。
CSSモジュールの分割
まず、シンプルなカードモジュールを作ります。
.bl_card {
width: 100%;
background-color: #fff;
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16);
}
コンポーネントの分割
つぎにカードコンポーネントにスタイルを適用させます。
<div class="bl_card">
<!-- slot -->
</div>
このようにシンプルな構成であれば、モジュール分割とコンポーネント分割の親和性は悪くないように見えます。
再利用性とHelperClass
つぎに、Cardコンポーネントに背景色をつけたいという要望があったとしましょう。
今回はデフォルトの白に加えて赤と青を追加するとします。
PRECSSとコンポーネントの分割方法の違い
もし、これをPRECSSで実現する場合は、モジュールを分割して実現します。
以下のような3つのモジュールを用意して、それぞれbackground-color
の値だけを変えるという方法をとります。
- .bl_card
- .bl_cardRed
- .bl_cardBlue
しかし、コンポーネントではわざわざ別のコンポーネントを用意するのではなく、Propsから取得した値によって背景色を変更できるようにします。
HelperClassの作成
PRECSSにはHelper Classという考え方があります。
モジュールから分割して1つのスタイルのみを担当する補助的なクラスです。
.hp_bgColor_white { background-color: #fff; }
.hp_bgColor_red { background-color: #ff0000; }
.hp_bgColor_blue { background-color: #0000ff; }
このクラスを切り替えることでコンポーネントの色を変える仕組みにします。
スタイル取得のUtility関数
コンポーネントからHelper Classを取得する関数も作っておきます。
export type Color = 'white' | 'red' | 'blue'
export const useHelperStyles = () => {
const getBgColor = (color: Color) => {
switch(color) {
case 'white':
return 'hp_bgColor_white'
case 'red':
return 'hp_bgColor_red'
case 'blue':
return 'hp_bgColor_blue'
default:
return 'hp_bgColor_white'
}
}
}
コンポーネントのPropsでスタイルの変更
では、制作したHelper ClassとUtility関数を利用してコンポーネントを作成してみましょう。
ReactとVue.jsの2パターン紹介します。
Reactの場合
import React from 'react';
import { useHelperStyles, Color } from 'utils/useHelperStyles';
interface CardProps {
color: Color;
children?: React.ReactNode;
}
const Card: React.FC<CardProps> = ({ color, children }) => {
const { getBgColor } = useHelperStyles();
return (
<div className={`bl_card ${getBgColor(color)}`}>
{children}
</div>
);
};
export default Card;
Vue.jsの場合
<script setup lang="ts">
import { useHelperStyles, Color } from 'utils/useHelperStyles'
defineProps<{
color: Color
}>()
const { getBgColor } = useHelperStyles()
</script>
<template>
<div class="bl_card" :class="getBgColor(color)">
<slot />
</div>
</template>
TailwindCSSの再発明
HelperClassの増殖
ここまでで紹介してきたHelper Classで見た目を切り替えるというのは非常に便利です。
しかし、便利さゆえにこれを多様したくなってきます。
例えば文字の配置もこのように変更したいと考えた場合、新たにhp_text_left
hp_text_center
hp_text_right
のようなクラスを新設することが考えられます。
このような繰り返しでHelper Classが無限に増えていくことが予想されました。
命名規則で悩んだり、Class・Typesの設置の時間を考えると、単純作業の割に時間がかかり過ぎてしまいます。
状態変化の対応にもHelperClass
また、近年のUIは状態に合わせてスタイルが変化していくことが考えられます。
例えば、公開状態ではデフォルトの色で、非公開時は色が薄くなるなどの対応で、これもHelper Classを利用して動的に対応することが考えられます。
このように、Helper Classはありとあらゆるスタイルに対して必要になります。
それならTailwindCSSでいいじゃん
作業の途中で薄々感じてはいましたが、このHelper Classを多様するというのは、結局Tailwind CSSを利用するのと同じことになってしまっていました。
自作のHelper ClassとTailwindのUtility Classはほぼ同義で、劣化版車輪の再発明に無駄な時間をとられてしまっていたのです。
また、今後のCSSの仕様変更への対応なども考えると、保守運用の点で技術的負債となる懸念もあります。
こういった経緯で私はTailwind CSSの導入を決めたのでした。
さいごに
私がTailwind CSSにたどりつくまでを簡単に説明させていただきました。
コンポーネント指向でUIをつくっていくことが主流になった現在、CSSの命名の問題は以前とは異なる考え方になりました。
また状態や状況に合わせてUIのスタイルは以前より細かく変化し、それに対応していかなければなりません。
そうなってくると、どうしても「スタイルをモジュールとしてまとめることの意味が薄くなっている」といえるのではないでしょうか。
Discussion