BadgeからIndicatorへのパラダイムシフト
はじめに
株式会社ドワンゴのニコニコ生放送でフロントエンドを担当している misuken です。
前回の記事でBadgeという名前の曖昧さについて取り上げました。
今回の記事では、Badgeの曖昧さを解消するために、Indicatorという概念を導入して解決する方法を説明します。
Badgeの本質は何なのか、そしてIndicatorとはどういうもので、なぜそれが問題を解決できるのかを詳細に解説していきますので、Badge周辺に対する一つのアプローチとして参考にして頂けると幸いです。
Badgeと呼ばれているUIは何なのか
前回の記事で述べた、見た目からの由来と、役割からの由来のBadgeについて、それぞれの視点からBadgeの本質を探ってみます。
見た目からの視点
見た目が由来であるとしても、そのUIが使用されるときは、何かしらの意図や役割が存在しているはずです。
小さな円形に数値
小さな円形に数値が表示される場合、数値を伝えるためのUIとして広く使われています。
これは、ある対象が「どれくらい存在しているか」という量的な情報を伝えます。
小さな円形にアイコン
小さな円形にアイコンが使用されている場合は、何かのサインを伝えるためのUIとして使われています。
これは、ある対象が「どういう状態にあるか」という質的な情報を伝えます。
ドット表現
ドット表現の場合は、何か情報があることを伝えるためのUIとして使われています。
これは、ある対象が「活性か非活性か」という二択の情報を伝えます。(未読の有無、更新の有無、検索の絞り込み設定適用の有無)
役割からの視点
役割が由来の場合、そのUIが使用されるときは、ユーザーに何かしらの状態を伝えるために使われるはずです。
このようにラベルのような見た目で、Approved、Beta、Warningなどの状態をユーザーに伝えるためのUIとして使われています。
これも、ある対象が「どういう状態にあるか」という質的な情報を伝えます。
共通点
これらに共通するポイントは、ユーザーに何かしらの状態を伝えるためのUIとして使われているということです。
- 数値表現で状態を伝えるもの
- サイン表現で状態を伝えるもの
- 活性表現で状態を伝えるもの
- テキスト表現で状態を伝えるもの
現実の世界で同等の意味を持つもの
現実世界には、ここまでで説明してきたUIとしてのBadgeと同等の役割を持つものが多く存在します。
受け付けの待ち人数の表示(数値表現)
!
順番くん より引用
飛行機内の天井に付いているシートベルト着用サインのランプ(サイン表現 + 活性表現)
モバイルバッテリーの残量を示すLEDランプ(活性表現)
テレビやラジオのスタジオに付いているON AIRランプ(テキスト表現 + 活性表現)
コインロッカーの使用状況を表すランプ(テキスト表現 + 活性表現)
電車の種別を示す種別幕(テキスト表現)
アナログだったり、デジタルだったりといった違いはあるかもしれませんが、本質は全て「状態を伝えるもの」です。
このように捉えると、人は日常生活の中で、様々な状態を効率的に解釈して行動していることがわかります。
交差点にある信号もその一つですね。
そして、これらの総称は何かと言うと インジケータ(表示器) に該当します。
BadgeからIndicatorへのパラダイムシフト
Indicatorとは
Indicatorとは、あるシステムやプロセスにおける状態や状況を、ユーザーに伝えるための表示器のことです。
実は「Badge」と呼ばれているUIは、見た目由来、役割由来を問わず、Indicatorの一種であると考えられます。
Indicatorの種類
Indicatorは、その機能や表示内容によって、少なくとも4つの種類に分類できると考えています。
ここからは、もしも実際にコンポーネントとして存在していた場合、どのようなインタフェースになるかも考えてみましょう。
※ 個人的に <input type="xxx" />
に似ている側面があると感じているので、 <indicator type="xxx" />
のようなインタフェースも併記する形で説明します
1. CountIndicator
CountIndicatorは、増減する数値を表現します。未読数、ショッピングカート内の商品数など、数量的な情報をユーザーに伝えます。
<CountIndicator value={10} overflowThreshold={100} />
// または
<Indicator type="count" value={10} overflowThreshold={100} />
2. ActiveIndicator
ActiveIndicatorは、ある状態が活性状態か非活性状態かを表現します。未読の有無、更新の有無、検索の絞り込み設定適用の有無など、二択の情報をユーザーに伝えます。
<ActiveIndicator value={true}>未読情報があります</ActiveIndicator>
<ActiveIndicator value={false}>未読情報はありません</ActiveIndicator>
// または
<Indicator type="active" value={true}>未読情報があります</Indicator>
<Indicator type="active" value={false}>未読情報はありません</Indicator>
3. StatusIndicator
StatusIndicatorは、ある状態や状況をテキスト、色、アイコン(optional)で表現します。認証済み、承認、β版、注意など、多様な状態をユーザーに伝えます。(エラーメッセージのような文ではなく、あくまで状態のみを表現するものになります)
<StatusIndicator value="approved" icon={CheckIcon}>Approved</StatusIndicator>
// または
<Indicator type="status" value="approved" icon={CheckIcon}>Approved</Indicator>
ここで面白いのが、元々小さくて丸いUIで、数値を表示するものと同じグループに属していると思われていたアイコンのみを表示するタイプが、実は本質的な意味で捉えるとラベル風に表現するグループに属していたことがわかる点です。
詳細は後述します。
4. ProgressIndicator
ProgressIndicatorは、ある処理の進捗状況を視覚的に表現します。進捗を示すバーや円などの形状を使い、進行状況をユーザーに伝えます。
いわゆるプログレスバーはこれに該当すると考えられます。
<ProgressIndicator value={10} min={0} max={100} />
// または
<Indicator type="progress" value={10} min={0} max={100} />
スタイルによる変形の概念
先ほど紹介した、StatusIndicatorの表現方法の説明です。
これはスタイルによる変形の概念と捉えられます。
- 元のラベル的表現
- 文字列をVisuallyHiddenで不可視化
- サイズと余白の調整
- 角丸タイプに変更
<StatusIndicator value="authenticated" icon={CheckIcon} shape="icon">Authenticated</StatusIndicator>
// または
<Indicator type="status" value="authenticated" icon={CheckIcon} shape="icon">Authenticated</Indicator>
HTMLのマークアップやアクセシビリティ的な変化は一切行わず、見た目のみを変形させることで、意味はそのままに目的の表現を実現できます。これらの変形はスタイルのみで実現できるので、classやdata属性だけでなく、表示スペースの大きさによって動的に表現を変えることさえも、CSSのみで可能になります。
また、Adobe の Spectrum には Status Light というコンポーネントがあります。
<StatusIndicator value="approved" icon={CheckIcon} shape="light">Approved</StatusIndicator>
// または
<Indicator type="status" value="approved" icon={CheckIcon} shape="light">Approved</Indicator>
この手のコンポーネントもStatusIndicatorの変形と捉えられます。
- 元のラベル的表現
- 色をリセット
- アイコンをライト表現に変更
もちろん、テキスト部分をVisuallyHiddenで不可視化し、ライトのみの表現にしたり、ホバーしたときに不可視化してあるテキストをツールチップで表示するといったフォローも可能です。
このように、Indicatorという概念は様々な状態表現の核であり、柔軟に各表現に対応できることがわかります。
Indicatorの分類
上で説明した各Indicatorは、状態表現のバリエーションの違いであるため、共通部分をスマートに管理するディレクトリ構成に従えば、ディレクトリ構成としては以下のようにまとめられます。
base
└─ indicator
├─ _abstract
│ └─ AbstractIndicator.tsx (必要に応じて)
├─ active-indicator
│ └─ ActiveIndicator.tsx
├─ count-indicator
│ └─ CountIndicator.tsx
├─ progress-indicator
│ └─ ProgressIndicator.tsx
└─ status-indicator
└─ StatusIndicator.tsx
Indicatorの存在感
状態を表示していたUIをIndicatorと捉えてみると、動的なWebサイトにおいて、意外と多くの部分で使用されていることに気付きます。
名前がしっくり来なかったコンポーネントも、実は何かのIndicatorだったということもあります。
抽象度の高いStatusIndicatorを使用して具体的なUserStatusLabelといったコンポーネントを作れば、コンポーネントの関係性がより明確になったり、全体的に統一感のあるインタフェースに揃えやすくなったりするでしょう。
このように、Indicatorは想像以上に存在感のあるUIだったということに気付かされます。
UIの世界におけるBadgeの本質的な役割
ここまででUIの世界において、Badgeと呼ばれていたUIが、実はIndicatorの一種であることがわかりました。
そのうえで、UIの世界においてのBadgeは普通のコンポーネントとは別の形で生き残る余地があります。
レイアウト方法としての側面
最初はBadgeを「小さな丸などの形状」や、「数値や状態を示すUI」として見てきましたが、それらはIndicatorによって網羅されているものでした。しかし、Badgeという単語が持つ性質には形状や役割の性質を除いても、レイアウトの性質というものが残ります。
これは前回記事に書いてあった性質の一つであり、これこそがUIの世界におけるBadgeの本質であると言えます。
UIの見た目としてのBadgeは、小さな丸など、形状が由来していると考えられるものです。
また、小さな物を何かの上に重ねて配置する(取り付ける)というレイアウト的な意味合いも一部に含まれていると考えられます。
この性質はBadgeという例えがとてもよく合致しており、どのようなレイアウトを意図しているかが明確なため、BadgeはUIのレイアウト方法の一つであると言えるでしょう。
Badgeの定義を再考
BadgeをUIとしてのコンポーネントではなく、ある要素(例えば、Icon、Buttonなど)に別の要素(Indicatorなど)を重ねて配置する、というレイアウトコンポーネントのバリエーションの一つと捉えてみます。
つまりこのレイアウトのパターンを実装したコンポーネントであれば BadgeLayout と呼べます。
例えば以下のように、アイコンに未読数を表示する場合、BadgeLayoutを使って実装することができます。
<BadgeLayout topRight={<CountIndicator value={10} overflowThreshold={100} />}>
<Button><BellIcon /></Button>
</BadgeLayout>
// または
<Layout type="badge" topRight={<CountIndicator value={10} overflowThreshold={100} />}>
<Button><BellIcon /></Button>
</Layout>
これも共通部分をスマートに管理するディレクトリ構成に従えば、ディレクトリ構成としては以下のようにまとめられます。
base
├─ indicator
└─ layout
├─ _abstract
│ └─ AbstractLayout.tsx (必要に応じて)
├─ badge-layout
│ └─ BadgeLayout.tsx
├─ xxx-layout
│ └─ XxxLayout.tsx
├─ yyy-layout
│ └─ YyyLayout.tsx
└─ zzz-layout
└─ ZzzLayout.tsx
ちなみに、レイアウトコンポーネントとした場合のBadgeという名前は、ドメインでもUIとしてのBadgeではなく、レイアウトのバリエーション名としてのBadgeであるため、名前の問題は発生しません。
装飾方法としての側面
前回の記事で一つ課題を残した部分がありました。
Icon は アイコン単体を表すものであり、アイコンの周囲を丸い背景色やボーダーで囲った形状になる場合、Icon の責務を超えているのではないか?というものです。
元からバッジの見た目が完結しているアイコンであれば Icon で十分ですが、そうでない場合は Icon 以上の何か、見た目由来の Badge コンポーネントが欲しくなります。
この点については、レイアウトと同様に装飾コンポーネントのバリエーションとして解決する方法が考えられます。(ちなみに、Icon を配置する側のCSSで解決できるのであれば、そもそも装飾コンポーネントは不要です)
<BadgeDecoration variant="star">
<Icon />
</BadgeDecoration>
// または
<Decoration type="badge" variant="star">
<Icon />
</Decoration>
ドメインの表示物はアイコンかサムネイル画像になるはずなので、囲ったものをバッジ風に見せる装飾パターンという形で用意しておけば、Iconが責務を超えることなく、目的の見た目にできるうえ、Badgeというコンポーネントも誕生させずに済みます。
あくまで並んでいるのは達成バッジをIconで表現したものであり、その見た目が現実世界のバッジに似ているというだけの話です。
バッジ表現というものは、特別な機能性や構成を有しているわけではなく、あくまで見た目の問題であるため、こういったアプローチが有効であると言えるでしょう。
これも共通部分をスマートに管理するディレクトリ構成に従えば、ディレクトリ構成としては以下のようにまとめられます。
base
├─ decoration
│ ├─ _abstract
│ │ └─ AbstractDecoration.tsx (必要に応じて)
│ ├─ badge-decoration
│ │ └─ BadgeDecoration.tsx
│ ├─ xxx-decoration
│ │ └─ XxxDecoration.tsx
│ ├─ yyy-decoration
│ │ └─ YyyDecoration.tsx
│ └─ zzz-decoration
│ └─ ZzzDecoration.tsx
├─ indicator
└─ layout
もし、装飾内容に達成バッジに依存した内容を含む場合は、AchievementBadge側のディレクトリ配下にdecorationディレクトリを作成すれば、適切なディレクトリ構成を保てます。
Badgeの名前問題の解決
UIとしてのBadgeをIndicatorとすることで、UIとしてのBadgeは存在しなくなりました。
これにより、Badgeという名前が登場するのは、ドメイン名、バリエーション名、装飾名ということになります。
これであれば、もしBadgeListというコンポーネントを作ったとしても、ドメインのBadgeを意味しているとわかるように、Badgeという名前がどのような意味を持っているのかが明確になります。
また、Badgeという名前がドメインとレイアウトと装飾の三箇所に現れますが、これらは元々独立した視点から発生したものであり、関心の千切り(アンチパターンを理解して package by feature へを参照)にあたることもありません。
まとめ
今回はBadgeというコンポーネントが持つ曖昧さの本質を探り、Badgeはドメイン名、バリエーション名、装飾名、IndicatorをUI名という関係性にすることで、名前問題を解決できることがわかりました。
BadgeからIndicatorへパラダイムシフトすることで、今まで見えなかったスマートな世界で物事を考えることができます。
元々そのもの自体が持っていた本質及び性質を根本から理解し、それに基づいて再構築することで、無理なく自然に問題が解消されたと言えるでしょう。
この根底には、単語の持つ意味の性質を重んじるBCD Designの考え方があります。
その考え方があるからこそ、Badgeのような違和感に気付きやすく、問題解決の糸口も見つけやすかったと言えます。
これを機会に、今まで作ってきたものも、新しい視点で見つめ直してみてはいかがでしょうか?
新たな発見があるかもしれません。
株式会社ドワンゴでは、様々なサービス、コンテンツを一緒につくるメンバーを募集しています。 ドワンゴに興味がある。または応募しようか迷っている方がいれば、気軽に応募してみてください。
Discussion