Slide Stage Layout & API
When authoring 16:9 widescreen presentation decks (drawlib init slide), Drawlib provides a precision 1920 × 1080 pixel virtual stage (::: block, ::: note), built-in layout patterns, inline vector SVG font bundling, and the programmatic drawlib.slide Python runtime API (current_slide, SlideContext, BoundingBox, build_slide). (For initializing and building a slide deck project, see Slide Deck Project Guide.)
Source Code
from drawlib.canvas import save, setup
from drawlib.icons import phosphor
from drawlib.shapes import rectangle
from drawlib.styles import Styles
from drawlib.text import text
setup(width=128, height=74)
# Outer 1920x1080 Stage Frame
rectangle((64, 35.5), width=122, height=65, style=Styles.MutedDashed.patch(shape_r=2.0))
text((6, 70.5), "(0, 0) Top-Left Stage Origin", style=Styles.DarkBold.patch(text_size=10.5, halign="left"))
text((122, 70.5), "1920 x 1080 px Stage (16:9)", style=Styles.Muted.patch(text_size=10.5, halign="right"))
# Header Zone: ::: block xy=(80, 40) width=1760 height=60
rectangle((64, 61.5), width=114, height=8.0, style=Styles.PrimaryFlat.patch(shape_r=1.5))
phosphor.layout((13.5, 61.5), width=4.4, style=Styles.WhiteBold)
text((67.0, 61.5), "Header Block — ::: block xy=(80, 40) width=1760 height=60", style=Styles.WhiteBold.patch(text_size=10.8))
# Left Narrative Column: ::: block xy=(80, 140) width=740 height=840
rectangle((31.5, 35.0), width=49, height=40, style=Styles.Neutral.patch(shape_r=1.5))
phosphor.columns((14.5, 48.5), width=4.4, style=Styles.PrimaryBold)
text((35.0, 48.5), "Left Narrative", style=Styles.DarkBold.patch(text_size=10.8))
text(
(31.5, 31.0),
"::: block xy=(80, 140) width=740 height=840\n\n• Headings & bullets\n• Tables & code\n• Styled cards",
style=Styles.Dark.patch(text_size=10.0),
)
# Right Diagram Column: ::: block xy=(860, 140) width=980 height=840
rectangle((90.0, 35.0), width=62, height=40, style=Styles.PrimaryNeutral.patch(shape_r=1.5))
phosphor.squares_four((66.5, 48.5), width=4.4, style=Styles.PrimaryBold)
text((93.5, 48.5), "Right Diagram Block", style=Styles.DarkBold.patch(text_size=10.8))
text(
(90.0, 31.0),
"::: block xy=(860, 140) width=980 height=840\n\n```drawlib file=arch.svg\nsetup(width=98, height=84)\n(0, 0) Bottom-Left Canvas",
style=Styles.DarkBold.patch(text_size=10.0),
)
# Footer & Page Counter Blocks
rectangle(
(44, 8.8),
width=74,
height=6.5,
style=Styles.SecondaryNeutral.patch(shape_r=1.0),
text="Footer — ::: block xy=(80, 1010) width=820 height=30",
text_style=Styles.Dark.patch(text_size=10.0),
)
rectangle(
(104, 8.8),
width=34,
height=6.5,
style=Styles.SecondaryNeutral.patch(shape_r=1.0),
text="Page # (1700, 1010)",
text_style=Styles.DarkBold.patch(text_size=10.0),
)
save()
1. Dual Coordinate Systems: 1920×1080 Stage vs. Drawlib Canvas
Two coordinate systems work together on every slide:
- Slide Stage (
::: block xy=(x, y) width=W height=H) — Top-Left Origin(0, 0):- Every slide renders on a fixed 1920 × 1080 pixel widescreen stage (16:9 aspect ratio) that automatically scales to fit the browser viewport or PDF page.
- Container blocks (
::: block) are positioned in CSS stage pixels where(0, 0)is the top-left corner:xincreases rightward (0→1920).yincreases downward (0→1080).
- Standard Safe Content Zone: Keep primary text and diagrams inside
x: 80..1840(width=1760) andy: 140..980(height=840), reservingy: 40..100for the slide header andy: 1010..1040for the footer and page counter.
- Drawlib Canvas (
drawlibPython block) — Bottom-Left Origin(0, 0):- Inside any
drawlibcode block,setup(width=W, height=H)defines a mathematical Cartesian canvas where(0, 0)is the bottom-left corner:xincreases rightward (0→W).yincreases upward (0→H).
- Inside any
1.1. Golden Rule: Match setup(width, height) Aspect Ratio to Block width & height
Always set setup(width=W, height=H) proportional to the enclosing ::: block xy=(x, y) width=W height=H pixel dimensions (typically dividing pixel width and height by 10 or 20):
Enclosing ::: block Dimensions |
Recommended setup(width, height) |
Aspect Ratio |
|---|---|---|
width=980 height=840 (Right diagram column) |
setup(width=98, height=84) |
98 : 84 |
width=1020 height=840 (Wide right column) |
setup(width=102, height=84) |
102 : 84 |
width=1760 height=840 (Full-width content area) |
setup(width=176, height=84) |
176 : 84 |
width=980 height=1080 (Right half full-bleed) |
setup(width=98, height=108) |
98 : 108 |
width=1920 height=1080 (Full-canvas hero slide) |
setup(width=192, height=108) |
16 : 9 |
2. Stage Container Block Syntax (::: block & ::: note)
Slide Markdown files use pure Markdown without YAML frontmatter (---). Every visual element on a slide is placed inside one or more ::: block containers using Python keyword-argument style (key=value):
::: block xy=(x, y) width=W height=H [options...]
<Markdown content or ```drawlib code fence>
:::
If a slide file contains no ::: block tags at all, Drawlib automatically wraps the slide content in the default content zone ::: block xy=(80, 140) width=1760 height=840. Similarly, if xy, width, or height are omitted on a ::: block header, they default to xy=(80, 140), width=1760, and height=840.
2.1. Complete ::: block Options Reference
| Option Key | Syntax Examples | Compiled CSS / Behavior |
|---|---|---|
Position (xy) |
xy=(80, 140), xy=(0, 0) |
position: absolute; left: 80px; top: 140px; |
Dimensions (width, height) |
width=740 height=840, width=1920 height=1080 |
width: 740px; height: 840px; |
Font Size (font_size) |
font_size=22px, font_size=1.2rem, font_size=18 |
font-size: 22px; (bare numbers automatically append px). |
Compact Density (compact) |
compact=true |
Adds .compact class to <div class="slide-block slide-text-box compact"> for tighter line-height and list spacing. |
Text Alignment (align) |
align=left, align=center, align=right |
text-align: center; |
Layer Depth (z_index) |
z_index=5, z_index=10 |
z-index: 5; — controls foreground/background stacking when blocks overlap. |
Custom CSS Classes (class) |
class="card panel accent border shadow", class=card |
Appends custom utility or theme classes (card, panel, accent, transparent, border, shadow, middle, etc.) to the container <div>. |
Inline CSS Styles (style) |
style="padding: 24px; background: #f8fafc; border-radius: 12px" |
Appends custom inline CSS rules (padding, background, border, etc.) to the container <div>. |
2.2. Speaker Notes (::: note)
Add presenter notes anywhere inside a slide Markdown file using ::: note. Speaker notes are stripped from the visible slide stage and compiled into <aside class="slide-notes" hidden> for the synchronized dual-window Presenter View (P or S key):
::: note
- Emphasize that **SQLite hash caching** restores unchanged diagrams in **< 1ms**.
- Press `A` or click **Play Animation** to step through the pipeline animation.
:::
3. Built-In Slide Layout Patterns & Templates
Source Code
from drawlib.canvas import save, setup
from drawlib.icons import phosphor
from drawlib.shapes import rectangle
from drawlib.styles import Styles
from drawlib.text import text
setup(width=128, height=76)
# 1. Top-Left: Title / Cover Slide (title)
phosphor.layout((10.5, 70.5), width=4.0, style=Styles.PrimaryBold)
text((35.5, 70.5), "1. Title / Cover (title)", style=Styles.DarkBold.patch(text_size=10.8))
rectangle((33, 52), width=56, height=30, style=Styles.MutedDashed.patch(shape_r=1.5))
rectangle(
(23.5, 52),
width=32,
height=22,
style=Styles.Neutral.patch(shape_r=1.0),
text="Title & Subtitle\nSpeaker Info\n(1160 x 600)",
text_style=Styles.Dark.patch(text_size=10.0),
)
rectangle(
(49.5, 52),
width=14,
height=22,
style=Styles.PrimaryNeutral.patch(shape_r=1.0),
text="Logo",
text_style=Styles.DarkBold.patch(text_size=10.0),
)
# 2. Top-Right: Section Divider / Closing (section)
phosphor.layout((73.5, 70.5), width=4.0, style=Styles.PrimaryBold)
text((98.5, 70.5), "2. Section Divider", style=Styles.DarkBold.patch(text_size=10.8))
rectangle((95, 52), width=56, height=30, style=Styles.MutedDashed.patch(shape_r=1.5))
rectangle(
(95, 52),
width=48,
height=16,
style=Styles.PrimaryFlat.patch(shape_r=1.5),
text="Section Header\n(1600 x 360) center",
text_style=Styles.WhiteBold.patch(text_size=10.2),
)
# 3. Bottom-Left: Two-Column Split (default)
phosphor.columns((10.5, 33.5), width=4.0, style=Styles.PrimaryBold)
text((35.5, 33.5), "3. Two-Column (default)", style=Styles.DarkBold.patch(text_size=10.8))
rectangle((33, 15.5), width=56, height=29, style=Styles.MutedDashed.patch(shape_r=1.5))
rectangle(
(33, 25.5),
width=51,
height=5.5,
style=Styles.SecondaryNeutral.patch(shape_r=0.8),
text="Header (1760 x 60)",
text_style=Styles.DarkBold.patch(text_size=10.0),
)
rectangle(
(19.5, 13.0),
width=24,
height=16.5,
style=Styles.Neutral.patch(shape_r=0.8),
text="Narrative\n(740x840)",
text_style=Styles.Dark.patch(text_size=10.0),
)
rectangle(
(46.0, 13.0),
width=25,
height=16.5,
style=Styles.PrimaryNeutral.patch(shape_r=0.8),
text="Diagram\n(980x840)",
text_style=Styles.DarkBold.patch(text_size=10.0),
)
# 4. Bottom-Right: Full-Canvas Hero ((0,0)-(1920,1080))
phosphor.squares_four((73.5, 33.5), width=4.0, style=Styles.PrimaryBold)
text((98.5, 33.5), "4. Full-Canvas Hero", style=Styles.DarkBold.patch(text_size=10.8))
rectangle((95, 15.5), width=56, height=29, style=Styles.MutedDashed.patch(shape_r=1.5))
rectangle(
(95, 15.5),
width=51,
height=25,
style=Styles.PrimaryNeutral.patch(shape_r=1.2),
text="Full-Bleed Stage Canvas\nsetup(width=192, height=108)\n+ z:10 Overlay Cards",
text_style=Styles.DarkBold.patch(text_size=10.0),
)
save()
3.1. Title / Cover Slide (title)
Places the main title, subtitle, and speaker metadata on the left and a hero illustration or logo on the right:
::: block xy=(160, 240) width=1160 height=600
# Presentation Title
## Subtitle or Core Value Proposition
Brief one-line summary of the presentation topic
---
**Speaker Name** | Role / Organization
*October 2026*
:::
::: block xy=(1360, 260) width=400 height=480

:::
3.2. Section Header / Chapter Divider Slide (draw_section_header)
Use from drawlib.slide import draw_section_header inside a full-bleed ::: block xy=(0, 0) width=1920 height=1080 block (01_section.md) with one of four layout patterns (variant="split", "centered", "banner", or "minimal"):
::: block xy=(0, 0) width=1920 height=1080
```drawlib file=section_cover.svg
from drawlib.slide import draw_section_header
draw_section_header(
2,
"Distributed Storage Engine",
"Consensus, Replication & Recovery Workflows",
[
"Raft Consensus & Leader Election",
"Quorum Write Path & WAL Durability",
"Automated Shard Rebalancing",
],
total_sections=4,
variant="split",
)
```
:::
3.3. Default Content & Two-Column Split (default)
Combines a standard header ((80, 40) (1760, 60)), footer ((80, 1010) (820, 30)), page counter ((1700, 1010) (140, 30)), left narrative column ((80, 140) (740, 840)), and right diagram column ((860, 140) (980, 840)):
::: block xy=(1700, 1010) width=140 height=30
```drawlib file=page.svg
from drawlib.slide import draw_page_number
draw_page_number()
```
:::
::: block xy=(80, 40) width=1760 height=60
# Cloud Microservices Topology
:::
::: block xy=(80, 140) width=740 height=840
## Key Architectural Decisions
- **API Gateway**: Centralized TLS termination and rate limiting
- **Service Mesh**: mTLS zero-trust east-west communication
- **Event Streaming**: Asynchronous decoupling via Pub/Sub
:::
::: block xy=(860, 140) width=980 height=840
```drawlib file=arch_topology.svg
from drawlib.canvas import clear, save, setup
from drawlib.shapes import rectangle
from drawlib.styles import Styles
clear()
setup(width=98, height=84)
rectangle((49, 42), width=80, height=60, style=Styles.Neutral, text="Architecture Diagram")
save()
```
:::
::: block xy=(80, 1010) width=820 height=30 font_size=14px
*Deck Footer / Presentation Title*
:::
3.4. Full-Canvas Hero Slide (xy=(0, 0) width=1920 height=1080)
Use a single xy=(0, 0) width=1920 height=1080 block with setup(width=192, height=108) and z_index=10 overlays when you want to draw the entire 16:9 slide as a unified vector canvas.
4. Inline Vector .svg vs. Animated .png / .webp in Slides
| Output Format | Fence Syntax | How It Renders in slide_html/ and slide.pdf |
|---|---|---|
| Inline Vector SVG (default for static diagrams) | ```drawlib file=diagram.svg |
Inlined directly as <div class="slide-svg-container"><svg>...</svg></div>. Provides infinite vector sharpness at any zoom level and full Ctrl+F text searchability. Automatically bundles any referenced Drawlib .ttf/.otf text and icon fonts into _assets/fonts/ with @font-face rules injected into style.css. |
Interactive <canvas> Animation (default when Animation() is used) |
```drawlib file=flow.png anim_trigger=click anim_loop=once anim_pause=(2, 4) |
Encodes the APNG (.png) or Animated WebP (.webp) payload as Base64 inside an interactive <canvas class="drawlib-anim-canvas"> player so playback works seamlessly over both http:// and file://. In PDF export (build_pdf.sh), Frame 0 is rendered cleanly as a static poster image. |
5. Programmatic drawlib.slide Python API
All slide runtime helpers, stage/page helpers, geometry models, and compilation functions are exported by drawlib.slide:
from drawlib.slide import (
BoundingBox,
SlideContext,
build_slide,
current_slide,
draw_page_number,
draw_section_header,
)
| Symbol | Category | Description |
|---|---|---|
current_slide |
Runtime Proxy | Dynamic contextvars-backed proxy exposing the active slide's 1-based index, total slide count, .text ("{index} / {total}"), and .format(template) during deck compilation. |
draw_page_number |
Stage Helper | Renders a standardized slide page counter badge (14×3, alpha=0.0) using current_slide.text or a custom template. |
draw_section_header |
Stage Helper | Renders a full-bleed 16:9 section header slide (192×108) with 4 selectable layout variants ("split", "centered", "banner", "minimal"). |
SlideContext |
Frozen Dataclass | Immutable context model (index: int = 1, total: int = 1, .text, .format()) holding slide numbering state. |
BoundingBox |
Frozen Dataclass | Immutable stage geometry model (x: float, y: float, width: float, height: float) in 1920×1080 stage pixels. |
build_slide |
Compiler Function | Programmatically compiles a slide_src/ Markdown directory into a standalone HTML presentation deck (index.html). |
5.1. current_slide, SlideContext, and BoundingBox Reference
| Object | Attribute / Method | Type | Description |
|---|---|---|---|
current_slide / SlideContext |
.index |
int |
1-based index of the current slide in sorted deck order (defaults to 1 outside slide builds). |
current_slide / SlideContext |
.total |
int |
Total number of slides in the presentation deck (defaults to 1 outside slide builds). |
current_slide / SlideContext |
.text |
str |
Standard formatted slide counter string f"{index} / {total}" (e.g. "4 / 9"). |
current_slide / SlideContext |
.format(template="{index} / {total}") |
str |
Custom formatted counter string using {index} and {total} placeholders (e.g. current_slide.format("Slide {index} of {total}")). |
BoundingBox |
.x, .y, .width, .height |
float |
Top-left stage coordinates (x, y) and dimensions (width, height) in 1920×1080 stage pixels. |
During build_slide(), Drawlib binds SlideContext(index=idx, total=N) before executing each slide's drawlib blocks (defaulting safely to 1 / 1 when run standalone):
from drawlib.slide import draw_page_number, draw_section_header
# 1. Bottom-right page counter badge (::: block xy=(1700, 1010) width=140 height=30)
draw_page_number()
# 2. Full-bleed 16:9 section header slide (::: block xy=(0, 0) width=1920 height=1080)
draw_section_header(
1,
"Architecture Overview",
"Core Engine, SmartArts & Auto-Layout Graphs",
["Coordinate Geometry", "Semantic Design Tokens", "High-Level Components"],
total_sections=4,
variant="split", # "split" | "centered" | "banner" | "minimal"
)
5.2. Compiling Slides Programmatically (build_slide)
from drawlib.slide import build_slide
index_html_path = build_slide(
input_dir="slide_src",
output_dir="slide_html",
title="System Architecture Deck",
theme="google",
image_format="svg",
styles_path="slide_src/styles.py",
utils_path="slide_src/utils.py",
no_cache=False,
)