React と AtomicDesign - Atom(原子)編

すべて個人的な考えです。都度更新。

使用される要素の最小サイズが Atom(原子)

よく例にあるのがボタン(<button>ボタン</button>)です。これは「クリックで何かしらアクションを起こす」という役割だけの要素だからです。

<pre><code>...</code></pre>はコードを表示するための構成で、2つの HTML 要素で構成されています。これは Atom なのかすが、僕は Atom に分類しています。2つの要素を使っていますが、単にコードの入れる場所って役割以外に使うことないからです。

Atom と Atom に分類し使うこともできると思いますが、それらを単体で使わないのであれば恐らく単に細分化しすぎで、やはりatom + atom = atomとなるんじゃないかと思います。「この役目を果たすための最小サイズのラインはどこだろう?」と分類する感じで考えています。

OOCSS

OOCSSというのはオブジェクト指向CSSと呼ばれるCSS設計方法で、スタイル付けをスキン(見た目)とストラクチャ(構造)に分けて考えようという設計方法です。スキンは色とか装飾系だけをまとめたセレクタのことで、ストラクチャは幅、高さ、余白とか見た目以外のスタイルをまとめたセレクタです。

Atom では「できる」を保証します。実際にどんな見た目になるかは上位層が決めます。
フォームの中では幅100%だけどヘッダーでは300pxで使いたい<input>など、こういうものは上位層でそういった構造スタイルを付けるようにします。

// molecule
const SearchOfTopMenu = () => {
  return (
    {/* ... */}
    <Input structure="fluid" skin="standard" />
    {/* ... */
  );
}

Atomic では上位層に行くほど使いまわしの回数は減るはずで、どんどん具体的な内容にもなっていくかなと思うので、具体的になってから初めて構造は付ければ良いかなと思います。

大抵デザインから Atomic を作ろうとするとあるレベルで要素をどんどん分解して最後に組み立てるだけとなってしまいがちですが、コンポーネントを作る段階ではなるべく「この要素はここで使うから」みたいな具体的な背景も頭の中から落とすよう(考えないよう)にするといいかもしれないなと思います。

ロジックを持って、状態は持たない

これはHTMLの要素の動きに近いです。

ロジックは持ってもいいです。disabledという属性値を持たせて、これが真ならアクションを起こせなくするや(まぁdisabledなら CSS で制御できそうですが)、loadingtrueならテキストをローディングアイコンにするような簡単なものなど。ただしそれらを状態として保持しないようにします。

const PostButton = ({loading = false, disabled = false, children}) => {
  return (
    <button className="..." disabled={disabled}>
      {loading ? <svg /* ローディングアイコン */ /> : children}
    </button>
  );
};
//
// 使う
// <PostButton loading disabled>投稿</PostButton>

HTML でも<input disabled placeholder="...">などで Atom 単位である程度動的に制御できるように作られています。disabledを消せばまた入力できるようになるでしょう。ロジックを持って状態は持たないのは自然だと思います。

Functional Component で定義

実装する際は、あとで拡張しやすいように最低でも Functional Component として定義するといいかと思います。あと、TypeScript を使っている場合は自分の型で完結させたほうが後々変にならないだろう(少しは)とも思います。


ここから2019-06までの内容(アーカイブ)


<div><span> のようにある要素を組み上げるための部品が Atom です。 僕はよくstyled-componentsを使いますが、これで生成したコンポーネントは全て最小単位の部品で Atom に属すと考えれます。

// FlexはAtom
const Flex = styled.div`
  display: flex;
`;
// MyFormはAtom
const MyForm = styled.form`
  /* ... */
`;
// MyInputはAtom
const MyInput = styled.input`
  /* ... */
`;

これらはこのように使えます。

<Flex>
  <MyForm>
    <MyInput />
  </MyForm>
</Flex>

Styled Components を使っていないなら単一要素の SFC と1対1で CSS ファイルを作成し管理するのがいいと思います。

foo
 ├─ index.jsx
 └─ foo.css
// atoms/index.js
const Flex = props => <div className="atom-Flex" {...props} />;
/* atoms/flex.css */
.atom-Flex {
  display: flex;
}

属性値で見た目の情報を変える

HTML の<input>readonlydisabledといった属性を渡すとスタイルが変わるようにaria-*data-*といった属性を振り、それでスタイルを振り分けます。

const Flex = styled.div`
  display: flex;

  &[aria-orientation='vertical'] {
    flex-direction: column;
  }
`;

const FlexItem = styled.div`
  flex: 0;

  & + & {
    margin-left: 1em;
  }

  &[aria-label='main'] {
    flex: 0 1 1000px;
  }
`;

// <Flex>
//   <FlexItem>...</FlexItem>
//   <FlexItem aria-label="main">
//     <Flex aria-orientation="vertical" />
//   </FlexItem>
// </Flex>

使えそうなaria-*属性一覧

追記 (2019-01) - 使えるaria-*属性一覧

と思いましたが、要素にはもともとデフォルトでroleが割り当てられていて、このroleにこのaria-*は相応しくないというようなルールがあるようです。なので適当に上の属性を当てているとその辺りで Lighthouse のアクセシビリティのスコアが結構下がってしまいます。

僕的にはaria-*を使ってのスタイル制御は分かりやすく管理しやすいと思っているので、ariadataに変えたもの(data-hidden)を今は使っています。