7.アトミックデザイン
コンポーネントの粒度と抽象化
Reactのコンポーネントの抽象化と共通化にアトミックデザインを採用するためのガイドライン。
Reactで共通コンポーネントを作ろうとしても、それが十分に抽象化されていないと、 再利用に向かず、結局そのプロジェクトでのみ利用できるコンポーネントとなります。
根本的な問題は、React で表現されうる全ての要素がコンポーネントになってしまうということにあります。 抽象度が高いコンポーネントも、そのプロジェクトでしか利用できないコンポーネントも、全てコンポーネントという単位で表現されます。
確かに開発中はどのコンポーネントが共通化できるなどと考えて作るのは開発効率的にも悪いかもしれません。 だからといって、サービスリリース後にコンポーネントを分類し、手を加えて、他のプロジェクトでも使えそうなものを整備するのも無理があります。
あらかじめ、コンポーネントを作る際に分類可能な基準があれば、 最初から粒度と抽象度を合わせたコンポーネントが作成できます。 アトミックデザインはそう言うニーズに答えられる設計思想です。
アトミックデザインについて
1からコンポーネントの抽象度を測る基準を作るのは大変なので、有名なアトミックデザインの原則を利用し、提案してみます。
アトミックデザインの原文はここにあります。
アトミックデザインはデザインパーツをいくつかの抽象度で階層に分けて作成し、その組み合わせだけでwebデザインを設計するものです。
階層は次のように分かれています。

もっとも抽象度の高いAtomsを組み合わせ、さらに上位のコンポーネントを作っていくイメージです。
もちろん、Reactにこの設計を取り入れる場合は手を加え、ある程度解釈を変更する必要があります。
React + styled-system にアトミックデザインを取り入れる
ATOMS について
アトミックデザインで最も抽象度の高いコンポーネントが Atoms で、ウェブサイトの最小パーツとして使われます。 公式サイトにはこのような画像がサンプルとして用意されています。

上の画像にはアトミックデザインの問題点が1つあります。 それは、最も抽象度が高いと言いつつも、それ自体に複数の要素が含まれてしまっていることです。
画像をよく見ると、Button には「テキスト」と「クリック可能な範囲」という2つの要素が確認できます。 Atoms は最小単位と定義されているものの、最小単位自体がどの程度の粒度であり抽象度なのかが原文では曖昧です。
そこで、Atomsの定義をこのサイトを参考に、再定義します。
Atomic Design はなぜ難しいか?どうやって難しさを解消するか
Atomsの原則
- 特定のプロダクト (Web サイト) についての知識を持たない
- 基本的な Web UI としての知識 (機能) しか持たない
- Emotoin + styled-system をベースとしたコンポーネントである
- 複数のAtomsを内包しない
Atoms において重要なルールは1番目の特定のプロダクトについての情報を持たないというところです。 例えば、このようなEmotionのコンポーネントを作ったとしましょう。
export const Text = styled('div', { shouldForwardProp })<TextProps>`
color: red;
`
文字を扱うコンポーネントですが、文字の色の指定が赤で固定されています。 これは、そのサイト特有の情報となるので、この様なコンポーネントはAtomsの粒度では適切ではありません。
自分が扱っているAtomコンポーネントは現在以下の9個です。
- Box
- Clickable
- Link
- Text
- Image
- Svg
- Motion
- TextField
- TextArea
Molecules について
MoleculesはAtomsで構成されたコンポーネントです。
3つのルールで作られている。 Moleculesの原則
- 特定のプロダクト (Web サイト) についての知識を持たない
- 1つ以上のコンポーネントを組み合わせて構成されるようなWeb UIの知識 (機能) を持つ
- いくらかの複雑性はもつがこれ単体では成立しない
原文ではこの様なコンポーネントが想定されています。

上の図は「検索フォーム」コンポーネントで、構成要素に、「ラベル」、「インプット」、「ボタン」が組み合わされています。
特に重要なのは、同じく原則1の特定のプロダクト情報を持たないというところです。
例えば、この様な Button コンポーネントを作ったとします。
export function Button({ children, ...props }) {
return (
<Clickable
width={[180, null, 235]}
height={[44, null, 50]}
display="flex"
alignItems="center"
justifyContent="center"
overflow="hidden"
{...props}
>
<Text as="span" fontWeight="bold" fontSize={16} letterSpacing={0.8} lineHeight={1} color="white">
{children}
</Text>
</Clickable>
)
}
このMoleculeコンポーネントにはwebサイト固有の値がいくつかあります。
ClickableのwidthやTextのwhiteは、そのサイトでのみ利用する値です。
このようなコンポーネントはたとえ、原則の2と3を守っていたとしても、 1の「特定のプロダクト (Web サイト) についての知識を持たない」に反するため、Atomsコンポーネントとしては分類されません。
Organisms、Templates、Pages に関して
Organisms
Organisms は今までのコンポーネントとは違いプロジェクト固有の知識を保有します。
今まで散々作られてきた多くのコンポーネントがこちらになると思われます。
Footer.tsxやHeader.tsx、UserList.tsxなどは Organisms に該当します。
Organismsに関しては今まで通り、components フォルダにファイルを作成し利用します。
templates
自分は、まだテンプレートを使いこなせていないので、ここでは割愛します。 確かにほとんどのページでFooter コンポーネントやHeader コンポーネントは利用するので、 それを含むテンプレートを作成し、それぞれのページに反映させるのは良さそうです。
pages
pages はそのまま Next.js のapp フォルダにあるコンポーネントを指します。
まとめ
最後は駆け足でまとめてしまいましたが、以上のような抽象度分類を行うことで、 再利用可能なコンポーネントとプロジェクト内で使いまわせるコンポーネントをうまく扱える様になります。
次の章では、共通コンポーネントを作成します。