株式会社メドレーDeveloper Portal

2018-02-27

Composable な UI 設計を目指したフロントエンド開発

こんにちは、開発本部の舘野です。医療介護の求人サイト「ジョブメドレー」の開発を担当しています。

昨年、ジョブメドレーでは事業所が利用する採用管理画面の UI リニューアルを行いました。ユーザが使いやすい UI づくりを目指すために、長期間にわたり誰が開発しても一貫性ある UI を実現できるようなシステムが必要です。そこで今回は「Composable」な UI システムの実現をテーマに、どのように開発を行ったのかについて、共有させていただきます。

背景:画面や機能追加のたびに UI の一貫性がなくなっていた

ジョブメドレーの採用管理画面とは、医療機関や介護施設の採用担当者が求人情報の管理や応募者の選考状況の管理などを行う画面です。

この採用管理画面ですが、リニューアル以前はAngularをフレームワークとして採用した SPA で、UI に関してはAngularUI の Bootstrapを利用して、それぞれのエンジニアが実装を行っていました。

それなりの UI をスピーディーに実現できる点においては、Bootsrap のような UI フレームワークを利用することで受けられる恩恵は大きかったのですが、一方で、包括的に UI 設計を行っているわけではなく、各人が局所的に UI を作っていくので、画面や機能を追加していく中で一貫性がない UI が増えていく状態になっていました。

実際にユーザインタビューなどを行ってみると、「ログインした後どうすれば良いのか分からない」、「〇〇という機能があることを今まで知らなかった」、「xx がどこにあるのか分からない」などの意見が多々あり、全面的な UI の見直しが必要になっていました。

医療や介護の現場での人材不足を解消するために採用担当者に提供するツールとして、今後さらに機能拡充していくことが求められていましたが、機能拡充していくことに耐えうる状態にはないというのがプロダクトチームのメンバーの共通認識でした。

そこで、全体的に情報設計から見直してデザインを刷新し、今後プロダクトを成長させていく上でスケール可能な UI を提供できるようにするため、UI リニューアルを決定しました。

フロントエンドで必要だったこと

Bootstrap を用いてエンジニアのみで UI を作っていたのとは異なり、リニューアルでは社内のデザイナーが現状の UI 上の課題を整理したデザインを作成しました。

これに伴って、自前で全ての UI パーツを作成することになりましたが、Bootstrap に頼りきっていたときとは違い、堅牢性と柔軟性を伴った UI システムを自分たちで構築する必要がありました。

リニューアル前の採用管理画面の UI は一貫性に欠けており、ユーザは非常に多くの操作を学ぶ必要がありましたが、この責任はデザイナーだけでなく UI 開発をするエンジニアにも大いにあります。 良いデザインができても、最終的にプロダクトの UI はコードによって作り上げられるものなので、エンジニア次第で一貫性に欠ける UI になってしまうことは十分にあり得ると思います。

往々にして起こり得るのは、目にする機会が比較的少ない画面であったり、改修対象ではない部分などが気づいたら崩れていたり、意図しない UI になってしまっていたりということですが、こういった状況に陥る大きな要因としてはフロントエンドの部分で一貫性に対する配慮ができてないことが 1 つだと思います。

そこで、すでにある採用管理画面を使いやすくするのはもちろん、今後スケールしていく中で一貫性のある UI を担保し続けていくためには、リニューアルでフロントエンドも堅牢で柔軟な UI システムへと変える必要がありました

UI リニューアルで開発上大切にしたこと

UI の一貫性を保つとなると、今のフロントエンドではもはや当然のことかもしれませんが、コンポーネント指向で構成することになると思います。

技術選定としては、上述の通りリニューアル以前は Angular(v1.4.11)を利用していましたが、リニューアルのタイミングで React へ移行しました。

React を選択した理由としては、学習コストの点やコミュニティが活発でエコシステムが充実している点、単一方向のデータフロー、シンプルな API などを総合的に判断してのものですが、目下の課題である UI コンポーネントのメンテナビリティに関しても適切な選択肢であると考えました。

CSS の方はというと、リニューアル前は Bootstrap でまかなえない部分は Sass でそれぞれのエンジニアが書きたいように書くという状態でしたが、リニューアルで Sass に加えて一部 PostCSS という構成に変更して、設計は ITCSS、Lint を stylelint で行う、という形にしました。

ITCSS を選択した背景としては、その詳細度順のレイヤー階層によってカスケードを管理しやすい点やレイヤーの増減で容易にスケールできる点などから選択しました。

CSS in JS も考慮はしましたが、リニューアルの時点ではこれという決定的な選択肢が無かったこともあり(まだ styled-components も正式リリースされてなかった)、classnamesを利用しました。

フレームワークやライブラリの選定も重要ですが、UI システムを刷新する上で開発上最も重視したのは「Composability(コンポーネントの組み合わせが容易であること)」でした。

Composable であるということは、つまり様々な状況において組み込み可能な状態であり、再利用性が高いということになります。 それぞれのコンポーネントを組み合わせることが容易に出来るとともに、複数のコンポーネントを組み合わせた状態から 1 つ 1 つ分解することも容易な状態が望ましく、結果的にそれで UI が構築しやすく改修しやすい状態に自然となるはずです。

モーダルを例にあげると、モーダルの中で表示するコンテンツ要素やモーダルの背面に敷くオーバーレイコンポーネントは、モーダルコンポーネント自体には含まず別のコンポーネントとして切り出した方が再利用しやすく、組み合わせやすい、ということです。

<ModalFrame>
  <Modal>
    <ModalHead>...</ModalHead>
    <ModalBody>...</ModalBody>
  </Modal>
  <Overlay />
</ModalFrame>

上記の例でいうと、モーダルを画面の中央に配置することは<ModalFrame />が行い、<Modal />自体はモーダルに内包されるコンテンツのコンテナとしての役割だけを持ちます。<Overlay />も独立したコンポーネントの 1 つで、モーダル以外とも組み合わせて利用しています。

コンテナとなるコンポーネントとその子となるコンポーネントは、別コンポーネントに分離されていることで、お互いに依存しないようになります。

また、Sass ファイルもこのコンポーネント構成に合わせて分けています。 ITCSS において、<ModalFrame />のようなレイアウトのみの役割を持つ場合のスタイルは Objects レイヤー(装飾を持たない UI パターンのレイヤー)となり、装飾を持つ<Modal /><Overlay />は Components レイヤーとして扱います。

@import “objects.modal-frame”;
@import “components.modal”;
@import “components.overlay”;

CSS はその特有のカスケードや詳細度によって決定されるスタイルがあり、依存関係を持たない状態を作ることが困難ですが、ITCSS の考えに則ってそれらの CSS の特徴に逆らわないように詳細度の低いものから順番に@importするようにしています。

Sass ファイルの中身ですが、_objecst.modal-frame.scss<ModalFrame />のスタイルのみを記述するようにします。

.o-modal-frame {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: map-get($zIndexMap, modalFrame);
}

_components.modal.scssも同様に<Modal />のスタイルのみを記述します。

.c-modal {
  position: relative;
  margin: 0 auto;
  width: 900px;
  min-width: 640px;
  background-color: $JM-White;
  box-shadow: 0 1px 6px 0 rgba($JM-Black, 0.2);
  z-index: map-get($zIndexMap, modal);
}

このように Sass と React コンポーネント毎に 1 対 1 の関係になるようにしています。 プレフィックスとして付与しているc-o-は ITCSS のレイヤーのことを指します。 o-は Objects レイヤーのプレフィックスで、c-は Components レイヤーのプレフィックスです。 基本的に React の UI コンポーネント内では、コンポーネントの種別に応じてc-o-のプレフィックスを持つクラスと、状態によって付けたり外したりする State レイヤーのs-プレフィックスのクラスのみを使用します。

話を React に戻すと、下記のようなヘッダー要素を画面上部に固定表示するだけの役割を持つ<AppBar />コンポーネントは、props.childrenで子要素を受け取れるようになっているだけで、その内容には関知しないようになっています。

const AppBar = (props) => {
  return <div className="o-app-bar">{props.children}</div>;
};

内包する子コンポーネントが何であれ、<AppBar />は自分自身の責任だけを果たせば良いので、開発上もシンプルに考えられます。

classNameに渡しているo-app-barは ITCSS の Objects レイヤーのクラスです。

.o-app-bar {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: map-get($zIndexMap, appBar);
}

ヘッダー要素を画面上部に固定表示する、レイアウトのみの役割を持つコンポーネントなので、Objects レイヤーとなり、o-app-barにはレイアウト目的のスタイルのみを持たせます。

ジョブメドレーの採用管理画面では、医療機関や介護施設から求職者に向けた情報を入力していただくために多くのフォーム要素があり、非常に煩雑になりがちですが、それぞれの役割を果たすコンポーネントを組み合わせることで、UI 開発上の堅牢性、柔軟性を高められるように努めました。

実際のリニューアル開発時には、全ての UI コンポーネントを実装する前に、開発側ではデザイナーが用意した Sketch から、全ての UI パーツを洗い出す作業を行い、その中で分解不可能なレベルまでコンポーネントを分解していき、実装すべきコンポーネントを一覧化しました。

その後、作成したコンポーネント一覧から全 UI コンポーネントを Storybook に実装していきました。

Storybook は、UI コンポーネント開発のサンドボックス環境として、React や Vue を利用した開発では割と一般的に利用されるようになっていると思います。リニューアル時も各コンポーネントの開発環境として利用して、コンポーネントのパターンや組み合わせの確認などを Storybook 上で行いました。

画面を作っていく段階では、用意した UI コンポーネントを組み合わせて利用すれば画面全体の大半の UI が出来上がるようになっていました。

細かい部分では、事前に用意するコンポーネントに不足があったり、実装した後で仕様の変更によりコンポーネント自体を削除することや、分解不可能な状態まで落とし込めてないコンポーネントが見つかったりと、様々な反省点はありました。ですがリニューアル全体を通して振り返ると、Composable な UI コンポーネントで堅牢で柔軟性のある構成にするということに一定の成果は出せたかなと思います。

まとめ

UI リニューアル以降、採用管理画面ではリニューアル時の UI システムを土台にして、継続的に機能を追加・改修しています。

プロダクトで設けている KPI も順調に遷移していて、顧客からの問い合わせもリニューアル以前のような、UI 上の問題で利用が困難であるというものは減少し、ポジティブな結果が得られています。

開発をする上でも Composable になるようにコンポーネント群を作成したことで、リニューアル以降は UI の改修がシンプルに行えるようになり、開発メンバーのスキルセットに左右される部分が少なくなり、開発効率が上がりました

このような点からリニューアル自体は良かったと思うと同時に、一方でさらに良い UI を提供するために取り組むべきことは、少なくないと感じます。

例えば採用管理画面が十分にアクセシブルだとは言えないし、パフォーマンス面でもより一層の努力が求められます。もちろん UI コンポーネントの堅牢性もまだ十分とは言えません。

より良いプロダクトを提供するためにそういった課題に対しても継続して取り組んでいきたいと思います。

お知らせ

メドレーでは、エンジニア・デザイナーを募集しています。 メドレーでの開発にご興味ある方は、こちらをご覧ください。

https://www.medley.jp/recruit/creative.html

株式会社メドレーDeveloper Portal

© Medley Developer Portal