T-CREATOR

<div />

Mermaid の色・タイポグラフィ設計:ブランドトークンを themeVariables に落とす

Mermaid の色・タイポグラフィ設計:ブランドトークンを themeVariables に落とす

近年、デザインシステムを導入する企業が増えており、ブランドの一貫性を保つことが重要視されています。しかし、ドキュメントやシステム図に使用する Mermaid の図表が、ブランドカラーやフォント設定と合わないという課題に直面していませんか。

今回は、Mermaid の themeVariables を活用して、デザイントークンやブランドトークンを図表に反映させる方法をご紹介いたします。この手法により、技術ドキュメント全体でブランドの統一感を実現できるでしょう。

背景

デザインシステムとブランドトークンの普及

現代の Web 開発では、デザインシステムが標準的な開発手法として定着してきました。Material Design、Carbon Design System、Ant Design などの有名なデザインシステムでは、色やタイポグラフィを「トークン」として管理し、プロダクト全体で一貫したデザインを実現しています。

ブランドトークンとは、企業やプロダクトのブランドアイデンティティを構成する基本要素を、再利用可能な形で定義したものです。具体的には、以下のような要素が含まれます。

#トークン種別
1カラートークンprimary-500secondary-300gray-100
2タイポグラフィトークンfont-family-basefont-size-lgfont-weight-bold
3スペーシングトークンspacing-smspacing-mdspacing-lg
4ボーダートークンborder-radius-smborder-width-thin

これらのトークンを CSS 変数や JSON として定義することで、プロダクト全体での一貫性が保たれ、ブランド変更時の作業コストも大幅に削減できます。

Mermaid とドキュメント作成の課題

Mermaid は、Markdown で図表を記述できる便利なツールです。フローチャート、シーケンス図、ER 図など、さまざまな図を簡潔な記法で表現できるため、技術ドキュメントや README、Wiki で広く使われています。

しかし、デフォルトのテーマでは、企業やプロダクトのブランドカラーと合わないケースがほとんどです。特に、顧客向けのドキュメントや公開ドキュメントでは、ブランドの統一感が重要になりますね。

以下の図は、デザインシステムにおけるトークンの階層構造を示したものです。

mermaidflowchart TB
  brand["ブランドトークン<br/>(プリミティブ)"]
  semantic["セマンティックトークン<br/>(意味的役割)"]
  component["コンポーネント<br/>トークン"]
  ui["UI 実装"]

  brand -->|"primary-500<br/>secondary-300"| semantic
  semantic -->|"color-button-bg<br/>color-link"| component
  component -->|"適用"| ui
markdown```mermaid
flowchart TB
  brand["ブランドトークン<br/>(プリミティブ)"]
  semantic["セマンティックトークン<br/>(意味的役割)"]
  component["コンポーネント<br/>トークン"]
  ui["UI 実装"]

  brand -->|"primary-500<br/>secondary-300"| semantic
  semantic -->|"color-button-bg<br/>color-link"| component
  component -->|"適用"| ui
```

トークンは階層的に管理され、ブランドトークンが最上位に位置します。このトークンを Mermaid にも適用することで、ドキュメント全体の一貫性を保つことができるでしょう。

課題

Mermaid のデフォルトテーマとブランドの不一致

Mermaid には、defaultforestdarkneutral などのビルトインテーマが用意されています。これらは汎用的なデザインで作られており、そのまま使うとブランドカラーとは異なる色合いになってしまいます。

例えば、コーポレートカラーが青系の企業で、Mermaid のデフォルトテーマ(黄色やオレンジが含まれる)を使うと、統一感が失われてしまうのです。

カスタマイズの難しさ

Mermaid のテーマをカスタマイズする方法として、CSS を上書きする手法がありますが、これには以下のような問題がありました。

#問題点詳細
1保守性の低下CSS のクラス名が変更されると動作しなくなる
2複雑な設定各要素ごとに CSS セレクタを調べる必要がある
3図の種類ごとの対応フローチャート、シーケンス図など、図の種類ごとに CSS が異なる
4トークンとの分離デザイントークンと CSS が別管理になり、変更時の同期が困難

これらの課題により、Mermaid の図表をブランドに合わせることは、多くの開発チームにとって後回しにされがちでした。

一貫性の欠如による影響

ブランドカラーが統一されていないドキュメントは、以下のような影響を及ぼします。

プロフェッショナルさの欠如により、顧客や利用者からの信頼度が低下する可能性があります。また、社内のデザインガイドラインとの乖離が生じ、デザイナーとエンジニアの間で認識のずれが発生することもあるでしょう。

さらに、複数のドキュメントで異なる色が使われることで、情報の重要度や意味が不明確になり、可読性が低下してしまいます。

解決策

themeVariables によるカスタマイズ

Mermaid v8.6.0 以降では、themeVariables という設定オプションが提供されており、これを使うことでブランドトークンを Mermaid の図表に直接反映できます。

themeVariables は、Mermaid の内部で使用される色やフォントの変数を、設定オブジェクトから上書きする仕組みです。CSS を直接触ることなく、図表の見た目を変更できるため、保守性が高く、トークンとの統合も容易になりました。

以下の図は、themeVariables を使った設定の流れを示しています。

mermaidflowchart LR
  tokens["デザイントークン<br/>(JSON/CSS変数)"]
  config["Mermaid<br/>設定オブジェクト"]
  vars["themeVariables"]
  render["図の描画"]

  tokens -->|"変換"| config
  config -->|"含む"| vars
  vars -->|"適用"| render
markdown```mermaid
flowchart LR
  tokens["デザイントークン<br/>(JSON/CSS変数)"]
  config["Mermaid<br/>設定オブジェクト"]
  vars["themeVariables"]
  render["図の描画"]

  tokens -->|"変換"| config
  config -->|"含む"| vars
  vars -->|"適用"| render
```

この仕組みにより、デザイントークンから Mermaid の設定への変換が一元化され、保守性が大幅に向上するでしょう。

themeVariables で設定可能な主要項目

themeVariables では、以下のような項目を設定できます。

カラー関連

色に関する設定は、図表の印象を大きく左右します。主要なカラー変数は以下の通りです。

#変数名説明デフォルト例
1primaryColorプライマリカラー(ノードの背景など)#ECECFF
2primaryTextColorプライマリテキストカラー#000
3primaryBorderColorプライマリボーダーカラー#9370DB
4secondaryColorセカンダリカラー#FFFFDE
5tertiaryColorターシャリカラー#FFEBCC
6lineColor線の色#333
7textColorテキストの色#333
8background背景色#fff or transparent

これらの変数を設定することで、図表全体の配色を制御できます。

タイポグラフィ関連

フォントに関する設定も重要です。ブランドで使用している書体を Mermaid にも適用することで、一貫性が生まれますね。

#変数名説明デフォルト例
1fontFamilyフォントファミリー"trebuchet ms", verdana, arial
2fontSizeフォントサイズ16px

ベーステーマの選択

themeVariables を適用する際は、ベースとなるテーマを選択します。base テーマを選ぶと、よりカスタマイズしやすくなるでしょう。

設定は以下のように、themethemeVariables を組み合わせて行います。

markdown```javascript
mermaid.initialize({
  theme: 'base',
  themeVariables: {
    // ここにカスタム変数を記述
  },
});
```

base テーマは最小限のデフォルト値のみを持つため、ブランドカラーを適用する際に予期せぬ色が混ざることを防げます。

具体例

デザイントークンの定義

まず、プロジェクトのデザイントークンを定義しましょう。ここでは、JavaScript オブジェクトとして定義します。

markdown```javascript
// design-tokens.js
const brandTokens = {
  colors: {
    primary: {
      50: '#e3f2fd',
      100: '#bbdefb',
      200: '#90caf9',
      300: '#64b5f6',
      400: '#42a5f5',
      500: '#2196f3', // メインブランドカラー
      600: '#1e88e5',
      700: '#1976d2',
      800: '#1565c0',
      900: '#0d47a1',
    },
    secondary: {
      500: '#ff9800',
      600: '#fb8c00',
    },
    neutral: {
      50: '#fafafa',
      100: '#f5f5f5',
      200: '#eeeeee',
      700: '#616161',
      900: '#212121',
    },
  },
  typography: {
    fontFamily: {
      base: '"Inter", "Noto Sans JP", sans-serif',
      mono: '"Fira Code", "Courier New", monospace',
    },
    fontSize: {
      base: '16px',
      sm: '14px',
      lg: '18px',
    },
  },
};

export default brandTokens;
```
javascript// design-tokens.js
const brandTokens = {
  colors: {
    primary: {
      500: '#2196f3', // メインブランドカラー
      600: '#1e88e5',
      700: '#1976d2',
    },
    secondary: {
      500: '#ff9800',
    },
    neutral: {
      100: '#f5f5f5',
      700: '#616161',
      900: '#212121',
    },
  },
  typography: {
    fontFamily: {
      base: '"Inter", "Noto Sans JP", sans-serif',
    },
  },
};

このトークン定義により、ブランドの色と書体が一元管理されます。変更が必要な場合も、この定義を更新するだけで、すべての図表に反映されるでしょう。

themeVariables へのマッピング

次に、デザイントークンを Mermaid の themeVariables にマッピングします。

markdown```javascript
// mermaid-config.js
import brandTokens from './design-tokens.js';

const mermaidThemeVariables = {
  primaryColor: brandTokens.colors.primary[500],
  primaryTextColor: brandTokens.colors.neutral[900],
  primaryBorderColor: brandTokens.colors.primary[700],

  secondaryColor: brandTokens.colors.secondary[500],
  secondaryTextColor: brandTokens.colors.neutral[900],
  secondaryBorderColor: brandTokens.colors.secondary[600],

  tertiaryColor: brandTokens.colors.neutral[100],
  tertiaryTextColor: brandTokens.colors.neutral[700],
  tertiaryBorderColor: brandTokens.colors.neutral[700],

  lineColor: brandTokens.colors.neutral[700],
  textColor: brandTokens.colors.neutral[900],
  background: 'transparent',

  fontFamily: brandTokens.typography.fontFamily.base,
  fontSize: brandTokens.typography.fontSize.base,
};

export default mermaidThemeVariables;
```
javascript// mermaid-config.js
import brandTokens from './design-tokens.js';

const mermaidThemeVariables = {
  primaryColor: brandTokens.colors.primary[500],
  primaryTextColor: brandTokens.colors.neutral[900],
  primaryBorderColor: brandTokens.colors.primary[700],

  lineColor: brandTokens.colors.neutral[700],
  textColor: brandTokens.colors.neutral[900],

  fontFamily: brandTokens.typography.fontFamily.base,
};

このマッピング層を設けることで、デザイントークンと Mermaid の設定が分離され、それぞれの変更に柔軟に対応できます。

Mermaid の初期化設定

それでは、Mermaid を初期化する際に、作成した themeVariables を適用しましょう。

markdown```javascript
// main.js
import mermaid from 'mermaid';
import mermaidThemeVariables from './mermaid-config.js';

mermaid.initialize({
  startOnLoad: true,
  theme: 'base',
  themeVariables: mermaidThemeVariables,
  logLevel: 'error',
  securityLevel: 'loose',
  flowchart: {
    useMaxWidth: true,
    htmlLabels: true,
    curve: 'basis',
  },
});
```
javascript// main.js
import mermaid from 'mermaid';
import mermaidThemeVariables from './mermaid-config.js';

mermaid.initialize({
  startOnLoad: true,
  theme: 'base',
  themeVariables: mermaidThemeVariables,
});

startOnLoad: true を指定することで、ページ読み込み時に自動的に Mermaid 図表が描画されます。また、theme: 'base' により、カスタム変数がそのまま反映されるようになるでしょう。

フローチャートでの適用例

実際に、ブランドカラーが適用されたフローチャートを作成してみましょう。

mermaidflowchart TD
  start["開始"]
  input["ユーザー入力"]
  validate{"バリデーション<br/>チェック"}
  process["データ処理"]
  save["データベース<br/>保存"]
  success["成功メッセージ<br/>表示"]
  error["エラーメッセージ<br/>表示"]
  done["終了"]

  start --> input
  input --> validate
  validate -->|"OK"| process
  validate -->|"NG"| error
  process --> save
  save --> success
  success --> done
  error --> done
markdown```mermaid
flowchart TD
  start["開始"]
  input["ユーザー入力"]
  validate{"バリデーション<br/>チェック"}
  process["データ処理"]
  save["データベース<br/>保存"]
  success["成功メッセージ<br/>表示"]
  error["エラーメッセージ<br/>表示"]
  done["終了"]

  start --> input
  input --> validate
  validate -->|"OK"| process
  validate -->|"NG"| error
  process --> save
  save --> success
  success --> done
  error --> done
```

上記の図では、設定した primaryColor がノードの背景に、primaryBorderColor がノードの枠線に適用されます。ブランドカラーが統一されることで、プロフェッショナルな印象を与えられるでしょう。

シーケンス図での適用例

シーケンス図でも、同様にブランドカラーが適用されます。

mermaidsequenceDiagram
  participant user as ユーザー
  participant app as アプリ
  participant api as API
  participant db as データベース

  user->>app: ログイン要求
  app->>api: 認証リクエスト
  api->>db: ユーザー情報取得
  db-->>api: ユーザーデータ
  api-->>app: 認証トークン
  app-->>user: ログイン成功
markdown```mermaid
sequenceDiagram
  participant user as ユーザー
  participant app as アプリ
  participant api as API
  participant db as データベース

  user->>app: ログイン要求
  app->>api: 認証リクエスト
  api->>db: ユーザー情報取得
  db-->>api: ユーザーデータ
  api-->>app: 認証トークン
  app-->>user: ログイン成功
```

シーケンス図では、アクターの背景色や矢印の色にブランドカラーが適用され、統一感のあるドキュメントが作成できます。

クラス図での適用例

クラス図でも、themeVariables の設定が反映されます。

mermaidclassDiagram
  class User {
    +String id
    +String name
    +String email
    +login()
    +logout()
  }

  class Post {
    +String id
    +String title
    +String content
    +Date createdAt
    +publish()
    +delete()
  }

  class Comment {
    +String id
    +String text
    +Date createdAt
    +create()
    +update()
  }

  User "1" --> "*" Post : creates
  Post "1" --> "*" Comment : has
  User "1" --> "*" Comment : writes
markdown```mermaid
classDiagram
  class User {
    +String id
    +String name
    +String email
    +login()
    +logout()
  }

  class Post {
    +String id
    +String title
    +String content
    +Date createdAt
    +publish()
    +delete()
  }

  class Comment {
    +String id
    +String text
    +Date createdAt
    +create()
    +update()
  }

  User "1" --> "*" Post : creates
  Post "1" --> "*" Comment : has
  User "1" --> "*" Comment : writes
```

クラス図のボックスや関連線にもブランドカラーが適用され、技術ドキュメント全体で統一されたビジュアルが実現できるでしょう。

React / Next.js での実装例

React や Next.js プロジェクトで Mermaid を使用する場合の実装例をご紹介します。

まず、Mermaid をインストールしましょう。

markdown```bash
yarn add mermaid
```

次に、カスタムフックを作成します。

markdown```typescript
// hooks/useMermaid.ts
import { useEffect } from 'react';
import mermaid from 'mermaid';
import brandTokens from '@/config/design-tokens';

export const useMermaid = () => {
  useEffect(() => {
    mermaid.initialize({
      startOnLoad: true,
      theme: 'base',
      themeVariables: {
        primaryColor: brandTokens.colors.primary[500],
        primaryTextColor: brandTokens.colors.neutral[900],
        primaryBorderColor: brandTokens.colors.primary[700],
        secondaryColor: brandTokens.colors.secondary[500],
        lineColor: brandTokens.colors.neutral[700],
        textColor: brandTokens.colors.neutral[900],
        fontFamily: brandTokens.typography.fontFamily.base,
        fontSize: brandTokens.typography.fontSize.base,
      },
    });

    mermaid.contentLoaded();
  }, []);
};
```
typescript// hooks/useMermaid.ts
import { useEffect } from 'react';
import mermaid from 'mermaid';
import brandTokens from '@/config/design-tokens';

export const useMermaid = () => {
  useEffect(() => {
    mermaid.initialize({
      startOnLoad: true,
      theme: 'base',
      themeVariables: {
        primaryColor: brandTokens.colors.primary[500],
        lineColor: brandTokens.colors.neutral[700],
        fontFamily: brandTokens.typography.fontFamily.base,
      },
    });
  }, []);
};

このフックを使用することで、コンポーネント内で簡単に Mermaid を初期化できます。

続いて、Mermaid 図を表示するコンポーネントを作成しましょう。

markdown```typescript
// components/MermaidDiagram.tsx
import React, { useEffect, useRef } from 'react';
import { useMermaid } from '@/hooks/useMermaid';

interface MermaidDiagramProps {
  chart: string;
  id?: string;
}

export const MermaidDiagram: React.FC<
  MermaidDiagramProps
> = ({
  chart,
  id = `mermaid-${Math.random().toString(36).substr(2, 9)}`,
}) => {
  const ref = useRef<HTMLDivElement>(null);
  useMermaid();

  useEffect(() => {
    if (ref.current) {
      ref.current.removeAttribute('data-processed');
      ref.current.innerHTML = chart;
      mermaid.contentLoaded();
    }
  }, [chart]);

  return (
    <div ref={ref} className='mermaid' id={id}>
      {chart}
    </div>
  );
};
```
typescript// components/MermaidDiagram.tsx
import React, { useEffect, useRef } from 'react';
import { useMermaid } from '@/hooks/useMermaid';

interface MermaidDiagramProps {
  chart: string;
}

export const MermaidDiagram: React.FC<
  MermaidDiagramProps
> = ({ chart }) => {
  const ref = useRef<HTMLDivElement>(null);
  useMermaid();

  useEffect(() => {
    if (ref.current) {
      ref.current.innerHTML = chart;
    }
  }, [chart]);

  return (
    <div ref={ref} className='mermaid'>
      {chart}
    </div>
  );
};

このコンポーネントにより、props として渡された Mermaid 記法の文字列が、ブランドカラーで描画されます。

最後に、ページコンポーネントで使用します。

markdown```typescript
// pages/documentation.tsx
import { MermaidDiagram } from '@/components/MermaidDiagram';

const DocumentationPage = () => {
  const flowchartCode = `
flowchart LR
  A["スタート"] --> B["処理"]
  B --> C{"判定"}
  C -->|"Yes"| D["完了"]
  C -->|"No"| B
  `;

  return (
    <div>
      <h1>システムフロー</h1>
      <MermaidDiagram chart={flowchartCode} />
    </div>
  );
};

export default DocumentationPage;
```
typescript// pages/documentation.tsx
import { MermaidDiagram } from '@/components/MermaidDiagram';

const DocumentationPage = () => {
  const flowchartCode = `
flowchart LR
  A["スタート"] --> B["処理"]
  B --> C{"判定"}
  `;

  return (
    <div>
      <MermaidDiagram chart={flowchartCode} />
    </div>
  );
};

これで、Next.js アプリケーション内でブランドカラーが適用された Mermaid 図表を表示できるようになりました。

CSS 変数との統合

CSS カスタムプロパティ(CSS 変数)でデザイントークンを管理している場合は、JavaScript から取得して themeVariables に適用できます。

markdown```css
/* styles/design-tokens.css */
:root {
  --color-primary-500: #2196f3;
  --color-primary-700: #1976d2;
  --color-secondary-500: #ff9800;
  --color-neutral-100: #f5f5f5;
  --color-neutral-700: #616161;
  --color-neutral-900: #212121;

  --font-family-base: 'Inter', 'Noto Sans JP', sans-serif;
  --font-size-base: 16px;
}
```
javascript// utils/getCSSVariable.js
export const getCSSVariable = (variableName) => {
  return getComputedStyle(document.documentElement)
    .getPropertyValue(variableName)
    .trim();
};
markdown```javascript
// mermaid-config.js
import { getCSSVariable } from './utils/getCSSVariable';

const initializeMermaidWithCSSVariables = () => {
  mermaid.initialize({
    theme: 'base',
    themeVariables: {
      primaryColor: getCSSVariable('--color-primary-500'),
      primaryTextColor: getCSSVariable(
        '--color-neutral-900'
      ),
      primaryBorderColor: getCSSVariable(
        '--color-primary-700'
      ),
      lineColor: getCSSVariable('--color-neutral-700'),
      textColor: getCSSVariable('--color-neutral-900'),
      fontFamily: getCSSVariable('--font-family-base'),
      fontSize: getCSSVariable('--font-size-base'),
    },
  });
};

export default initializeMermaidWithCSSVariables;
```
javascript// mermaid-config.js
import { getCSSVariable } from './utils/getCSSVariable';

const initializeMermaidWithCSSVariables = () => {
  mermaid.initialize({
    theme: 'base',
    themeVariables: {
      primaryColor: getCSSVariable('--color-primary-500'),
      lineColor: getCSSVariable('--color-neutral-700'),
      fontFamily: getCSSVariable('--font-family-base'),
    },
  });
};

CSS 変数を使用することで、デザイントークンの管理が一元化され、JavaScript と CSS の両方で同じトークンを参照できるようになります。

ダークモード対応

ブランドにダークモードがある場合、メディアクエリを使って自動的にテーマを切り替えられます。

markdown```javascript
// mermaid-theme-switcher.js
import mermaid from 'mermaid';
import {
  lightThemeVariables,
  darkThemeVariables,
} from './theme-variables';

const initializeMermaidWithTheme = () => {
  const isDarkMode = window.matchMedia(
    '(prefers-color-scheme: dark)'
  ).matches;

  mermaid.initialize({
    theme: 'base',
    themeVariables: isDarkMode
      ? darkThemeVariables
      : lightThemeVariables,
  });
};

// ダークモード切り替えの監視
window
  .matchMedia('(prefers-color-scheme: dark)')
  .addEventListener('change', (e) => {
    initializeMermaidWithTheme();
    mermaid.contentLoaded();
  });

export default initializeMermaidWithTheme;
```
javascript// theme-variables.js
export const lightThemeVariables = {
  primaryColor: '#2196f3',
  textColor: '#212121',
  background: '#ffffff',
};

export const darkThemeVariables = {
  primaryColor: '#64b5f6',
  textColor: '#ffffff',
  background: '#121212',
};

ユーザーのシステム設定に応じて、Mermaid のテーマが自動的に切り替わります。ブランドの一貫性を保ちながら、アクセシビリティにも配慮できるでしょう。

図解の要点

具体例の章で示した内容を、以下のようにまとめられます。

  • デザイントークンを JavaScript オブジェクトとして定義し、一元管理を実現
  • トークンから themeVariables へのマッピング層を設け、保守性を向上
  • React / Next.js では、カスタムフックとコンポーネントで再利用性を確保
  • CSS 変数との統合により、デザインシステム全体での一貫性を実現
  • ダークモード対応により、アクセシビリティにも配慮

まとめ

Mermaid の themeVariables を活用することで、ブランドトークンを図表に簡単に反映できるようになりました。デフォルトテーマのままでは実現できなかった、ブランドカラーやタイポグラフィの統一が、わずかな設定で可能になります。

デザイントークンと themeVariables をマッピングする層を設けることで、保守性が高く、変更に強い設計が実現できるでしょう。また、CSS 変数との統合により、デザインシステム全体での一貫性も保てます。

技術ドキュメントやシステム図が、ブランドの一部として統一された見た目になることで、プロフェッショナルな印象を与えられます。顧客向けドキュメントや公開ドキュメントでは、特にこの一貫性が重要になるでしょう。

ぜひ、皆さんのプロジェクトでも themeVariables を活用して、ブランドに沿った美しい図表を作成してみてください。デザインシステムとの統合により、ドキュメント全体のクオリティが向上するはずです。

関連リンク

著書

とあるクリエイター

フロントエンドエンジニア Next.js / React / TypeScript / Node.js / Docker / AI Coding

;