見出し画像

Figma / React で本当に使える ゼロから作らないデザインシステム

こんにちは、カミナシ プロダクトデザイナーの高橋です。デザインシステム作ったり運用したりするチーム最近多いですよね。

カミナシも各プロダクトで操作体験が分離せず横断的にデザイン課題の解決ができるようにデザインシステムを構築運用しています。自分のキャリア的にデザイナー、エンジニアのどちらの立場でも構築経験があるため、どちらの視点でも読めるように当社の状況をなるべく具体的な例を交えてご紹介してみます。

⚠️ なおこの記事でデザインシステムは「基本となるデザインやコンポーネントの仕様とその実装ライブラリ」を指します。

カミナシの「ゼロから作らないデザインシステム」

カミナシでは現在デザインシステムをゼロベースで用意せず、Material Design (MUI) / AntDesign にカスタムを施したものをデザインシステムとして活用しています。これは私が入社する前から運用されているスタイルで、かつてデザインシステムを実現するために尽力したプロジェクトがあり、そこで定義されたカラーシステムなどが活かされています。

具体的にフローを書くとこのような感じです。

  1. MUI / AntDesign が提供・販売している Figma Community File をクローン

  2. Local Variable に定義されているトークンをカスタマイズ、エイリアス設定

  3. 上記 Figma ファイルをチームライブラリとしてチームで共有

  4. プロダクトの各画面や UI を作成する際は別 Figma ファイルを作成し、上記チームライブラリとしてインポートして利用

  5. トークン情報はテーマとして React プロジェクトに実装(後述)

これにより豊富なコンポーネントがすでにある状態でソフトウェアデザインができ、フロントエンドの React で活用しやすいデザインデータを実現しています。

例えば React 用の API 名称と同じコンポーネント名やプロパティ名が用意されていて基礎的なコンポーネントの命名に悩むことがないとか、UI Stack もおおよそ抜け漏れがないなどのメリットがあります。

MUI の Figma コンポーネントが React コンポーネント名と一致している図

さらに、MUI / AntDesign の Figma Community File のコンポーネントを使ってプレビューした場合、ボタンコンポーネントであればホバーするとバリアブルを活用してホバー表示になり、チェックボックスであればクリックすると表示がトグルするなど非常に細かい定義が行き届いています。

そのため Figma データのみでプロトタイプを作成して、複雑な UI の使用感を検証するというリサーチ段階や、コンセプトの具体的なご説明となるセールス段階でも活用されています。

チェックボックスをクリックするとチェックがついていく。実装同様のリアルなモックが実現できている

それでは、実際に MUI や AntDesign をどうカスタマイズしているかご説明します。

MUI のカスタマイズ

デザイナーは Figma の Local Variable を修正することで MUI のコンポーネントカラーを変更してカスタマイズをしています。

MUI の Local Variables (Design Token) に Alias Token を設定した図

この修正済み Local Variable は運用のために JSON Exporter のような Figma plugin を介して別管理しておくとアップデートの運用対応がスムーズになるかと思います。

エンジニアの開発実装については、 MUI 自体が提供している ThemeProvider を使用しテーマ情報を上書きすることができます。デフォルトのテーマ情報はこのページでまとめられているので、このキーと値を上書きをすることでカスタムテーマを適用することができます。

React プロジェクトに以下をインストール。必要に応じて @mui/icons-material もインストールします。

npm install @mui/material @emotion/react @emotion/styled
npm install @mui/icons-material
{
  "dependencies": {
    "@emotion/react": "^11.13.5",
    "@emotion/styled": "^11.13.5",
    "@mui/icons-material": "^6.1.10",
    "@mui/material": "^6.1.10",
    ...
  }
  ..
}

以下のように createTheme 部分でテーマ情報を作成し、 ThemeProvider に渡すことで実現できます。カラー情報だけでなく、テキストやボタン角丸なども指定することができます。

import React from "react";
import { ThemeProvider, createTheme } from "@mui/material/styles";
import { Button, AppBar, Toolbar, Typography, Container } from "@mui/material";

// カスタムテーマの作成
const theme = createTheme({
  palette: {
    primary: {
      main: "#4caf50", // 緑色
    },
    secondary: {
      main: "#9e9e9e", // 灰色
    },
  },
  typography: {
    fontFamily: "serif",
  },
  components: {
    MuiButton: {
      styleOverrides: {
        root: {
          borderRadius: "8px", // ボタンの角を丸く
        },
      },
    },
  },
});

function App() {
  return (
    <ThemeProvider theme={theme}>
      <div>
        <AppBar position="static">
          <Toolbar>
            <Typography variant="h6" component="div">
              Sample
            </Typography>
          </Toolbar>
        </AppBar>
        <Container sx={{ marginTop: 4, marginBottom: 4 }}>
          <Typography variant="h4" gutterBottom>
            いらっしゃいませ〜
          </Typography>
          <Button variant="contained" color="primary" sx={{ marginRight: 2 }}>
            会員登録
          </Button>
          <Button variant="outlined" color="secondary">
            ログイン
          </Button>
        </Container>
      </div>
    </ThemeProvider>
  );
}

export default App;

このコードでテーマ適用する前と後だとこのような見た目になります。(theme を渡さない/渡す)

MUI にテーマ適用前と適用後の図

AntDesign のカスタマイズ

Ant Design も MUI と同様に LocalVariable を用いてデザインデータのカスタムをすることができます。

実装では Ant Design の ConfigProvider を使用しテーマ情報を上書きすることができます。
React プロジェクトに以下をインストールします。

npm install antd
{
  "dependencies": {
    "antd": "^5.22.3",
    ...
  }
  ..
}

ConfigProvider にテーマ情報を渡すことで上書きすることができます。

import React from 'react';
import { Button, DatePicker, Input, Layout, Menu, ConfigProvider } from 'antd';
import { HomeOutlined } from '@ant-design/icons';

const { Header, Content, Footer } = Layout;

function App() {
  return (
    <ConfigProvider
      theme={{
        token: {
          colorPrimary: '#52c41a', // Primary ボタンの色(緑)
          colorBorder: '#d9d9d9', // Secondary (default) ボタンの境界線(灰色)
          borderRadius: 8,        // ボタンや入力フィールドの角丸
          fontFamily: 'serif',    // 全体のフォントを serif に変更
        },
      }}
    >
      <Layout className="layout">
        <Header>
          <div className="logo"></div>
          <Menu theme="dark" mode="horizontal" defaultSelectedKeys={['1']}>
            <Menu.Item key="1" icon={<HomeOutlined />}>Home</Menu.Item>
            <Menu.Item key="2">About</Menu.Item>
            <Menu.Item key="3">Contact</Menu.Item>
          </Menu>
        </Header>
        <Content style={{ padding: '50px', textAlign: 'center' }}>
          <h1>いらっしゃいませ〜</h1>
          <div>
            <Input placeholder="名前を入力する" style={{ width: '300px', marginBottom: '20px' }} />
          </div>
          <div>
            <DatePicker style={{ marginBottom: '20px' }} />
          </div>
          {/* Primary ボタン */}
          <Button type="primary" style={{ marginRight: '10px' }}>
            会員登録
          </Button>
          {/* Secondary ボタン */}
          <Button type="default">
            ログイン
          </Button>
        </Content>
        <Footer style={{ textAlign: 'center' }}>
          ©2024 カミナシ
        </Footer>
      </Layout>
    </ConfigProvider>
  );
}

export default App;

このコードでテーマ適用する前と後だとこのような見た目になります。(theme を渡さない/渡す)

AntDesign にテーマ適用前と適用後の図

shadcn/ui のカスタマイズ

カミナシでは一部 shadcn/ui を使用しているプロジェクトもあります。

これは Radix UI と Tailwind CSS を活用する UI コンポーネント集ですが、不要なコンポーネントをあえて読み込まない(選んで読み込む)設計になっているためかテーマ情報は MUI や AntDesign のようなスクリプトベースのインターフェースはなく、CSS 変数を用いて上書きをするという形で提供されています。

shadcn/ui は柔軟性・カスタマイズ性が非常に高い設計になっているため、コードやデザインの一貫性を保つ努力が MUI や AntDesign に比べ必要と感じました。そのためそのチームでは MUI や AntDesign のプロジェクトよりも細かく Storybook を活用して実装レビューなどが行われています。

ちなみに MUI と AntDesign や shadcn/ui が並列して運用されている理由は、すでにこれベースで作られたプロダクトを持つチームがカミナシに合流したなどの経緯です。

運用の問題: MUI/AntDesign のアップデートがあった場合はどうする?

MUI/AntDesign は実装用の SDK に合わせて Figma も対応するバージョンが公開されます。

カミナシでは新しいバージョンの Figma で追加されるコンポーネントを今運用しているカスタマイズされたデータに手動で取り込むという形をとっています。ここはいずれ差分を検出して取り込むような方向に持っていきたいですが、まだ整備が行き届いていないため、来年の自分のミッションになりそうだなと思っています💪

実装用とデザイン用のライブラリバージョンは対応しているためなるべく揃えた方が良いでしょう。
揃っているとコンポーネント名やトークン名だけでなくプロパティなども Figma と React それぞれ同一のものが使用できます。

セマンティックバージョニングと呼ばれるバージョンの表記方法

慣例的な話ですが、バージョンの見方は x.y.z のうち z はほぼ揃える必要はなく、x はなるべく合わせた方が良いでしょう。メジャーバージョンと言ってここが変わると大きな変更が加えられることが多いためです。
y も細かく違いがあることがありますが、基本的には引っ掛かりなく使える変更に抑えられていることが多いため揃えなくても良いかもしれません。気になる場合は揃えた方が良いでしょう。

その他デザイン標準化の取り組み

横断でデザイナーで色々相談している絵

これらの仕組みのベースになるカミナシのデザインシステム整備の取り組みは以下のようなトピックを中心に引き続きデザインチーム横断で取り組んでいます。

  1. 抽象的な問題の言語化

    1. デザイン原則、ユーザビリティガイドライン

  2. 具体的な問題のガイドライン化

    1. UI ライティングガイドライン

    2. カラーシステム、アクセシビリティ基準の明確化

    3. デザインシステムへの組み込み

  3. 具体的な解決策の共有

    1. 各プロダクトで作成した UI を週次で共有

    2. Gather / Meet / Figma を使ったモブ・ペアデザインワーク

    3. オフサイトミーティングでの集中ワーク

これに加え、linter を活用しテキスト表記の揺れをなくす取り組みとそのルール決め、Figma データの運用ルール相談など、プロダクトデザイナーはそれぞれの担当プロダクトの開発に取り組みながら、そこで得た課題やノウハウを横断で共有し連携をしています。

デザインシステムの今後・トレードオフ

いずれゼロベースで UI デザインコンポーネント集となるデザインシステムの構築に取り組みたいとも話していますが、巨大かつ各プロダクトと密結合になるデザインリソースであるデザインシステム運用のために運用ルールと専用のチームは必要になりがちです。ここは自分のキャリア的にも遭遇した問題でもありました。

業界的によく聞く話としては、プロダクト開発チームとデザインシステム運用チームで運用サイクルをどう合わせるだとかは課題に上がりがちですし、デザインシステムを使う側がデザインシステムにフィードバックし改善していただくのは難易度が高いとされ、「オリジナルなデザインシステムを持つことは負債である」もよく聞く話です。解決策としてコミュニケーションチャネルを厚めにする例が多そうですが、それが無く短期的に参画したデザインシステム制作者が成果物を残し去ったあと、UI Stack やコンポーネントの不足、環境変化によりデザインシステムやそれを実現するレポジトリが腐って新規施策でデザイントンマナが急に変わるはあるあるではないでしょうか。

現時点でカミナシはもろもろ加味しノンデスクワーカー領域という複雑なドメイン理解と探索に集中し、かつ開発しやすさ運用しやすさとのバランスをとった選択をしています。とはいえ今後について、まだまだカミナシは流動的にやり方を作っていく/変えていくチームなので、ここに書いてあることは来年ナシになっているかもしれません。UX/UI に関する課題や改善しなければならない点は本当にたくさんあります。

直接お話ししましょう 👋

というわけで、このあたりをさらに細かくご紹介することもできますし、もしこの辺りの課題がある方でしたら課題解決についてご相談に乗ることもできるかと思います。情報交換もできたらと思います。

デザイナーもエンジニアもプロダクトマネージャーも、カミナシのプロダクト作りについてお気軽にカジュアル面談くださいませ〜。