mcpField
H
html-presentations

html-presentations

github.com/ericmjlドキュメント4165 回インストール5 日前に更新
リポジトリ

html-presentations について

html-presentationsは、素のHTML・CSS・JavaScriptで単一HTMLファイルのスライドを作るスキルです。テーマ、キーボード操作、URLによるスライド位置の保持、SVG図表とアニメーションに対応します。

主な機能

  1. 01単一ファイルのHTMLスライド
  2. 023種類のカラーテーマ
  3. 03キーボードでのスライド操作
  4. 04URLハッシュによる位置保持
  5. 05テーマ対応のSVG図表とアニメーション

活用シーン

  1. 01ビルド手順なしでブラウザー用資料を作成
  2. 02特定のスライドを開くリンクの共有
  3. 03SVGアニメーションでプロセスを説明

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/ericmjl/skills --skill html-presentations

インストール後、Claude Code で/html-presentationsを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

assets/
canvas-chat-overview.html
example-agent-skills-overview.html
references/
svg-animations.md
svg-diagrams.md
theme-catppuccin.css
theme-nord.css
theme-terminal.css

詳細

HTML プレゼンテーション

自己完結型の HTML スライドプレゼンテーションを作成します。フレームワーク、CDN、ビルド手順は不要です。どのブラウザーでも開ける単一の .html ファイルです。

使い方

ユーザーがプレゼンテーション、スライドデッキ、またはスライドを求めており、素の HTML、CSS、JavaScript を使った単一の HTML ファイルを希望する場合に、このスキルを使用します。reveal.js やフレームワークベースのプレゼンテーションには使用しません。スライドを生成する前に、目的、対象者、提供方法について質問します(ステップ 0)。回答に基づいて、長さ、強調点、コンテンツの密度を調整します。

要件

ありません。出力は単一の .html ファイルです。最新のブラウザー(Chrome、Firefox、Safari、Edge)で動作します。

機能

  1. 単一ファイル — すべての CSS、JS、SVG 図、コンテンツを 1 つの .html ファイルに収めます
  2. テーマ設定 — 3 種類のカラープリセット:terminal.css(デフォルト)、catppuccin、nord
  3. キーボード操作 — Space/Right/k で次へ、Shift+Space/Left/j で前へ移動します。Escape を押すと、すべてのスライドを一覧できるビジュアルグリッドが開き、任意のスライドに移動できます
  4. URL ハッシュの保持 — 現在のスライド番号を URL ハッシュに保存します(例:#5)。ページを更新しても同じスライドが表示され、ハッシュ付きリンクを共有すると、そのスライドが直接開きます
  5. SVG 図 — フローチャート、アーキテクチャ図、パイプライン、比較図などを CSS カスタムプロパティでテーマ設定します
  6. SVG アニメーション — パスの描画、順次フェードイン、パルス/グロー、フローなどを、スライドがアクティブになったときに再生します

仕組み

参考例:実現できること

このスキルで作成できるものを確認するため、次のプレゼンテーション全体をご覧ください。

  1. assets/example-agent-skills-overview.html — スポットライトレイアウト、2 列のグリッド、詳細な SVG 図、ピル型バッジ、進行状況ナビゲーション、概要グリッドを備えた洗練された概要です。構成とビジュアルの品質基準として使用してください。

  2. 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.cssreferences/theme-terminal.css黒地に緑の蛍光色、等幅フォント、走査線
catppuccinreferences/theme-catppuccin.css温かみのある暗い背景(Mocha バリアント)にパステルカラーのアクセント
nordreferences/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 &mdash; 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">&#8249;</span><span class="key">j</span></div>
    <div class="progress"></div>
    <div class="nav-arrow nav-next"><span class="arrow">&#8250;</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, '&lt;') + '</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 を指定する必要があります。

プレゼンテーションの構成

適切に構成されたプレゼンテーションは、次の順序で進みます。

  1. タイトル — 名前、著者、日付
  2. メインスライド — 中心となる内容(定義、理由、方法、例など)
  3. 謝辞 — 協力者、ツール、情報源への謝辞
  4. Thank You — リンクと補足トピックの一覧を含む締めくくりのスライド
  5. 補足 — 詳細スライド、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-drawSVG パスを描画する(stroke-dashoffset)スライドがアクティブになる
.anim-pulseSVG 要素に脈動するグローを表示スライドがアクティブになる

インラインの --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 枚につきアイデア 1 つ — 詰め込みすぎないでください。スライドを増やしても問題ありません。
  2. 大きな文字 — コンテンツが少ないスライドでは、文字を大きくします。font-size: 2.5vw 以上を使用します。
  3. 箇条書きより図 — プロセス、アーキテクチャ、フローを説明するときは、箇条書きの代わりに SVG 図を使用します。
  4. 一貫した間隔 — ビューポートに合わせて間隔が拡大・縮小するよう、パディングとマージンには vh/vw 単位を使用します。
  5. 色の階層 — タイトルには --heading、本文には --fg、補足テキストには --muted、強調には --accent を使用します。
  6. コードブロック — テーマの --code-bg/--code-fg を使い、<pre><code> を使用します。コードは短く保ちます(スライド 1 枚につき 15 行未満)。
  7. スライド内の Python — Python コードを含める場合は、uv run script.py で実行できるようにして、自己完結型にし、外部のソフトウェア環境を不要にします。スクリプトの先頭に PEP 723 のインラインスクリプトメタデータ(# /// script、requires-python、dependencies、# ///)を記述し、視聴者が uv run script.py で実行することをスライドまたは発表者ノートに明記します。
  8. 外部アセットを使わない — すべてインラインにします。画像が必要な場合は、ラスター画像ではなくインライン SVG イラストを使用します。
  9. スライドを埋める — コンテンツが片隅に集まり、残りのスペースが空白になるレイアウトは避けます。2 列のグリッド(grid-template-columns: 1fr 1fr)を使ってテキストと図を横に並べ、align-items:center で両方の列を垂直方向の中央に配置します。
  10. 機能には箇条書きよりキャッチフレーズ — 機能や概念を目立たせるときは、箇条書きを短いキャッチフレーズ(<br> で区切った 2〜3 行)と、その下のキーワードピルに置き換えます。より印象的で、ひと目で把握しやすくなります。
  11. SVG を主役にする — 図を含むスライドでは、図の幅をスライドの半分以上にします。余裕のある viewBox 寸法を使い、複数のノード、ラベル、フェーズ、ワイヤーフレームの線、ループバック矢印などで空間を埋めます。余白に小さな図が浮かんでいるだけでは、未完成に見えます。

Ericのデッキ作成原則(すべてのデッキ:HTML、PPTX、コンサルティングスタイル。BCGスタイルのコンサルティングデッキ用スキルの依頼時に、2026-09-01に提示)

  1. 1枚につき重要なメッセージは1つだけにし、アクションタイトル(発見事項+そこから導かれる示唆)で表現します。トピック名だけのタイトルにはしません。
  2. 誰にでも伝わる平易な英語を使います。気取ったコンサルタント用語(シナジー、レバレッジ、全体論的など)は使いません。
  3. スライドはすっきりさせます。余白を十分に取り、コンテンツが詰まる場合はスライドを分割します。
  4. ポイントをボックスやグリッド、柱(4ボックス、2×2、6本柱など)に配置するのは、図形そのものが論拠を伝える場合に限ります。レイアウトによってメッセージがより速く伝わらないなら、文章かリストを使います。
  5. アクセントカラーは、決定的な比較を示すために1色だけ使用します。根拠を示すスライドには必ず出典を記載します。
  6. ブランドデッキでは、ブランド公式の色を使用します。第三者のまとめサイトの色一覧は使わず、ブランド自身の公開サイトのCSSで確認します(HTML形式で取得してください。取得方法は webfetch-empty-use-agent-browser を参照)。BCGのカラーパレットは、2026-09-01に bcg.com の id=global-colors のインラインスタイルブロックで確認済みです:#144622、#0C2B15、#197A56、#21BF61、#96F878。

コメント