# Design Agent - Technology Research Report Date: 2026-03-24 --- ## 1. CSS Grid for Slide Layouts ### 1.1 Fixed-Viewport Approach (16:9, 1280x720) **Recommended technique: Fixed container + CSS transform scaling.** The slide container should be authored at a fixed "normal" size (1280x720), then scaled to fit any viewport using `transform: scale()`. This is the same approach used by reveal.js, the dominant HTML presentation framework. ```css .slide { width: 1280px; height: 720px; aspect-ratio: 16 / 9; overflow: hidden; position: relative; } ``` For preview/embedding, wrap in a container that calculates a scale factor: ```css .slide-wrapper { width: 100%; max-width: 1280px; aspect-ratio: 16 / 9; overflow: hidden; } .slide-wrapper > .slide { transform-origin: top left; transform: scale(var(--slide-scale, 1)); } ``` The scale factor can be computed with minimal JS: `containerWidth / 1280`. **Key insight from reveal.js:** All presentations have a "normal" size at which they are authored. The framework automatically scales uniformly to fit different resolutions without changing aspect ratio or layout. Default is 960x700; for our use case, 1280x720 is the standard 16:9 HD dimension. **Why this works for Design Agent:** The renderer produces HTML at exactly 1280x720. It never needs to be "responsive" -- it's a fixed-format document like a PDF page. Scaling is only for preview purposes. ### 1.2 Grid-Template-Areas for Block Combinations `grid-template-areas` provides named regions that map directly to the block composition concept in the CLAUDE.md: ```css .layout-quote-compare-cards-conclusion { display: grid; grid-template-areas: "quote quote" "compare cards" "diagram diagram" "conclusion conclusion"; grid-template-columns: 1fr 1fr; grid-template-rows: auto 1fr auto auto; gap: var(--spacing-block); padding: var(--spacing-page); } ``` **Best practice:** Define each layout as a separate CSS class with its own `grid-template-areas`. The Sonnet agent selects which layout class to apply based on the block combination it decides. This keeps the renderer deterministic -- it just applies the class. **Reusability:** CSS variables allow the same grid template to adapt: - Column count: `grid-template-columns: repeat(var(--cols, 3), 1fr)` - Gap: `gap: var(--spacing-block)` - Row sizing: `grid-template-rows` can mix `auto` (content-sized) and `1fr` (fill remaining) ### 1.3 Design Tokens **Naming convention:** `--{category}-{property}-{variant}` The CLAUDE.md already defines a good token set. The industry standard approach (from EightShapes, Nord Design System) uses kebab-case with semantic naming: ``` --color-primary, --color-accent, --color-neutral --font-title, --font-subtitle, --font-body, --font-caption --spacing-page, --spacing-block, --spacing-inner --radius, --border-width, --accent-border ``` This matches what's already in the project's CLAUDE.md. No changes needed. **For slide-specific tokens, add:** ```css --slide-width: 1280px; --slide-height: 720px; --slide-aspect: 16 / 9; ``` ### 1.4 Overflow Handling in Fixed Pages Three techniques for ensuring content fits within fixed dimensions: 1. **Single-line truncation:** ```css .truncate { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; } ``` 2. **Multi-line truncation (line clamping):** ```css .line-clamp-3 { display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; } ``` 3. **Container overflow hidden (safety net):** ```css .block { overflow: hidden; } .slide { overflow: hidden; } ``` **Korean-specific consideration:** `word-break: keep-all` affects how text wraps, which impacts line count. Content fitting calculations must account for this. The Sonnet agent should be instructed with character limits per slot, not word limits. --- ## 2. Content-to-Layout Classification ### 2.1 How to Prompt an LLM for Reliable Classification **Claude Structured Output (recommended):** Anthropic launched Structured Outputs in November 2025, supporting Claude Sonnet 4.5 and Opus 4.1. This guarantees JSON schema conformance at the token generation level -- the model literally cannot produce tokens that violate the schema. Implementation: ```python from pydantic import BaseModel, Field from anthropic import Anthropic class ContentBlock(BaseModel): content_type: str = Field(description="One of: comparison, process, relationship, big-number, definition, list, timeline, emphasis, problem") evidence: str = Field(description="Which text patterns led to this classification") suggested_block: str = Field(description="Block type from the template library") class ContentAnalysis(BaseModel): blocks: list[ContentBlock] layout_direction: str = Field(description="How blocks should be arranged on the page") primary_message: str = Field(description="The single key takeaway for this slide") ``` **Reliability strategies:** - Set temperature to 0.0-0.1 for classification tasks (reduces format drift) - Use the `output_format` parameter with JSON schema (not just prompting) - Include one perfect example in the system prompt - Add explicit validation instructions ### 2.2 Information Type Taxonomy for Presentations Based on research from SlideSpeak (16 layout types), PPTAgent (EMNLP 2025), Beautiful.ai (300 templates), and Dr. Andrew Abela's Chart Chooser: **The CLAUDE.md already defines 9 excellent content types.** Here is how they map to industry precedent: | CLAUDE.md Type | SlideSpeak Equivalent | PPTAgent Category | Common Slide Type | |---|---|---|---| | comparison | SS_ITEMS_*_A/B | Content slide (multi-column) | Comparison slide | | process | SS_STEPS_3/4/5 | Content slide (sequential) | Process/workflow slide | | relationship | (custom) | Content slide (diagram) | Venn/tree diagram slide | | big-number | SS_BIGNUMBER_1/3 | Content slide (metric) | KPI/statistics slide | | definition (card-grid) | SS_ITEMS_3/4/5/6 | Content slide (grid) | Definition/feature slide | | list | SS_CONTENT | Content slide (list) | Bullet point slide | | timeline | (custom) | Content slide (sequential) | Timeline slide | | emphasis (quote-block) | (custom) | Structural slide | Quote/callout slide | | problem | (custom) | Structural slide | Problem statement slide | **Additional types to consider (from industry):** - **SWOT** (SlideSpeak has SS_SWOT) -- 4-quadrant grid - **Matrix/Table** (already covered by comparison-table) - **Cover/Title** -- for when the content is just a single title/subtitle ### 2.3 Structured Output Schema for Layout Decisions The PPTAgent paper (EMNLP 2025) uses a two-stage approach that aligns perfectly with the Design Agent architecture: - **Stage 1 (Opus):** Analyze content, classify into functional types, extract content schemas - **Stage 2 (Sonnet):** Select reference layouts, fill content into slots, apply editing actions PPTAgent represents all parsed outputs in JSON format for LLM compatibility. The Design Agent should do the same. --- ## 3. Slot-Based Template Systems ### 3.1 SlideSpeak's Named Slot System SlideSpeak uses a comprehensive naming convention for template placeholders: **Layout names:** SS_COVER, SS_CONTENT, SS_TABLE_OF_CONTENT, SS_BIGNUMBER_1_A, SS_BIGNUMBER_3_A, SS_ITEMS_3_A through SS_ITEMS_6_B, SS_STEPS_3 through SS_STEPS_5_ICONS, SS_SWOT **Universal slot names:** - `SS_TITLE` -- slide title - `SS_SUBTITLE` -- subtitle - `SS_LOGO` -- logo placeholder - `SS_IMAGE` -- general image - `SS_PAGE` -- page number - `SS_PRESENTATION_TITLE` -- footer title **Multi-item slot naming pattern:** - `SS_ITEM_{N}_TITLE` -- title for item N - `SS_ITEM_{N}_CONTENT` -- content for item N - `SS_ITEM_{N}_NUMBER` -- number for item N (big-number layouts) - `SS_ICON_{N}` -- icon for item N **Key insight for Design Agent:** The `{{SLOT_NAME}}` convention in CLAUDE.md maps well. Adopt a similar systematic naming: `{{BLOCK_TITLE}}`, `{{ITEM_1_TITLE}}`, `{{ITEM_1_CONTENT}}`, etc. ### 3.2 Jinja2 for Template Rendering Jinja2 is the recommended engine. It integrates natively with FastAPI and Python. **Block inheritance for base layout:** ```jinja2 {# base_slide.html #}
{% block content %}{% endblock %}
``` **Block-level templates:** ```jinja2 {# blocks/comparison.html #}

{{ left_title }}

{{ left_content }}

{{ right_title }}

{{ right_content }}

``` **Composition via includes:** ```jinja2 {# Generated by renderer based on Sonnet's layout decision #} {% extends "base_slide.html" %} {% block content %} {% include "blocks/quote-block.html" %}
{% include "blocks/comparison.html" %} {% include "blocks/card-grid.html" %}
{% include "blocks/conclusion-bar.html" %} {% endblock %} ``` ### 3.3 Slot Constraints Each slot should have defined constraints that Sonnet respects: | Slot Type | Max Characters (Korean) | Required | Notes | |---|---|---|---| | slide_title | 30 | Yes | Single line | | block_title | 20 | Yes | Single line | | item_title | 15 | Yes | Single line | | item_content | 80 | No | 2-3 lines | | quote_text | 120 | Yes | 3-4 lines | | big_number | 8 | Yes | Number + unit | | conclusion | 60 | Yes | Single line | | caption | 40 | No | Single line | **Korean consideration:** Korean characters are roughly 2x the width of Latin characters at the same font size. Character limits should be specified in characters, not words, since Korean doesn't use spaces the same way as English. --- ## 4. HTML to PDF Conversion ### 4.1 Playwright (Recommended) **Why Playwright over Puppeteer:** - Native Python SDK (no Node.js dependency for a Python project) - Multiple browser support (Chromium, Firefox, WebKit), though PDF only works in Chromium - Growing community, active maintenance, better CI/CD integration - Full CSS Grid support via real Chromium rendering engine **Python implementation:** ```python from playwright.async_api import async_playwright async def html_to_pdf(html_content: str, output_path: str) -> None: async with async_playwright() as p: browser = await p.chromium.launch() page = await browser.new_page() await page.set_content(html_content, wait_until="networkidle") await page.pdf( path=output_path, width="1280px", height="720px", print_background=True, prefer_css_page_size=True, ) await browser.close() ``` **Key options:** - `print_background=True` -- required for background colors/images - `prefer_css_page_size=True` -- lets CSS `@page` rules control dimensions - `width`/`height` -- custom page dimensions (accepts px, in, mm, cm units) ### 4.2 Print CSS for Slide Format ```css @media print { @page { size: 1280px 720px; margin: 0; } body { margin: 0; -webkit-print-color-adjust: exact; print-color-adjust: exact; } .slide { width: 1280px; height: 720px; page-break-after: always; overflow: hidden; } } ``` **`-webkit-print-color-adjust: exact`** is critical -- without it, background colors and images may be stripped in PDF output. ### 4.3 Quality Comparison Both Puppeteer and Playwright use Chromium's print-to-PDF engine, so output quality is identical. The choice comes down to: | Factor | Playwright | Puppeteer | |---|---|---| | Language | Python, JS, C#, Java | JS/Node.js only | | PDF engine | Chromium only | Chromium only | | CSS Grid quality | Excellent (Chromium) | Excellent (Chromium) | | Korean font rendering | Excellent | Excellent | | Install size | ~400MB (browser binary) | ~300MB | | API ergonomics | Better async patterns | More established | **Recommendation:** Playwright, because the Design Agent backend is Python. No need to bridge to Node.js. ### 4.4 Korean-Specific Considerations - Fonts must be available on the server. Self-host Pretendard/Noto Sans KR WOFF2 files or use CDN. - Set `lang="ko"` on the HTML element for proper line-breaking algorithms. - Ensure `@font-face` declarations are loaded before PDF generation (`wait_until="networkidle"`). --- ## 5. Pure CSS Diagrams ### 5.1 Venn Diagrams (Pure CSS) **Technique:** Overlapping circles with opacity and negative margins. ```css .venn-container { display: flex; align-items: center; justify-content: center; } .venn-circle { width: 200px; height: 200px; border-radius: 50%; opacity: 0.7; display: flex; align-items: center; justify-content: center; padding: 20px; text-align: center; } .venn-a { background: var(--color-accent); } .venn-b { background: var(--color-neutral); margin-left: -60px; } ``` **Advanced approach (Adrian Roselli):** CSS Grid + `shape-outside` for text wrapping within overlapping regions. More complex but better for text-heavy Venn diagrams. **Limitation:** Pure CSS Venn diagrams work well for 2-3 circles. Beyond that, SVG is more practical. ### 5.2 Flowcharts / Process Arrows (Pure CSS) **Technique:** Flexbox/Grid layout + pseudo-elements for arrows. ```css .process-steps { display: flex; align-items: center; gap: 0; } .process-step { background: var(--color-bg-subtle); padding: var(--spacing-inner); position: relative; flex: 1; } .process-step + .process-step::before { content: ''; position: absolute; left: -12px; top: 50%; transform: translateY(-50%); border: 8px solid transparent; border-left-color: var(--color-accent); } ``` **CSS Anchor Positioning (2025-2026):** A new CSS feature for connecting elements with lines. Supported in Chrome 125+, Safari 26+, not yet in Firefox. Since we target Chromium (for PDF generation), this is usable but adds complexity. For the Design Agent, pseudo-element arrows are simpler and more reliable. ### 5.3 Tree/Hierarchy Diagrams (Pure CSS) **Technique:** Nested `