SuperAPI AI Directory

ppt-design-system

Design & creation

View repository

Updated 10-15 20:05

About this skill

PPT Korean-style Design System

Overview

This is a PPT design system optimized for the Korean business environment. It generates professional and highly readable slides using a font combination of Pretendard (sans-serif) + Chosun Ilbo Myeongjo (serif).

★ PPT Native Element Mapping (Core Principles)

Absolute Principle: Tabular data must use addTable(), and numerical comparisons/trends must use addChart(). Do not simulate tables/charts with shapes (addShape) and text (addText).

Element Selection Decision Tree

콘텐츠 유형 판별
├─ 행×열 구조의 데이터인가? ──────────→ addTable()
│   예: 매출표, 일정표, 비교표, 가격표, 스펙표, 예산표
│
├─ 수치의 추이/비교/비율인가? ─────────→ addChart()
│   예: 매출 추이, 점유율, KPI 변화, 분기 비교
│
├─ 외부 이미지/로고/사진인가? ─────────→ addImage()
│   예: 제품 사진, 회사 로고, 스크린샷, 아이콘
│
├─ 단순 텍스트 블록인가? ──────────────→ addText()
│   예: 제목, 본문 단락, 글머리 기호 목록, 인용문
│
├─ 장식/배경/구분선/도형인가? ─────────→ addShape()
│   예: 배경 사각형, 구분선, 강조 바, 아이콘 원형
│
└─ 오디오/비디오인가? ─────────────────→ addMedia()

Element-by-Element Usage Mapping Table

PptxGenJS Method Purpose Usage Scenario Absolutely Prohibited
addTable() Structured data Sales tables, schedules, comparison tables, price tables, budget tables, spec tables, curriculum tables, staffing status, checklists Drawing grids with addShape()
addChart() Data visualization Bar charts, line charts, pie charts, donut charts, area charts, radar charts, bubble charts, scatter plots Drawing bars/circles with addShape()
addImage() Inserting external images Photos, logos, screenshots, SVG→PNG converted icons Putting base64 into addText
addText() Text blocks Titles, body text, captions, bullet points, quotations, numbered lists Listing table data as text
addShape() Decoration/layout assistance Background rectangles, divider lines, accent bars, card backgrounds, circular icon backgrounds Simulating tables/charts
addNotes() Speaker notes Presentation scripts, reference notes, anticipated Q&A answers -

⚠️ Common Mistakes and Corrections

❌ 잘못된 사용 (표를 도형으로 시뮬레이션)
──────────────────────────────
// 이렇게 하지 말 것!
slide.addShape('rect', { x:1, y:1, w:3, h:0.4, fill:'1A1F36' }); // 헤더 배경
slide.addText('항목', { x:1, y:1, w:1, h:0.4, color:'FFFFFF' }); // 헤더 텍스트
slide.addText('1월', { x:2, y:1, w:1, h:0.4, color:'FFFFFF' });
slide.addShape('line', { x:1, y:1.4, w:3, h:0 });                // 구분선
slide.addText('매출', { x:1, y:1.4, w:1, h:0.4 });               // 데이터 행
slide.addText('100M', { x:2, y:1.4, w:1, h:0.4 });
// ... 수십 줄의 반복 코드

✅ 올바른 사용 (네이티브 테이블)
──────────────────────────────
const rows = [
  [{ text:'항목', options: STYLE.tableHeader },
   { text:'1월',  options: STYLE.tableHeader }],
  [{ text:'매출', options: STYLE.tableCell },
   { text:'100M', options: {...STYLE.tableCellRight} }]
];
slide.addTable(rows, TABLE_OPTIONS);
// 깔끔하고, PowerPoint에서 편집 가능

Font System

Default Fonts (included in the fonts/ folder)

Font Purpose File Name Weight
Pretendard Sans-serif (titles/body/captions) Pretendard-{Weight}.otf 9종: Thin(100)~Black(900)
Chosun Ilbo Myeongjo Serif (quotations/emphasis/formality) ChosunilboNM.ttf Regular

Pretendard 9 Weight Guide

Weight File Name Purpose
Thin (100) Pretendard-Thin.otf Decorative large text, watermarks
ExtraLight (200) Pretendard-ExtraLight.otf Subtitles (light theme), background text
Light (300) Pretendard-Light.otf Long body text, descriptive text
Regular (400) Pretendard-Regular.otf Default body text, captions
Medium (500) Pretendard-Medium.otf Emphasized body text, labels
SemiBold (600) Pretendard-SemiBold.otf Subheadings, card titles
Bold (700) Pretendard-Bold.otf Section titles, headers
ExtraBold (800) Pretendard-ExtraBold.otf Main titles, slide titles
Black (900) Pretendard-Black.otf KPI numbers, hero text

Font Application Rules

제목(Title):     Pretendard ExtraBold / Black    36~44pt
소제목(Subtitle): Pretendard SemiBold / Bold      24~28pt
본문(Body):      Pretendard Regular / Medium      16~20pt
캡션(Caption):   Pretendard Light / Regular       12~14pt
인용구(Quote):   조선일보명조 Regular              18~22pt (격식체, 인용문)
숫자강조(KPI):   Pretendard Black                  48~72pt
장식텍스트:      Pretendard Thin / ExtraLight      60~120pt (배경 대형 텍스트)

Letter Spacing & Line Height

Element Letter Spacing Line spacing PptxGenJS properties
Title -0.5pt ~ 0pt 1.1배 charSpacing: -0.5, lineSpacingMultiple: 1.1
Subtitle 0pt 1.2배 lineSpacingMultiple: 1.2
Body 0.5pt ~ 1pt 1.4~1.5배 charSpacing: 0.5, lineSpacingMultiple: 1.4
Caption 0.5pt 1.3배 charSpacing: 0.5, lineSpacingMultiple: 1.3

Color system (6:3:1 rule)

Default palette: "Midnight Executive"

const COLORS = {
  // 메인 60% - 배경/여백
  bg_primary:   'FFFFFF',  // 흰 배경
  bg_secondary: 'F5F7FA',  // 연한 회색 배경
  bg_dark:      '1A1F36',  // 다크 네이비 (표지/구분)

  // 보조 30% - 본문/구조
  text_primary:   '1A1F36', // 제목 텍스트 (다크 네이비)
  text_secondary: '4A5568', // 본문 텍스트 (차콜 그레이)
  text_tertiary:  '718096', // 캡션/보조 (미디엄 그레이)
  text_on_dark:   'FFFFFF', // 다크 배경 위 텍스트

  // 강조 10% - 포인트/CTA
  accent_blue:    '4A7BF7', // 메인 블루
  accent_cyan:    '00D4AA', // 시안 그린
  accent_yellow:  'FFB020', // 골드 옐로우
  accent_red:     'FF6B6B', // 경고/주의 레드
  accent_purple:  '8B5CF6', // 보조 퍼플
};

Additional palette options

"Warm Corporate" (warm corporate tone)

const WARM = {
  bg_dark: '2D1B4E', accent_blue: 'E8725A', accent_cyan: '4ECDC4',
  accent_yellow: 'F9C74F', accent_purple: '7B68EE'
};

"Nature Green" (eco-friendly/ESG)

const GREEN = {
  bg_dark: '1B3A2D', accent_blue: '2D9CDB', accent_cyan: '27AE60',
  accent_yellow: 'F2C94C', accent_red: 'EB5757'
};

"Minimal Mono" (minimal)

const MONO = {
  bg_dark: '111111', accent_blue: '333333', accent_cyan: '666666',
  accent_yellow: 'AAAAAA', text_primary: '111111'
};

Layout system

Slide size

  • For presentations: 16:9 (default, width 13.33" x height 7.5")
  • For print/reports: A4 (width 11.69" x height 8.27")

Margin guide (based on 16:9)

+-------------------------------------+
|  0.6"                         0.6"  |
|  +-------------------------------+  |
|  |  콘텐츠 영역                  |  |  상단: 0.5"
|  |  가로: 12.13" / 세로: 6.5"   |  |
|  |                               |  |
|  +-------------------------------+  |
|                                0.5" |  하단: 0.5"
+-------------------------------------+
Area Position (x, y) Size (w, h)
Left margin x=0.6" -
Right margin - w=12.13" (13.33-0.6-0.6)
Top margin y=0.5" -
Bottom margin - h=6.5" (7.5-0.5-0.5)
Page number x=12.3", y=7.1" fontSize: 9pt

Core layout patterns

1. Title Slide

[다크 배경 전체]
  중앙: 과정명 (44pt ExtraBold, 흰색)
  중앙 아래: 부제목 (20pt Regular, 70% 흰색)
  하단: 발표자/날짜 (14pt, 50% 흰색)

2. Section Divider

[좌측 40% 다크 배경 | 우측 60% 밝은 배경]
  좌측: 섹션 번호 (72pt Black, accent_cyan)
  우측: 섹션 제목 (36pt Bold) + 설명 (16pt Regular)

3. Two Column

[상단: 제목 바]
[좌측 50% | 우측 50%]
  각 컬럼: 소제목 (24pt Bold) + 본문 (16pt Regular)
  컬럼 간격: 0.4"

4. Card Grid

[상단: 제목 바]
[2x2 또는 2x3 카드 그리드]
  카드: 둥근 모서리 (rectRadius=0.1)
  카드 간격: 0.3"
  카드 헤더: accent 색상 바 (높이 0.08")

5. Timeline

[상단: 제목 바]
[좌측 색상 바 | 우측 항목 리스트]
  각 항목: 시간 (Bold) + 내용 (Regular) + 구분선

6. KPI Dashboard (Data Dashboard)

[상단: 제목 바]
[3~4열 KPI 카드]
  숫자: Pretendard Black 48pt (accent 색상)
  라벨: Regular 14pt (text_tertiary)
[하단: 차트/그래프 영역] → addChart() 사용

7. Data Table — NEW

[상단: 제목 바]
[네이티브 테이블] → addTable() 사용
  헤더: 다크 배경 + 흰색 텍스트
  짝수행: 연한 회색 배경 (zebra stripe)
  합계행: bold + 상단 border 강조

8. Chart & Insight — NEW

[상단: 제목 바]
[좌측 60%: addChart() | 우측 40%: 핵심 인사이트 텍스트]
  인사이트: 숫자 강조 (KPI 스타일) + 짧은 설명

PptxGenJS native element detailed guide

1. addTable() — Table

For tabular data, always addTable()use this.

Table style constants

// ===== 테이블 공통 스타일 =====
const TABLE_STYLE = {
  // 헤더 셀 (다크 배경)
  header: {
    bold: true,
    fill: { color: COLORS.bg_dark },
    color: COLORS.text_on_dark,
    fontFace: 'Pretendard',
    fontSize: 11,
    align: 'center',
    valign: 'middle'
  },
  // 일반 셀
  cell: {
    fontFace: 'Pretendard',
    fontSize: 11,
    color: COLORS.text_secondary,
    valign: 'middle'
  },
  // 숫자 셀 (우측 정렬)
  cellRight: {
    fontFace: 'Pretendard',
    fontSize: 11,
    color: COLORS.text_secondary,
    align: 'right',
    valign: 'middle'
  },
  // 짝수행 배경 (zebra stripe)
  cellAlt: {
    fontFace: 'Pretendard',
    fontSize: 11,
    color: COLORS.text_secondary,
    fill: { color: COLORS.bg_secondary },
    valign: 'middle'
  },
  // 합계/소계 행
  cellTotal: {
    bold: true,
    fontFace: 'Pretendard',
    fontSize: 11,
    color: COLORS.text_primary,
    border: [{ type: 'solid', pt: 1.5, color: COLORS.text_primary }, null, null, null],
    valign: 'middle'
  }
};

// 테이블 공통 옵션
const TABLE_OPTIONS = {
  x: 0.6,
  y: 1.8,  // 제목 바 아래
  w: 12.13,
  border: { type: 'solid', pt: 0.5, color: 'E2E8F0' },
  autoPage: false,
  margin: [5, 8, 5, 8]  // 셀 내부 여백 [top, right, bottom, left] pt
};

Table helper functions

/**
 * 디자인 시스템 적용된 테이블 추가
 * @param {Slide} slide
 * @param {string[]} headers - 헤더 텍스트 배열
 * @param {Array<Array<string|{text,options}>>} dataRows - 데이터 행 배열
 * @param {object} opts - 추가 옵션 (x, y, w, colW, rowH 등)
 */
function addStyledTable(slide, headers, dataRows, opts = {}) {
  const rows = [];

  // 헤더 행
  rows.push(headers.map(h => ({
    text: h,
    options: { ...TABLE_STYLE.header }
  })));

  // 데이터 행 (zebra stripe 자동 적용)
  dataRows.forEach((row, i) => {
    const isAlt = i % 2 === 1;
    const baseStyle = isAlt ? TABLE_STYLE.cellAlt : TABLE_STYLE.cell;
    rows.push(row.map(cell => {
      if (typeof cell === 'string') {
        return { text: cell, options: { ...baseStyle } };
      }
      // {text, options} 객체인 경우 스타일 병합
      return { text: cell.text, options: { ...baseStyle, ...cell.options } };
    }));
  });

  slide.addTable(rows, {
    ...TABLE_OPTIONS,
    ...opts
  });
}

/**
 * colspan을 활용한 제목 행 포함 테이블
 */
function addTitledTable(slide, tableTitle, headers, dataRows, opts = {}) {
  const colCount = headers.length;
  const rows = [];

  // 테이블 제목 행 (colspan 병합)
  rows.push([{
    text: tableTitle,
    options: {
      colspan: colCount,
      bold: true,
      fill: { color: COLORS.bg_dark },
      color: COLORS.text_on_dark,
      fontFace: 'Pretendard',
      fontSize: 13,
      align: 'center',
      valign: 'middle'
    }
  }]);

  // 헤더 행
  rows.push(headers.map(h => ({
    text: h,
    options: {
      bold: true,
      fill: { color: COLORS.bg_secondary },
      color: COLORS.text_primary,
      fontFace: 'Pretendard',
      fontSize: 11,
      align: 'center',
      valign: 'middle'
    }
  })));

  // 데이터 행
  dataRows.forEach((row, i) => {
    const isAlt = i % 2 === 1;
    rows.push(row.map(cell => {
      const base = isAlt
        ? { ...TABLE_STYLE.cellAlt }
        : { ...TABLE_STYLE.cell };
      if (typeof cell === 'string') return { text: cell, options: base };
      return { text: cell.text, options: { ...base, ...cell.options } };
    }));
  });

  slide.addTable(rows, { ...TABLE_OPTIONS, ...opts });
}

Table usage example

// ===== 매출 계획표 =====
addTitledTable(slide, '2026년 매출 계획표',
  ['구분', 'Q1', 'Q2', 'Q3', 'Q4', '연간 합계'],
  [
    ['온라인',
     { text: '120M', options: { align: 'right' } },
     { text: '150M', options: { align: 'right' } },
     { text: '180M', options: { align: 'right' } },
     { text: '200M', options: { align: 'right' } },
     { text: '650M', options: { align: 'right', bold: true } }],
    ['오프라인',
     { text: '80M', options: { align: 'right' } },
     { text: '90M', options: { align: 'right' } },
     { text: '100M', options: { align: 'right' } },
     { text: '110M', options: { align: 'right' } },
     { text: '380M', options: { align: 'right', bold: true } }],
  ],
  { colW: [2, 1.8, 1.8, 1.8, 1.8, 2.5] }
);

// ===== 일정표/시간표 =====
addStyledTable(slide,
  ['교시', '시간', '과목', '내용', '비고'],
  [
    ['1', '09:00~09:50', 'Excel 기초', '데이터 정리 자동화', '이론'],
    ['2', '10:00~10:50', 'Excel 분석', 'KPI 대시보드 설계', '이론+실습'],
    ['3', '11:00~11:50', 'PPT 생성', '슬라이드 자동 변환', '이론'],
  ],
  { colW: [1, 2.2, 2.5, 4, 1.8], rowH: [0.45, 0.4, 0.4, 0.4] }
);

// ===== 비교표 (스펙/기능 비교) =====
addStyledTable(slide,
  ['기능', 'Free', 'Pro', 'Enterprise'],
  [
    ['스킬 수', '12개', { text: '24개', options: { bold: true, color: COLORS.accent_blue } }, '무제한'],
    ['지원', '커뮤니티', '이메일', { text: '전담 매니저', options: { bold: true } }],
    ['가격', '무료', '월 9,900원', '문의'],
  ]
);

2. addChart() — Chart

For trends/comparisons/proportions of numerical data, always addChart()use this.

Chart style constants

// ===== 차트 공통 스타일 =====
const CHART_STYLE = {
  // 공통 기본값
  base: {
    showTitle: true,
    titleFontFace: 'Pretendard',
    titleFontSize: 14,
    titleColor: COLORS.text_primary,
    showLegend: true,
    legendFontFace: 'Pretendard',
    legendFontSize: 9,
    legendColor: COLORS.text_secondary,
    catAxisLabelFontFace: 'Pretendard',
    catAxisLabelFontSize: 10,
    catAxisLabelColor: COLORS.text_tertiary,
    valAxisLabelFontFace: 'Pretendard',
    valAxisLabelFontSize: 10,
    valAxisLabelColor: COLORS.text_tertiary,
  },
  // 차트 색상 팔레트 (최대 6색)
  colors: [
    COLORS.accent_blue,     // 4A7BF7
    COLORS.accent_cyan,     // 00D4AA
    COLORS.accent_yellow,   // FFB020
    COLORS.accent_red,      // FF6B6B
    COLORS.accent_purple,   // 8B5CF6
    '38BDF8'                // 라이트 블루 (6번째)
  ]
};

Usage criteria by chart type

Data type Chart type PptxGenJS constant
Size comparison by item Vertical bar pptx.charts.BAR
Time-series trend/change Line pptx.charts.LINE
Proportion of total (5 or fewer) Pie pptx.charts.PIE
Proportion of total (central KPI) Doughnut pptx.charts.DOUGHNUT
Trend + cumulative amount Area pptx.charts.AREA
Multidimensional item comparison Radar pptx.charts.RADAR
Relationship between two variables Scatter pptx.charts.SCATTER
Relationship among three variables Bubble pptx.charts.BUBBLE

Chart helper functions

/**
 * 디자인 시스템 적용된 차트 추가
 * @param {Slide} slide
 * @param {object} pptx - PptxGenJS 인스턴스 (charts 참조용)
 * @param {string} type - 'BAR'|'LINE'|'PIE'|'DOUGHNUT'|'AREA'|'RADAR'|'SCATTER'
 * @param {Array} chartData - [{name, labels, values}]
 * @param {object} opts - 위치/크기/추가 옵션
 */
function addStyledChart(slide, pptx, type, chartData, opts = {}) {
  const typeMap = {
    BAR: pptx.charts.BAR,
    LINE: pptx.charts.LINE,
    PIE: pptx.charts.PIE,
    DOUGHNUT: pptx.charts.DOUGHNUT,
    AREA: pptx.charts.AREA,
    RADAR: pptx.charts.RADAR,
    SCATTER: pptx.charts.SCATTER,
    BUBBLE: pptx.charts.BUBBLE
  };

  const defaults = {
    x: 0.6, y: 1.8, w: 12.13, h: 5.0,
    ...CHART_STYLE.base,
    chartColors: CHART_STYLE.colors.slice(0, chartData.length || 6),
    ...opts
  };

  // 차트 유형별 기본값 추가
  if (type === 'BAR') {
    defaults.barGapWidthPct = 80;
    defaults.catAxisOrientation = 'minMax';
    defaults.valAxisOrientation = 'minMax';
  }
  if (type === 'LINE') {
    defaults.lineDataSymbol = 'circle';
    defaults.lineDataSymbolSize = 8;
    defaults.lineSmooth = false;
  }
  if (type === 'PIE' || type === 'DOUGHNUT') {
    defaults.showPercent = true;
    defaults.showLegend = true;
    defaults.legendPos = 'b';
    defaults.chartColors = CHART_STYLE.colors.slice(0, chartData[0]?.values?.length || 6);
  }

  slide.addChart(typeMap[type], chartData, defaults);
}

Chart usage example

// ===== 매출 추이 (꺾은선) =====
addStyledChart(slide, pptx, 'LINE',
  [{ name: '매출(억)', labels: ['1월','2월','3월','4월','5월','6월'],
     values: [12, 15, 18, 22, 25, 30] }],
  { x: 0.6, y: 1.8, w: 7, h: 4.5, title: '2026 상반기 매출 추이' }
);

// ===== 부서별 비교 (막대) =====
addStyledChart(slide, pptx, 'BAR',
  [
    { name: '목표', labels: ['영업','마케팅','개발','인사'], values: [100, 80, 120, 50] },
    { name: '실적', labels: ['영업','마케팅','개발','인사'], values: [95, 85, 110, 48] }
  ],
  { x: 0.6, y: 1.8, w: 12.13, h: 5, title: '부서별 목표 대비 실적' }
);

// ===== 구성비 (원형) =====
addStyledChart(slide, pptx, 'PIE',
  [{ name: '점유율', labels: ['제품A','제품B','제품C','기타'],
     values: [45, 25, 20, 10] }],
  { x: 3, y: 1.5, w: 7, h: 5, title: '제품별 매출 구성비' }
);

// ===== 진행률 (도넛) =====
addStyledChart(slide, pptx, 'DOUGHNUT',
  [{ name: '진행률', labels: ['완료','잔여'], values: [75, 25] }],
  { x: 4, y: 2, w: 5, h: 4, title: '프로젝트 진행률',
    chartColors: [COLORS.accent_cyan, 'E2E8F0'] }
);

3. addImage() — Image

// 파일 경로로 삽입
slide.addImage({
  path: '/path/to/image.png',
  x: 0.6, y: 1.8, w: 5, h: 3.5
});

// SVG → PNG 변환 후 삽입 (sharp 라이브러리)
const sharp = require('sharp');
const svgBuffer = Buffer.from(svgString);
const pngBuffer = await sharp(svgBuffer).png().toBuffer();
const base64 = pngBuffer.toString('base64');
slide.addImage({
  data: 'image/png;base64,' + base64,
  x: 0.6, y: 1.8, w: 5, h: 3.5
});

// 로고 삽입 (우측 하단)
slide.addImage({
  path: '/path/to/logo.png',
  x: 11.5, y: 6.8, w: 1.2, h: 0.5
});

4. addText() — Text

// 제목
slide.addText('슬라이드 제목', {
  x: 0.6, y: 0.65, w: 10, h: 0.6,
  fontSize: 28, fontFace: 'Pretendard', bold: true,
  color: COLORS.text_primary, charSpacing: -0.3
});

// 글머리 기호 목록 (bullet: true)
slide.addText([
  { text: '첫 번째 항목', options: { bullet: true, indentLevel: 0 } },
  { text: '두 번째 항목', options: { bullet: true, indentLevel: 0 } },
  { text: '하위 항목', options: { bullet: true, indentLevel: 1 } },
], {
  x: 0.6, y: 1.8, w: 12.13, h: 4,
  fontSize: 16, fontFace: 'Pretendard',
  color: COLORS.text_secondary,
  lineSpacingMultiple: 1.5,
  paraSpaceAfter: 6
});

// 번호 목록 (bullet: {type:'number'})
slide.addText([
  { text: '첫 번째 단계', options: { bullet: { type: 'number' } } },
  { text: '두 번째 단계', options: { bullet: { type: 'number' } } },
], {
  x: 0.6, y: 1.8, w: 12.13, h: 3,
  fontSize: 16, fontFace: 'Pretendard',
  color: COLORS.text_secondary
});

// 인용문 (명조체)
slide.addText('\u201C변화를 두려워하지 마세요\u201D', {
  x: 2, y: 3, w: 9, h: 1,
  fontSize: 22, fontFace: 'ChosunilboNM', italic: true,
  color: COLORS.text_tertiary, align: 'center'
});

5. addShape() — Decoration/layout assistance only

// ✅ 올바른 사용: 배경 영역
slide.addShape('rect', {
  x: 0, y: 0, w: '100%', h: '100%',
  fill: { color: COLORS.bg_dark }
});

// ✅ 올바른 사용: 악센트 라인
slide.addShape('rect', {
  x: 0.6, y: 0.5, w: 1.2, h: 0.06,
  fill: { color: COLORS.accent_blue }
});

// ✅ 올바른 사용: 카드 배경 (둥근 모서리)
slide.addShape('roundRect', {
  x: 0.6, y: 1.8, w: 5.5, h: 3,
  rectRadius: 0.1,
  fill: { color: 'FFFFFF' },
  shadow: { type: 'outer', blur: 6, offset: 2, color: '00000015' }
});

// ✅ 올바른 사용: 구분선
slide.addShape('line', {
  x: 0.6, y: 4, w: 12.13, h: 0,
  line: { color: 'E2E8F0', width: 0.5 }
});

// ❌ 금지: 표 격자를 도형으로 그리기
// ❌ 금지: 막대 차트를 사각형으로 그리기

6. addNotes() — Speaker notes

slide.addNotes('이 슬라이드에서는 매출 추이를 설명합니다.\n핵심 포인트: Q3 성장률 40% 강조');

PptxGenJS code conventions

Font embedding

// Pretendard OTF 경로 (스킬 폴더 기준)
const FONT_DIR = path.join(__dirname, 'fonts');

// PptxGenJS에서 폰트 등록은 지원하지 않으므로
// 시스템에 Pretendard가 설치된 환경에서 다음과 같이 사용:
const FONTS = {
  title:    { fontFace: 'Pretendard', bold: true },   // ExtraBold/Black
  subtitle: { fontFace: 'Pretendard', bold: true },   // SemiBold/Bold
  body:     { fontFace: 'Pretendard', bold: false },   // Regular/Medium
  caption:  { fontFace: 'Pretendard', bold: false },   // Light/Regular
  serif:    { fontFace: 'ChosunilboNM', bold: false }, // 조선일보명조
  kpi:      { fontFace: 'Pretendard', bold: true },    // Black
  deco:     { fontFace: 'Pretendard', bold: false },   // Thin/ExtraLight (장식용)
};

Slide creation helper

// 표준 제목 바 추가 함수
function addTitleBar(slide, title, subtitle = '') {
  // 얇은 accent 라인
  slide.addShape('rect', {
    x: 0.6, y: 0.5, w: 1.2, h: 0.06,
    fill: { color: COLORS.accent_blue }
  });
  // 제목
  slide.addText(title, {
    x: 0.6, y: 0.65, w: 10, h: 0.6,
    fontSize: 28, fontFace: 'Pretendard', bold: true,
    color: COLORS.text_primary, charSpacing: -0.3
  });
  // 부제목 (있을 경우)
  if (subtitle) {
    slide.addText(subtitle, {
      x: 0.6, y: 1.25, w: 10, h: 0.4,
      fontSize: 16, fontFace: 'Pretendard',
      color: COLORS.text_tertiary
    });
  }
}

// 카드 생성 함수 (장식용 - 데이터 표시 아님)
function addCard(slide, { x, y, w, h, title, body, accentColor }) {
  // 카드 배경
  slide.addShape('roundRect', {
    x, y, w, h, rectRadius: 0.1,
    fill: { color: 'FFFFFF' },
    shadow: { type: 'outer', blur: 6, offset: 2, color: '00000015' }
  });
  // 상단 accent 바
  slide.addShape('rect', {
    x: x + 0.02, y, w: w - 0.04, h: 0.06,
    fill: { color: accentColor || COLORS.accent_blue }
  });
  // 카드 제목
  slide.addText(title, {
    x: x + 0.2, y: y + 0.2, w: w - 0.4, h: 0.35,
    fontSize: 16, fontFace: 'Pretendard', bold: true,
    color: COLORS.text_primary
  });
  // 카드 본문
  slide.addText(body, {
    x: x + 0.2, y: y + 0.55, w: w - 0.4, h: h - 0.75,
    fontSize: 13, fontFace: 'Pretendard',
    color: COLORS.text_secondary,
    lineSpacingMultiple: 1.4, valign: 'top'
  });
}

// 페이지 번호 추가
function addPageNumber(slide, num, total) {
  slide.addText(`${num} / ${total}`, {
    x: 12.0, y: 7.05, w: 1.0, h: 0.3,
    fontSize: 9, fontFace: 'Pretendard',
    color: COLORS.text_tertiary, align: 'right'
  });
}

Practical slide composition patterns

Pattern A: Sales report slide

const slide = pptx.addSlide();
addTitleBar(slide, '월별 매출 실적', '2026년 상반기');

// 상단: KPI 카드 3개 (addText + addShape)
// → KPI 숫자는 카드 형태 = addShape(배경) + addText(숫자)
['매출 합계|₩3.2B', '전년 대비|+18%', '목표 달성률|94%'].forEach((item, i) => {
  const [label, value] = item.split('|');
  addCard(slide, {
    x: 0.6 + i * 4.1, y: 1.6, w: 3.8, h: 1.2,
    title: label, body: value, accentColor: CHART_STYLE.colors[i]
  });
});

// 중단: 데이터 테이블 (addTable)
addStyledTable(slide, ['월','매출','비용','영업이익','이익률'], [...data],
  { y: 3.1, rowH: [0.4, 0.35, 0.35, 0.35, 0.35, 0.35, 0.35] });

Pattern B: Comparative Analysis Slide

const slide = pptx.addSlide();
addTitleBar(slide, '경쟁사 비교 분석');

// 좌측: 비교 차트 (addChart)
addStyledChart(slide, pptx, 'BAR',
  [
    { name: '자사', labels: ['가격','품질','서비스','인지도'], values: [85, 92, 88, 70] },
    { name: '경쟁A', labels: ['가격','품질','서비스','인지도'], values: [90, 80, 75, 85] }
  ],
  { x: 0.6, y: 1.6, w: 6.5, h: 5.2, title: '' }
);

// 우측: 비교표 (addTable)
addStyledTable(slide, ['항목','자사','경쟁A'],
  [['가격', '85', '90'], ['품질', '92', '80'], ['서비스', '88', '75']],
  { x: 7.5, y: 1.6, w: 5.2 }
);

Pattern C: Project Status Dashboard

const slide = pptx.addSlide();
addTitleBar(slide, '프로젝트 현황 대시보드', '2026년 2월 기준');

// 좌측 상단: 진행률 도넛 (addChart)
addStyledChart(slide, pptx, 'DOUGHNUT',
  [{ name: '진행', labels: ['완료','잔여'], values: [72, 28] }],
  { x: 0.6, y: 1.6, w: 4, h: 3, showTitle: false,
    chartColors: [COLORS.accent_cyan, 'E2E8F0'] }
);

// 우측 상단: 주간 이슈 테이블 (addTable)
addStyledTable(slide, ['이슈','담당','상태','기한'],
  [['서버 지연', '김개발', '진행중', '2/20'],
   ['UI 버그', '박디자', '완료', '2/18']],
  { x: 5, y: 1.6, w: 7.7, rowH: [0.4, 0.35, 0.35] }
);

// 하단: 마일스톤 타임라인 (addTable로 구현 가능)
addStyledTable(slide, ['마일스톤','시작일','종료일','진행률','상태'],
  [['기획', '1/15', '2/10', '100%', '\u2705'],
   ['개발', '2/11', '4/30', '45%', '\uD83D\uDD27'],
   ['테스트', '5/1', '5/31', '0%', '\u23F3']],
  { x: 0.6, y: 5.0, w: 12.13, rowH: [0.4, 0.35, 0.35, 0.35] }
);

6x6 Content Rule

  • Per slide maximum 6 bullet points
  • Per bullet within 6 words (15-18 characters in Korean)
  • On one slide, 1 key message
  • If there is a lot of text, split into 2 slides

Image & Icon Guide

Use SVG Icons

  • Separately, svg-diagram use skills to generate SVG icons/diagrams matching the theme colors
  • Convert SVG → PNG then insert (using the sharp library)

Hero Image Rules

  • Use only 1 key image per slide
  • Image selection criteria: topic, mood, resolution, color, whitespace

2025-2026 Korean-style PPT Design Trends

Functional Minimalism

  • Exclude unnecessary decorative elements, content-centered design
  • Actively utilize wide white space
  • Strengthen the one slide = one message principle
  • Background: pure white (#FFFFFF) or light gray (#F5F7FA~#F8FAFC) as default

Modular Layout

  • Card-based grid system (2x2, 2x3, 1+2 variations)
  • Place independent information units in each module
  • Maintain consistent spacing between modules (0.3"~0.4")
  • Ensure consistency with repeatable layout patterns

Accent Color Strategy

  • Background: light gray (#F5F7FA)
  • Accent: navy (#1A1F36) + 1-2 accent colors
  • 6:3:1 rule: background 60% + text 30% + emphasis 10%
  • Prefer flat solid colors over gradients

Typography

  • Pretendard -- established as the standard font for Korean business PPT
  • Fine weight control through the use of variable fonts
  • Titles in ExtraBoldBlack, body text in RegularMedium
  • Letter spacing: tight for titles (-0.5pt), slightly wider for body text (+0.5pt)
  • Use serif fonts (Chosun Ilbo Myeongjo) as accents only for quotes/emphasis

Enhanced Data Visualization

  • Number-centered KPI card layouts are popular
  • 2D flat charts instead of complex 3D charts (using addChart)
  • Infographic style combining icons + numbers
  • Linked with SVG diagrams (using the svg-diagram skill)

Checklist (QA)

After generating the document, check the following:

  • Title 3644pt / subtitle 2428pt / body text within 16~20pt range
  • Letter spacing: title -0.50pt / body text 0.51pt
  • Line spacing: body text 1.4x or more
  • Comply with 6:3:1 color ratio
  • Margins: left/right 0.6" / top/bottom 0.5" or more
  • One slide, one message principle
  • Within 2 font types (Pretendard + Myeongjo)
  • No Korean text corruption (check Pretendard font)
  • Table data → check use of addTable() (shape simulation prohibited)
  • Numeric comparison/trend → check use of addChart() (shape simulation prohibited)
  • Image/logo → check use of addImage()
  • Bullet points → check use of addText({ bullet: true })