Generate a static PNG figure for a blog post using Neo-Brutalism style (30 patterns: flow, comparison, architecture, data viz, charts, infographic, concept diagram, etc.). Invoke when: user wants any visual — figure, image, diagram, chart, graph, infographic, illustration, 시각화, 개념도, 도표, 그래프, 차트, 인포그래픽, 도식, 그림 — and context is a blog post or MDX file. Also invoke when an MDX file references a missing image in /blog/images/, or when user says "이 섹션에 이미지", "한 장으로 보여줘", "블로그 그림", "figure 만들어", "블로그 이미지", "다이어그램". NOT for interactive UI components, web pages, or app screens — those go to frontend-design.
Generate Neo-Brutalism styled figure images for blog posts: HTML → browser → PNG.
AskUserQuestion with ASCII art previews (see Pattern Selection below)references/design-rules.md for all design constraints. Write standalone HTML to /tmp/blog-figure-{name}.html linking assets/figure.cssapps/content/src/content/blog/images/{slug}/.md or .mdx file, insert the image tag at the contextually correct location (see Document Insertion below)패턴을 고르기 전에, 블로그에서 시각화할 핵심 개념을 추출하여 사용자에게 확인받는다. Figure의 내용이 블로그와 동떨어지거나 너무 추상적이 되는 것을 방지하는 핵심 단계.
| 항목 | 설명 | 예시 |
|---|---|---|
| 핵심 메시지 | 이 Figure가 전달해야 할 한 문장 | "사용자가 말하는 것과 실제 행동은 다르다" |
| 키워드 | Figure에 실제로 들어갈 단어 3~5개 | "좋은데요", "0건", "말 vs 행동" |
| 구조 | 개념 간 관계 유형 | 대비(A vs B), 순서(A→B→C), 계층(A⊃B), 순환(A↻B) |
| 강조점 | 보는 사람이 가장 먼저 인식해야 할 것 | "0건이라는 숫자의 충격" |
블로그 글에서 다음을 찾는다:
AskUserQuestion으로 2~3가지 시각화 방향을 제시한다. 각 옵션은 "이 글에서 무엇을 figure로 만들지"에 대한 서로 다른 해석이다.
AskUserQuestion({
questions: [{
question: "어떤 장면을 Figure로 만들까요?",
header: "Content Brief",
multiSelect: true,
options: [
{
label: "해석 A: {1줄 핵심 메시지}",
description: "키워드: {단어1}, {단어2}, {단어3}",
markdown: "**구조**: {관계 유형}\n**강조점**: {가장 눈에 띄어야 할 것}\n**근거**: 블로그에서 이 부분이 Figure로 적합한 이유 1줄"
},
{
label: "해석 B: {1줄 핵심 메시지}",
description: "키워드: {단어1}, {단어2}, {단어3}",
markdown: "**구조**: {관계 유형}\n**강조점**: {가장 눈에 띄어야 할 것}\n**근거**: 블로그에서 이 부분이 Figure로 적합한 이유 1줄"
},
{
label: "해석 C: {1줄 핵심 메시지}",
description: "키워드: {단어1}, {단어2}, {단어3}",
markdown: "**구조**: {관계 유형}\n**강조점**: {가장 눈에 띄어야 할 것}\n**근거**: 블로그에서 이 부분이 Figure로 적합한 이유 1줄"
}
]
}]
})
중요: 각 해석은 글의 서로 다른 부분/관점을 포착해야 한다. 같은 내용을 다른 말로 바꾼 3개가 아니라, 진짜로 다른 장면 3개를 제시하라.
사용자가 Brief를 확인하면, 그 Brief의 구조가 패턴 선택을 자연스럽게 좁힌다:
| Brief 구조 | 적합한 패턴 (우선순위) |
|---|---|
| 대비 (A vs B) | Comparison, Flow (split), Matrix, Network |
| 순서 (A→B→C) | Flow, Journey, Timeline, Storyboard |
| 계층 (A⊃B⊃C) | Architecture, Hierarchy, Isometric, Schema |
| 순환 (A↻B) | Loop, State, Graph |
| 수치 비교 | Data Viz, Funnel, Timeline, Waffle, Dumbbell, Bullet |
| 비율/구성비 | Waffle, Treemap, Funnel |
| 다차원 평가 | Radar, Matrix |
| 순위 변화 | Slope, Data Viz |
| 밀도/빈도 | Heatmap, Waffle |
| 실적/목표 | Bullet, Data Viz |
| 트렌드 요약 | Sparkline Grid, Data Viz |
| 증감 분해 | Waterfall, Data Viz |
| 인용/선언 | Typographic Statement |
| 상호작용 | Interaction, Terminal, IconDiagram, Storyboard |
| 추상/공간 구조 | Isometric, Network, Graph |
| 시스템 연결 | IconDiagram, Architecture, Interaction |
After understanding context, use AskUserQuestion to let the user pick from the 4 most relevant patterns. Each option MUST include a markdown field with an ASCII art preview showing the pattern's layout structure.
Analyze the user's content and rank all 30 patterns by relevance:
Top 4를 선택하여 아래처럼 AskUserQuestion 호출:
AskUserQuestion({
questions: [{
question: "어떤 Figure 패턴이 가장 적합할까요?",
header: "패턴 선택",
multiSelect: false,
options: [
{
label: "{Pattern 1 이름}",
description: "{왜 이 패턴이 적합한지 1줄 설명}",
markdown: "{ASCII art preview}"
},
// ... 3개 더
]
}]
})
4개 패턴을 결정한 뒤 AskUserQuestion 호출 전에
references/pattern-previews.md를 읽고 해당 패턴의 ASCII art를 가져와라.
User가 "사용자 인터뷰 프로세스를 시각화해줘"라고 요청한 경우:
AskUserQuestion({
questions: [{
question: "어떤 Figure 패턴이 가장 적합할까요?",
header: "패턴 선택",
multiSelect: false,
options: [
{
label: "Flow (추천)",
description: "인터뷰 단계를 수직 플로우로 표현. 프로세스 시각화에 최적",
markdown: "┌──────────────────────────────────┐\n│ ┌──────────────┐ │\n│ │ 맥락 확인 │ │\n│ └──────┬───────┘ │\n│ ▼ │\n│ ┌──────────────┐ │\n│ │ 사례 복기 │ │\n│ └──────┬───────┘ │\n│ ▼ │\n│ ┌──────────────┐ │\n│ │ 니즈 발견 │ │\n│ └──────────────┘ │\n└──────────────────────────────────┘"
},
{
label: "Journey",
description: "인터뷰이의 여정을 수평 터치포인트로 표현",
markdown: "┌──────────────────────────────────┐\n│ ①─────────②─────────③────────④ │\n│ 준비 라포 질문 정리 │\n└──────────────────────────────────┘"
},
{
label: "Timeline",
description: "인터뷰 시간 배분을 비율로 시각화",
markdown: "┌──────────────────────────────────┐\n│ ┌────────┬──────────┬────────┐ │\n│ │ 라포 │ 질문 │ 정리 │ │\n│ │ 3min │ 5min │ 2min │ │\n│ └────────┴──────────┴────────┘ │\n└──────────────────────────────────┘"
},
{
label: "Comparison",
description: "좋은 인터뷰 vs 나쁜 인터뷰를 좌우 대비",
markdown: "┌──────────────────────────────────┐\n│ ┌──────────┐ ┌──────────┐ │\n│ │ 나쁜방법 │VS│ 좋은방법 │ │\n│ │ ┌──────┐ │ │ ┌──────┐ │ │\n│ │ │평가요청│ │ │ │맥락확인│ │ │\n│ │ └──────┘ │ │ └──────┘ │ │\n│ └──────────┘ └──────────┘ │\n└──────────────────────────────────┘"
}
]
}]
})
중요: markdown 필드에는 해당 컨텍스트에 맞는 실제 키워드를 넣어라. 제네릭 플레이스홀더(Step 1, Card 1)가 아닌 실제 내용을 반영한 프리뷰를 보여줘야 사용자가 판단할 수 있다.
사용자가 .md 또는 .mdx 파일을 제공한 경우, PNG 저장 후 해당 파일에 이미지를 삽입한다.
## 헤딩 전.mdx):
<Figure src="/blog/images/{slug}/{filename}.png" alt="설명" caption="캡션" />
.md):

---) 내부에는 삽입하지 않는다<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=1440">
<link rel="stylesheet" href="file://{SKILL_DIR}/assets/figure.css">
</head>
<body>
<!-- figure content here -->
</body>
</html>
Replace {SKILL_DIR} with the absolute path to this skill's directory (the directory containing this SKILL.md). Resolve via the file path of this skill file, e.g. if SKILL.md is at /path/to/skills/blog-figure/SKILL.md, then {SKILL_DIR} = /path/to/skills/blog-figure.
30개 패턴 전체 검수용 gallery:
python3 {SKILL_DIR}/scripts/render_pattern_previews.py --clean --output-dir /tmp/blog-figure-previews
검수용 gallery는 file:///tmp/blog-figure-previews/index.html 이다. Playwright MCP/CLI는 file://를 차단할 수 있으니 필요하면 python3 -m http.server 8123 --directory /private/tmp 후 http://127.0.0.1:8123/blog-figure-previews/index.html로 연다. 상세 검수는 ?density=detail 쿼리를 붙여 2열 확대 모드로 본다. 상단 ready counter가 30 / 30 ready가 된 뒤 캡처하라.
Chrome DevTools MCP (preferred when Chrome is open):
mcp__chrome-devtools__emulate → viewport {width:1440, height:810, deviceScaleFactor:2} (retina 2880×1620)mcp__chrome-devtools__navigate_page → file:///tmp/blog-figure-{name}.htmlmcp__chrome-devtools__take_screenshot → filePath: {target}.pngmcp__chrome-devtools__emulate → viewport null (reset)Playwright MCP:
mcp__playwright__browser_resize → 1440×810mcp__playwright__browser_navigate → file:///tmp/blog-figure-{name}.htmlmcp__playwright__browser_take_screenshot → filename: {target}.pngPlaywright CLI:
npx playwright screenshot --viewport-size="1440,810" file:///tmp/blog-figure-{name}.html {target}.png
npx playwright screenshot --viewport-size="1600,4200" --wait-for-selector="body[data-gallery-ready='1']" --wait-for-timeout=3000 "http://127.0.0.1:8123/blog-figure-previews/index.html?density=detail" /tmp/blog-figure-previews/gallery-playwright.png
Chrome CLI:
google-chrome --headless --disable-gpu --hide-scrollbars --virtual-time-budget=5000 --window-size=1600,4200 --screenshot=/tmp/blog-figure-previews/gallery-chrome.png "file:///tmp/blog-figure-previews/index.html?density=detail"
캡처 실패 시 복구:
Read로 확인 후 file:// 경로가 올바른지 점검. {SKILL_DIR} 경로가 실제 figure.css 위치와 일치하는지 확인document.fonts.ready.then() 래핑 누락 여부 확인| Pattern | Use case | Key classes |
|---|---|---|
| Comparison | X vs Y, 좌우 대비 | .split, .vs-badge |
| Flow | 단계별 프로세스 | .flow-card, .arrow-down, .icon |
| Timeline | 시간 배분, 비율 | .timeline, .tl-block |
| Concept | 관계도, 개념 비교 | .concept-block |
| Architecture | 시스템 구성도, 레이어 | .arch, .arch-layer, .arch-node |
| Interaction | 시퀀스, 요청/응답 | .seq, .seq-msg |
| State | 상태 전이, 라이프사이클 | .state-chain, .state-node |
| Schema | DB 모델, 엔티티 관계 | .schema-table, .schema-field |
| Hierarchy | 트리, 조직도 | .tree, .tree-node, .tree-level |
| Matrix | 2x2 분석, 비교표 | .matrix, .matrix-cell |
| Journey | 사용자 여정, 터치포인트 | .journey, .journey-step |
| Funnel | 전환율, 단계별 감소 | .funnel, .funnel-stage |
| Loop | 순환 프로세스, 피드백 | .loop, .loop-node |
| Data Viz | 수치 비교, 바 차트 | .bar-chart, .bar-row |
| Storyboard | 시나리오, 단계별 장면 | .storyboard, .story-panel |
| Terminal | CLI 시각화, 터미널 UI | .terminal, .terminal-card, .terminal-option |
| Isometric | 3D 블록, 레이어, 와이어프레임 | SVG <polygon>, .iso |
| IconDiagram | 기술 다이어그램, 아이콘 연결 | SVG <rect>, <marker> |
| Network | 노드 네트워크, 추상 관계 | <canvas>, JS render |
| Graph | 포스-다이렉티드 그래프 | D3.js, <svg> |
| Waffle | 비율 체감, 퍼센트 | SVG <rect> 10x10 grid |
| Typographic Statement | 에디토리얼 인용, 핵심 정의 | SVG <text>, quote card |
| Slope | 전후 순위 변화, 랭킹 이동 | SVG <line>, <circle> |
| Treemap | 면적 비례 구성비 | D3.js treemap |
| Radar | 다축 프로파일 비교 | SVG + JS (cos/sin) |
| Dumbbell | 두 값 사이 갭/범위 | SVG <circle> x2 + <line> |
| Heatmap | 2D 빈도/밀도 분포 | <canvas>, JS lerp |
| Bullet | 실적 vs 목표, KPI | SVG nested <rect> + <line> |
| Sparkline Grid | 다수 항목 트렌드 요약 | SVG + JS, <polyline>, <polygon> |
| Waterfall | 증감 분해, 누적 변화 | SVG floating <rect> + connector |
Full HTML examples by category — 선택한 패턴이 속한 파일만 읽어라:
| Category | File | Patterns |
|---|---|---|
| Layout | references/patterns-layout.md | Comparison, Flow, Timeline, Concept, Architecture, Interaction, State, Schema, Hierarchy, Matrix, Journey, Funnel, Loop, Storyboard, Terminal (15) |
| Data Viz (static) | references/patterns-dataviz-static.md | Data Viz, Waffle, Slope, Dumbbell, Bullet, Waterfall (6) |
| Data Viz (dynamic) | references/patterns-dataviz-dynamic.md | Treemap, Radar, Heatmap, Sparkline Grid (4) |
| Visual | references/patterns-visual.md | Isometric, IconDiagram, Network, Graph, Typographic Statement (5) |
Full design constraints are in references/design-rules.md. Read it before writing HTML.
이 PNG는 모바일에서 원본의 25% 크기로 보인다. 손톱만한 크기에서도 패턴 구조와 핵심 키워드가 인식되어야 한다.
적은 수의 큰 컴포넌트 > 많은 수의 작은 컴포넌트
| 패턴 | 최대 수량 | 이유 |
|---|---|---|
| Flow | 3단계 | 3개면 비교 충분, 5개면 텍스트 덩어리 |
| Timeline | 3블록 | 블록 크기↑, 라벨 가독성↑ |
| Storyboard | 4패널 (2×2) | 패널 크기 2배 확보 |
| Bar chart | 4행 | 바 높이 충분히 확보 |
| Architecture | 3레이어, 레이어당 3노드 (2노드면 너무 빈약) | 시스템 구조의 풍부함 |
| Split 비교 | 양쪽 각 2~3카드 | 카드 크기 유지 |
| Journey | 4단계 | dot 간 여백 확보 |
| Schema | 3테이블, 테이블당 3~4필드 | 글자 크기 유지 |
| Terminal | 3카드, 카드당 옵션 2개 (max 3) | 질문 max 12자 1줄, <br> 금지 |
| Matrix | 2×2. 코너 4자 이내 또는 빈칸 | 축 라벨이 의미 전달 |
| Waffle | 1 grid (10x10), 2 카테고리 | 총 ~8단어 (범례 포함) |
| Typographic | 1 primary text (max 8단어) | attribution max 4단어 |
| Slope | max 5항목, 2시점 | 항목명 1단어 |
| Treemap | max 6~8 cells | 라벨 1단어, 80px 미만 숨김 |
| Radar | max 5축, 1~2 series | 축 라벨 1단어 |
| Dumbbell | max 5 rows | 라벨 max 3단어 |
| Heatmap | max 7x5 grid (35 cells) | 셀 ~110x90px |
| Bullet | max 3~4 charts | 범위 3단계 + 타겟 마커 |
| Sparkline Grid | max 6 sparklines (3x2) | 항목당 라벨 1단어 + 값 1개 |
| Waterfall | max 6~8 bars | 라벨 1~2단어, 시작/합계 포함 |