html-presentations について
html-presentationsは、素のHTML・CSS・JavaScriptで単一HTMLファイルのスライドを作るスキルです。テーマ、キーボード操作、URLによるスライド位置の保持、SVG図表とアニメーションに対応します。
主な機能
- 01単一ファイルのHTMLスライド
- 023種類のカラーテーマ
- 03キーボードでのスライド操作
- 04URLハッシュによる位置保持
- 05テーマ対応のSVG図表とアニメーション
活用シーン
- 01ビルド手順なしでブラウザー用資料を作成
- 02特定のスライドを開くリンクの共有
- 03SVGアニメーションでプロセスを説明
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ ericmjl/ skills --skill html-presentationsインストール後、Claude Code で/html-presentationsを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
assets/canvas-chat-overview.htmlexample-agent-skills-overview.htmlreferences/svg-animations.mdsvg-diagrams.mdtheme-catppuccin.csstheme-nord.csstheme-terminal.css詳細
HTML プレゼンテーション
自己完結型の HTML スライドプレゼンテーションを作成します。フレームワーク、CDN、ビルド手順は不要です。どのブラウザーでも開ける単一の .html ファイルです。
使い方
ユーザーがプレゼンテーション、スライドデッキ、またはスライドを求めており、素の HTML、CSS、JavaScript を使った単一の HTML ファイルを希望する場合に、このスキルを使用します。reveal.js やフレームワークベースのプレゼンテーションには使用しません。スライドを生成する前に、目的、対象者、提供方法について質問します(ステップ 0)。回答に基づいて、長さ、強調点、コンテンツの密度を調整します。
要件
ありません。出力は単一の .html ファイルです。最新のブラウザー(Chrome、Firefox、Safari、Edge)で動作します。
機能
- 単一ファイル — すべての CSS、JS、SVG 図、コンテンツを 1 つの
.htmlファイルに収めます - テーマ設定 — 3 種類のカラープリセット:terminal.css(デフォルト)、catppuccin、nord
- キーボード操作 — Space/Right/k で次へ、Shift+Space/Left/j で前へ移動します。Escape を押すと、すべてのスライドを一覧できるビジュアルグリッドが開き、任意のスライドに移動できます
- URL ハッシュの保持 — 現在のスライド番号を URL ハッシュに保存します(例:
#5)。ページを更新しても同じスライドが表示され、ハッシュ付きリンクを共有すると、そのスライドが直接開きます - SVG 図 — フローチャート、アーキテクチャ図、パイプライン、比較図などを CSS カスタムプロパティでテーマ設定します
- SVG アニメーション — パスの描画、順次フェードイン、パルス/グロー、フローなどを、スライドがアクティブになったときに再生します
仕組み
参考例:実現できること
このスキルで作成できるものを確認するため、次のプレゼンテーション全体をご覧ください。
-
assets/example-agent-skills-overview.html — スポットライトレイアウト、2 列のグリッド、詳細な SVG 図、ピル型バッジ、進行状況ナビゲーション、概要グリッドを備えた洗練された概要です。構成とビジュアルの品質基準として使用してください。
-
assets/canvas-chat-overview.html — ループするインタラクティブなスライドデモを備えた製品概要です:
- ミニキャンバス — スライド上でノードをドラッグでき、SVG のエッジがリアルタイムに更新されます
- チャットと返信 — タイピング、ノードの表示、エッジの描画をシミュレートし、自動的にループします
- ハイライトと分岐 — テキストの選択、抜粋ノード、ソースのハイライトをシミュレートしてループします
- 複数選択 — ノードの選択、返信、「考え中…」、統合ノードをシミュレートしてループします
- 自動レイアウト — ツールバーの「Layout」ボタンへカーソルが移動する様子、クリック時のサンバースト/光線、ノードが乱雑な状態から整った状態へアニメーションする様子をシミュレートします。リセットしてループします
- ナビゲーション — カーソルとスクロールのヒントをシミュレートします。セマンティックズームで拡大(ノードのコンテンツ全体を表示)し、縮小(要約ビューを表示)します。ループします
- URL ハッシュの保持 — ページを更新しても現在のスライドが表示されます。
slidechangeはsetTimeout(0)でディスパッチされるため、デモスクリプトを読み込み時に実行できます
静的な図ではなく、自動でループするデモ(カーソル、クリック効果、ズーム、または段階的な UI シミュレーション)をユーザーが求めている場合は、Canvas Chat の概要を使用してください。
ステップ 0:ユーザーに確認する(作成前)
ユーザーに次の内容を確認するまで、スライドを生成しないでください。 会話の中で自然に尋ねてください。メッセージごとに1〜2問ずつでもかまいません。回答を参考に、長さ、強調する点、テキストと図の比率を決めてください。
(a)プレゼンテーションの目的
ユーザーが明確な目的を伝えていない場合は、選択肢を提示して、選んでもらうか、組み合わせてもらいます。
- 提案/説得 — アイデア、製品、または決定について、聴衆を納得させる
- 説明/教育 — 概念、プロセス、またはシステムを伝え、聴衆に理解してもらう
- 更新/状況報告 — 進捗、結果、または調査結果を共有する(例:プロジェクトの更新、実験結果)
- 参考資料/配布資料 — 後で見返せるように保存する資料。情報量は多くても、ざっと確認しやすい
- 概要/ロードマップ — 全体像や計画を示す。詳細は最小限にして、全体像を重視する
- その他 — 内容を自由に説明してもらう
(b)対象となる聴衆
誰が見るのかを尋ねてください(例:経営幹部、エンジニア、顧客、学生、一般の人々)。専門用語のレベル、詳しさ、説明する内容と前提とする内容を決めるうえで役立ちます。
(c)発表形式
- 発表者あり — スライドは発表を補助します。各スライドのテキストを減らし、ビジュアルを増やして、発表者が内容を補足します。短い箇条書きと図を優先してください。
- キオスク形式(発表者なし) — 聴衆が自分で読みます。スライドだけで内容が伝わるようにする必要があります。説明文を増やし、ラベルをわかりやすくし、スライドの枚数を増やしたり、情報量を多くしたりする場合があります。
(d)その他の役立つ質問
- おおよその長さ — 「数分程度ですか?」「10〜15分ですか?」「じっくり掘り下げますか?」など、スライドの枚数と詳しさを調整します。
- 必須事項と省略事項 — 「必ず含めたい内容や、省略したい内容はありますか?」
- トーン — 表現やデザインに影響する場合は、フォーマルかカジュアルか、真剣か軽めかを尋ねます。
ユーザーの回答を記録し、テーマの選択、アウトラインの作成、各スライドに盛り込む情報量の決定に活用してください。
ステップ 1:テーマを選ぶ
テーマのリファレンスファイルを1つ読み、その CSS をプレゼンテーションの <style> ブロックに貼り付けます。ユーザーが別のテーマを希望しない限り、デフォルトは catppuccin です。
| テーマ | ファイル | デザイン |
|---|---|---|
| terminal.css | references/theme-terminal.css | 黒地に緑の蛍光色、等幅フォント、走査線 |
| catppuccin | references/theme-catppuccin.css | 温かみのある暗い背景(Mocha バリアント)にパステルカラーのアクセント |
| nord | references/theme-nord.css | 北極を思わせる青みがかったグレー、落ち着いた色調 |
すべてのテーマで、同じ CSS カスタムプロパティ(--bg、--fg、--accent など)が定義されています。そのため、ベースレイアウト CSS とすべての図は、どのテーマでも動作します。
ステップ 2:HTML ファイルを生成する
次の骨組みを使用してください。3つのセクション、テーマ CSS、ベースレイアウト CSS、ナビゲーション JS は常に同じ構造で、スライドの内容だけが変わります。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PRESENTATION_TITLE</title>
<style>
/* ===== Theme (paste from reference file) ===== */
:root { /* ... theme variables ... */ }
/* ===== Base layout ===== */
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
html, body { height: 100%; overflow: hidden; }
body {
background: var(--bg);
color: var(--fg);
font-family: var(--body-font);
}
.deck { position: relative; width: 100vw; height: 100vh; overflow: hidden; }
.slide {
position: absolute;
inset: 0;
display: flex;
flex-direction: column;
justify-content: center;
padding: 6vh 8vw;
opacity: 0;
transition: opacity 0.5s ease;
pointer-events: none;
overflow: hidden;
}
.slide.active {
opacity: 1;
pointer-events: auto;
}
/* ===== Typography ===== */
h1 { font-size: 3.5vw; font-family: var(--heading-font); color: var(--heading); margin-bottom: 2vh; }
h2 { font-size: 2.8vw; font-family: var(--heading-font); color: var(--heading); margin-bottom: 2vh; }
h3 { font-size: 2.2vw; font-family: var(--heading-font); color: var(--heading); margin-bottom: 1.5vh; }
p, li { font-size: 1.6vw; line-height: 1.7; margin-bottom: 1vh; }
ul, ol { padding-left: 2vw; }
code {
font-family: var(--mono-font);
background: var(--surface);
padding: 0.15em 0.4em;
border-radius: 4px;
font-size: 0.9em;
}
pre {
background: var(--surface);
padding: 2vh 2vw;
border-radius: 8px;
overflow-x: auto;
}
pre code { background: none; padding: 0; font-size: 1.3vw; }
/* ===== Bottom nav bar (arrows + progress dots) ===== */
.nav-bar {
position: fixed;
bottom: 2.5vh;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 12px;
align-items: center;
z-index: 100;
}
.progress {
display: flex;
gap: 6px;
align-items: center;
}
.progress-dot {
width: 8px;
height: 8px;
border-radius: 4px;
background: var(--muted);
transition: width 0.35s ease, background 0.35s ease;
cursor: pointer;
}
.progress-dot.active {
width: 32px;
background: var(--accent);
}
.nav-arrow {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 36px;
height: 32px;
background: var(--surface);
border: 1px solid var(--border);
border-radius: 6px;
color: var(--muted);
cursor: pointer;
transition: color 0.2s, border-color 0.2s;
user-select: none;
flex-shrink: 0;
}
.nav-arrow:hover {
color: var(--accent);
border-color: var(--accent);
}
.nav-arrow .arrow {
font-size: 1vw;
line-height: 1;
font-family: var(--body-font);
}
.nav-arrow .key {
font-size: 0.55vw;
font-family: var(--mono-font);
opacity: 0.7;
}
/* ===== Overview grid (Escape) ===== */
.overview-overlay {
position: fixed;
inset: 0;
background: var(--bg);
z-index: 200;
display: none;
align-items: center;
justify-content: center;
padding: 3vh 3vw;
overflow: auto;
}
.overview-overlay.visible {
display: flex;
flex-direction: column;
}
.overview-overlay .overview-hint {
font-size: 1.2vw;
color: var(--muted);
margin-bottom: 2vh;
}
.overview-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
gap: 1rem;
max-width: 90vw;
}
.overview-card {
background: var(--surface);
border: 2px solid var(--border);
border-radius: 8px;
padding: 1rem;
cursor: pointer;
text-align: center;
transition: border-color 0.2s, transform 0.2s;
}
.overview-card:hover {
border-color: var(--accent);
transform: scale(1.02);
}
.overview-card.active {
border-color: var(--accent);
background: var(--surface2);
}
.overview-card .num {
font-size: 1.5vw;
font-weight: 700;
color: var(--accent);
display: block;
}
.overview-card .title {
font-size: 0.9vw;
margin-top: 0.5rem;
color: var(--fg);
line-height: 1.3;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
/* ===== Supplementary slides ===== */
.supp-label {
display: inline-block; font-size: 0.85vw; text-transform: uppercase;
letter-spacing: 0.15em; color: var(--muted); border: 1px solid var(--border);
border-radius: 4px; padding: 0.2em 0.7em; margin-bottom: 2vh;
}
.supp-link {
color: var(--accent); text-decoration: none;
border-bottom: 1px dashed var(--accent); cursor: pointer;
}
.supp-link:hover { border-bottom-style: solid; }
.progress-sep {
width: 1px; height: 12px; background: var(--border); margin: 0 4px; flex-shrink: 0;
}
.progress-dot.supplementary { opacity: 0.5; }
.progress-dot.supplementary.active { opacity: 1; }
.overview-card.supplementary { opacity: 0.7; }
/* ===== Diagram base classes ===== */
.diagram-node { fill: var(--diagram-fill); stroke: var(--diagram-stroke); stroke-width: 2; }
.diagram-highlight { fill: var(--diagram-highlight); stroke: var(--diagram-highlight); stroke-width: 2; }
.diagram-diamond { fill: var(--surface2); stroke: var(--diagram-stroke); stroke-width: 2; }
.diagram-label { fill: var(--diagram-text); font-family: var(--body-font); font-size: 14px; text-anchor: middle; dominant-baseline: central; }
.diagram-label-inv { fill: var(--bg); font-family: var(--body-font); font-size: 14px; text-anchor: middle; dominant-baseline: central; }
.diagram-label-sm { fill: var(--diagram-text); font-family: var(--body-font); font-size: 11px; text-anchor: middle; dominant-baseline: central; opacity: 0.7; }
.diagram-edge { stroke: var(--diagram-arrow); stroke-width: 2; fill: none; }
.diagram-edge-accent { stroke: var(--diagram-stroke); stroke-width: 2; fill: none; }
.slide svg { max-width: 100%; height: auto; }
/* ===== Animation base classes ===== */
.slide .anim-fade {
opacity: 0;
transform: translateY(20px);
}
.slide.active .anim-fade {
opacity: 1;
transform: translateY(0);
transition: opacity 0.6s ease var(--delay, 0s),
transform 0.6s ease var(--delay, 0s);
}
.slide .anim-draw {
stroke-dasharray: var(--len, 1000);
stroke-dashoffset: var(--len, 1000);
}
.slide.active .anim-draw {
stroke-dashoffset: 0;
transition: stroke-dashoffset 1.2s ease-in-out var(--delay, 0s);
}
@keyframes pulse-glow {
0%, 100% { filter: drop-shadow(0 0 3px var(--diagram-stroke)); }
50% { filter: drop-shadow(0 0 10px var(--diagram-stroke)); }
}
.slide.active .anim-pulse {
animation: pulse-glow 2s ease-in-out infinite;
animation-delay: var(--delay, 0s);
}
/* Additional theme-specific rules (paste from theme file if any) */
</style>
</head>
<body>
<div class="deck">
<section class="slide" id="title">
<h1>Presentation Title</h1>
<p>Author — Date</p>
</section>
<section class="slide" id="slide-2">
<h2>Slide Heading</h2>
<ul>
<li>Point one</li>
<li>Point two</li>
</ul>
</section>
</div>
<div class="nav-bar">
<div class="nav-arrow nav-prev"><span class="arrow">‹</span><span class="key">j</span></div>
<div class="progress"></div>
<div class="nav-arrow nav-next"><span class="arrow">›</span><span class="key">k</span></div>
</div>
<div class="overview-overlay" id="overview" aria-label="Slide overview">
<p class="overview-hint">Click a slide to jump · Esc to close</p>
<div class="overview-grid"></div>
</div>
<script>
(function () {
var slides = document.querySelectorAll('.slide');
var current = 0;
var total = slides.length;
var progress = document.querySelector('.progress');
var prevBtn = document.querySelector('.nav-prev');
var nextBtn = document.querySelector('.nav-next');
var overview = document.getElementById('overview');
var overviewGrid = overview.querySelector('.overview-grid');
var suppInserted = false;
for (var i = 0; i < total; i++) {
if (!suppInserted && slides[i].hasAttribute('data-supplementary')) {
var sep = document.createElement('div');
sep.className = 'progress-sep';
progress.appendChild(sep);
suppInserted = true;
}
var dot = document.createElement('div');
dot.className = 'progress-dot';
if (slides[i].hasAttribute('data-supplementary')) dot.classList.add('supplementary');
dot.dataset.index = i;
dot.addEventListener('click', function () {
show(parseInt(this.dataset.index));
});
progress.appendChild(dot);
}
var dots = progress.querySelectorAll('.progress-dot');
for (var i = 0; i < total; i++) {
var card = document.createElement('button');
card.type = 'button';
card.className = 'overview-card';
if (slides[i].hasAttribute('data-supplementary')) card.classList.add('supplementary');
card.dataset.index = i;
var titleEl = slides[i].querySelector('h1, h2, h3');
var title = titleEl ? titleEl.textContent.trim() : 'Slide ' + (i + 1);
card.innerHTML = '<span class="num">' + (i + 1) + '</span><span class="title">' + title.replace(/</g, '<') + '</span>';
card.addEventListener('click', function () {
show(parseInt(this.dataset.index));
overview.classList.remove('visible');
});
overviewGrid.appendChild(card);
}
var overviewCards = overviewGrid.querySelectorAll('.overview-card');
var slideIdMap = {};
for (var i = 0; i < total; i++) {
if (slides[i].id) slideIdMap[slides[i].id] = i;
}
function getSlideIndexFromHash() {
var hash = window.location.hash;
if (!hash || hash.length < 2) return 0;
var n = parseInt(hash.slice(1), 10);
if (isNaN(n) || n < 0 || n >= total) return 0;
return n;
}
function updateHashForSlide(idx) {
var url = window.location.pathname + window.location.search + '#' + idx;
if (window.history.replaceState) {
window.history.replaceState(null, '', url);
} else {
window.location.hash = idx;
}
}
function show(idx) {
if (idx < 0 || idx >= total) return;
slides[current].classList.remove('active');
dots[current].classList.remove('active');
if (overviewCards[current]) overviewCards[current].classList.remove('active');
current = idx;
slides[current].classList.add('active');
dots[current].classList.add('active');
if (overviewCards[current]) overviewCards[current].classList.add('active');
updateHashForSlide(current);
}
document.addEventListener('click', function (e) {
var link = e.target.closest('[data-goto]');
if (link) {
e.preventDefault();
var target = link.getAttribute('data-goto');
if (slideIdMap[target] !== undefined) show(slideIdMap[target]);
}
});
prevBtn.addEventListener('click', function () { show(current - 1); });
nextBtn.addEventListener('click', function () { show(current + 1); });
document.addEventListener('keydown', function (e) {
if (e.key === 'Escape') {
overview.classList.toggle('visible');
if (overview.classList.contains('visible')) {
for (var i = 0; i < overviewCards.length; i++) {
overviewCards[i].classList.toggle('active', i === current);
}
}
return;
}
if (overview.classList.contains('visible')) return;
switch (e.key) {
case ' ':
e.preventDefault();
show(current + (e.shiftKey ? -1 : 1));
break;
case 'ArrowRight': case 'k':
show(current + 1);
break;
case 'ArrowLeft': case 'j':
show(current - 1);
break;
}
});
window.addEventListener('hashchange', function () {
var idx = getSlideIndexFromHash();
if (idx !== current) show(idx);
});
show(getSlideIndexFromHash());
})();
</script>
</body>
</html>
Step 3: スライドの内容を追加する
タイトルスライド — 中央揃えの大きな見出しとサブタイトル:
<section class="slide" id="title">
<h1>Title Text</h1>
<p>Subtitle or author</p>
</section>
コンテンツスライド — 見出しと本文:
<section class="slide" id="topic-name">
<h2>Heading</h2>
<p>Body text or a list:</p>
<ul>
<li>Item</li>
</ul>
</section>
2 列スライド — インライン CSS グリッドを使用します:
<section class="slide" id="comparison">
<h2>Side by Side</h2>
<div style="display:grid; grid-template-columns:1fr 1fr; gap:4vw; flex:1;">
<div>Left column content</div>
<div>Right column content</div>
</div>
</section>
図解スライド — 図解用クラスを使用したインライン SVG:
<section class="slide" id="architecture">
<h2>System Architecture</h2>
<svg viewBox="0 0 800 400">
</svg>
</section>
スポットライト/機能紹介スライド — 左側にテキスト、右側に大きな SVG を配置した 2 列グリッドです。視覚的に訴求力のある形で、ツール、機能、コンセプトを紹介する場合に使用します:
<section class="slide" id="spotlight-feature" style="justify-content:center;">
<div style="display:grid; grid-template-columns:1fr 1fr; gap:4vw; align-items:center;">
<div>
<p class="dim anim-fade" style="--delay:0.1s; font-size:1vw; text-transform:uppercase; letter-spacing:0.2em; margin-bottom:1vh;">Label</p>
<h2 class="anim-fade" style="--delay:0.2s; font-size:3.2vw;"><code style="background:var(--surface); padding:0.15em 0.4em; border-radius:6px;">Feature Name</code></h2>
<p class="anim-fade" style="--delay:0.4s; font-size:2.4vw; color:var(--accent); margin-top:2vh; line-height:1.3;">Short tagline.<br>One line per idea.</p>
<div class="anim-fade" style="--delay:0.7s; display:flex; gap:0.8vw; flex-wrap:wrap; margin-top:3vh;">
<span style="background:var(--accent); color:white; padding:0.4em 1em; border-radius:999px; font-size:1.1vw; font-weight:600;">Primary pill</span>
<span style="background:var(--surface); color:var(--fg); padding:0.4em 1em; border-radius:999px; font-size:1.1vw;">Secondary pill</span>
<span style="background:var(--surface); color:var(--fg); padding:0.4em 1em; border-radius:999px; font-size:1.1vw;">Another pill</span>
</div>
</div>
<div class="anim-fade" style="--delay:0.5s;">
<svg viewBox="0 0 400 340">
</svg>
</div>
</div>
</section>
スポットライトのデザイン原則:
- 両方の列を埋めます。 SVG の
viewBoxは余裕のあるサイズ(例:0 0 400 340)にして、図がスライドの右半分を埋めるようにします。余白が多すぎる小さな SVG は未完成に見えます。 - 箇条書きは使いません。 箇条書きリストの代わりに、短いキャッチコピー(
<br>で改行)と、その下にキーワードのピルを配置します。 - 強調するピルは 1 つにします。 主要なキーワードには
background:var(--accent); color:white; font-weight:600を使用し、残りはニュートラルな色(var(--surface)/var(--fg))にします。 - 情報量のある図解にします。 右列の SVG でストーリーを伝えます。複数のノード、矢印、ラベル、フェーズ、または小さなワイヤーフレームを使用します。スライドの主役は図解です。
補足スライド — 詳細、FAQ、または参考資料のスライドを、Thank You スライドの後に配置します。メインスライドから data-goto でリンクします:
<section class="slide" id="supp-topic" data-supplementary>
<span class="supp-label">Supplementary</span>
<h2>Detailed Topic</h2>
<p>Deep-dive content that supports a main slide.</p>
</section>
補足スライドの規則:
- Thank You の後に配置します。 プレゼンテーションの流れは、メインスライド → 謝辞 → Thank You → 補足です。聴衆にはすっきりとした締めくくりを示し、補足資料は質疑応答や自習で利用できるようにします。
data-supplementaryを付けます。 この属性を<section>に追加します。JS はこれを使ってプログレスバーに視覚的な区切りを挿入し、補足用のドットを暗く表示します。<span class="supp-label">を追加します。 スライド上部に「Supplementary」または「Supplementary · FAQ」のバッジを表示します。- メインスライドからリンクします。 任意のメインスライドで
<a class="supp-link" data-goto="supp-topic">Detail →</a>を使用します。JS がクリックを処理し、ID で指定した対象スライドに移動します。 - Thank You からリンクします。 聴衆が利用可能な資料を把握できるよう、Thank You スライドにすべての補足スライドへのリンクを一覧表示します。
- ID の規則。 補足スライドの ID には
supp-を接頭辞として付けます(例:supp-detail、supp-harness)。
必須の CSS(上記のベースレイアウトにすでに含まれています):
.supp-label {
display: inline-block; font-size: 0.85vw; text-transform: uppercase;
letter-spacing: 0.15em; color: var(--muted); border: 1px solid var(--border);
border-radius: 4px; padding: 0.2em 0.7em; margin-bottom: 2vh;
}
.supp-link {
color: var(--accent); text-decoration: none;
border-bottom: 1px dashed var(--accent); cursor: pointer;
}
.supp-link:hover { border-bottom-style: solid; }
.progress-sep {
width: 1px; height: 12px; background: var(--border); margin: 0 4px; flex-shrink: 0;
}
.progress-dot.supplementary { opacity: 0.5; }
.progress-dot.supplementary.active { opacity: 1; }
必須の JS 追加項目(上記のベーススクリプトにすでに含まれています):
- 各スライドの
idとインデックスを対応付けるslideIdMapを作成します。 data-gotoリンクのクリック時にshow(slideIdMap[target])を呼び出します。- プログレスドットの生成時に、最初の補足スライドの前に
.progress-sep<div>を挿入します。 data-supplementaryが指定されたスライドのドットと概要カードに.supplementaryクラスを追加します。- URL ハッシュの保持: 現在のスライドインデックスを URL ハッシュに保存します(例:
#5)。ページの読み込み時にはshow(0)の代わりにshow(getSlideIndexFromHash())を呼び出し、ページを再読み込みしても同じスライドが表示されるようにします。show(idx)内でupdateHashForSlide(current)を呼び出し(利用可能な場合はhistory.replaceStateを使用)、URL が常に現在のスライドを反映するようにします。ユーザーが URL を編集したり、ブラウザーの戻る/進むを使用したりしたときにデッキを同期するため、hashchangeリスナーを追加します。
すべての <section> に一意の id を指定する必要があります。
プレゼンテーションの構成
適切に構成されたプレゼンテーションは、次の順序で進みます。
- タイトル — 名前、著者、日付
- メインスライド — 中心となる内容(定義、理由、方法、例など)
- 謝辞 — 協力者、ツール、情報源への謝辞
- Thank You — リンクと補足トピックの一覧を含む締めくくりのスライド
- 補足 — 詳細スライド、FAQ、参考資料(メインスライドと Thank You スライドからリンク)
プログレスバーでは、細い縦線を使ってメイン部分と補足部分を視覚的に区切ります。
ステップ 4:SVG 図を追加する
完全なパターンについては、references/svg-diagrams.md をお読みください。
- フローチャート — 判断ツリー、ボックス・ひし形・矢印を使ったプロセスフロー
- アーキテクチャ図 — レイヤー構造、接続されたサービス
- パイプライン図 — 処理ステージを通過するデータの流れ
- 比較図 — 機能や選択肢を並べて比較
すべての図の SVG 要素は、ベースレイアウトで定義された .diagram-* CSS クラスを使用するため、アクティブなテーマに自動的に合致します。
矢印マーカー — 矢印を使うすべての SVG には、この <defs> ブロックが必要です。
<defs>
<marker id="arrow" markerWidth="10" markerHeight="7"
refX="9" refY="3.5" orient="auto-start-reverse">
<polygon points="0 0, 10 3.5, 0 7" fill="var(--diagram-arrow)" />
</marker>
<marker id="arrow-accent" markerWidth="10" markerHeight="7"
refX="9" refY="3.5" orient="auto-start-reverse">
<polygon points="0 0, 10 3.5, 0 7" fill="var(--diagram-stroke)" />
</marker>
<filter id="shadow" x="-5%" y="-5%" width="110%" height="120%">
<feDropShadow dx="0" dy="2" stdDeviation="3" flood-opacity="0.15" />
</filter>
</defs>
.diagram-edge の線には marker-end="url(#arrow)" を、.diagram-edge-accent の線には marker-end="url(#arrow-accent)" を使用します。
ステップ 5:適切な箇所に SVG アニメーションを追加する
完全なパターンと、各アニメーションの種類をいつ使用するのが適切かについてのガイダンスは、references/svg-animations.md をお読みください。
組み込みのアニメーションクラスは 3 つあります(ベース CSS で定義されています)。
| クラス | 効果 | トリガー |
|---|---|---|
.anim-fade | フェードしながら表示位置まで上昇 | スライドがアクティブになる |
.anim-draw | SVG パスを描画する(stroke-dashoffset) | スライドがアクティブになる |
.anim-pulse | SVG 要素に脈動するグローを表示 | スライドがアクティブになる |
インラインの --delay でタイミングをずらします。
<p class="anim-fade" style="--delay:0.2s">Appears first</p>
<p class="anim-fade" style="--delay:0.5s">Appears second</p>
SVG パスの描画では、パスの全長を --len に設定します。
<path class="diagram-edge anim-draw" style="--len:350"
d="M 50,100 C 150,50 250,150 350,100" />
アニメーションを使用する場面(概要 — 詳細なガイダンスはリファレンスファイルを参照してください)。
- パスの描画:接続の確立、ネットワークトポロジー、図の組み立てを示す場合
- 順次フェードイン:概念を段階的に組み立てる場合、ストーリー性のあるリストを示す場合
- パルス/グロー:プロセスの現在のステップを強調する場合、重要なノードに注意を引く場合
- 装飾のみの要素、すべての箇条書き、または視聴者がすぐに読む必要のあるコンテンツはアニメーション化しないでください
デザインガイドライン
- スライド 1 枚につきアイデア 1 つ — 詰め込みすぎないでください。スライドを増やしても問題ありません。
- 大きな文字 — コンテンツが少ないスライドでは、文字を大きくします。
font-size: 2.5vw以上を使用します。 - 箇条書きより図 — プロセス、アーキテクチャ、フローを説明するときは、箇条書きの代わりに SVG 図を使用します。
- 一貫した間隔 — ビューポートに合わせて間隔が拡大・縮小するよう、パディングとマージンには
vh/vw単位を使用します。 - 色の階層 — タイトルには
--heading、本文には--fg、補足テキストには--muted、強調には--accentを使用します。 - コードブロック — テーマの
--code-bg/--code-fgを使い、<pre><code>を使用します。コードは短く保ちます(スライド 1 枚につき 15 行未満)。 - スライド内の Python — Python コードを含める場合は、
uv run script.pyで実行できるようにして、自己完結型にし、外部のソフトウェア環境を不要にします。スクリプトの先頭に PEP 723 のインラインスクリプトメタデータ(# /// script、requires-python、dependencies、# ///)を記述し、視聴者がuv run script.pyで実行することをスライドまたは発表者ノートに明記します。 - 外部アセットを使わない — すべてインラインにします。画像が必要な場合は、ラスター画像ではなくインライン SVG イラストを使用します。
- スライドを埋める — コンテンツが片隅に集まり、残りのスペースが空白になるレイアウトは避けます。2 列のグリッド(
grid-template-columns: 1fr 1fr)を使ってテキストと図を横に並べ、align-items:centerで両方の列を垂直方向の中央に配置します。 - 機能には箇条書きよりキャッチフレーズ — 機能や概念を目立たせるときは、箇条書きを短いキャッチフレーズ(
<br>で区切った 2〜3 行)と、その下のキーワードピルに置き換えます。より印象的で、ひと目で把握しやすくなります。 - SVG を主役にする — 図を含むスライドでは、図の幅をスライドの半分以上にします。余裕のある
viewBox寸法を使い、複数のノード、ラベル、フェーズ、ワイヤーフレームの線、ループバック矢印などで空間を埋めます。余白に小さな図が浮かんでいるだけでは、未完成に見えます。
Ericのデッキ作成原則(すべてのデッキ:HTML、PPTX、コンサルティングスタイル。BCGスタイルのコンサルティングデッキ用スキルの依頼時に、2026-09-01に提示)
- 1枚につき重要なメッセージは1つだけにし、アクションタイトル(発見事項+そこから導かれる示唆)で表現します。トピック名だけのタイトルにはしません。
- 誰にでも伝わる平易な英語を使います。気取ったコンサルタント用語(シナジー、レバレッジ、全体論的など)は使いません。
- スライドはすっきりさせます。余白を十分に取り、コンテンツが詰まる場合はスライドを分割します。
- ポイントをボックスやグリッド、柱(4ボックス、2×2、6本柱など)に配置するのは、図形そのものが論拠を伝える場合に限ります。レイアウトによってメッセージがより速く伝わらないなら、文章かリストを使います。
- アクセントカラーは、決定的な比較を示すために1色だけ使用します。根拠を示すスライドには必ず出典を記載します。
- ブランドデッキでは、ブランド公式の色を使用します。第三者のまとめサイトの色一覧は使わず、ブランド自身の公開サイトのCSSで確認します(HTML形式で取得してください。取得方法は webfetch-empty-use-agent-browser を参照)。BCGのカラーパレットは、2026-09-01に bcg.com の id=global-colors のインラインスタイルブロックで確認済みです:#144622、#0C2B15、#197A56、#21BF61、#96F878。