メインコンテンツまでスキップ

デザイントークン

jamjam UI で使用する CSS Custom Properties(CSS変数)の定義。

実装先: ui/src/styles/tokens.css


概要

すべてのスタイリングは CSS 変数を通じて行う。 これにより、テーマ切り替え(ダーク/ライト)が容易になり、 デザインの一貫性が保たれる。

命名規則

--{category}-{property}-{variant}

例:
--color-bg-primary
--space-md
--radius-lg

カラートークン

ダークテーマ(デフォルト)

:root,
:root[data-theme="dark"] {
/* === Background === */
--color-bg-primary: #1E1E2E; /* メイン背景 */
--color-bg-secondary: #2A2A3E; /* カード背景 */
--color-bg-elevated: #363652; /* ホバー・フォーカス */
--color-bg-overlay: rgba(0, 0, 0, 0.5); /* モーダル背景 */

/* === Accent === */
--color-accent: #7C5CFF; /* プライマリアクション */
--color-accent-hover: #9B7FFF; /* ホバー時 */
--color-accent-active: #6B4FE0; /* アクティブ時 */

/* === Semantic === */
--color-success: #4ADE80; /* 接続成功、正常状態 */
--color-success-bg: rgba(74, 222, 128, 0.1);
--color-warning: #FBBF24; /* 警告、不安定 */
--color-warning-bg: rgba(251, 191, 36, 0.1);
--color-danger: #F87171; /* エラー、切断 */
--color-danger-bg: rgba(248, 113, 113, 0.1);

/* === Text === */
--color-text-primary: #FFFFFF; /* 主要テキスト */
--color-text-secondary: #A0A0B0; /* 補助テキスト */
--color-text-disabled: #606070; /* 無効状態 */
--color-text-inverse: #1E1E2E; /* 反転テキスト */

/* === Border === */
--color-border: #404060; /* 通常ボーダー */
--color-border-focus: #7C5CFF; /* フォーカス時 */
--color-border-hover: #505070; /* ホバー時 */

/* === Connection Status === */
--color-status-disconnected: #606070; /* グレー */
--color-status-connecting: #FBBF24; /* 黄(点滅) */
--color-status-connected: #4ADE80; /* 緑 */
--color-status-unstable: #FBBF24; /* 黄 */
--color-status-error: #F87171; /* 赤 */

/* === Mixer === */
--color-meter-low: #4ADE80; /* -∞ to -12dB */
--color-meter-mid: #FBBF24; /* -12 to -3dB */
--color-meter-high: #F87171; /* -3dB to 0dB */
--color-mute-active: #F87171; /* ミュートON */
--color-solo-active: #FBBF24; /* ソロON */
}

ライトテーマ

:root[data-theme="light"] {
/* === Background === */
--color-bg-primary: #FAFAFA;
--color-bg-secondary: #FFFFFF;
--color-bg-elevated: #F0F0F5;
--color-bg-overlay: rgba(0, 0, 0, 0.3);

/* === Accent === */
--color-accent: #6B4FE0;
--color-accent-hover: #5A3FD0;
--color-accent-active: #4A2FC0;

/* === Semantic === */
--color-success: #22C55E;
--color-success-bg: rgba(34, 197, 94, 0.1);
--color-warning: #EAB308;
--color-warning-bg: rgba(234, 179, 8, 0.1);
--color-danger: #EF4444;
--color-danger-bg: rgba(239, 68, 68, 0.1);

/* === Text === */
--color-text-primary: #1E1E2E;
--color-text-secondary: #606070;
--color-text-disabled: #A0A0B0;
--color-text-inverse: #FFFFFF;

/* === Border === */
--color-border: #E0E0E8;
--color-border-focus: #6B4FE0;
--color-border-hover: #D0D0D8;

/* === Connection Status === */
--color-status-disconnected: #A0A0B0;
--color-status-connecting: #EAB308;
--color-status-connected: #22C55E;
--color-status-unstable: #EAB308;
--color-status-error: #EF4444;

/* === Mixer === */
--color-meter-low: #22C55E;
--color-meter-mid: #EAB308;
--color-meter-high: #EF4444;
--color-mute-active: #EF4444;
--color-solo-active: #EAB308;
}

タイポグラフィトークン

:root {
/* === Font Family === */
--font-family-sans:
-apple-system,
BlinkMacSystemFont,
'Segoe UI',
Roboto,
'Hiragino Sans',
'Hiragino Kaku Gothic ProN',
'Noto Sans JP',
sans-serif;

--font-family-mono:
'SF Mono',
'Fira Code',
Consolas,
'Courier New',
monospace;

/* === Font Size === */
--font-size-h1: 24px;
--font-size-h2: 18px;
--font-size-h3: 16px;
--font-size-body: 14px;
--font-size-caption: 12px;
--font-size-small: 11px;

/* === Font Weight === */
--font-weight-bold: 700;
--font-weight-semibold: 600;
--font-weight-normal: 400;

/* === Line Height === */
--line-height-tight: 1.25;
--line-height-normal: 1.5;
--line-height-relaxed: 1.75;

/* === Letter Spacing === */
--letter-spacing-tight: -0.02em;
--letter-spacing-normal: 0;
--letter-spacing-wide: 0.02em;
}

タイポグラフィの使用例

/* 見出し1 */
.h1 {
font-family: var(--font-family-sans);
font-size: var(--font-size-h1);
font-weight: var(--font-weight-bold);
line-height: var(--line-height-tight);
}

/* 本文 */
.body {
font-family: var(--font-family-sans);
font-size: var(--font-size-body);
font-weight: var(--font-weight-normal);
line-height: var(--line-height-normal);
}

/* 数値表示(遅延時間など) */
.numeric {
font-family: var(--font-family-mono);
font-size: var(--font-size-body);
font-weight: var(--font-weight-semibold);
}

スペーシングトークン

4px ベースのスペーシングシステム。

:root {
/* === Base Unit === */
--space-unit: 4px;

/* === Spacing Scale === */
--space-xs: 4px; /* 1 unit */
--space-sm: 8px; /* 2 units */
--space-md: 16px; /* 4 units */
--space-lg: 24px; /* 6 units */
--space-xl: 32px; /* 8 units */
--space-2xl: 48px; /* 12 units */
--space-3xl: 64px; /* 16 units */

/* === Component Padding === */
--padding-button: var(--space-sm) var(--space-md);
--padding-card: var(--space-md);
--padding-input: var(--space-sm) var(--space-sm);
--padding-modal: var(--space-lg);

/* === Layout === */
--gap-xs: var(--space-xs);
--gap-sm: var(--space-sm);
--gap-md: var(--space-md);
--gap-lg: var(--space-lg);
}

角丸トークン

:root {
--radius-none: 0;
--radius-sm: 4px;
--radius-md: 8px;
--radius-lg: 12px;
--radius-xl: 16px;
--radius-full: 9999px; /* 円形ボタン用 */
}

使用ガイドライン

コンポーネント角丸
ボタン--radius-md
カード--radius-lg
入力フィールド--radius-sm
モーダル--radius-xl
バッジ・タグ--radius-full
アイコンボタン(円形)--radius-full

シャドウトークン

:root {
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.1);
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.15);
--shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.2);
--shadow-xl: 0 20px 25px rgba(0, 0, 0, 0.25);

/* Focus ring */
--shadow-focus: 0 0 0 3px rgba(124, 92, 255, 0.4);
}

アニメーショントークン

:root {
/* === Duration === */
--duration-instant: 0ms;
--duration-fast: 100ms;
--duration-normal: 200ms;
--duration-slow: 300ms;
--duration-slower: 500ms;

/* === Easing === */
--ease-default: ease-out;
--ease-in: ease-in;
--ease-in-out: ease-in-out;
--ease-bounce: cubic-bezier(0.68, -0.55, 0.265, 1.55);

/* === Transition Presets === */
--transition-fast: var(--duration-fast) var(--ease-default);
--transition-normal: var(--duration-normal) var(--ease-default);
--transition-slow: var(--duration-slow) var(--ease-default);
}

/* アクセシビリティ: motion 設定を尊重 */
@media (prefers-reduced-motion: reduce) {
:root {
--duration-instant: 0ms;
--duration-fast: 0ms;
--duration-normal: 0ms;
--duration-slow: 0ms;
--duration-slower: 0ms;
}
}

アニメーション使用ガイドライン

シーンDuration理由
ボタンホバーfast即時フィードバック
モーダル表示normalスムーズな遷移
ページ遷移slow視覚的区切り
接続中スピナーslower穏やかな回転

注意: 演奏中(セッションアクティブ時)はアニメーションを最小限に抑える。


Z-Index トークン

:root {
--z-base: 0;
--z-dropdown: 100;
--z-sticky: 200;
--z-overlay: 300;
--z-modal: 400;
--z-popover: 500;
--z-tooltip: 600;
--z-toast: 700;
}

レスポンシブトークン

:root {
/* === Breakpoints === */
--breakpoint-sm: 640px; /* Mobile */
--breakpoint-md: 768px; /* Tablet */
--breakpoint-lg: 1024px; /* Desktop */
--breakpoint-xl: 1280px; /* Large Desktop */

/* === Container === */
--container-sm: 640px;
--container-md: 768px;
--container-lg: 1024px;
--container-xl: 1200px;
}

レスポンシブ使用例

/* Mobile first */
.container {
padding: var(--space-sm);
}

@media (min-width: 768px) {
.container {
padding: var(--space-md);
}
}

実装チェックリスト

ファイル作成

ui/src/styles/
├── tokens.css # このドキュメントの CSS
├── reset.css # CSS リセット
└── global.css # グローバルスタイル(tokens.css をインポート)

使用規則

  • 生のカラー値(#7C5CFF)を直接使用しない
  • 生のピクセル値(16px)を直接使用しない(例外: ボーダー幅)
  • テーマ切り替えは data-theme 属性で行う
  • prefers-reduced-motion を尊重する

テーマ切り替え実装

// テーマ切り替え
function setTheme(theme: 'dark' | 'light') {
document.documentElement.setAttribute('data-theme', theme);
localStorage.setItem('theme', theme);
}

// 初期化(システム設定を尊重)
function initTheme() {
const saved = localStorage.getItem('theme');
if (saved) {
setTheme(saved as 'dark' | 'light');
} else if (window.matchMedia('(prefers-color-scheme: light)').matches) {
setTheme('light');
} else {
setTheme('dark');
}
}