カラーテーマシステム
16色パレットからセマンティック CSS カスタムプロパティへの2層カラーアーキテクチャ
カラーテーマシステム
カラーテーマシステムは2層アーキテクチャを使用する。Tier 1 は16色パレット(ターミナルカラースキームと同様)を定義し、Tier 2 はパレットインデックスからセマンティックトークン(背景、アクセント、危険、ホバー状態)を導出する。両層とも CSS カスタムプロパティとして公開され、Tailwind CSS ユーティリティやコンポーネントスタイルで使用できる。
Tier 1: カラーパレット
各カラースキームは標準のターミナルカラー規約(ノーマル8色 + ブライト8色)に合わせた16色パレットを定義する。
export interface ColorScheme {
name: string;
label: string;
isDark: boolean;
background: ColorRef; // パレットインデックスまたは直接カラー
foreground: ColorRef;
cursor: ColorRef;
selectionBg: ColorRef;
selectionFg: ColorRef;
palette: [
string, string, string, string, // 0-3: black, red, green, yellow
string, string, string, string, // 4-7: blue, magenta, cyan, white
string, string, string, string, // 8-11: ブライトバリアント
string, string, string, string, // 12-15: ブライトバリアント
];
semantic?: { /* Tier 2 オーバーライド */ };
}
ColorRef 型はパレットインデックス(数値)または直接16進値(文字列)でカラーを参照できる。
export type ColorRef = number | string;
この柔軟性により、スキームは自身のパレットエントリを参照することも、パレットにないカスタムカラーを使用することもできる。
// "default-dark" はパレットインデックスを参照
{
background: 0, // palette[0] = "#1C1C1C"
foreground: 7, // palette[7] = "#A0A0A0"
cursor: 6, // palette[6] = "#90A1B9"
}
// "catppuccin-mocha" は直接16進値を使用
{
background: "#1e1e2e",
foreground: "#cdd6f4",
cursor: "#f5e0dc",
}
組み込みスキーム
パッケージには10のカラースキームが同梱されている。
| スキーム | タイプ | 説明 |
|---|---|---|
default-dark | ダーク | セマンティックオーバーライド付きカスタムウォームダークテーマ |
default-light | ライト | セマンティックオーバーライド付きカスタムライトテーマ |
catppuccin-mocha | ダーク | Catppuccin パステルパレット(mocha バリアント) |
catppuccin-latte | ライト | Catppuccin パステルパレット(latte バリアント) |
tokyo-night | ダーク | Tokyo Night カラースキーム |
dracula | ダーク | Dracula クラシックテーマ |
nord | ダーク | 北欧風ブルーイッシュパレット |
solarized-dark | ダーク | Ethan Schoonover の Solarized(ダーク) |
one-dark | ダーク | Atom One Dark テーマ |
gruvbox-dark | ダーク | レトロなグルーヴカラー |
Tier 2: セマンティックトークン
生のパレットカラーは UI コンポーネントにとって意味を持たない。「palette[3] を使用」は意図を伝えない。セマンティックトークンがこのギャップを埋める。
export interface ColorSettings {
bgPrimary: string;
bgSecondary: string;
bgSurface: string;
textPrimary: string;
textSecondary: string;
accent: string;
accentSubtle: string;
border: string;
danger: string;
dangerStrong: string;
onAccent: string; // アクセント背景上のテキスト色
hoverOverlay: string;
hoverBg: string;
hoverFg: string;
selection: string;
cursor: string;
}
セマンティックカラーの導出
schemaToColors() 関数がスキームのパレットからセマンティックトークンを生成する。
export function schemaToColors(scheme: ColorScheme): ColorSettings {
const p = scheme.palette;
const bg = resolveColor(scheme.background, p, p[0]);
const fg = resolveColor(scheme.foreground, p, p[15]);
const sem = resolveSemanticColors(scheme);
return {
bgPrimary: bg,
bgSecondary: sem.bgSecondary,
bgSurface: sem.surface,
textPrimary: fg,
textSecondary: sem.textSecondary,
accent: sem.accent,
accentSubtle: sem.accentSubtle,
border: sem.border,
danger: sem.danger,
dangerStrong: sem.dangerStrong,
onAccent: sem.onAccent,
hoverOverlay: sem.hoverOverlay,
hoverBg: sem.hoverBg,
hoverFg: sem.hoverFg,
selection: resolveColor(scheme.selectionBg, p, p[8]),
cursor: resolveColor(scheme.cursor, p, p[6]),
};
}
セマンティックオーバーライド
スキームは semantic オブジェクトを通じてデフォルトのパレット→セマンティックマッピングをオーバーライドできる。異なるパレットが正しく見えるためには異なるマッピングが必要なため、これは不可欠である。
// "default-dark" はいくつかのセマンティックトークンをオーバーライド
semantic: {
bgSecondary: "bg", // Use background color (special ref)
surface: 0, // Palette index 0
accent: 3, // Palette index 3 (yellow)
accentSubtle: 12, // Palette index 12
border: 8, // Palette index 8
onAccent: "bg", // Text on accent = background color
hoverOverlay: 13,
hoverBg: 13,
hoverFg: 11,
}
特殊文字列値 "bg" と "fg" はスキームの解決済み背景色と前景色を参照し、自己参照的な定義を可能にする。
デフォルトフォールバック
セマンティックオーバーライドを提供しないスキームにはデフォルトが使用される。
export const SEMANTIC_DEFAULTS: Record<string, number> = {
bgSecondary: 0,
surface: 0,
textSecondary: 15,
accent: 3,
accentSubtle: 3,
border: 0,
danger: 1,
dangerStrong: 9,
onAccent: 9,
hoverOverlay: 15,
hoverBg: 8,
hoverFg: 7,
};
オーバーライドのないスキーム(Catppuccin、Tokyo Night など)では、カラーミキシングを使用した計算済みフォールバックも生成される。
accentSubtle: colorMixAlpha(accentColor, 0.1, bg), // 背景上に10%のアクセント
hoverOverlay: colorMixAlpha(fg, 0.03, bg), // 背景上に3%の前景
hoverBg: colorMixAlpha(fg, 0.1, bg), // 背景上に10%の前景
CSS カスタムプロパティ
Tier 1: パレット変数
applyTheme() 関数により :root に適用される。
export function applyTheme(themeName: string): ColorScheme {
const scheme = getSchemeByName(themeName);
const root = document.documentElement;
const p = scheme.palette;
root.style.setProperty("--palette-bg", resolveColor(scheme.background, p, p[0]));
root.style.setProperty("--palette-fg", resolveColor(scheme.foreground, p, p[15]));
root.style.setProperty("--palette-cursor", resolveColor(scheme.cursor, p, p[6]));
root.style.setProperty("--palette-selection", resolveColor(scheme.selectionBg, p, p[8]));
for (let i = 0; i <= 15; i++) {
root.style.setProperty(`--palette-${i}`, p[i]);
}
return scheme;
}
これにより CSS から --palette-0 から --palette-15、--palette-bg、--palette-fg などにアクセスできる。
Tier 2: セマンティック変数
applyColors() 関数により適用される。
const colorKeyToCssVar: Record<keyof ColorSettings, string> = {
bgPrimary: "--theme-bg-primary",
bgSecondary: "--theme-bg-secondary",
bgSurface: "--theme-bg-surface",
textPrimary: "--theme-text-primary",
textSecondary: "--theme-text-secondary",
accent: "--theme-accent",
accentSubtle: "--theme-accent-subtle",
border: "--theme-border",
danger: "--theme-danger",
dangerStrong: "--theme-danger-strong",
onAccent: "--theme-on-accent",
hoverOverlay: "--theme-hover-overlay",
hoverBg: "--theme-hover-bg",
hoverFg: "--theme-hover-fg",
selection: "--theme-selection",
cursor: "--theme-cursor",
};
コンポーネントはパレットインデックスではなくセマンティック変数を使用する。
/* 良い例: セマンティックな意図が明確 */
.sidebar { background: var(--theme-bg-secondary); }
.button { background: var(--theme-accent); color: var(--theme-on-accent); }
.error { color: var(--theme-danger); }
/* 避ける: パレットインデックスにはセマンティックな意味がない */
.sidebar { background: var(--palette-0); }
xterm 統合
ターミナルには独自のテーマオブジェクトが必要(CSS 変数ではない)。toXtermTheme() 関数が ColorScheme を xterm の ITheme 形式に変換する。
export function toXtermTheme(scheme: ColorScheme) {
const p = scheme.palette;
return {
background: resolveColor(scheme.background, p, p[0]),
foreground: resolveColor(scheme.foreground, p, p[15]),
cursor: resolveColor(scheme.cursor, p, p[6]),
black: p[0],
red: p[1],
green: p[2],
yellow: p[3],
blue: p[4],
// ...all 16 ANSI colors
};
}
カスタム DOM イベント(zudotext:scheme-changed)がスキーム変更時にターミナルコンポーネントに通知し、xterm テーマを再適用できるようにする。
カラーユーティリティ関数
パッケージにはカラー操作のユーティリティが含まれている。
// Mix two hex colors at a given ratio
colorMix("#ff0000", "#0000ff", 0.5) // -> purple
// Blend foreground over background with alpha
colorMixAlpha(fg, 0.1, bg) // -> 10% fg over bg
// HSL conversion for programmatic color manipulation
hexToHsl("#1C1C1C") // -> { h: 0, s: 0, l: 11 }
// Determine readable text color for a background
contrastTextColor("#1C1C1C") // -> "#ffffff"
まとめ
2層システムは利用可能なカラー(パレット)とその意味(セマンティックトークン)を分離する。スキームは16色のパレットを定義し、オプションでセマンティックマッピングをオーバーライドするだけでよい。システムが残りを処理する:ホバー状態の生成、微妙なバリアントの計算、CSS 変数の設定、フォーマット固有のテーマオブジェクトへの変換。これにより、エディタ、ターミナル、UI コンポーネント、Tailwind ユーティリティ全体でテーマの一貫性が保たれる。