この記事は、Mercari Bold Challenge Monthの1日目の記事です。
こんにちは、MercariのArchitectチームでDesign Systemに取り組んでいる@usagi-fです。
Design Systemはただのスタイルガイドラインではなく、会社として保持するデザインフィロソフィーから実装コードに落とし込まれたUIコンポーネントまで、広い範囲をさす言葉として認知されてきています。
現在私たちは本格的に構築へ着手しており、少しずつ進捗が見えてきました。この記事では主にDesign Systemにおける「UIコンポーネントの定義と実装」の部分に焦点をあて、私が担当しているWeb Frontendの事例を紹介していきます。
MercariにおけるDesign System
Mercariでは将来的な組織規模の拡大に向けて様々な取り組みを行っていますが、Design Systemもその一つです。
これは会社全体のOKRでも目標として大きく掲げられており、担当のデザイナーとエンジニアを配置しつつ進められています。
メルカリというアプリは現在iOS/Android/Webの三つの環境でサービス展開されているため、クロスプラットフォームを前提として考えていく必要があります。
既に非常に大きいプロダクトへ成長しているため、開発時にチームが別れていることが多くなっています。そのため関わるエンジニアも多く車輪の再発明が起こりやすい構造になってきていました。私たちはDesign Systemを通して、一貫したサービス体験をお客さまに提供できるようになることを目標としています。
コンポーネント化に対する技術的方針
UIをコンポーネントとして定義していくにあたり、Atomic Designの階層化の思想を根幹に据えています。
まず始めにデザイナーがベースとなるデザインのルールを設計していきます。
その後Sketchを駆使して、基本的なコンポーネントまで落とし込んでいきます。これらはZeplinを通じてエンジニアとシェアされていて、どう実装に落とし込むのが最適か両者の視点からブラッシュアップされていきます。
全デバイスで共通に考えられる部分を基本的な単位としていますが、各デバイス毎にユーザーが期待する体験が違いますし、実装に利用する言語や最適な手段も異なります。
そのため、最終的な構造などはそれぞれのエンジニアと担当のデザイナーが最適化しながら実装していきます。例えばコンポーネントの命名についても、各領域のエンジニアに馴染み深く想像がつきやすいものに変更していることもあります。
私はUIのライブラリを作るにあたり、以下の項目に重点を置きました。
- 十分なWebアクセシビリティの担保
- 軽量である
- 内部的に副作用を持たない
- 開発者体験の良さ
- (公開されている一般的なOSSのライブラリと同じ感覚で利用できる)
Design Systemのコンポーネントは常に他人が使うことを考慮しながら実装しなければなりません。そのためコンポーネントのインターフェイス(ここでは引数とレンダリング結果)がとても重要になります。
独立したプロダクトとしてつくる
Web用のUIライブラリとして、私が採用した主な技術スタックは次の通りです。
これらの技術を選択しつつ開発環境をつくるに辺り、念頭に置いていたのはDesign System側がプロダクト側のアーキテクチャを制限しないようにすることです。
唯一の依存性として、素早く開発・導入を進めるために現場で多く活用されていたReactをベースとしました。
それ以上のライブラリ依存をしないようにしつつも、提供するのは純粋なReactコンポーネントになるように設計しています。
もちろんstyled-componentsのような別のライブラリに渡して拡張することも可能になります。
このように、提供する要素を「どう使うか」は組み込み側へ一任するようにしています。
また、それと同時に組み込み先の文脈に引っ張られないようにすることも大事だと考えています。
Design System自体が一つのプロダクトとして独立できるように、特定の組み込み先にとってしか利点がないような仕様や実装は絶対に行いません。
特に互いのインターフェイスとなるReactのprops名称には気を使いました。常に複数の視点から見て妥当性のある名前を付けるようにしています。
例として、以下のような数値を表示するだけのBadgeコンポーネントを説明します。
組み込み先Aでは通知数を表示するためにコンポーネントを使うが、組み込み先Bでは検索結果のヒット数に使うとします。ここでBadgeに渡す数値として、組み込み先Aのことだけを考えてnotification
のようなprops名にするのはNGということです。この例では汎用性を鑑みて、count
というpropsにしています。
// × <Badge notification={value} /> // ○ <Badge count={value} />
より品質の高いプロダクトにするために
Design Systemから提供されるライブラリは様々なプロダクトへインストールされることになるため、容量には特に気をつけたいと考えています。そこで今回はmonorepoを採用しました。
これは複数のパッケージを一つのGitリポジトリで管理する手法で、無駄なコードを読み込ませないための取り組みの一つです。容量が大きくなってしまう可能性のあるアイコン系や、用途が限られる機能などは別パッケージとして社内向けのnpmレジストリへpublishしています。
またDesign Systemには実体のあるUIだけではなく、カラーコードや余白定義のような抽象的な要素も存在しています。これらも単純なオブジェクト定義が成された別パッケージとして提供しているため、Reactコンポーネントを利用せずにこれだけをインストールすることもできます。
その他にも、ReactのContext APIを利用したテーマ機能で、将来的な拡張やブランディング刷新に備えたり、各コンポーネントの型定義を同時にexportすることで、TypeScriptでの実装環境を強力にサポートできるようになっています。
約30個ほどのUIコンポーネントが構築されており、すべての表示条件毎にユニットテストが用意してあります。(現在のテストケースは約900個*1)
そしてCIによってGitリポジトリへのpush毎に自動的にStorybookをビルドし、社内向けに公開しています。
ここではStorybookの便利なpluginを使って型情報なども同時に表示できるため、「どんなコンポーネントが使えるのか」「どんな挙動をするのか」が、誰でもいつでもブラウザから直接触ることができるようになっています。
今後の課題
Design Systemはコンポーネントをつくること自体よりも、それらを長期的に運用していくことが大事なことだと思っています。
現在運用ルールを含めて継続的なアップデート方法を模索中です。該当のリポジトリは社内でオープンになっており、issueやPull Requestは誰でもあげることができるようになっていますが、特定の人だけではなく社内にいる様々なエンジニアの知見が集結するようなものに育てあげていけるかがこれからの課題となるでしょう。
既にMercariでは一部のプロダクトへDesign Systemの反映も始まっていますが、これがさらに広がっていけばDesign Systemへのコントリビュートがそのまま会社全体への貢献にもなると思っています。
ここまでお読み頂きありがとうございました。
明日の記事は、@tadashi による「マルチブラウザ時代のUIテスト戦略と、Microsoft Edge ブラウザでの自動テストをクラウド(Azure DevTest Labs)で実行する」です。お楽しみに!
*1:コンポーネント以外のコードに対するユニットテストも含みます