phase-5-design-system について
プラットフォームに依存しないデザインシステムを構築し、再利用可能なUIコンポーネントと共通ルールを整えるスキルです。Webとモバイルを対象に、トークン、コンポーネント、カスタマイズ、ドキュメントを扱います。
主な機能
- 01デザイントークンの定義
- 02基本・複合コンポーネントの設計
- 03コンポーネントのカスタマイズ
- 04Web・モバイル向け実装ガイド
- 05プラットフォーム間のトークン共有
- 06コンポーネントドキュメント
活用シーン
- 01プロジェクト内のUI規範の統一
- 02再利用可能なコンポーネントライブラリの構築
- 03複数プラットフォーム間でのトークン共有
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ ww-w-ai/ bkit-claude-code --skill phase-5-design-systemインストール後、Claude Code で/phase-5-design-systemを入力して読み込まれたか確認します。
詳細
フェーズ 5:デザインシステム
プラットフォームに依存しないデザインシステムを構築します
目的
再利用可能な UI コンポーネントライブラリを構築します。一貫したデザインと迅速な開発を実現します。
デザインシステムとは?
定義
デザインシステムとは、大規模な環境でも一貫したユーザー体験を構築できる、再利用可能なコンポーネントと明確な標準の集合です。
なぜ必要なのか?(フレームワーク非依存)
| 課題 | デザインシステムによる解決策 |
|---|---|
| デザイナーと開発者の認識のずれ | 信頼できる唯一の情報源 |
| コンポーネントの重複開発 | 再利用可能なコンポーネントライブラリ |
| UI/UX の不統一 | 統一されたデザイントークンとルール |
| メンテナンスコストの増加 | 変更管理の一元化 |
| 新メンバーのオンボーディングの遅れ | 文書化されたコンポーネントカタログ |
デザインシステムの 3 層
┌─────────────────────────────────────────────────────┐
│ Design Tokens │
│ Color, Typography, Spacing, Radius, Shadow, ... │
├─────────────────────────────────────────────────────┤
│ Core Components │
│ Button, Input, Card, Dialog, Avatar, Badge, ... │
├─────────────────────────────────────────────────────┤
│ Composite Components │
│ Form, DataTable, Navigation, SearchBar, ... │
└─────────────────────────────────────────────────────┘
プラットフォーム固有の実装ツール
| プラットフォーム | 推奨ツール | デザイントークンの方式 |
|---|---|---|
| Web(React/Next.js) | shadcn/ui、Radix | CSS Variables |
| Web(Vue) | Vuetify、PrimeVue | CSS Variables |
| Flutter | Material 3、Custom Theme | ThemeData |
| iOS(SwiftUI) | ネイティブコンポーネント | Asset Catalog、Color Set |
| Android(Compose) | Material 3 | MaterialTheme |
| React Native | NativeBase、Tamagui | StyleSheet + Theme |
このフェーズで行うこと
- 基本コンポーネントのインストール:Button、Input、Card など
- カスタマイズ:プロジェクトのスタイルに合わせて調整します
- 複合コンポーネント:複数の基本コンポーネントを組み合わせます
- ドキュメント作成:コンポーネントの使用方法を文書化します
成果物
components/
└── ui/ # shadcn/ui components
├── button.tsx
├── input.tsx
├── card.tsx
└── ...
lib/
└── utils.ts # Utilities (cn function, etc.)
docs/02-design/
└── design-system.md # Design system specification
PDCA の適用
- Plan:必要なコンポーネントの一覧
- Design:コンポーネントの構造、バリエーションの設計
- Do:コンポーネントの実装/カスタマイズ
- Check:一貫性をレビュー
- Act:仕上げてフェーズ 6 に進みます
レベル別の適用
| レベル | 適用方法 |
|---|---|
| Starter | 任意(シンプルなプロジェクトでは省略可能) |
| Dynamic | 必須 |
| Enterprise | 必須(デザイントークンを含む) |
shadcn/ui のインストール
# Initial setup
npx shadcn@latest init
# Add components
npx shadcn@latest add button
npx shadcn@latest add input
npx shadcn@latest add card
必要なコンポーネント一覧
基本
- Button、Input、Textarea
- Card、Badge、Avatar
- Dialog、Sheet、Popover
フォーム
- Form、Label、Select
- Checkbox、Radio、Switch
ナビゲーション
- Navigation Menu、Tabs
- Breadcrumb、Pagination
カスタムテーマの構築
デザイントークンのオーバーライド
shadcn/ui は CSS 変数ベースのため、globals.css でテーマをカスタマイズします。
/* globals.css */
@layer base {
:root {
/* ===== Colors ===== */
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
--primary: 221.2 83.2% 53.3%; /* Brand main color */
--primary-foreground: 210 40% 98%;
--secondary: 210 40% 96.1%;
--accent: 210 40% 96.1%;
--destructive: 0 84.2% 60.2%;
--muted: 210 40% 96.1%;
--muted-foreground: 215.4 16.3% 46.9%;
/* ===== Typography ===== */
--font-sans: 'Pretendard', sans-serif;
--font-mono: 'JetBrains Mono', monospace;
/* ===== Spacing (rem units) ===== */
--spacing-xs: 0.25rem; /* 4px */
--spacing-sm: 0.5rem; /* 8px */
--spacing-md: 1rem; /* 16px */
--spacing-lg: 1.5rem; /* 24px */
--spacing-xl: 2rem; /* 32px */
/* ===== Border Radius ===== */
--radius: 0.5rem;
--radius-sm: 0.25rem;
--radius-lg: 0.75rem;
--radius-full: 9999px;
/* ===== Shadows ===== */
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
--shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1);
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1);
}
.dark {
--background: 222.2 84% 4.9%;
--foreground: 210 40% 98%;
--primary: 217.2 91.2% 59.8%;
/* ... dark mode overrides */
}
}
Tailwind 設定の拡張
// tailwind.config.js
module.exports = {
theme: {
extend: {
colors: {
brand: {
50: 'hsl(var(--brand-50))',
500: 'hsl(var(--brand-500))',
900: 'hsl(var(--brand-900))',
},
},
fontFamily: {
sans: ['var(--font-sans)', 'system-ui'],
mono: ['var(--font-mono)', 'monospace'],
},
spacing: {
'xs': 'var(--spacing-xs)',
'sm': 'var(--spacing-sm)',
'md': 'var(--spacing-md)',
'lg': 'var(--spacing-lg)',
'xl': 'var(--spacing-xl)',
},
borderRadius: {
'sm': 'var(--radius-sm)',
'DEFAULT': 'var(--radius)',
'lg': 'var(--radius-lg)',
'full': 'var(--radius-full)',
},
},
},
}
デザイントークンのドキュメント化
プロジェクトごとに docs/02-design/design-tokens.md を作成することをおすすめします。
| トークン | 値 | 用途 |
|---|---|---|
--primary | 221.2 83.2% 53.3% | ブランドのメインカラー |
--radius | 0.5rem | デフォルトの角丸 |
--font-sans | Pretendard | 本文のフォント |
コンポーネントのカスタマイズ
// Extend default button to project style
const Button = React.forwardRef<
HTMLButtonElement,
ButtonProps & { isLoading?: boolean }
>(({ isLoading, children, ...props }, ref) => {
return (
<ButtonPrimitive ref={ref} {...props}>
{isLoading ? <Spinner /> : children}
</ButtonPrimitive>
);
});
モバイルアプリのデザインシステム
Flutter:カスタムテーマの構築
Flutter では ThemeData を通じてデザイントークンを定義します。
// lib/theme/app_theme.dart
import 'package:flutter/material.dart';
class AppTheme {
// ===== Design Tokens =====
// Colors
static const Color primary = Color(0xFF3B82F6);
static const Color secondary = Color(0xFF64748B);
static const Color destructive = Color(0xFFEF4444);
static const Color background = Color(0xFFFFFFFF);
static const Color foreground = Color(0xFF0F172A);
// Spacing
static const double spacingXs = 4.0;
static const double spacingSm = 8.0;
static const double spacingMd = 16.0;
static const double spacingLg = 24.0;
static const double spacingXl = 32.0;
// Border Radius
static const double radiusSm = 4.0;
static const double radiusMd = 8.0;
static const double radiusLg = 12.0;
static const double radiusFull = 9999.0;
// ===== Theme Data =====
static ThemeData get lightTheme => ThemeData(
useMaterial3: true,
colorScheme: ColorScheme.fromSeed(
seedColor: primary,
brightness: Brightness.light,
),
fontFamily: 'Pretendard',
// Button Theme
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
padding: EdgeInsets.symmetric(
horizontal: spacingMd,
vertical: spacingSm,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(radiusMd),
),
),
),
// Card Theme
cardTheme: CardTheme(
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(radiusLg),
),
),
// Input Theme
inputDecorationTheme: InputDecorationTheme(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(radiusMd),
),
contentPadding: EdgeInsets.all(spacingSm),
),
);
static ThemeData get darkTheme => ThemeData(
useMaterial3: true,
colorScheme: ColorScheme.fromSeed(
seedColor: primary,
brightness: Brightness.dark,
),
fontFamily: 'Pretendard',
// ... dark theme overrides
);
}
Flutter:再利用可能なコンポーネント
// lib/components/app_button.dart
import 'package:flutter/material.dart';
import '../theme/app_theme.dart';
enum AppButtonVariant { primary, secondary, destructive, outline }
class AppButton extends StatelessWidget {
final String label;
final VoidCallback? onPressed;
final AppButtonVariant variant;
final bool isLoading;
const AppButton({
required this.label,
this.onPressed,
this.variant = AppButtonVariant.primary,
this.isLoading = false,
super.key,
});
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: isLoading ? null : onPressed,
style: _getStyle(),
child: isLoading
? SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(strokeWidth: 2),
)
: Text(label),
);
}
ButtonStyle _getStyle() {
switch (variant) {
case AppButtonVariant.destructive:
return ElevatedButton.styleFrom(
backgroundColor: AppTheme.destructive,
);
case AppButtonVariant.outline:
return ElevatedButton.styleFrom(
backgroundColor: Colors.transparent,
side: BorderSide(color: AppTheme.primary),
);
default:
return ElevatedButton.styleFrom();
}
}
}
Flutter:プロジェクト構成
lib/
├── theme/
│ ├── app_theme.dart # ThemeData + Design Tokens
│ ├── app_colors.dart # Color constants
│ ├── app_typography.dart # TextStyle definitions
│ └── app_spacing.dart # Spacing constants
├── components/
│ ├── app_button.dart
│ ├── app_input.dart
│ ├── app_card.dart
│ └── app_dialog.dart
└── main.dart
クロスプラットフォームでのデザイントークン共有
デザイントークン JSON(プラットフォーム非依存)
Figma Tokens または Style Dictionary を使用して、トークンを一元管理します。
// tokens/design-tokens.json
{
"color": {
"primary": { "value": "#3B82F6" },
"secondary": { "value": "#64748B" },
"destructive": { "value": "#EF4444" }
},
"spacing": {
"xs": { "value": "4px" },
"sm": { "value": "8px" },
"md": { "value": "16px" },
"lg": { "value": "24px" }
},
"radius": {
"sm": { "value": "4px" },
"md": { "value": "8px" },
"lg": { "value": "12px" }
},
"font": {
"family": { "value": "Pretendard" },
"size": {
"sm": { "value": "14px" },
"md": { "value": "16px" },
"lg": { "value": "18px" }
}
}
}
プラットフォーム固有の変換
# Generate tokens for each platform with Style Dictionary
npx style-dictionary build
# Output:
# - build/css/variables.css (Web)
# - build/dart/app_tokens.dart (Flutter)
# - build/swift/AppTokens.swift (iOS)
# - build/kt/AppTokens.kt (Android)
デザインシステムのチェックリスト(プラットフォーム非依存)
必須項目
-
デザイントークンの定義
- 色(プライマリー、セカンダリー、セマンティック)
- タイポグラフィ(フォントファミリー、サイズ、ウェイト)
- 余白(xs、sm、md、lg、xl)
- 角丸
- シャドウ/エレベーション
-
コアコンポーネント
- ボタン(バリエーション:プライマリー、セカンダリー、アウトライン、破壊的操作)
- 入力欄/テキストフィールド
- カード
- ダイアログ/モーダル
- アバター
- バッジ
-
複合コンポーネント
- フォーム(バリデーション付き)
- ナビゲーション(ヘッダー、サイドバー、ボトムナビ)
- データ表示(テーブル、リスト)
-
ドキュメント
- コンポーネントカタログ(Storybook / Widgetbook)
- 使用ガイドライン
- 推奨事項と禁止事項
テンプレート
templates/pipeline/phase-5-design-system.template.md を参照してください。
次のフェーズ
フェーズ 6:UI の実装+API 連携 → コンポーネントの準備ができたので、実際の画面を実装します
コメント
Markdown 対応