こんにちは。ひらやま(@rhirayamaaan)です。
この記事は、GLOBIS Advent Calendar 2020 に参加している記事です。
さて、Atomic Design という言葉が巷で話題になってからだいぶ日は経ちますが、私の「Atomic Designってデザイナーには難しくない!?という話」という記事の LGTM 数も現時点(2020/12/08 時点)でゆるやかに増加傾向にあり、まだまだ Atomic Design を考えている人たちは多いのではないかと思います。
そんな中で、昨今「OOUI」という言葉をよく耳にするようになりました。
ソシオメディアの上野さんが「オブジェクト指向UIデザイン」という本を刊行されたことで、より一層この言葉が世に広がったように思えます。
オブジェクト指向というものを UI デザインの領域に持ってくることで、エンジニアがオブジェクト指向でプログラムを書くように、ユーザがオブジェクト指向で UI を使えるようになるはずです。
例えば「商品を購入したい」と考えた場合、「購入する」という行動を先に選ぶのではなく「商品」という「オブジェクト」を選択するはずです。
このようにオブジェクトを選択してから、どう行動をするかを考えるのが自然です。
それを可能にするのが OOUI という設計方法であり、これは Atomic Design を設計する上でも参考にできるのではないかと考えました。
この考えをベースに持った状態で、Atomic Design による設計をもう一度考えてみたいと思います。
Atomic Design の「ユーザ」は誰なのか
Atomic Design は、UI を一つ一つ分解して共通化を図り、車輪の再開発を減らしていくために作られることが多いのではないでしょうか。
例えば、Button のようなコンポーネントを作成しておけば、いちいち角丸の矩形を作成して色を塗って文字を入れ…という作業が不要になり、簡単に Button を配置できるようになります。
今では、Figma, Sketch 等の UI 作成ツールでコンポーネント化できますし、言わずもがなですが、プログラミングにおいても React, Vue, Angular 等で同様にコンポーネント化できます。
使い回しが効くように Atomic Design を設計するということは、その出来上がったコンポーネントライブラリを誰かに使ってもらうために作ることが目的となります。
では、誰に使ってもらうためなのでしょうか?
それはもちろん、デザイナーやエンジニアです。
Atomic Design はデザイナーやエンジニアがものづくりをする過程においてより創造的な活動を行えるように、ものづくりにおける無駄を省きながら、ものづくりにより一層集中できる環境を提供する一要素として位置づけられるべきです。
つまり、Atomic Design を設計する上で重要なのは、デザイナーやエンジニアがオブジェクトベースでものづくりをできるようにすることだと私は考えています。
Atomic Design の分解・分類の難しさ
例えば、以下のような UI があったとしましょう。
モーダルで確認を促す UI です。
この UI をユーザ目線で考えるとタスク指向寄りなデザインではありますが、今回はその視点では考えません。
今回考えたいのはあくまでも、**「この UI を作る人に対して、どう要素を分解しておくとものづくりがしやすいか」**なので、この視点で考えを進めていきます。
上記のデザインを見たときに、みなさんならどのようにコンポーネントを分解していくでしょうか?
一緒に見ていきたいと思います。
Atoms を定義してみる
まず、Heading
, Button
, Checkbox
というような要素を Atoms として定義できそうです。
私の記事でも紹介していますが「Atomic Design ~堅牢で使いやすいUIを効率良く設計する」という本に以下の文章が記述されています。
つまり、Atoms層は、「それ以上UIとしての機能性を破壊しない最小要素」となるように分割します。
機能性を破壊しない形で最小単位になっていればいいので、Atoms は上記の分解で良さそうです。
Molecules を定義してみる
次に、もう一つ大きな要素を考えていくと、白い矩形で囲われている箇所でコンポーネントにできそうでしょうか。
Atoms 群で構成されている要素なので、こちらは Molecules として定義してみましょう。
こちらは確認を促すので Confirm
という名前で定義しておくと、使い回しが効くような感じがします。
Organisms を定義してみる
最後に、さらに大きな要素を考えると、この Confirm が黒い半透明のオーバーレイを活用してモーダルで表示されている状態で一つのコンポーネントにできそうでしょうか。
名前を ConfirmModal
という名前にして共通化しておくと良さそうな感じがします。
というように、小さい要素から順に分解してみましたがいかがでしょうか?
みなさん、何か違和感を感じませんでしたか?
Atoms まではそこまで違和感がなかったと思いますが、Molecules あたりから、え?本当にそれでいいの?という、なんとも言えないもやもやがあります。
(私が Molecules 以降、曖昧な言い回しをしているからというのもあるとは思いますが……)
これは、 Confirm
や ConfirmModal
を作ったときに、果たしてそれらのコンポーネントは本当に使いまわしの効くコンポーネントなのか?という疑問が残るからではないでしょうか。
二つのコンポーネントは、実は限定的な使われ方をするものであって、わざわざ Molecules や Organisms として共通化したパーツとして定義する必要がないものかもしれない、という考えを拭いきれません。
もしかしたら、Templates に定義した1ページの中の一つの要素として Confirm
や ConfirmModal
を定義していた方が、逆にスッキリするかもしれません。
このような混乱は、Atomic Design としてパーツを分割することだけを盲目的に考えているときに発生してしまいます。
しっかりと、利用者であるデザイナーとエンジニアのことを考えた上で、もう一度要素を分解してみたいと思います。
アクションをオブジェクト化する
前提として、一旦便宜上 Molecules に Confirm
が定義されているとして、ConfirmModal
について考えていきたいと思います。
(Confirm
については後ほど考察します。)
まずは、ConfirmModal
を作る際に、作り手の思考を簡単に言語化してみます。
「Confirm
をモーダルで表示する」
この文言をそのままにコンポーネントを作成すると、ただ Confirm
にモーダル機能を付随させたオブジェクトが出来上がってしまいそうです。
そうなると、使い回しが効きそうなコンポーネントになっているかという疑問は拭いきれません。
なので、ここで OOUI の考え方を使って考えていきます。
上記の文章をもう少し良く見てみると、以下のように分解できるはずです。
- 「
Confirm
というオブジェクト」 - 「対象をモーダル表示するというアクション」
OOUI では、この**「アクション」自体もオブジェクトとして捉えていきます**。
アクションを「オブジェクト化」した上で言語化した文章を整理すると、以下のようになります。
- 「
Confirm
というオブジェクト」 - 「○○をモーダル表示するオブジェクト」
このように、「モーダルで表示する」というアクション自体をコンポーネントにしてしまうことができるのです。
さらに「○○をモーダルで表示するオブジェクト」というものは、Atoms の「機能性を破壊しない最小要素」にも一致します。
なので、Organisms として作っていた ConfirmModal
を作らなくても、Confirm
という Molecules と Modal
という Atoms を組み合わせるだけで実装できてしまうのです。
もし、React で UI を作成することを想定した場合、以下のようにシンプルにコーディングできます。
(トップページ用の Template を作成している想定のコードです。)
import React, { FC } from 'react'
import { Modal } from '../atoms/Modal'
import { Confirm } from '../molecules/Confirm'
export const Top:FC = () => {
<>
<div>
something...
</div>
<Modal>
{/* この children が ○○ にあたる部分です */}
<Confirm />
</Modal>
</>
}
今回のように OOUI の考えを借りながらしっかりとコンポーネントを定義できていれば、わざわざ Organisms を作らなくても Template に直接 Atoms と Molecules を組み合わせて UI を作成できるのです。
アクションのオブジェクト化のメリット
Modal
というアクションをオブジェクト化したコンポーネントを作成したことによって、どのようなメリットがあるのかをもう少し説明していきます。
Confirm 以外のオブジェクトもモーダル化できる
保守をしていく上で、いくら最初に「確認でしかモーダルは使わない!」と決めたとしても、ビジネス要件によってどうしても他のものをモーダルにしたくなるケースは出てきてしまうのが常です。
今回のようにアクションをオブジェクト化しておけば、Confirm
以外コンポーネントをモーダル化することができます。
何かしらの Organisms を入れるのでもいいし、ただの文字列や、緊急的に style 属性でスタイリングしたものもモーダル化できます。
モーダル自体の表現(オーバーレイのスタイリングや中央配置の仕方等など)の仕方は固定しつつ、中身のコンテンツだけ可変にできます。
このように、使い方に余白が生まれるため、便利にしつつも表現を狭めないような設計にすることができます。
Confirm をモーダル以外の表現をしても違和感がない
例えば Confirm
を、画面の下側にフローティングさせて表示させたくなったとします。
その場合に ConfirmModal
というものが前例としてあると、ConfirmFloating
というコンポーネントを作って法則性を担保したくなります。
しかし、今回のように設計しておけば、Floating
というコンポーネントに切り出すことができるので、好きなものをフローティングさせられるようになるわけです。
このような考えを持っておけば、Accordion
, Carousel
というような機能も Atoms としてコンポーネントを切り出すことができます。
アクションを「機能性を破壊しない最小要素」で分解していることになるので、Atoms として切り出してよいのです。
ConfirmModal というコンポーネントも作れる
もし、ConfirmModal
というコンポーネントの出現頻度が高い場合には、上記の設計から再度 ConfirmModal
を作ることもできます。
import React, { FC } from 'react'
import { Modal } from '../atoms/Modal'
import { Confirm } from '../molecules/Confirm'
export const ConfirmModal:FC = () => {
return (
<>
<Modal>
<Confirm />
</Modal>
</>
)
}
もともと考えていた ConfirmModal
のようにモーダルの機能と Confirm
が密結合になっていると分解するのは大変です。
しかし、今回のように疎結合なものを作っておいて結合するのは容易になので、Atoms をしっかりと定義することはとても重要です。
見た目を頼りにグルーピングしない
さて、次に Molecules の Confirm
について考えていきたいと思います。
先ほど、Confirm
を考える際に、以下のような考えでコンポーネント化しました。
白い矩形で囲われている箇所でコンポーネントにできそうでしょうか。
このように、「矩形で囲われている」というような形で「見た目」をベースにコンポーネント化してしまうと、密結合になりやすく、使い回しが効かないコンポーネントになってしまいがちです。
それを避けながら使いやすいコンポーネントを作るためには、Confirm
に必要なオブジェクトとアクションは何かを考えてみると良いです。
もう一度 UI を見てみましょう。
まずは、Enroll
, Cancel
というアクションを実行するものは必ずないといけません。
これがないと「確認」に対しての「回答」をユーザから得ることができないからです。
なので、「押したら○○する」というアクションをオブジェクト化した Button
というオブジェクト(Atom)を Confirm
内で利用するのが良さそうです。
ただし、Button
内の Enroll
, Cancel
という文言自体は、Confirm
の利用する文脈に依存するので、外から流せるようにすべきです。
次に、タイトルと説明文に関してです。
これも、存在していないとユーザに質問を行えないので確認が取れないため必須です。
説明文は文章を外から流せるようにし、見出しはそのサービス内で適切な見出しを表現する Heading
というオブジェクト(Atom)を Confirm
内で利用するようにします。
見出しの文言自体は Enroll
と同様に、文脈によって変わるので、外から流せると良いでしょう。
そして最後に、チェックボックスについてです。
このチェックボックスは、「Don't show this message(今後このメッセージを表示しない)」と書かれているため、Confirm
の利用文脈においては必要なチェックボックスです。
なので、このチェックボックスを使わなくても良いケースも考えられるため、この機能はオプションとして位置づけられると考えられます。
この場合に、このチェックボックスを闇雲に Confirm
内で利用するようにしたとします。
そうしてしまうと、もし保守をしていく中でチェックボックスを使わないケースの方がメジャーになっていったとしたら、チェックボックスの機能は完全に過多になってしまいます。
なので、ここはチェックボックスの機能を Confirm
には含めずに、外部から様々なオブジェクトを代入できるようにした方が使い勝手が良さそうです。
上記を踏まえた上で、React でコード化してみます。
import React, { FC, ComponentProps, MouseEventHandler } from 'react'
import { Button } from 'components/atoms/Button'
import { Heading } from 'components/atoms/Heading'
type Props = {
title: string,
description: string,
cancelButton: {
onClick: MouseEventHandler<HTMLButtonElement>,
text: string,
},
continueButton: {
onClick: ComponentProps<typeof Button>,
text: string,
},
className?: string,
}
export const Confirm: FC<Props> = ({
title,
description,
cancelButton,
continueButton,
className = '',
children,
}) => {
return (
<div className={['confirm', className].join(' ')}>
<Heading>{title}</Heading>
<p className="confirm__description">{description}</p>
{children ? <div className="confirm__note">{children}</div> : null}
<div className="confirm__buttons">
<button onClick={cancelButton.onClick} className="confirm__cancel">
{cancelButton.text}
</button>
<Button onClick={continueButton.onClick}>{continueButton.text}</Button>
</div>
</div>
)
}
考慮したいことを踏まえた上で、上記のようにコーディングしてみました。
ポイントとなる箇所は何箇所かありますが、今回ピックアップしたいのは、children
を受け付けている点です。
children
は、オプションであるチェックボックスの受け入れ口となります。
こうすることで「様々なオブジェクトを受け付ける」という要件を満たせています。
例えば、ラジオボタンによる選択式の UI でも、「詳細はこちら」というようにリンクを含んだ UI でも実装可能となります。
このように実装しておけば、Confirm
という機能を保ちつつ、ある程度フレキシブルに UI を作成できるようになっているはずです。
ただ、一点注意していただきたいのは、今回の実装はあくまでも例であり、上記コードがベストプラクティスであるということを述べたいわけではありません。
どこを必須としてどこをオプションにするかは、サービスや事業の環境に応じて変わってくるので、最適解を明示することはできません。
しかし今回の例から、汎用性が高く、使い勝手の良いコンポーネント作成をする際のきっかけになればと思っています。
作成すべきコンポーネントがサービスや事業の中でどういう振る舞いをすべきを考え、それを一般化・抽象化し、その上でもう一度目の前にある UI の見たときに、どの要素と機能が必須・オプションなのかを考えていくことこそが、Atomic Design を基にコンポーネントを作成する上で重要だと考えています。
さいごに
いかがだったでしょうか。
ここまで散々つらつらと述べてきましたが、私自身、OOUI に対してとても詳しいというわけではないです。
しかし、OOUI の考え方をベースに Atomic Design を捉え直してみると、より使いやすいコンポーネント設計を考えやすくなる感覚があったので記事に起こしてみました。
実際に使いやすいコンポーネントライブラリを作るのはなかなか難しく、やってみないとわからないことも多いですが、今回の記事によって少しでも良いコンポーネントライブラリが作れるようになることを願っています。
また、今回 React でコンポーネント実装をしていますが、ツールやフレームワークが違っても、考え方自体は転用できるはず。
Figma や Sketch、Vue や Angular 等などの様々なツール上で、かつ職能を超えて、この記事の中身を参考にしていただけたら、大変嬉しい限りです。