teaching-site-design-system について
色トークン、タイポグラフィ、形状、コンポーネント、ダークモードを扱う、教材サイト向けのビジュアルデザインシステムです。設計判断の理由も示し、ウェブと電子書籍の一貫したスタイルづくりを支援します。
主な機能
- 01OKLCHベースのカラートークン
- 02セマンティックな面と2系統のアクセント
- 03タイポグラフィと形状の指針
- 04教材サイト向け定番コンポーネント
- 05ダークモードと形式間のトークン再利用
活用シーン
- 01教材サイトのビジュアル基盤づくり
- 02カードやプロンプトブロックのスタイル統一
- 03コンポーネント配置を変えずにダークモードを調整
- 04ウェブと電子書籍でトークンを共用
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ kevintsai1202/ teaching-site-skills --skill teaching-site-design-systemインストール後、Claude Code で/teaching-site-design-systemを入力して読み込まれたか確認します。
同梱ファイルtemplates/tokens.css
詳細
教育サイトのデザインシステム
トークンのスターター(そのままコピーしてください。再構成する必要はありません):
templates/tokens.css— 完全な:rootブロック+ダークテーマ+主要コンポーネント(day-hero、glass-card、prompt-card、task-list、learning-goal、concept、unit、material、table)。index.htmlの<style>ブロック(バニラ SPA パターン)に貼り付けるか、外部のstyle.cssとして使用してください。スキーマの基準:このスキルではビジュアルトークンを定義します。データフィールド名は
_shared/domain-primitives.mdに記載されています。コンポーネントのスタイルを設定する際は、レンダリングするデータの形状を把握するためにこのファイルを確認してください。リファレンス実装:
d:/GitHub/ai-workshop/index.html:11-1700— このスキルのトークンの抽出元となった、本番環境用の完全な CSS です。
このスキルでは、教育サイトのビジュアルレイヤー(カラートークン、タイポグラフィ、形状、コンポーネント、ダークモード戦略)と、特に重要な各判断の根拠を定義します。文字どおりの値よりも、その理由が重要です。将来のサイトでは値を再調整できますが、なぜそうするのかはプロジェクトを越えて引き継がれます。
呼び出すタイミング
- 新しい教育サイトを設定するとき(最初の CSS を書く前にトークンを確認してください)。
- 新しいビジュアル要素(バッジ、カード、ヒーロー)を追加するとき — 新たに考案する前に、既存のパターンで対応できないか確認してください。
- ダークモードを調整するとき(どのトークンを上書きし、どれを上書きしないか)。
- 企業向け版や電子書籍を制作するとき(同じトークンが印刷用 CSS にも反映されます)。
- ビジュアルの一貫性に関する指摘をレビューするとき(「ここは一貫性がないように見える」)。
基本方針:HEX/RGB ではなく OKLCH
:root {
--bg: oklch(99% 0.002 240);
--surface: oklch(100% 0 0);
--fg: oklch(18% 0.012 250);
--accent: oklch(58% 0.18 255);
--accent-2: oklch(66% 0.14 65);
}
OKLCH を使う理由(#1a73e8 や hsl(...) ではなく):
- 知覚的に均一 —
oklch(60% ...)とoklch(70% ...)は同じ程度の差に見えますが、HSL はそうではありません(黄色の 60→70 は視覚的に大きく変化しますが、青はほとんど変わりません)。 - ダークモードを予測しやすい — 明るいテーマの各トークンが
L=N%なら、暗いテーマの値は同じ彩度と色相で、おおよそL=(100-N)%になります。機械的に設定できます。 color-mix(in oklab, ...)がネイティブに使える — たとえば、柔らかなアクセントのティント(color-mix(in oklab, var(--accent) 18%, transparent))を手作業で rgba を計算せず、きれいに合成できます。- 最新ブラウザーで十分にサポートされている(Chrome 111+、Safari 15.4+、Firefox 113+)。静的な教育サイトでは問題ありません。
関係者からブランドカラーを 16 進数で渡された場合は、一度 OKLCH に変換したら、以後は戻さないでください。
トークンの構成(二軸)
軸 1:セマンティックなサーフェストークン
--bg /* page background */
--surface /* card / panel */
--surface-2 /* nested card / hover state */
--fg /* primary text */
--muted /* secondary text */
--muted-2 /* tertiary text / placeholder */
--border /* default border */
--border-strong /* emphasis border */
これらは構造を表すものです。色ではなく、レイアウト上の役割を表します。ダークモードでは値を切り替えますが、コンポーネントの CSS は変更しません。
軸 2:アクセントトークン(二重アクセント戦略)
--accent /* primary action color — blue/violet typically */
--accent-soft /* faint tint, for hover backgrounds, badges */
--accent-deep /* high-contrast variant, for hover text */
--accent-2 /* secondary accent — orange/amber typically */
--accent-2-soft
--accent-2-deep
アクセントを 2 つ使う理由(1 つとグレースケールではなく):教育サイトには、同じくらい重要な 2 つの注目経路があります。
- アクション経路(
--accent): 「クリックしてください」「コピーしてください」「ここへ移動してください」 — 通常は青などの寒色です。 - コンテンツ経路(
--accent-2): 「ここに注目してください。豊かなコンテンツです」 — Day ヒーローの数字、PROMPT バッジ、コールアウトの背景など。通常はオレンジや琥珀色などの暖色です。
アクセントが 1 つのデザインでは、コンテンツの強調にグレースケールを使うことになり、すべてがボタンのように感じられます。二重アクセントなら、「クリックするもの」と「注意深く読むもの」を区別できます。
ステータストークン
--success, --success-soft /* learning goals ✓, copy success */
--warning, --warning-soft /* gotchas, optional warnings */
--danger, --danger-soft /* errors, destructive actions */
背景には常に -soft バリアントを用意してください。ベースの値はテキストやアイコンに使用します。
タイポグラフィ:すべてを統一する 1 つのフォントスタック
--font-display: "Nunito", "jf-openhuninn-1.1", "jf-openhuninn",
"Gen Jyuu Gothic", "Taipei Sans TC Beta",
"PingFang TC", "Noto Sans TC",
system-ui, sans-serif;
/* All four roles point to the same stack */
--font-body: var(--font-display);
--font-serif: var(--font-display);
--font-mono: var(--font-display);
中国語の教育サイトですべてに同じスタックを使う理由:
- 段落の 90% 以上で、ラテン文字と中文が混在します。フォントを切り替えるたびに「フォントの断絶感」が生じます。
- フォールバックチェーンが二言語のレンダリングを処理します。ラテン文字の字形には Nunito、中文には openhuninn を使います。
- 等幅やセリフのバリエーションは、実際に異なる書体を使うためではなく、主に間隔や太さの効果を出すために使います。フォントを切り替えるのではなく、
font-feature-settings、letter-spacing、font-weightで実現してください。
例外:正式な印刷用の電子書籍 PDF では、本物のセリフ体(Source Han Serif)を使ってもかまいません。このルールを破るのはその場合だけです。
Geometry Tokens
--r-sm: 6px; /* buttons, small chips */
--r-md: 10px; /* cards, panels */
--r-lg: 14px; /* heroes, large containers */
--sidebar-w: 268px;
--header-h: 56px;
--content-max: 880px; /* reading line length cap */
--shadow-lift:
0 6px 18px -10px oklch(20% 0.02 250 / 0.18),
0 2px 4px -2px oklch(20% 0.02 250 / 0.06);
3種類の半径で十分です。それ以上増やすと、見た目が雑然とします。要素のスケールに合わせて半径を選びます。ボタン → sm、カード → md、ヒーロー → lg。
--content-max: 880px は意図的な設定です。読みやすい行の長さは約60~75文字です。これより広いとドキュメントのように感じられ、狭いとデスクトップに固定されたモバイル画面のように感じられます。
二重レイヤーの影(--shadow-lift)は、幅広く柔らかな影と、狭くくっきりした影を重ねます。単層の影はデジタルな印象になりますが、二重レイヤーにすると、カードが物理的に浮いているように見えます。影が要素の下にはみ出さないよう、y方向のオフセットは必ず負の値(-10px、-2px)にします。
特徴的なコンポーネント
1. Day ヒーローの数字
.day-hero-numeral {
font-family: var(--font-serif);
font-size: clamp(120px, 16vw, 200px);
font-weight: 500;
line-height: 0.85;
letter-spacing: -0.05em;
color: var(--accent-2);
text-shadow: 0 1px 0 color-mix(in oklab, var(--accent-2) 18%, transparent);
}
.day-hero:hover .day-hero-numeral {
color: var(--accent-deep);
text-shadow:
0 2px 0 color-mix(in oklab, var(--accent-2) 22%, transparent),
0 18px 40px color-mix(in oklab, var(--accent) 22%, transparent);
}
Day の見出しに装飾的な SVG やイラストではなく、大きな数字を使う理由:抽象的な SVG はプレースホルダー(「本物の画像を入れ忘れた」)のように見えます。200px の「D2」は、意図的で、自信に満ち、ナビゲーションとして機能する印象を与えます。具体的なもののほうが抽象的なものより優れています。
clamp(120px, 16vw, 200px) を使う理由:ビューポート幅に応じて拡大縮小しますが、下限と上限があるため、モバイルでは表示が崩れず(120px)、4K では大きくなりすぎません(最大200px)。
opacity: 0 → transition ではなく @keyframes でフェードインする理由:IntersectionObserver が発火しない場合(たとえば CSS の zoom が使われている場合。static-spa-interactions を参照)、数字は永遠に opacity: 0 のままになります。@keyframes ならデフォルトの状態で表示され、アニメーションは「トリガーされた場合に一度だけ再生」されます。
2. PROMPT ブロック(オレンジのバッジ+ダークなコード)
.prompt-block {
background: oklch(15% 0.02 250); /* always dark, even in light mode */
color: oklch(92% 0.005 250);
border-left: 4px solid var(--accent-2);
padding: 14px 16px;
border-radius: var(--r-md);
}
.prompt-block::before {
content: 'PROMPT';
display: inline-block;
background: var(--accent-2);
color: white;
padding: 2px 10px;
border-radius: 999px;
font-size: 11px;
font-family: var(--font-mono);
letter-spacing: 0.1em;
margin-bottom: 8px;
}
PROMPT にオレンジ(--accent-2)を使う理由:青(--accent)は「クリック可能/ナビゲーション用」に使います。オレンジは「これはレシピです。注意して読み、そのままコピーしてください」という意味を示します。3か月もすれば、学習者はオレンジを見るだけでプロンプトブロックだと認識するようになります。
ライトモードでも暗い背景を使う理由:コードのようなコンテンツは、コードらしく見える必要があります。ライトモードとダークモードで切り替えると、ライトモードではプロンプトが「重要度の低いもの」に感じられてしまいます。
3. グラスカード(glass-card)
.glass-card {
background: color-mix(in oklab, var(--surface) 60%, transparent);
backdrop-filter: blur(12px) saturate(140%);
border: 1px solid color-mix(in oklab, var(--border) 80%, transparent);
border-radius: var(--r-md);
padding: 16px 20px;
box-shadow: var(--shadow-lift);
}
半透明と backdrop-filter を使う理由:背景(グラデーション、ドットパターン、イラストなど)が豊かなページでは、不透明なカードは重たい印象になります。グラスカードならコンテンツが引き立ち、「ドキュメントの上に浮いている」ことを示せます。
注意点:backdrop-filter のブラウザー対応が必要です。Safari では -webkit-backdrop-filter のプレフィックスが必要です。未対応のブラウザーでは、フォールバックとして不透明度60%の色が表示され、自然に機能が低下します。
4. タスクのチェックボックス(3つの状態)
.task-checkbox {
width: 18px; height: 18px;
border: 1.5px solid var(--border-strong);
border-radius: var(--r-sm);
display: grid; place-items: center;
transition: all 120ms ease;
}
.task-item:hover .task-checkbox { border-color: var(--accent); }
.task-item.done .task-checkbox {
background: var(--success);
border-color: var(--success);
}
.task-item.done .task-label { text-decoration: line-through; color: var(--muted); }
状態は3つです。デフォルト(空で、はっきりした枠線)、ホバー(アクセントカラーの枠線で、操作できることをさりげなく示す)、完了(緑の塗りつぶし+取り消し線)。ホバー状態は重要です。これがないと、学習者はチェックボックスをクリックできるのか判断できません。
5. ゴールバッジ(緑の ✓)
.learning-goal::before {
content: '✓';
display: inline-block;
width: 20px; height: 20px;
background: var(--success-soft);
color: var(--success);
border-radius: 999px;
text-align: center;
line-height: 20px;
margin-right: 8px;
font-weight: bold;
}
「学習成果」や「できるようになること」には、薄緑の円に緑色のマークを入れたデザインを一貫して使います。「完了」には使い回さないでください。それは task-checkbox の役割です。緑には意味の異なる2つの用途があり、見た目を区別する必要があります。ゴール(バッジの接頭部分)と完了(塗りつぶされたチェックボックス)です。
ダークモードの方針
:root { /* light tokens here */ }
[data-theme="dark"] {
--bg: oklch(16% 0.014 250);
--surface: oklch(19.5% 0.014 250);
--fg: oklch(96% 0.005 250);
--border: oklch(28% 0.014 250);
/* accents usually stay the same — accent-deep may flip to brighter */
--accent-deep: oklch(72% 0.18 255);
}
prefers-color-scheme ではなく [data-theme] 属性を使います。手動の切り替えを正とします。OS のテーマに自動追従すると、ユーザーがサイトごとに設定できません。切り替え設定は localStorage の読み書きを行います。
ダークモードで切り替えるもの:
- ✅ すべてのサーフェストークン(bg / surface / fg / muted / border)
- ✅
accent-deep(高コントラスト版)。より明るい L 値に切り替えます。 - ❌ ステータスカラー(success / danger / warning)。これらは意味に基づく色なので色相は変えません。
-softバリアントは、サーフェスの変更に伴って自然に暗くなります。 - ❌
accent-2。2色のアクセントによるアイデンティティはダークモードでも維持します(ブランドカラーのオレンジはそのままです)。
変わらないもの:
- すべてのジオメトリトークン(半径、サイドバーの幅、影の定義。ただし影の不透明度は調整が必要な場合があります)
- すべてのコンポーネントのレイアウトと構造
目安となるルールは、トークンの値は変えても、コンポーネントの CSS は変えないことです。[data-theme="dark"] .task-checkbox { ... } と書いていることに気づいたら、抽象化が漏れています。変化するプロパティにトークンを使うよう、コンポーネントをリファクタリングしてください。
フォーマットをまたいだトークンの再利用(Web → Ebook)
Ebook の印刷用 CSS(course-ebook-publishing)も同じトークンを読み込みます。具体的には次のとおりです。
/* style-ebook.css (print only) */
@media print {
:root {
/* Override only what print needs differently */
--bg: white;
--surface: white;
/* But colors of badges, prompts, headings stay the same */
}
.prompt-block { /* same .prompt-block style — looks identical to web */ }
.learning-goal::before { /* same green disc */ }
}
理由:最初に Web を見た学習者が、あとから PDF ハンドブックを開いたときに「同じ製品だ」と感じられるようにするためです。Web ではオレンジの PROMPT、PDF ではグレーの PROMPT だと、視覚的なブランドの一貫性が損なわれます。
企業向けエディション(course-corporate-edition)も同様です。ブランドに合わせてカスタマイズしたアセットを使う場合でも、デザイントークンは共通です。
小さく見えて、実はそうではない3つの決定
決定1:ソフトな色合いには color-mix(in oklab, X 18%, transparent) を使う
rgba(...) を手書きしないでください。色合いが元のトークンから派生するように、color-mix を使います。
background: color-mix(in oklab, var(--accent-2) 18%, transparent);
いずれ --accent-2 を再調整すると、すべての色合いにも反映されます。rgba を手動で指定すると、更新が必要な箇所が50か所も発生します。
決定2:グリッドシステムは使わず、--content-max のみを使う
12カラムのグリッドフレームワークを導入するのは避けてください。直線的に読み進める教育サイトなら、max-width: var(--content-max); margin: 0 auto で十分です。列は図版グリッドやツールギャラリーなど、特定のコンポーネント内で grid-template-columns を使って局所的に設けます。ページ自体は、ひと続きのコンテンツです。
決定3:アニメーションの予算は「ほぼゼロ」
許可されるもの:
- スクロール時の
fade-in(要素ごとに1回のみ) - ホバー時のマイクロインタラクション(色/影の変化、120~200ms)
- サイドバーのスライド(transform、200ms)
許可されないもの:
- セクション間のページ遷移
- 装飾目的のアイドルアニメーション
- 400msを超えるもの
教材は情報量が多いため、アニメーションは注意をそらすコストになります。注意力の予算は動きではなく、コンテンツに使ってください。
アンチパターン
- 「このセクションだけ」のために新しい色を作る — 既存のトークンを使うか、適切な名前のトークンを追加するようリファクタリングしてください。その場しのぎの16進数値は、システムを劣化させます。
- コンテンツの強調(PROMPT バッジなど)に
--accentを使う — 2つのアクセントの意味上の区別が失われます。--accent-2を使ってください。 - コードブロックに別のフォントを使う — タイポグラフィのセクションを参照してください。区別するには、同じフォントスタックで
font-weight/letter-spacingを使うか、視覚的な差を受け入れてください。 - コンポーネントごとにダークモードを手動でスタイルする — ダークモードのセクションを参照してください。コンポーネントに特別なダークモード対応が必要なら、どこかでトークン化されていない値を使っています。
-softバリアントを忘れる — アクセントカラー/ステータスカラーには、それぞれソフトなバリアントが必要です。これがないと、デザイナーは場当たり的な不透明度の値を使うことになります。
引き継ぎ
このスキルが別のスキル(SPA/インタラクション/ebook/企業向け)で使われる場合:
:rootのトークンを参照し、生の値は使わないでください。#...の16進数やrgb(...)を直接書いていることに気づいたら、システムを壊しています。その値は新しいトークンにするか、既存のトークンを再利用してください。- 新しいコンポーネントを作るときは、独自に考案する前に既存のパターン(glass-card + prompt-block + task-list)を組み合わせてください。
トークンファイル(:root { ... } ブロック)が唯一の信頼できる情報源です。壁に貼ってください。ブランドの進化に合わせて値を再調整しても、トークンの種類を安易に増やしてはいけません。