Create Branch

Atomic Designを用いたコンポーネントの実装について(atoms〜organisms)

Published on 2025年2月19日

メモ

基本方針

  1. 一貫した実装が可能であること
  2. 実装者とデザイナー間で認識を共有できる状態を作ること

スタイリング方針の選択肢

1. 自前でCSSを実装する場合

必要な定義

  1. クラスの命名規則
  2. CSS実装方法の選定
    • CSS in JS
    • CSS modules
    • Sass
  3. CSSのスタイル管理方法

特徴と課題

  • 自由にスタイリングが可能
  • デザイナーとエンジニアの認識が異なりやすい
  • 1つのタグごとに以下の細かい認識合わせが必要:
    • px指定
    • フォント設定
    • その他の詳細なスタイル
  • 認識合わせを行わない場合、デザイナーがイメージしているUXと全く異なる結果になる

2. CSSフレームワークを使用する場合

必要な定義:

  1. ライブラリの選定
  2. ライブラリの運用認識確認

利点:

  • 定義されているクラスに則ることで、デザイナーとエンジニアの細かいスタイルの認識調整が軽減される
  • ベースとなる基準があるため、細かいことは考えずにUI実装に注力できる

Atomic Designの実装方針

基本原則

  • コンポーネントを作成しすぎない
  • コンポーネントを作成する際、基準をどこに持っていくか考える

各レイヤーの詳細

Atoms(原子)

  • 最小単位のコンポーネント
  • 基本的なスタイルの状態を提供する必要がある

Molecules(分子)

基本概念:

  • 再利用できるコンポーネント
  • ユースケースでコンポーネントを作成する

実装アプローチの変更点

当初の理解:

  • molecules/Cardのような組み合わせを作成し、Storyごとにユースケースで場合分けする

改善後の方針

  • ユースケースごとにコンポーネントを分ける
    • molecules/ImageCard
    • molecules/TextCard
    • molecules/CardList

実装の重要ポイント

  1. 個別の処理/スタイリングについて

    • 各コンポーネント内で完結させる
    • そのユースケースが持つ個別の動作をコンポーネント内にカプセル化
    • スタイルも各コンポーネントで管理
  2. 外部との関係

    • 外部ではそのコンポーネントに関する情報は一切持たせない
    • organismsでmoleculesを参照する場合
      • 呼び出し元 → 参照するコンポーネント の流れで依存
      • 参照するコンポーネントは呼び出し元の情報を知らない状態を維持
      • 呼び出し元が全てclassNameでスタイルを渡すことでorganisms独立のスタイルを反映することができる。
  3. スタイル打ち消しの例:

    • atoms/CardにPadding:10pxの設定がある場合
    • molecules/ImageCardでPaddingが不要な場合
      • molecules/ImageCard内で打ち消す

ユースケースで管理する理由

  • molecules/Cardのように複数の使い方が考えられる実装を避ける
  • 実装者の判断による統一性の欠如を防ぐ
  • コンポーネントの実装の複雑化を防ぐ
  • コンポーネントの役割と独立性を明確にする

問題のある実装例:

// molecules/card/index.tsx
import React, { ComponentProps } from "react";
import { Card as BaseCard } from "../../../shadcn/card";
import { cn } from "../../../utils";
import { cardVariants, type CardVariants } from "./index.variants";

type CardProps = ComponentProps<typeof BaseCard> & CardVariants;

export const Card = ({ size, background, layout, spacing, className, ...props }: CardProps) => {
  return (
    <BaseCard
      className={cn(cardVariants({ size, background, layout, spacing }), className)}
      {...props}
    />
  );
};

export type { CardProps };

// molecules/card/index.variants.ts
import { cva, VariantProps } from "class-variance-authority";

const cardVariants = cva(
  "flex flex-col items-start bg-card rounded-[8px] border border-[#E4E4E7] text-card-foreground bg-white shadow-[0px_1px_2px_0px_rgba(0,0,0,0.05)]",
  {
    variants: {
      size: {
        default: "min-w-[248px] min-h-[348px]",
      },
      background: {
        default: "bg-white",
        gray: "bg-[#F5F5F5]",
      },
      layout: {
        fixed: "w-fit",
      },
      spacing: {
        normal: "p-6",
      },
    },
    defaultVariants: {
      size: "default",
      background: "default",
      layout: "fixed",
      spacing: "normal",
    },
  }
);

type CardVariants = VariantProps<typeof cardVariants>;

export { cardVariants, type CardVariants };


このコードの問題点

  • Cardの複数のユースケースのスタイリングを一つのコンポーネントで保持している
  • どの時にコンポーネントのスタイルを適用するか判断が人によって変わる。

Organisms(有機体)

  • 特定の機能を提供する時の箱のような存在
  • moleculesのユースケースを複数組み合わせたもの
  • 複数の条件を持ち、特定の条件がマッチすることによって動作を制御

具体例: Google Formのような構造

  • 各moleculesのカードコンポーネントが集結
  • それぞれの制約をコンポーネントごとに保持

実装上の重要な注意点

  1. コンポーネントの役割の明確化
  2. 依存関係の一方向性の維持
  3. カプセル化の徹底
  4. ユースケースベースでの分割
  5. スタイリングの責任範囲の明確化
hirotobeat