AtomicDesignでAtomsの設計を失敗した話

#概要
今回は、**AtomicDesign**でのコンポーネント設計をした行った時にやらかしたミスを記事にしてみました。
#AtomicDesignって
最初に**AtomicDesign**とはなんなのかを軽く振り返ってみましょう。
Qiita内外でも多くの説明がなされているので、ざっくりと。

**AtomicDesign**とは、コンポーネントを以下の**5つの階層**に分け、
コンポーネントの役割や責任を明確しそれらをルールを決めて分割していこう、という概念です。

- Atoms(原子)
- Molecules(分子)
- Organisms(有機体)
- Templates
- Pages

上が最小のコンポーネントで、下が一番大きいコンポーネントとなります。
また、**AtomicDesignのルール**として、

- Atoms
    - これ以上機能として分割できない機能達
- Molecules
    - Atomsの集合体
- Organisms
    - AtomsやMoleculesの集合体で、複雑な構成
- Templates
    - MoleculesやOrganismsを配置する設計図
- Pages
    - Templatesに対してデータを反映した物

といった感じですね。
それぞれの役割が明確化されていて、コンポーネントをどう作っていけばいいかの方針がわかりやすくなってますね。
~~それでもやらかした自分とは一体~~

#Atomsをつくってみた
まずは、設計がマズい形のAtomsを見てみましょう
今回はButton要素のコンポーネントを例に使います。

```vuejs:button
<template>
  <button @click="onClick">
    <slot></slot>
  </button>
</template>

<script>
export default {
  methods: {
    onClick() {
      return this.$emit("click");
    }
  }
};
</script>

<style lang="scss" scoped>
.Base__Button {
  &--green {
    //省略
  }
  &--red {
    //省略
  }
  &--yellow {
    //省略
  }
}
</style>
```
HTML要素とScript要素は至ってシンプルな構成です。
CSS要素に関しては、1つのファイルの中に複数のClass属性が記述する形で作ってみました。
このComponentではclass属性の指定をせずに、Componentを配置した親側でClass属性を指定するという方法をとりました。
呼び出す側の例も書いておきます。

```vuejs:Parent
<base-button class="Base__Button--green">HOGE</base-button>
```

~~親でClass属性を指定してる時点でとっても嫌な予感がするぞ~~

#が、だめ！
最初はこの設計でも問題なく使えてましたが、使用するClass属性が増えて、記述が多くなってくると、
当然ながら使いづらくなってきました。

というのも、新しいスタイルが必要となった場合も同じファイルに追加していくために、
アイコンに使用する目的の無色透明で小さめのスタイルを…。
次はタブ用のスタイルを…
次はgreenだけ、ボタンのサイズを色々作りたい
次は…

となり、気づいたらClass属性の記述だけでファットになって、しかも複雑に絡み合い濃厚な味に…。

▂▅▇█▓▒░(‘ω’)░▒▓█▇▅▂　**うわあああああああああ**

###Badな設計
どのような点が**Bad**であったかを確認してみましょう。

- **親と密結合**
    - 親でクラス名を指定しないと見た目を固定できない
    - 下手したら親にスタイルのコードを書いている
- **ComponentのCSSがファット**
    - 1つのComponentに色んなスタイルを詰め込みすぎ
- **CSSが上書きなどでスパゲッティコード化**
- **Atomsの外でClass属性/CSS属性を記述**
    - コンポーネントの状態を閉じ込めれてない

基礎的な注意点ばかりではありますね…。
なぜ、このような設計のComponentを作ってしまったかも反省しておきましょう。

- 同じようなComponentを作るのはNG
    - 1つのComponentで管理やったら楽じゃん！
- Atomsのルール通り処理の機能は分割しつつスタイルはまとめたい
- 色んな箇所で使うなら同じComponentのほうがいいのでは
- でも色んなデザインにしたいけど細かく制御できないから親にCSS属性を書くしか無い

というのを考えて作りやらかしてしまいました。
特に最後のは、もはやAtomsを親からスタイルを上書きしていたので、
これ普通のButtonタグでもよくない？のとこれ疎結合になっていないよね？という、色々なルール違反な状態となっていました。

#そうだ、いっぱいつくればいいんだ
では、どうすればいいんだ！と悩み、色々ググって情報を漁っていたところ気づきました。
色んな人のLT資料や、記事を見ていっそのこと振り切って考えていいのかもと思ってすえたどり着いた答えが、
**そうだ、必要な分のAtomsをいっぱいつくればいいんだ**

HTML要素やJavascriptと同じように、**CSSも見た目毎に最小単位まで絞り込んだComponent**を作ってしまえばいいのだと。
方針としては、「Importしたら見た目も完成していたそのまま使える」ぐらいの疎結合と独立感で。
早速、先程のButtonを作り直してみました。
~~ImportしてるComponentが多くてパス修正が大変だった~~

```javascript
BaseButtonGreen.vue
BaseButtonRed.vue
BaseButtonYellow.vue
BaseButtonRadius.vue
BaseButtonSemiRadius.vue
```
分解し、それぞれ作ってみました。
中身の方は、試しに`BaseButtonGreen.vue`を見てみましょう。

```vuejs:BaseButtonGreen.vue
<template>
  <button @click="onClick" class="Base__Button--green">
    <slot></slot>
  </button>
</template>

<script>
export default {
  methods: {
    onClick() {
      return this.$emit("click");
    }
  }
};
</script>

<style lang="scss" scoped>
.Base__Button--green {
  //省略
}
</style>
```
以前のComponentと違うのは、class属性をここで指定しCSSでは他のclass属性を記述していない、というところです。
`BaseButtonGreen.vue`は名前で表すように**緑色のボタン**に関係するCSS属性だけを記述し、
`BaseButtonRed.vue`は、**赤色のボタン**を。
`BaseButtonRadius.vue`では、**円形**に関係するCSS属性だけを。
こうすることで、親とは疎結合になり独立性が高いComponentとなり、Importしそのまま使える事ができます。

###差分がある場合には？
`BaseButtonGreen.vue`の縦横幅を大きい/小さいのを使いたいとなった場合ですが、
この場合は`BaseButtonGreen.vue`の中に差分を書くのではなく、いっそのこと別Componentにしましょう。

```javascript
BaseButtonGreen.vue
BaseButtonGreenLarge.vue
BaseButtonGreenSmall.vue
```

ファイル名に修飾子で「**Large**」か「**Small**」などを付け加えて、違いを明確化。
このように、同じ構成や見た目だけど**細部が微妙に違うAtoms**を作る場合には、ファイル名に**修飾子**をつけました。
サイズだけではなく、**hover**などの擬似クラスでちょっとリッチなエフェクトを付けたいけど、
エフェクトがないのも必要という場合には、Componentを2つに分割する事で変更内容を1ファイルに閉じ込める事ができます。

#おわり
今回は**AtomicDesign**で私がやらかした勘違いと失敗を、どのようにルールを見直した修正したかを記事にしてみました。
割と**AtomicDesign**の解説記事では、Moleculesからの作り方が多く書かれていますが、
**Atoms**でどうやって作るの？て記事はなかなか見ないので今回ネタにしてみました。
~~(ついでに自分犯した失敗の反省も込めて)~~

実際の現場では**Atoms**は作らず、**Vuetify**などのComponentFrameworkを使う事も多いと思われますが、
いざ自分でつくろうとすると案外どうやるんだったかな？とか、これでよかったよね？となりやすい部分であるので、
しっかりルール作りや方針確認をしたほうがいいですね！

また、アプリやサイトのデザインにがっつり影響する部分でもあるので
やっぱり自分で作ってると楽しいし、これからもたくさん**Atoms**を作っていこうかなと確信しました。
~~でも作りすぎると**Import**がめんどくさいので、まとめて読み込む処理を組み入れなくちゃ…(確信)~~