chore: initial commit — portable agent memory & skill scaffold
Extracted and cleaned up from a prior WSL/Windows agent-memory setup. Contains only project-agnostic content: - Claude_Memory/: feedback rules, user profile, reusable reference docs, session scratch space (claude_info/, claude_log/), mirrored skill defs - caveman/: ultra-compressed communication mode skill + subcommands - html-slide-deck/: self-contained HTML slide deck builder skill Project-specific knowledge (previously mixed in) was moved out to each project's own repository as part of this cleanup.
This commit is contained in:
commit
25256ec417
61 files changed
+4431
No files matched your search
Executable
+97
@@ -0,0 +1,97 @@
|
||||
---
|
||||
name: html-slide-deck
|
||||
description: |
|
||||
PPT 스타일의 HTML 슬라이드 덱을 생성하거나 수정합니다. 기술 가이드, 업무 보고서, 교육 자료 등 다양한 용도의 프레젠테이션을 완전히 자체 포함된 단일 HTML 파일로 만듭니다.
|
||||
|
||||
다음 상황에서 이 스킬을 사용하세요: "슬라이드 만들어줘", "PPT 형식으로 정리해줘", "가이드 문서를 슬라이드로", "발표 자료 만들어줘", "단계별 절차를 슬라이드로", 기존 HTML 슬라이드에 페이지 추가/수정/삭제, 레이아웃 변경 요청 등. HTML이나 슬라이드를 명시하지 않아도 프레젠테이션 형태가 적합하다고 판단되면 이 스킬을 사용하세요.
|
||||
---
|
||||
|
||||
# HTML 슬라이드 덱 생성/수정 스킬
|
||||
|
||||
## 개요
|
||||
|
||||
완전 자체 포함(self-contained) 단일 HTML 파일로 PPT 스타일 슬라이드 덱을 만드는 스킬입니다.
|
||||
외부 서버 없이 브라우저에서 바로 열리며, 키보드/클릭 네비게이션, 사이드바 목차, 진행 표시줄을 포함합니다.
|
||||
|
||||
## Step 1: 요구사항 파악
|
||||
|
||||
작업 시작 전 다음을 확인하세요:
|
||||
|
||||
- **목적**: 기술 가이드? 업무 보고? 교육 자료?
|
||||
- **내용 구조**: 슬라이드 수, 계층(Step/Task 구분 여부), 코드 포함 여부
|
||||
- **신규 생성 vs 기존 수정**: 파일이 이미 있는지 확인
|
||||
- **출력 경로**: 저장할 폴더
|
||||
|
||||
## Step 2: 템플릿 선택
|
||||
|
||||
### 🔷 기술 가이드 (Technical Guide) — 이번 세션 검증됨
|
||||
절차적 기술 문서에 최적. 단계(Step) + 태스크(Task) 계층, 코드 블록, 다이어그램 지원.
|
||||
- 다크 제목 슬라이드 + 라이트 콘텐츠 슬라이드
|
||||
- Step 헤더 슬라이드 (강조색 배경) + Task 콘텐츠 슬라이드
|
||||
- Highlight.js (C++/Python/JS 등 코드 강조) + Mermaid.js (플로우차트)
|
||||
→ `references/engine.md`의 **Technical Guide 보일러플레이트** 사용
|
||||
|
||||
### 🔶 업무 보고서 (Business Report) — 기본 엔진 사용
|
||||
데이터, 표, 불릿포인트 중심. 밝은 배경, 헤더/푸터 포함.
|
||||
→ `references/engine.md`의 **기본 엔진**을 기반으로 커스텀 CSS 적용
|
||||
|
||||
### 🟢 교육 자료 (Training Material) — 기본 엔진 사용
|
||||
퀴즈, 예시, 체크리스트 위주. 색상 풍부, 시각적 강조.
|
||||
→ `references/engine.md`의 **기본 엔진**을 기반으로 커스텀 CSS 적용
|
||||
|
||||
## Step 3-A: 신규 생성 워크플로우
|
||||
|
||||
1. **콘텐츠 아웃라인 작성**: 슬라이드 목록과 각 슬라이드의 내용을 먼저 정리
|
||||
2. **SLIDE_META 설계**: 사이드바에 표시될 슬라이드 메타데이터 배열 설계
|
||||
```javascript
|
||||
const SLIDE_META = [
|
||||
{ idx: 0, label: '제목 · 목차', dot: '🏠', color: '#475569', step: false },
|
||||
{ idx: 1, label: 'Step 1 · 개요', dot: '1', color: '#1D4ED8', step: true },
|
||||
{ idx: 2, label: 'Task 1-1 · ...',dot: '1', color: '#1D4ED8', step: false },
|
||||
// idx는 DOM 순서(0부터 시작)와 정확히 일치해야 함
|
||||
];
|
||||
```
|
||||
3. **보일러플레이트 삽입**: `references/engine.md`에서 전체 HTML 구조 복사
|
||||
4. **슬라이드 추가**: `references/slide-types.md`의 타입별 템플릿 참조
|
||||
5. **레이아웃 적용**: `references/layout-patterns.md`의 패턴 참조
|
||||
6. **제목 슬라이드 뱃지 업데이트**: "N 슬라이드" 카운트를 실제 슬라이드 수로
|
||||
|
||||
## Step 3-B: 기존 수정 워크플로우
|
||||
|
||||
수정 전 반드시:
|
||||
1. **전체 SLIDE_META 파악**: 현재 슬라이드 목록과 idx 값 확인
|
||||
2. **DOM 슬라이드 총 수 확인**: `class="slide ` 패턴 grep으로 총 개수 파악
|
||||
3. **data-idx는 장식용**: JS 엔진은 DOM 순서(querySelectorAll 결과)를 사용함.
|
||||
SLIDE_META의 `idx` 값이 DOM 순서와 일치해야 사이드바 네비게이션이 동작함.
|
||||
|
||||
**슬라이드 추가 시**:
|
||||
- 원하는 위치에 HTML 삽입
|
||||
- 해당 위치 이후의 모든 SLIDE_META idx 값을 +1
|
||||
- "N 슬라이드" 뱃지 +1
|
||||
|
||||
**슬라이드 삭제/병합 시**:
|
||||
- HTML에서 슬라이드 div 제거
|
||||
- SLIDE_META에서 해당 항목 제거, 이후 idx -1
|
||||
- "N 슬라이드" 뱃지 -1
|
||||
|
||||
**레이아웃 수정 시**:
|
||||
- 변경 전 `grep`으로 현재 CSS 값 정확히 확인 후 수정
|
||||
- 추측으로 old_string 작성하지 말 것
|
||||
|
||||
## Step 4: 품질 체크
|
||||
|
||||
완성 후 확인:
|
||||
- [ ] SLIDE_META idx가 DOM 순서와 정확히 일치하는가
|
||||
- [ ] The End 슬라이드가 SLIDE_META에 포함되지 않는가 (네비게이션 전용)
|
||||
- [ ] 제목 슬라이드의 "N 슬라이드" 뱃지가 실제 수와 맞는가
|
||||
- [ ] 모든 코드 블록이 `hljs.highlightAll()` 범위 내에 있는가
|
||||
- [ ] Mermaid 다이어그램에 `class="mermaid"`가 있는가
|
||||
- [ ] 모바일/좁은 화면에서 overflow가 적절히 처리되는가
|
||||
|
||||
## 레퍼런스 파일 안내
|
||||
|
||||
| 파일 | 내용 | 언제 읽기 |
|
||||
|------|------|-----------|
|
||||
| `references/engine.md` | HTML 보일러플레이트 (head, CSS, JS 엔진 전체) | 신규 생성 시, 엔진 동작 이해 필요 시 |
|
||||
| `references/slide-types.md` | 슬라이드 타입별 HTML 템플릿 | 새 슬라이드 추가 시 |
|
||||
| `references/layout-patterns.md` | 콘텐츠 레이아웃 패턴 (코드, 그리드, 테이블 등) | 슬라이드 내부 레이아웃 구성 시 |
|
||||
Executable
+498
@@ -0,0 +1,498 @@
|
||||
# HTML 슬라이드 엔진 레퍼런스
|
||||
|
||||
이 파일에는 슬라이드 덱의 뼈대(boilerplate) 전체가 담겨 있습니다.
|
||||
신규 파일을 만들 때 아래 보일러플레이트를 복사한 뒤 `<!-- SLIDES HERE -->` 영역에 슬라이드를 채워 넣으세요.
|
||||
|
||||
---
|
||||
|
||||
## Technical Guide 보일러플레이트
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>{{TITLE}}</title>
|
||||
|
||||
<!-- 코드 하이라이팅 (Highlight.js) -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/atom-one-dark.min.css" />
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/cpp.min.js"></script>
|
||||
<!-- Mermaid 다이어그램 (필요 없으면 제거 가능) -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/mermaid/10.6.1/mermaid.min.js"></script>
|
||||
|
||||
<style>
|
||||
/* ══ Reset ══════════════════════════════════════════════════ */
|
||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
html, body {
|
||||
width: 100%; height: 100%; overflow: hidden;
|
||||
font-family: 'Segoe UI', 'Apple SD Gothic Neo', sans-serif;
|
||||
background: #0F172A;
|
||||
}
|
||||
|
||||
/* ══ Sidebar ════════════════════════════════════════════════ */
|
||||
#sidebar {
|
||||
position: fixed; top: 0; left: 0; bottom: 0; width: 300px;
|
||||
background: rgba(10,17,32,0.97); backdrop-filter: blur(16px);
|
||||
border-right: 1px solid rgba(255,255,255,.08);
|
||||
z-index: 600; transform: translateX(-300px);
|
||||
transition: transform .28s cubic-bezier(.4,0,.2,1);
|
||||
display: flex; flex-direction: column; overflow: hidden;
|
||||
box-shadow: 4px 0 32px rgba(0,0,0,.5);
|
||||
}
|
||||
#sidebar.open { transform: translateX(0); }
|
||||
|
||||
.sb-head { padding: 18px 16px 14px; border-bottom: 1px solid rgba(255,255,255,.06); flex-shrink: 0; }
|
||||
.sb-badge {
|
||||
display: inline-block; background: #1D4ED8; color: #fff;
|
||||
font-size: 9px; font-weight: 800; letter-spacing: .1em;
|
||||
text-transform: uppercase; padding: 3px 8px; border-radius: 4px; margin-bottom: 8px;
|
||||
}
|
||||
.sb-title { color: #F8FAFC; font-size: 13px; font-weight: 700; line-height: 1.4; }
|
||||
.sb-list { flex: 1; overflow-y: auto; padding: 10px 8px; }
|
||||
.sb-list::-webkit-scrollbar { width: 4px; }
|
||||
.sb-list::-webkit-scrollbar-thumb { background: rgba(255,255,255,.15); border-radius: 2px; }
|
||||
|
||||
.sb-item {
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
padding: 8px 10px; border-radius: 8px; cursor: pointer;
|
||||
transition: background .15s; margin-bottom: 2px;
|
||||
}
|
||||
.sb-item:hover { background: rgba(255,255,255,.07); }
|
||||
.sb-item.active { background: rgba(255,255,255,.12); }
|
||||
.sb-item.is-step { margin-top: 6px; }
|
||||
.sb-dot {
|
||||
width: 26px; height: 26px; border-radius: 6px;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
font-size: 11px; font-weight: 800; flex-shrink: 0;
|
||||
}
|
||||
.sb-label { color: #CBD5E1; font-size: 12px; font-weight: 500; line-height: 1.3; }
|
||||
.sb-item.active .sb-label { color: #F1F5F9; font-weight: 700; }
|
||||
|
||||
/* 사이드바 최하단 도움말 버튼 */
|
||||
.sb-help-btn {
|
||||
margin: 10px 12px 14px; padding: 8px 12px;
|
||||
background: rgba(255,255,255,.06); border: 1px solid rgba(255,255,255,.1);
|
||||
border-radius: 8px; color: #94A3B8; font-size: 11px;
|
||||
cursor: pointer; text-align: center; flex-shrink: 0;
|
||||
transition: background .15s;
|
||||
}
|
||||
.sb-help-btn:hover { background: rgba(255,255,255,.1); }
|
||||
|
||||
/* ══ Deck ═══════════════════════════════════════════════════ */
|
||||
#deck {
|
||||
position: fixed; top: 0; left: 0; right: 0; bottom: 48px;
|
||||
transition: left .28s cubic-bezier(.4,0,.2,1),
|
||||
right .28s cubic-bezier(.4,0,.2,1);
|
||||
overflow: hidden;
|
||||
}
|
||||
body.sb-open #deck { left: 300px; }
|
||||
|
||||
/* ══ Slides ═════════════════════════════════════════════════ */
|
||||
.slide {
|
||||
position: absolute; inset: 0;
|
||||
transform: translateX(100%);
|
||||
transition: transform .32s cubic-bezier(.4,0,.2,1);
|
||||
overflow: hidden;
|
||||
}
|
||||
.slide.active { transform: translateX(0); }
|
||||
.slide.exit-left { transform: translateX(-100%); }
|
||||
.slide.exit-right { transform: translateX(100%); }
|
||||
|
||||
/* ── 제목/목차 슬라이드 ─── */
|
||||
.slide-title {
|
||||
background: linear-gradient(135deg, #0F172A 0%, #1E1B4B 50%, #0F172A 100%);
|
||||
display: flex; flex-direction: row;
|
||||
justify-content: flex-start; align-items: stretch;
|
||||
padding: 0; overflow: hidden;
|
||||
}
|
||||
.tc-left {
|
||||
flex: 0 0 50%; display: flex; flex-direction: column; justify-content: center;
|
||||
padding: 52px 44px 52px 72px;
|
||||
border-right: 1px solid rgba(255,255,255,.1);
|
||||
position: relative; z-index: 1;
|
||||
}
|
||||
.tc-right {
|
||||
flex: 0 0 50%; display: flex; flex-direction: column;
|
||||
padding: 32px 52px 24px 44px;
|
||||
}
|
||||
.t-eyebrow { font-size: 11px; font-weight: 700; letter-spacing: .15em; text-transform: uppercase; color: #818CF8; margin-bottom: 16px; }
|
||||
.t-title { font-size: 36px; font-weight: 800; color: #F8FAFC; line-height: 1.2; margin-bottom: 16px; }
|
||||
.t-subtitle { font-size: 15px; color: #94A3B8; line-height: 1.6; margin-bottom: 28px; }
|
||||
.t-meta { display: flex; gap: 16px; flex-wrap: wrap; }
|
||||
.t-meta-item { font-size: 12px; color: #64748B; }
|
||||
.t-meta-item strong { color: #94A3B8; }
|
||||
|
||||
/* TOC 오른쪽 패널 */
|
||||
.toc-label { font-size: 10px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase; color: #6366F1; margin-bottom: 14px; flex-shrink: 0; }
|
||||
.toc-grid {
|
||||
display: grid; grid-template-columns: 1fr;
|
||||
gap: 8px; flex: 1; align-content: stretch; overflow: hidden;
|
||||
}
|
||||
.toc-item {
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
background: rgba(255,255,255,.05); border: 1px solid rgba(255,255,255,.08);
|
||||
border-radius: 8px; padding: 0 14px; cursor: pointer;
|
||||
transition: background .15s; min-height: 0;
|
||||
}
|
||||
.toc-item:hover { background: rgba(255,255,255,.1); }
|
||||
.toc-num { font-size: 11px; font-weight: 800; color: #818CF8; min-width: 20px; }
|
||||
.toc-text { font-size: 12px; color: #CBD5E1; font-weight: 500; }
|
||||
|
||||
/* ── Step 헤더 슬라이드 ─── */
|
||||
.slide-step {
|
||||
display: flex; flex-direction: column;
|
||||
justify-content: center; align-items: flex-start;
|
||||
padding: 64px 80px;
|
||||
}
|
||||
/* Step 배경색 변형 */
|
||||
.bg-s1 { background: linear-gradient(135deg, #1e3a8a 0%, #1d4ed8 100%); }
|
||||
.bg-s2 { background: linear-gradient(135deg, #3b0764 0%, #7c3aed 100%); }
|
||||
.bg-s3 { background: linear-gradient(135deg, #831843 0%, #db2777 100%); }
|
||||
.bg-s4 { background: linear-gradient(135deg, #064e3b 0%, #059669 100%); }
|
||||
.bg-s5 { background: linear-gradient(135deg, #7c2d12 0%, #ea580c 100%); }
|
||||
|
||||
.sh-big-num {
|
||||
font-size: 120px; font-weight: 900; color: rgba(255,255,255,.12);
|
||||
line-height: 1; margin-bottom: -20px; user-select: none;
|
||||
}
|
||||
.sh-eyebrow { font-size: 11px; font-weight: 700; letter-spacing: .15em; text-transform: uppercase; color: rgba(255,255,255,.6); margin-bottom: 16px; }
|
||||
.sh-title { font-size: 42px; font-weight: 800; color: #fff; line-height: 1.15; margin-bottom: 20px; }
|
||||
.sh-desc { font-size: 15px; color: rgba(255,255,255,.75); line-height: 1.7; max-width: 560px; margin-bottom: 28px; }
|
||||
.sh-pills { display: flex; gap: 8px; flex-wrap: wrap; }
|
||||
.sh-pill { background: rgba(255,255,255,.15); color: #fff; font-size: 11px; font-weight: 600; padding: 5px 12px; border-radius: 20px; }
|
||||
|
||||
/* ── 콘텐츠 슬라이드 ─── */
|
||||
.slide-content { background: #F8FAFC; display: flex; flex-direction: column; }
|
||||
.sc-header {
|
||||
display: flex; align-items: center; gap: 12px;
|
||||
padding: 20px 52px 16px; border-bottom: 2px solid #E2E8F0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.sc-header h2 { font-size: 20px; font-weight: 800; color: #0F172A; }
|
||||
.sc-body { flex: 1; overflow-y: auto; padding: 22px 52px 36px; }
|
||||
.sc-body-flex { display: flex; flex-direction: column; }
|
||||
|
||||
/* Task 뱃지 색상 */
|
||||
.task-badge {
|
||||
font-size: 10px; font-weight: 800; letter-spacing: .08em;
|
||||
text-transform: uppercase; padding: 4px 10px; border-radius: 6px; flex-shrink: 0;
|
||||
}
|
||||
.b1 { background: #DBEAFE; color: #1D4ED8; }
|
||||
.b2 { background: #EDE9FE; color: #7C3AED; }
|
||||
.b3 { background: #FCE7F3; color: #DB2777; }
|
||||
.b4 { background: #D1FAE5; color: #059669; }
|
||||
.b5 { background: #FFEDD5; color: #EA580C; }
|
||||
|
||||
/* ── 엔딩 슬라이드 ─── */
|
||||
.slide-end {
|
||||
background: linear-gradient(135deg, #0F172A 0%, #1E1B4B 100%);
|
||||
display: flex; flex-direction: column;
|
||||
justify-content: center; align-items: center; text-align: center;
|
||||
position: relative; overflow: hidden;
|
||||
}
|
||||
.end-deco1 {
|
||||
position: absolute; width: 400px; height: 400px; border-radius: 50%;
|
||||
background: radial-gradient(circle, rgba(99,102,241,.2) 0%, transparent 70%);
|
||||
top: -100px; right: -100px;
|
||||
}
|
||||
.end-deco2 {
|
||||
position: absolute; width: 300px; height: 300px; border-radius: 50%;
|
||||
background: radial-gradient(circle, rgba(139,92,246,.15) 0%, transparent 70%);
|
||||
bottom: -80px; left: -80px;
|
||||
}
|
||||
.end-label { font-size: 11px; font-weight: 700; letter-spacing: .2em; text-transform: uppercase; color: #6366F1; margin-bottom: 20px; position: relative; z-index: 1; }
|
||||
.end-title { font-size: 72px; font-weight: 900; color: #F8FAFC; margin-bottom: 24px; position: relative; z-index: 1; }
|
||||
.end-divider { width: 60px; height: 3px; background: #6366F1; border-radius: 2px; margin-bottom: 24px; position: relative; z-index: 1; }
|
||||
.end-subtitle { font-size: 18px; color: #94A3B8; margin-bottom: 12px; position: relative; z-index: 1; }
|
||||
.end-meta { font-size: 13px; color: #64748B; position: relative; z-index: 1; }
|
||||
|
||||
/* ══ 공용 컴포넌트 ═══════════════════════════════════════════ */
|
||||
.desc { font-size: 13.5px; color: #475569; line-height: 1.7; }
|
||||
code { font-family: 'Consolas', 'Courier New', monospace; background: #EEF2FF; color: #4338CA; padding: 2px 6px; border-radius: 4px; font-size: .9em; }
|
||||
|
||||
/* 파일 경로 뱃지 */
|
||||
.file-row { margin-bottom: 12px; }
|
||||
.file-pill { display: inline-block; background: #1E293B; color: #94A3B8; font-family: 'Consolas', monospace; font-size: 11px; padding: 4px 10px; border-radius: 6px; }
|
||||
|
||||
/* 코드 블록 */
|
||||
.code-wrap { border-radius: 10px; overflow: hidden; border: 1px solid #E2E8F0; }
|
||||
.code-hdr { display: flex; justify-content: space-between; align-items: center; padding: 8px 14px; background: #1E293B; }
|
||||
.code-fn { color: #94A3B8; font-size: 11px; font-family: 'Consolas', monospace; }
|
||||
.code-lang { color: #64748B; font-size: 10px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; }
|
||||
.code-wrap pre { margin: 0; max-height: 320px; overflow-y: auto; }
|
||||
.code-wrap pre code { font-size: 12.5px; line-height: 1.6; }
|
||||
|
||||
/* Callout */
|
||||
.callout { display: flex; gap: 12px; padding: 12px 16px; border-radius: 10px; margin-top: 14px; }
|
||||
.callout.note { background: #EFF6FF; border-left: 3px solid #3B82F6; }
|
||||
.callout.warn { background: #FFFBEB; border-left: 3px solid #F59E0B; }
|
||||
.callout.tip { background: #F0FDF4; border-left: 3px solid #22C55E; }
|
||||
.callout.danger { background: #FEF2F2; border-left: 3px solid #EF4444; }
|
||||
.callout .ci { font-size: 16px; flex-shrink: 0; margin-top: 1px; }
|
||||
.callout div { font-size: 12.5px; color: #334155; line-height: 1.6; }
|
||||
.callout strong { display: block; margin-bottom: 4px; color: #0F172A; font-size: 13px; }
|
||||
|
||||
/* 체크리스트 */
|
||||
.checklist { display: flex; flex-direction: column; gap: 14px; margin-top: 8px; }
|
||||
.check-item { display: flex; gap: 14px; align-items: flex-start; }
|
||||
.check-circle {
|
||||
width: 32px; height: 32px; border-radius: 50%; flex-shrink: 0;
|
||||
background: #EEF2FF; color: #4F46E5; font-size: 13px; font-weight: 800;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
}
|
||||
.check-item div { font-size: 13px; color: #334155; line-height: 1.65; }
|
||||
.check-item strong { display: block; color: #0F172A; margin-bottom: 2px; }
|
||||
|
||||
/* 테이블 */
|
||||
.ini-table { width: 100%; border-collapse: collapse; font-size: 13px; }
|
||||
.ini-table th { background: #1E293B; color: #94A3B8; padding: 10px 14px; text-align: left; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; }
|
||||
.ini-table td { padding: 10px 14px; border-bottom: 1px solid #E2E8F0; color: #334155; vertical-align: top; }
|
||||
.ini-table tr:last-child td { border-bottom: none; }
|
||||
.ini-table td:first-child { font-family: 'Consolas', monospace; color: #6D28D9; font-weight: 600; }
|
||||
.ini-table.compact td, .ini-table.compact th { padding: 6px 10px; font-size: 12px; }
|
||||
|
||||
/* ══ 진행 표시줄 & 네비게이션 ════════════════════════════════ */
|
||||
#progress-bar {
|
||||
position: fixed; top: 0; left: 0; right: 0; height: 3px;
|
||||
background: rgba(255,255,255,.08); z-index: 500;
|
||||
transition: left .28s cubic-bezier(.4,0,.2,1), right .28s cubic-bezier(.4,0,.2,1);
|
||||
}
|
||||
body.sb-open #progress-bar { left: 300px; }
|
||||
#progress-fill { height: 100%; background: #6366F1; transform: scaleX(0); transform-origin: left; transition: transform .32s ease; }
|
||||
|
||||
#nav {
|
||||
position: fixed; bottom: 0; left: 0; right: 0; height: 48px;
|
||||
background: rgba(15,23,42,.95); backdrop-filter: blur(12px);
|
||||
border-top: 1px solid rgba(255,255,255,.06);
|
||||
display: flex; align-items: center; justify-content: center; gap: 16px;
|
||||
z-index: 500;
|
||||
transition: left .28s cubic-bezier(.4,0,.2,1), right .28s cubic-bezier(.4,0,.2,1);
|
||||
}
|
||||
body.sb-open #nav { left: 300px; }
|
||||
#nav button {
|
||||
background: rgba(255,255,255,.08); border: 1px solid rgba(255,255,255,.1);
|
||||
color: #94A3B8; width: 32px; height: 32px; border-radius: 8px;
|
||||
font-size: 16px; cursor: pointer; transition: all .15s;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
}
|
||||
#nav button:hover:not(:disabled) { background: rgba(255,255,255,.15); color: #F1F5F9; }
|
||||
#nav button:disabled { opacity: 0.3; cursor: default; }
|
||||
#slide-counter { font-size: 11px; color: #64748B; min-width: 44px; text-align: center; }
|
||||
#dot-nav { display: flex; gap: 6px; align-items: center; }
|
||||
.dot { width: 6px; height: 6px; border-radius: 50%; background: rgba(255,255,255,.2); cursor: pointer; transition: all .2s; }
|
||||
.dot.active { background: #6366F1; transform: scale(1.4); }
|
||||
|
||||
/* 사이드바 토글 버튼 */
|
||||
#sb-toggle {
|
||||
position: fixed; top: 12px; left: 12px; z-index: 700;
|
||||
width: 36px; height: 36px; border-radius: 10px;
|
||||
background: rgba(15,23,42,.9); border: 1px solid rgba(255,255,255,.12);
|
||||
color: #94A3B8; font-size: 18px; cursor: pointer;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
transition: all .15s;
|
||||
}
|
||||
#sb-toggle:hover { background: rgba(255,255,255,.15); color: #F1F5F9; }
|
||||
|
||||
/* 도움말 모달 */
|
||||
#help-modal { display: none; position: fixed; inset: 0; background: rgba(0,0,0,.6); z-index: 900; align-items: center; justify-content: center; }
|
||||
#help-modal.open { display: flex; }
|
||||
.hm-box { background: #1E293B; border-radius: 16px; padding: 28px 32px; max-width: 480px; width: 90%; border: 1px solid rgba(255,255,255,.1); }
|
||||
.hm-title { color: #F8FAFC; font-size: 16px; font-weight: 700; margin-bottom: 20px; }
|
||||
.kb-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
|
||||
.kb-item { display: flex; gap: 10px; align-items: center; }
|
||||
.kb-key { background: #334155; color: #F1F5F9; font-size: 10px; font-family: monospace; padding: 3px 7px; border-radius: 4px; white-space: nowrap; flex-shrink: 0; }
|
||||
.kb-desc { color: #94A3B8; font-size: 12px; }
|
||||
.hm-close { margin-top: 20px; width: 100%; padding: 10px; background: #334155; border: none; color: #F1F5F9; border-radius: 8px; cursor: pointer; font-size: 13px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- 사이드바 토글 -->
|
||||
<button id="sb-toggle" onclick="toggleSidebar()" title="목차 (T)">☰</button>
|
||||
|
||||
<!-- 사이드바 -->
|
||||
<div id="sidebar">
|
||||
<div class="sb-head">
|
||||
<div class="sb-badge">{{SHORT_TITLE}}</div>
|
||||
<div class="sb-title">{{FULL_TITLE}}</div>
|
||||
</div>
|
||||
<div class="sb-list" id="sb-list"></div>
|
||||
<div class="sb-help-btn" onclick="document.getElementById('help-modal').classList.toggle('open')">⌨ 단축키 도움말</div>
|
||||
</div>
|
||||
|
||||
<!-- 진행 표시줄 -->
|
||||
<div id="progress-bar"><div id="progress-fill"></div></div>
|
||||
|
||||
<!-- 슬라이드 덱 -->
|
||||
<div id="deck">
|
||||
|
||||
<!-- SLIDES HERE -->
|
||||
|
||||
</div><!-- #deck -->
|
||||
|
||||
<!-- 네비게이션 -->
|
||||
<div id="nav">
|
||||
<button id="btn-prev" onclick="prevSlide()" disabled title="이전 (← PgUp)">←</button>
|
||||
<div id="dot-nav"></div>
|
||||
<span id="slide-counter">1 / 1</span>
|
||||
<button id="btn-next" onclick="nextSlide()" title="다음 (→ PgDn)">→</button>
|
||||
</div>
|
||||
|
||||
<!-- 도움말 모달 -->
|
||||
<div id="help-modal">
|
||||
<div class="hm-box">
|
||||
<div class="hm-title">⌨ 키보드 단축키</div>
|
||||
<div class="kb-grid">
|
||||
<div class="kb-item"><span class="kb-key">→ / PgDn</span><div class="kb-desc">다음 슬라이드</div></div>
|
||||
<div class="kb-item"><span class="kb-key">← / PgUp</span><div class="kb-desc">이전 슬라이드</div></div>
|
||||
<div class="kb-item"><span class="kb-key">Home</span><div class="kb-desc">첫 슬라이드로</div></div>
|
||||
<div class="kb-item"><span class="kb-key">End</span><div class="kb-desc">마지막 슬라이드로</div></div>
|
||||
<div class="kb-item"><span class="kb-key">T</span><div class="kb-desc">목차 사이드바 토글</div></div>
|
||||
<div class="kb-item"><span class="kb-key">Esc</span><div class="kb-desc">모달/사이드바 닫기</div></div>
|
||||
</div>
|
||||
<button class="hm-close" onclick="document.getElementById('help-modal').classList.remove('open')">닫기</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
/* ─── Mermaid (다이어그램 필요 시) ──────────────────────────── */
|
||||
mermaid.initialize({
|
||||
startOnLoad: true, securityLevel: 'loose', theme: 'base',
|
||||
themeVariables: { fontFamily: "'Segoe UI','Apple SD Gothic Neo',sans-serif", fontSize: '14px' },
|
||||
flowchart: { curve: 'basis', htmlLabels: true, nodeSpacing: 60, rankSpacing: 80 }
|
||||
});
|
||||
|
||||
/* ─── Slide Engine ──────────────────────────────────────────── */
|
||||
const slides = Array.from(document.querySelectorAll('.slide'));
|
||||
const TOTAL = slides.length;
|
||||
let current = 0;
|
||||
let animating = false;
|
||||
const DURATION = 320; // CSS transition 길이와 일치해야 함
|
||||
|
||||
function goToSlide(idx) {
|
||||
if (idx < 0 || idx >= TOTAL || idx === current || animating) return;
|
||||
animating = true;
|
||||
const isForward = idx > current;
|
||||
const prevIdx = current;
|
||||
current = idx;
|
||||
const prevEl = slides[prevIdx];
|
||||
const nextEl = slides[current];
|
||||
|
||||
// 다음 슬라이드를 트랜지션 없이 오프스크린에 배치
|
||||
nextEl.style.transition = 'none';
|
||||
nextEl.style.transform = isForward ? 'translateX(100%)' : 'translateX(-100%)';
|
||||
void nextEl.offsetWidth; // reflow 강제
|
||||
|
||||
// 트랜지션 복원 후 동시 이동
|
||||
nextEl.style.transition = '';
|
||||
prevEl.style.transform = isForward ? 'translateX(-100%)' : 'translateX(100%)';
|
||||
nextEl.style.transform = 'translateX(0)';
|
||||
nextEl.classList.add('active');
|
||||
prevEl.classList.remove('active');
|
||||
|
||||
setTimeout(() => { animating = false; }, DURATION);
|
||||
updateUI();
|
||||
}
|
||||
|
||||
function nextSlide() { goToSlide(current + 1); }
|
||||
function prevSlide() { goToSlide(current - 1); }
|
||||
|
||||
/* ─── Sidebar ───────────────────────────────────────────────── */
|
||||
const SB_WIDTH = 300;
|
||||
function toggleSidebar() { document.body.classList.toggle('sb-open'); }
|
||||
|
||||
/* ─── SLIDE_META → 사이드바 + 도트 네비 생성 ──────────────── */
|
||||
// ⚠️ idx 값이 DOM 순서와 반드시 일치해야 합니다 (0부터 시작)
|
||||
// The End 슬라이드는 SLIDE_META에 포함하지 않습니다
|
||||
const SLIDE_META = [
|
||||
// { idx: 0, label: '제목 · 목차', dot: '🏠', color: '#475569', step: false },
|
||||
// 여기에 슬라이드 메타데이터 추가
|
||||
];
|
||||
|
||||
const sbList = document.getElementById('sb-list');
|
||||
SLIDE_META.forEach(m => {
|
||||
const el = document.createElement('div');
|
||||
el.className = 'sb-item' + (m.step ? ' is-step' : '') + (m.idx === 0 ? ' active' : '');
|
||||
el.dataset.idx = m.idx;
|
||||
const dot = document.createElement('div');
|
||||
dot.className = 'sb-dot';
|
||||
dot.style.background = m.color + '22';
|
||||
dot.style.color = m.color;
|
||||
dot.textContent = m.dot;
|
||||
const label = document.createElement('div');
|
||||
label.className = 'sb-label';
|
||||
label.textContent = m.label;
|
||||
el.appendChild(dot);
|
||||
el.appendChild(label);
|
||||
el.addEventListener('click', () => goToSlide(+el.dataset.idx));
|
||||
sbList.appendChild(el);
|
||||
});
|
||||
|
||||
/* ─── 도트 네비 생성 ────────────────────────────────────────── */
|
||||
const dotNav = document.getElementById('dot-nav');
|
||||
slides.forEach((_, i) => {
|
||||
const d = document.createElement('div');
|
||||
d.className = 'dot' + (i === 0 ? ' active' : '');
|
||||
d.addEventListener('click', () => goToSlide(i));
|
||||
dotNav.appendChild(d);
|
||||
});
|
||||
|
||||
/* ─── UI 업데이트 ───────────────────────────────────────────── */
|
||||
const counter = document.getElementById('slide-counter');
|
||||
const progressFill = document.getElementById('progress-fill');
|
||||
const btnPrev = document.getElementById('btn-prev');
|
||||
const btnNext = document.getElementById('btn-next');
|
||||
const dots = Array.from(dotNav.children);
|
||||
|
||||
function updateUI() {
|
||||
counter.textContent = `${current + 1} / ${TOTAL}`;
|
||||
progressFill.style.transform = `scaleX(${(current + 1) / TOTAL})`;
|
||||
btnPrev.disabled = current === 0;
|
||||
btnNext.disabled = current === TOTAL - 1;
|
||||
dots.forEach((d, i) => d.classList.toggle('active', i === current));
|
||||
document.querySelectorAll('.sb-item').forEach(el => {
|
||||
el.classList.toggle('active', +el.dataset.idx === current);
|
||||
});
|
||||
}
|
||||
|
||||
/* ─── 키보드 단축키 ─────────────────────────────────────────── */
|
||||
document.addEventListener('keydown', e => {
|
||||
const modal = document.getElementById('help-modal');
|
||||
const isOpen = modal.classList.contains('open');
|
||||
switch (e.key) {
|
||||
case 'ArrowRight': case 'PageDown': e.preventDefault(); nextSlide(); break;
|
||||
case 'ArrowLeft': case 'PageUp': e.preventDefault(); prevSlide(); break;
|
||||
case 'Home': e.preventDefault(); goToSlide(0); break;
|
||||
case 'End': e.preventDefault(); goToSlide(TOTAL - 1); break;
|
||||
case 't': case 'T': if (!isOpen) toggleSidebar(); break;
|
||||
case 'Escape':
|
||||
if (isOpen) modal.classList.remove('open');
|
||||
else document.body.classList.remove('sb-open');
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
/* ─── 초기화 ────────────────────────────────────────────────── */
|
||||
hljs.highlightAll();
|
||||
updateUI();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 핵심 동작 원리
|
||||
|
||||
### 슬라이드 트랜지션
|
||||
모든 슬라이드는 `position: absolute; inset: 0`으로 겹쳐 있고, `transform: translateX(±100%)`로 오프스크린에 숨겨집니다. `goToSlide()`는 `void el.offsetWidth`(reflow 강제)를 호출해 position 재설정 후 트랜지션을 시작하므로, 방향성 있는 슬라이드-인/아웃 애니메이션이 구현됩니다.
|
||||
|
||||
### 사이드바 Push
|
||||
`body.sb-open` 클래스가 토글되면 `#deck`, `#nav`, `#progress-bar`가 CSS `left: 300px`으로 밀려납니다. 사이드바 자체는 `translateX(-300px) → translateX(0)` 전환입니다.
|
||||
|
||||
### SLIDE_META vs DOM 순서
|
||||
- JS 엔진은 `querySelectorAll('.slide')` 결과의 **배열 인덱스**를 사용
|
||||
- `SLIDE_META[i].idx`가 그 배열 인덱스와 일치해야 사이드바 클릭이 올바른 슬라이드로 이동
|
||||
- 슬라이드 div의 `data-idx` 속성은 **시각적 레퍼런스용**일 뿐, JS가 읽지 않음
|
||||
+224
@@ -0,0 +1,224 @@
|
||||
# 콘텐츠 레이아웃 패턴
|
||||
|
||||
`sc-body` 안에서 사용하는 레이아웃 패턴 모음입니다.
|
||||
|
||||
---
|
||||
|
||||
## 1. 단일 코드 블록
|
||||
|
||||
```html
|
||||
<div class="file-row"><span class="file-pill">src/MyFile.cpp</span></div>
|
||||
<p class="desc" style="margin-bottom:12px;">{{설명 텍스트}}</p>
|
||||
<div class="code-wrap">
|
||||
<div class="code-hdr">
|
||||
<span class="code-fn">MyFile.cpp — functionName()</span>
|
||||
<span class="code-lang">C++</span>
|
||||
</div>
|
||||
<pre><code class="language-cpp">// 코드 여기에
|
||||
void MyFunction() {
|
||||
// ...
|
||||
}</code></pre>
|
||||
</div>
|
||||
```
|
||||
|
||||
**지원 언어**: `language-cpp`, `language-javascript`, `language-python`, `language-plaintext` 등 Highlight.js 지원 언어 모두 사용 가능.
|
||||
|
||||
---
|
||||
|
||||
## 2. 화면을 채우는 코드 블록 (sc-body-flex 필요)
|
||||
|
||||
```html
|
||||
<!-- sc-body에 sc-body-flex 클래스 추가 필요 -->
|
||||
<div class="file-row"><span class="file-pill">src/MyFile.cpp</span></div>
|
||||
<div style="display:flex; flex-direction:column; flex:1; min-height:0; gap:10px; margin-top:8px;">
|
||||
<div class="code-wrap" style="flex:1; display:flex; flex-direction:column; min-height:0;">
|
||||
<div class="code-hdr">
|
||||
<span class="code-fn">MyFile.cpp</span>
|
||||
<span class="code-lang">C++</span>
|
||||
</div>
|
||||
<pre style="flex:1; overflow-y:auto; margin:0;">
|
||||
<code class="language-cpp">// 긴 코드</code>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 2×2 스텝 그리드 (단계별 절차)
|
||||
|
||||
4단계 절차를 카드로 표현할 때 사용합니다. `sc-body-flex` 필요.
|
||||
|
||||
```html
|
||||
<!-- sc-body에 sc-body-flex 클래스 추가 필요 -->
|
||||
<p class="desc" style="margin-bottom:6px;">{{전체 설명}}</p>
|
||||
<div style="display:grid; grid-template-columns:1fr 1fr; grid-template-rows:1fr 1fr; gap:12px; flex:1; min-height:0; margin-top:10px;">
|
||||
|
||||
<!-- 카드 ① -->
|
||||
<div style="background:#F5F3FF; border-radius:10px; padding:14px 16px 12px; border-left:3px solid #7C3AED; display:flex; flex-direction:column; gap:8px; overflow:hidden;">
|
||||
<div style="display:flex; align-items:center; gap:8px; font-size:13px; font-weight:800; color:#3730A3; flex-shrink:0;">
|
||||
<span style="background:#7C3AED; color:#fff; width:22px; height:22px; border-radius:50%; display:flex; align-items:center; justify-content:center; font-size:11px;">①</span>
|
||||
{{단계 제목}}
|
||||
</div>
|
||||
<pre style="background:rgba(67,56,202,.1); border-radius:6px; padding:8px 12px; flex:1; overflow:auto; font-family:'Consolas','Courier New',monospace; font-size:10.5px; color:#312E81; line-height:1.65; white-space:pre; margin:0;">{{코드 또는 내용}}</pre>
|
||||
</div>
|
||||
|
||||
<!-- 카드 ② ③ ④ — 동일 구조 반복 -->
|
||||
|
||||
</div>
|
||||
```
|
||||
|
||||
**카드 수 조정**: 3개는 `grid-template-columns: 1fr 1fr 1fr; grid-template-rows: 1fr;`, 6개는 `1fr 1fr 1fr / 1fr 1fr`.
|
||||
|
||||
---
|
||||
|
||||
## 4. 2컬럼 레이아웃 (테이블 + 코드)
|
||||
|
||||
```html
|
||||
<!-- sc-body에 sc-body-flex 클래스 추가 필요 -->
|
||||
<div class="file-row"><span class="file-pill">config.ini</span></div>
|
||||
<div style="display:flex; gap:18px; flex:1; min-height:0; margin-top:8px;">
|
||||
|
||||
<!-- 좌: 필드 설명 테이블 -->
|
||||
<div style="flex:0 0 44%; display:flex; flex-direction:column; min-width:0;">
|
||||
<div style="font-size:10px; font-weight:700; text-transform:uppercase; letter-spacing:.1em; color:#EA580C; margin-bottom:7px;">📋 필드 구조</div>
|
||||
<table class="ini-table compact">
|
||||
<thead><tr><th>필드</th><th>설명</th><th>예시</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>Field1</td><td>설명</td><td>값</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- 우: 코드 + callout -->
|
||||
<div style="flex:1; display:flex; flex-direction:column; gap:10px; overflow-y:auto; min-width:0;">
|
||||
<div style="font-size:10px; font-weight:700; text-transform:uppercase; letter-spacing:.1em; color:#EA580C; margin-bottom:7px;">✏️ 등록 방법</div>
|
||||
<div class="code-wrap" style="flex:1; min-height:0;">
|
||||
<div class="code-hdr"><span class="code-fn">config.ini</span><span class="code-lang">INI</span></div>
|
||||
<pre style="flex:1; overflow-y:auto;"><code class="language-plaintext">; 코드</code></pre>
|
||||
</div>
|
||||
<div class="callout note" style="flex-shrink:0;">
|
||||
<span class="ci">💡</span>
|
||||
<div><strong>팁 제목</strong>팁 내용</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Callout 변형
|
||||
|
||||
```html
|
||||
<!-- 정보 (파랑) -->
|
||||
<div class="callout note">
|
||||
<span class="ci">💡</span>
|
||||
<div><strong>제목</strong>내용</div>
|
||||
</div>
|
||||
|
||||
<!-- 주의 (노랑) -->
|
||||
<div class="callout warn">
|
||||
<span class="ci">⚠️</span>
|
||||
<div><strong>제목</strong>내용</div>
|
||||
</div>
|
||||
|
||||
<!-- 성공/팁 (초록) -->
|
||||
<div class="callout tip">
|
||||
<span class="ci">✅</span>
|
||||
<div><strong>제목</strong>내용</div>
|
||||
</div>
|
||||
|
||||
<!-- 위험 (빨강) -->
|
||||
<div class="callout danger">
|
||||
<span class="ci">🚨</span>
|
||||
<div><strong>제목</strong>내용</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 체크리스트
|
||||
|
||||
검증 단계나 확인 사항 나열에 사용합니다.
|
||||
|
||||
```html
|
||||
<p class="desc" style="margin-bottom:16px;">{{서두}}</p>
|
||||
<div class="checklist">
|
||||
<div class="check-item">
|
||||
<div class="check-circle">①</div>
|
||||
<div><strong>항목 제목</strong>상세 설명</div>
|
||||
</div>
|
||||
<div class="check-item">
|
||||
<div class="check-circle">②</div>
|
||||
<div><strong>항목 제목</strong>상세 설명</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Mermaid 다이어그램
|
||||
|
||||
```html
|
||||
<div class="mermaid">
|
||||
flowchart LR
|
||||
A[시작] --> B{조건}
|
||||
B -->|Yes| C[처리]
|
||||
B -->|No| D[종료]
|
||||
C --> D
|
||||
</div>
|
||||
```
|
||||
|
||||
**주의**: Mermaid는 `mermaid.initialize()`가 실행된 이후 DOM에 있어야 렌더링됩니다.
|
||||
슬라이드가 처음부터 hidden 상태라면 첫 번째 슬라이드가 아닌 경우 렌더링이 지연될 수 있습니다.
|
||||
해결: `mermaid.initialize({ startOnLoad: true })`가 보일러플레이트에 포함되어 있으므로 보통 정상 동작합니다.
|
||||
|
||||
---
|
||||
|
||||
## 8. 플로우 & 파일 목록 (2열 Overview)
|
||||
|
||||
전체 흐름과 관련 파일을 한 슬라이드에 나란히 보여줄 때 사용합니다.
|
||||
|
||||
```html
|
||||
<!-- sc-body에 sc-body-flex 클래스 추가 필요 -->
|
||||
<div style="display:flex; gap:28px; flex:1; min-height:0; margin-top:4px;">
|
||||
|
||||
<!-- 좌: 플로우 다이어그램 -->
|
||||
<div style="flex:1; display:flex; flex-direction:column; min-width:0;">
|
||||
<div style="font-size:10px; font-weight:700; text-transform:uppercase; letter-spacing:.1em; color:#7C3AED; margin-bottom:12px;">🔄 전체 플로우</div>
|
||||
<div class="mermaid" style="flex:1; overflow:auto;">
|
||||
flowchart TD
|
||||
A[단계 1] --> B[단계 2] --> C[단계 3]
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 우: 파일 목록 -->
|
||||
<div style="flex:0 0 260px; display:flex; flex-direction:column; min-width:0;">
|
||||
<div style="font-size:10px; font-weight:700; text-transform:uppercase; letter-spacing:.1em; color:#1D4ED8; margin-bottom:12px;">📁 수정 파일</div>
|
||||
<!-- 파일 항목 반복 -->
|
||||
<div style="display:flex; flex-direction:column; gap:8px; overflow-y:auto; flex:1;">
|
||||
<div style="background:#F1F5F9; border-radius:8px; padding:10px 14px; border-left:3px solid #1D4ED8;">
|
||||
<div style="font-family:monospace; font-size:11px; color:#1D4ED8; font-weight:700;">파일명.cpp</div>
|
||||
<div style="font-size:12px; color:#475569; margin-top:3px;">파일 역할 설명</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 패턴 선택 가이드
|
||||
|
||||
| 콘텐츠 유형 | 권장 패턴 |
|
||||
|------------|----------|
|
||||
| 코드 설명 + 짧은 코드 | 패턴 1 (단일 코드 블록) |
|
||||
| 긴 코드 전체 표시 | 패턴 2 (화면 채우는 코드) |
|
||||
| 단계별 절차 (4단계) | 패턴 3 (2×2 스텝 그리드) |
|
||||
| 설정 파일 필드 설명 | 패턴 4 (2컬럼 테이블+코드) |
|
||||
| 주의사항/팁 강조 | 패턴 5 (Callout) |
|
||||
| 체크리스트/검증 | 패턴 6 (체크리스트) |
|
||||
| 전체 흐름 시각화 | 패턴 7 (Mermaid) |
|
||||
| 흐름 + 파일 목록 | 패턴 8 (2열 Overview) |
|
||||
Executable
+134
@@ -0,0 +1,134 @@
|
||||
# 슬라이드 타입별 HTML 템플릿
|
||||
|
||||
각 슬라이드 타입의 HTML 구조입니다. `<!-- SLIDES HERE -->` 위치에 삽입하세요.
|
||||
|
||||
---
|
||||
|
||||
## 1. 제목 + 목차 슬라이드 (slide-title)
|
||||
|
||||
프레젠테이션의 첫 슬라이드. 좌측 = 제목 정보, 우측 = 목차 그리드.
|
||||
|
||||
```html
|
||||
<div class="slide slide-title active" data-idx="0">
|
||||
<!-- 좌측: 제목 영역 -->
|
||||
<div class="tc-left">
|
||||
<div class="t-eyebrow">{{CATEGORY}} · {{SUBTITLE}}</div>
|
||||
<div class="t-title">{{MAIN TITLE}}</div>
|
||||
<div class="t-subtitle">{{DESCRIPTION}}</div>
|
||||
<div class="t-meta">
|
||||
<div class="t-meta-item">🖥 <strong>N 슬라이드</strong></div>
|
||||
<div class="t-meta-item">⏱ <strong>약 X분</strong></div>
|
||||
<div class="t-meta-item">📁 <strong>X개 파일</strong></div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 우측: 목차 그리드 -->
|
||||
<div class="tc-right">
|
||||
<div class="toc-label">📋 목차</div>
|
||||
<div class="toc-grid">
|
||||
<div class="toc-item" onclick="goToSlide(N)">
|
||||
<span class="toc-num">01</span>
|
||||
<span class="toc-text">{{TOC ITEM 1}}</span>
|
||||
</div>
|
||||
<div class="toc-item" onclick="goToSlide(N)">
|
||||
<span class="toc-num">02</span>
|
||||
<span class="toc-text">{{TOC ITEM 2}}</span>
|
||||
</div>
|
||||
<!-- 항목 추가 -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
**팁**: 목차 항목이 많을수록 각 행이 작아집니다. 5개 이하일 때 가장 보기 좋습니다. `goToSlide(N)`의 N은 해당 섹션의 첫 번째 슬라이드 DOM 인덱스입니다.
|
||||
|
||||
---
|
||||
|
||||
## 2. Step 헤더 슬라이드 (slide-step)
|
||||
|
||||
새로운 단계(Step)를 시작할 때 사용하는 전환 슬라이드. 강조색 그라디언트 배경.
|
||||
|
||||
```html
|
||||
<div class="slide slide-step bg-s1" data-idx="N">
|
||||
<!-- bg-s1~bg-s5 중 선택: s1=파랑, s2=보라, s3=핑크, s4=초록, s5=주황 -->
|
||||
<div class="sh-big-num">1</div> <!-- 단계 번호 (장식용) -->
|
||||
<span class="sh-eyebrow">Step 1 · {{CATEGORY}}</span>
|
||||
<div class="sh-title">{{STEP TITLE}}</div>
|
||||
<div class="sh-desc">{{STEP 설명 — 이 단계에서 무엇을 하는지 1~2문장}}</div>
|
||||
<div class="sh-pills">
|
||||
<span class="sh-pill">Task 1-1 · {{태스크명}}</span>
|
||||
<span class="sh-pill">Task 1-2 · {{태스크명}}</span>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
**배경색 선택 가이드**: 단계 순서에 따라 s1→s2→s3→s4→s5 순으로 사용하면 자연스러운 색상 흐름이 생깁니다.
|
||||
|
||||
---
|
||||
|
||||
## 3. 콘텐츠 슬라이드 (slide-content)
|
||||
|
||||
실제 내용이 담기는 슬라이드. 헤더 + 본문 영역으로 구성됩니다.
|
||||
|
||||
### 기본 구조
|
||||
|
||||
```html
|
||||
<div class="slide slide-content" data-idx="N">
|
||||
<div class="sc-header">
|
||||
<span class="task-badge b1">Task 1-1</span> <!-- b1~b5 중 해당 Step 색상과 일치시킬 것 -->
|
||||
<h2>{{슬라이드 제목}}</h2>
|
||||
</div>
|
||||
<div class="sc-body">
|
||||
<!-- 여기에 레이아웃 패턴 삽입 (layout-patterns.md 참조) -->
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### 콘텐츠가 많은 경우 (flex 확장)
|
||||
|
||||
```html
|
||||
<div class="slide slide-content" data-idx="N">
|
||||
<div class="sc-header">
|
||||
<span class="task-badge b2">Task 2-1</span>
|
||||
<h2>{{슬라이드 제목}}</h2>
|
||||
</div>
|
||||
<div class="sc-body sc-body-flex" style="padding-top:16px;">
|
||||
<!-- flex: 1로 높이를 최대한 활용하는 레이아웃 -->
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
**`sc-body-flex` 사용 시기**: 코드 블록, 다단 레이아웃, 테이블 등이 화면 높이를 최대한 채워야 할 때.
|
||||
|
||||
---
|
||||
|
||||
## 4. 엔딩 슬라이드 (slide-end)
|
||||
|
||||
프레젠테이션 마지막 슬라이드. SLIDE_META에 **포함하지 않습니다**.
|
||||
|
||||
```html
|
||||
<div class="slide slide-end" data-idx="N">
|
||||
<div class="end-deco1"></div>
|
||||
<div class="end-deco2"></div>
|
||||
<div class="end-label">End of Presentation</div>
|
||||
<div class="end-title">The End</div>
|
||||
<div class="end-divider"></div>
|
||||
<div class="end-subtitle">{{프레젠테이션 제목}}</div>
|
||||
<div class="end-meta">{{부제 — 예: "X단계 · Y개 Task · Z개 파일 수정"}}</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 태스크 뱃지 색상 매핑
|
||||
|
||||
Step 헤더의 배경색과 Task 슬라이드의 뱃지 색상을 일치시키세요:
|
||||
|
||||
| Step 배경 | Task 뱃지 클래스 | 색상 |
|
||||
|-----------|-----------------|------|
|
||||
| `bg-s1` | `b1` | 파랑 (#1D4ED8) |
|
||||
| `bg-s2` | `b2` | 보라 (#7C3AED) |
|
||||
| `bg-s3` | `b3` | 핑크 (#DB2777) |
|
||||
| `bg-s4` | `b4` | 초록 (#059669) |
|
||||
| `bg-s5` | `b5` | 주황 (#EA580C) |
|
||||
|
||||
SLIDE_META의 `color`도 동일한 색상 코드를 사용하면 사이드바의 색상이 통일됩니다.
|
||||
Reference in new issue
Block a user