Communitygithub.com

cdesiles/design-deck

하나의 독립된 HTML 파일로 1920x1080 프레젠테이션 데크를 빌드합니다. 키보드 탐색과 슬라이드별 인쇄를 지원합니다. 데크나 프레젠테이션을 만들기 전에 반드시 읽어야 합니다.

design-deck란 무엇인가요?

이 스킬은 1920x1080 해상도의 단일 HTML 파일 형식으로 프레젠테이션 슬라이드 데크를 제작하기 위한 명세와 템플릿을 제공합니다. 웹 디자인이 아닌 프레젠테이션 디자인에 초점을 맞추며, 뒤쪽 좌석에서도 읽을 수 있도록 고정된 레이아웃을 유지합니다. 주요 기능으로는 키보드 방향키, 스페이스바, Home/End 키를 통한 슬라이드 탐색, 인쇄 시 각 슬라이드가 한 페이지씩 출력되도록 하는 CSS 인쇄 규칙, 그리고 창 크기 변경에 대응하는 스케일링을 포함합니다. 또한 'design-verify' 도구와 연동하여 각 슬라이드의 시각적 검증을 지원하며, 디자인 토큰 파일을 활용한 일관된 스타일링, 슬라이드당 하나의 아이디어 원칙, 최소 24px 텍스트 크기, 페이지 번호 표시 등 프레젠테이션 품질을 높이기 위한 세부 지침을 제공합니다. 이 스킬은 Claude Code, Cursor 등 AI 코딩 어시스턴트에서 데크 작성을 의뢰할 때 기본 규칙으로 적용됩니다.

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

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

데크

당신은 웹 디자이너가 아닌 프레젠테이션 디자이너입니다. 데크는 방 뒷좌석에서 읽히며, 한 번에 하나의 아이디어를, 고정된 순서로 전달합니다.

마크업 전

개요를 작성하세요. 슬라이드당 한 줄로, 주제가 아닌 주장을 적으세요(예: "2월 릴리스 후 이탈률이 두 배로 늘었다" — "이탈률"이 아님). 두 줄이 같은 내용을 말한다면 하나를 삭제하세요. 근거가 없는 줄은 표시해두세요.

시간 예산이 주어지지 않았다면 물어보세요: 5분 발표에는 대략 5~8개의 슬라이드, 30분에는 20개 정도가 적당합니다. 슬라이드 수는 분에 맞추어야 하지, 그 반대가 아닙니다.

스타일링

프로젝트의 토큰 파일을 <head>에 링크하고, 슬라이드가 해당 컴포넌트를 사용한다면 components.css도 함께 링크하세요. 모든 색상과 타이포그래피 값은 토큰 파일에서 가져옵니다:

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

아래 스켈레톤의 CSS는 구조 전용입니다 — 스케일링, 트랙, 인쇄 규칙. 슬라이드 스타일링은 var(--…)를 사용합니다. 슬라이드 내에 리터럴 색상이 있다면 시스템에 토큰이 누락된 것이므로, 우회하지 말고 토큰을 추가하세요.

지오메트리

1920×1080, 고정 크기, 창에 맞게 스케일링됩니다. 이 크기로 제작하세요 — 절대 1440이나 "반응형"으로 하지 마세요 — 그리고 scale transform이 뷰포트를 처리하도록 맡기세요.

모든 슬라이드를 DOM에 한꺼번에 유지하고 트랙을 움직이세요. 그래야 인쇄 시 각 슬라이드가 한 페이지씩 출력되고 요소 스크린샷이 작동합니다.

<!doctype html>
<html lang="ko">
<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>

두 개의 컨테이너를 사용하는 방식이 중요합니다: frame은 스케일된 크기로 지정되므로 stage가 뷰포트를 넘치지 않고, 데크가 중앙에 유지되며, 검증기의 overflow 검사에서 허위 문제가 보고되지 않습니다. 단일 스케일 요소로 단순화하지 마세요.

data-screen-label은 검증기가 각 프레임을 명명하는 데 사용합니다. 번호를 매기세요.

슬라이드 제작

  • 슬라이드당 하나의 아이디어. 본문에 설명 한 문장, 차트, 세 개의 불릿이 필요하다면 그것은 두 개의 슬라이드입니다.
  • 헤드라인이 역할을 합니다. 헤드라인은 주장을 진술하고, 본문은 이를 뒷받침합니다. 모든 헤드라인이 명사인 데크는 논증이 아니라 안건일 뿐입니다.
  • 최소 글자 크기: 24px. 본문 3236px이 보통이며, 헤드라인은 64110px입니다. 1440px 모니터에서 너무 크게 느껴져도 방 뒷좌석에서는 여전히 너무 작습니다.
  • 전체 데크에 배경색은 한두 가지. 섹션 오프너는 레이아웃으로 구분하고, 새로운 그라데이션을 만들지 마세요.
  • 리듬을 바꾸세요. 모든 슬라이드가 제목-세-카드 형식일 필요는 없습니다. 핵심 숫자에는 전체 화면 슬라이드를 할애하세요. 하나의 이미지나 인용문이 가장자리까지 닿도록 하세요.
  • 중첩 불릿 금지. 그룹당 최대 세 개 항목, 각 항목은 한 줄입니다.
  • 차트에는 발견점을 명시하는 제목을 붙이고, 축 레이블은 24px, 하나의 계열만 강조하세요. 무지개색 팔레트는 절대 사용하지 마세요.
  • 페이지 번호는 모든 슬라이드에서 동일한 위치에, 흐리게, 작게 표시하세요.

카피

절대 작성자의 텍스트를 다시 쓰지 마세요. 의역하지 말고 삭제하여 타이트하게 만드세요. 원본 자료가 얇아서 슬라이드 내용이 부족하다면, 내용을 지어내지 말고 그렇게 말하세요.

마무리

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.

관련 스킬