Communitygithub.com

cdesiles/design-deck

スライドプレゼンテーションを1つの自己完結型HTMLファイル(1920×1080)として作成します。キーボードナビゲーションと1ページ1スライドの印刷に対応。デッキやプレゼンテーションを作成する前にお読みください。

design-deck とは?

このスキルは、プレゼンテーションデッキを自己完結型のHTMLファイルとして作成するためのものです。Claude CodeやCursorなどのAIエージェントと連携して、デッキの設計からマークアップ、スタイリング、検証までをガイドします。主な機能は、1920×1080の固定サイズ、キーボードナビゲーション(矢印キー、PageUp/Down、Home/End)、印刷時の1ページ1スライド表示、アクセシビリティ対応(prefers-reduced-motion)などです。スキルは、アウトライン作成、スライド構成(1スライド1アイデア)、タイポグラフィ(最小24px)、レイアウトのバリエーション、チャートのルール、コピーの編集方針を定義します。また、design-verifyツールによる検証手順も含まれています。これにより、一貫性のあるプロフェッショナルなプレゼンテーションを効率的に作成できます。

対応~Claude Code~Codex CLI~Cursor✓OpenCode
npx skills add https://github.com/cdesiles/opencode-kit/tree/HEAD/skills/design-deck

お気に入りのAIに質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

ドキュメント

デッキ

あなたはプレゼンテーションデザイナーであり、ウェブデザイナーではありません。デッキは部屋の後ろから読まれ、一度に一つのアイデアを、固定された順序で提示します。

マークアップの前に

アウトラインを書きます。1スライドにつき1行、各行はトピックではなく主張にします(「チャーン」ではなく「2月のリリース後にチャーンが倍増した」)。2行が同じことを言っている場合は、片方を削除します。根拠のない行はフラグを立てます。

時間枠が与えられていない場合は尋ねます。5分のトークは約5〜8スライド、30分のトークは約20スライドをサポートします。スライド数は分数に従い、その逆ではありません。

スタイリング

プロジェクトのトークンファイルを<head>内でリンクし、スライドがそのコンポーネントを使用する場合はcomponents.cssもリンクします。すべての色とタイプの値はそれらから取得します。

<link rel="stylesheet" href="tokens.css">

以下のスケルトンのCSSは構造のみ(スケーリング、トラック、印刷ルール)です。スライドのスタイリングにはvar(--…)を使用します。スライド内にリテラルの色がある場合は、システムにトークンがないことを意味します。回避策ではなく、トークンを追加します。

ジオメトリ

1920×1080、固定、ウィンドウに合わせてスケーリング。そのサイズで作成します(決して1440や「レスポンシブ風」ではダメ)。スケール変換でビューポートを処理させます。

すべてのスライドを一度にDOMに保持し、トラックを移動させることで、印刷時に1ページに1スライドが出力され、要素のスクリーンショットが機能します。

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>デッキタイトル</title>
<link rel="stylesheet" href="tokens.css">
<style>
  * { box-sizing: border-box }
  html, body { margin: 0; background: black }
  .viewport { position: fixed; inset: 0; display: grid; place-items: center; overflow: hidden }
  .frame { position: relative; overflow: hidden }   /* JSによってスケーリングされたステージのサイズに設定される */
  .stage { width: 1920px; height: 1080px; transform-origin: top left }
  .track { display: flex; flex-direction: column;
           transform: translateY(calc(var(--i, 0) * -1080px));
           transition: transform .35s cubic-bezier(.2, .7, .3, 1) }
  .slide { width: 1920px; height: 1080px; flex: 0 0 1080px; padding: 96px;
           display: flex; flex-direction: column }
  @media (prefers-reduced-motion: reduce) { .track { transition: none } }
  @media print {
    @page { size: 1920px 1080px; margin: 0 }
    html, body { background: white }
    .viewport { position: static; display: block; overflow: visible }
    .frame { overflow: visible; width: auto !important; height: auto !important }
    .stage { transform: none !important; width: auto; height: auto }
    .track { transform: none }
    .slide { break-after: page; page-break-after: always }
    .slide:last-child { break-after: auto; page-break-after: auto }
  }
</style>
</head>
<body>
  <div class="viewport">
    <div class="frame" id="frame">
      <div class="stage" id="stage">
        <div class="track" id="track">
          <section class="slide" data-screen-label="01 タイトル">…</section>
          <section class="slide" data-screen-label="02 問題">…</section>
        </div>
      </div>
    </div>
  </div>
  <script>
    const frame = document.getElementById('frame');
    const stage = document.getElementById('stage');
    const track = document.getElementById('track');
    const slides = [...track.children];
    let i = 0;
    function fit() {
      const s = Math.min(innerWidth / 1920, innerHeight / 1080);
      frame.style.width = `${1920 * s}px`;
      frame.style.height = `${1080 * s}px`;
      stage.style.transform = `scale(${s})`;
    }
    function go(n) {
      i = Math.max(0, Math.min(slides.length - 1, n));
      track.style.setProperty('--i', i);
    }
    addEventListener('resize', fit); fit(); go(0);
    addEventListener('keydown', (e) => {
      if (['ArrowRight', 'ArrowDown', ' ', 'PageDown'].includes(e.key)) { e.preventDefault(); go(i + 1); }
      if (['ArrowLeft', 'ArrowUp', 'PageUp'].includes(e.key)) { e.preventDefault(); go(i - 1); }
      if (e.key === 'Home') go(0);
      if (e.key === 'End') go(slides.length - 1);
    });
  </script>
</body>
</html>

2つのコンテナを使うトリックは重要です。フレームはスケーリングされた寸法にサイズ設定されるため、ステージがビューポートからはみ出さず、デッキが中央に保たれ、検証ツールのオーバーフローチェックで偽の問題が報告されません。単一のスケーリング要素に単純化してはいけません。

data-screen-labelは、検証ツールが各フレームを命名する際の名前です。番号を振ってください。

スライド作成の技術

  • 1スライドにつき1つのアイデア。 本文に説明の文章+チャート+3つの箇条書きが必要な場合、それは2つのスライドです。
  • 見出しが仕事をする。 見出しは主張を述べ、本文はそれをサポートします。すべての見出しが名詞であるデッキは、議論ではなくアジェンダです。
  • タイプの最小サイズ: 24px。 本文は32〜36pxが標準。見出しは64〜110px。1440pxモニターで大きすぎると感じても、部屋の後ろではまだ小さすぎます。
  • デッキ全体で背景色は1〜2色。 セクションの区切りはレイアウトで変化をつけ、新しいグラデーションを発明しない。
  • リズムに変化をつける。 すべてのスライドが「タイトル+3つのカード」ではない。重要な数字にはフルブリードのスライドを。1つの画像や引用を端から端まで配置する。
  • 箇条書きの箇条書きは禁止。 1グループあたり最大3項目、各行1行。
  • チャートには、その知見を述べるタイトル、24pxの軸ラベル、そして1つの強調された系列をつける。決して虹色パレットは使わない。
  • ページ番号はすべてのスライドで同じ位置に、控えめに、小さく配置する。

コピー

決して相手のテキストを書き換えてはいけません。言い換えるのではなく、削ることで引き締めます。ソース素材が薄いためにスライドが薄くなる場合は、内容を捏造するのではなく、その旨を伝えます。

仕上げ

design-verifyをスライドごとのフレームで実行します。

node ~/.config/opencode/lib/design/verify.mjs "design/Deck.html" \
  --each "section" --step --each-out design/.verify/deck --out design/.verify/deck.png

--stepは矢印キーでデッキをめくり、各スライドのビューポートをキャプチャします。これはプレゼンターが見るもの(スケーリングを含む)とまったく同じです。

すべてのフレームを開きます。確認する点:端でクリップされていないか、見出しが不格好に折り返されていないか、24px未満のテキストがないか、マージンが一貫しているか、ハイライトカラーがすべての場所で同じように使用されているか。

Individual skills in this repo

This repo contains 4 individual skills — each has its own dedicated page.

関連スキル