From 794c155c6d00fe9f9d4a648088920fe4110a337f Mon Sep 17 00:00:00 2001 From: Pranay Prakash Date: Wed, 22 Apr 2026 13:10:57 -0700 Subject: [PATCH 1/2] Add vslides presentations Co-Authored-By: Claude Opus 4.7 --- .vslides-guide.md | 1392 +++++++++++++++++ .../.vslides-guide.md | 1392 +++++++++++++++++ .../slides.md | 679 ++++++++ .../slides.with-notes.bak | 793 ++++++++++ .../speaker-notes.md | 130 ++ 5 files changed, 4386 insertions(+) create mode 100644 .vslides-guide.md create mode 100644 presentations/getting-started-workflows-webinar/.vslides-guide.md create mode 100644 presentations/getting-started-workflows-webinar/slides.md create mode 100644 presentations/getting-started-workflows-webinar/slides.with-notes.bak create mode 100644 presentations/getting-started-workflows-webinar/speaker-notes.md diff --git a/.vslides-guide.md b/.vslides-guide.md new file mode 100644 index 0000000..e7baa53 --- /dev/null +++ b/.vslides-guide.md @@ -0,0 +1,1392 @@ +# Vercel Slidev Theme Guide + +## Presentation Setup + +Set global config in the **first slide's frontmatter**: + +```yaml +--- +theme: ./ +title: My Presentation # Shown in footer +footerLogo: wordmark # "wordmark" | "triangle" | "none" +footerTitle: true # Show title in footer +layout: 1-title +--- +``` + +--- + +## Syntax Reference + +### Frontmatter + +```yaml +--- +layout: layout-name +variant: variant-name +propName: propValue +--- +``` + +### Named Slots + +```markdown +::slot-name:: +Content with **markdown** +``` + +### Icons + +[Lucide](https://lucide.dev/icons): `rocket`, `zap`, `globe`, `code`, `mail`, `twitter`, `github`, etc. + +### Tags + +```markdown +::s1-tag:: +Coming soon +``` + +Colors: `gray`, `blue`, `purple`, `pink`, `red`, `amber`, `green`, `teal` + +Tags are used with statements in `2-statement` layouts via `s1-tag`–`s4-tag` slots. See the `2-statement` section for `tagPosition` guidance. + +**Color guidance:** Use color sparingly — Vercel guidance is <5% color on a slide. Prefer `gray` as the default tag color. Only use a colored tag as a rare accent (e.g. one highlight per slide). Overusing color dilutes its impact. + +### Images + +```yaml +image: /photos/hero.jpg # Relative to public/ +grayscale: 0 # 0 = full color, 100 = grayscale (default) +``` + +--- + +## Layout Variants — Full Reference + +### 1-title + +#### `variant: title` / `variant: section` + +Opening slide or section divider. + +**Structure:** + +- Vercel logo (title only) +- Large centered title +- 2×2 grid of subtitle points (up to 4), each with icon OR avatar image + +**Props:** + +- `subtitle1Icon`–`subtitle4Icon` — Lucide icon name +- `subtitle1Image`–`subtitle4Image` — Avatar image URL (circular, grayscale) +- `subtitleSize` — `sm` (default) or `lg` (larger avatars for presenter intros) + +**Slots:** + +- `title` — Main heading +- `subtitle-1`, `subtitle-2`, `subtitle-3`, `subtitle-4` — Text next to each icon/avatar + +```yaml +--- +layout: 1-title +variant: title +subtitle1Icon: rocket +subtitle2Image: /team/alice.jpg +--- + +::title:: +# Welcome to Vercel + +::subtitle-1:: +Fast deployments + +::subtitle-2:: +Alice Chen, Engineering +``` + +--- + +#### `variant: agenda` + +Numbered agenda/outline with up to 8 items in two columns. + +**Structure:** + +- Badge in top-left +- 8 numbered items (01–08) in two columns of 4 + +**Props:** + +- `badge` — Badge text (default: "Agenda") + +**Slots:** + +- `item-1` through `item-8` — Agenda item text + +```yaml +--- +layout: 1-title +variant: agenda +badge: Today's Agenda +--- + +::item-1:: +Introduction + +::item-2:: +Demo + +::item-3:: +Q&A +``` + +--- + +### 2-statement + +#### `variant: large` + +Single big statement, centered. + +**Structure:** + +- Large centered title +- Smaller subtitle below + +**Slots:** + +- `title` — Main statement +- `subtitle` — Supporting text + +```yaml +--- +layout: 2-statement +variant: large +--- + +::title:: +# Ship 10x faster + +::subtitle:: +Deploy in seconds, scale automatically +``` + +--- + +#### `variant: title-2` / `title-3` / `title-4` + +Title on left, 2/3/4 statements stacked vertically on right. + +**Structure:** + +- Left half: Large title +- Right half: 2, 3, or 4 Statement blocks stacked + +**Props:** + +- `tagPosition` — `inline` (default) or `above`. Use `inline` for wide statement areas (e.g. `title-2`, `cols-2`). Only use `above` when columns are narrow enough that an inline tag would wrap awkwardly (e.g. `cols-4`, `grid-4`). Don't default to `above` — it adds vertical height and leads to cramped content in rows with limited space. + +**Slots:** + +- `title` — Left side heading +- `s1-title`, `s1-body`, `s1-tag` — First statement +- `s2-title`, `s2-body`, `s2-tag` — Second statement +- `s3-title`, `s3-body`, `s3-tag` — Third statement (title-3, title-4) +- `s4-title`, `s4-body`, `s4-tag` — Fourth statement (title-4 only) + +```yaml +--- +layout: 2-statement +variant: title-3 +--- + +::title:: +# Why Vercel? + +::s1-tag:: +Fast + +::s1-title:: +Speed + +::s1-body:: +Deploy in under 10 seconds + +::s2-title:: +Scale + +::s2-body:: +Auto-scaling to millions + +::s3-title:: +Security + +::s3-body:: +Enterprise-grade by default +``` + +--- + +#### `variant: cols-2` / `cols-3` / `cols-4` + +Title at top, 2/3/4 statements in columns below. + +**Structure:** + +- Top: Title row +- Bottom: 2, 3, or 4 Statement blocks side by side + +**Slots:** Same as title-N variants above. + +--- + +#### `variant: grid-4` + +Title at top, 4 statements in 2×2 grid. + +**Structure:** + +- Top: Title row +- Bottom: 2×2 grid of Statement blocks + +**Slots:** Same as title-4. + +--- + +#### `variant: transition` + +Narrative section connector with optional progress label. Use between major content blocks to guide the audience through the presentation's narrative arc. + +**Structure:** + +- Centered title + subtitle (like `large` but lighter, using `lg` Statement) +- Optional progress indicator at bottom (e.g. "Part 2 of 3") + +**Props:** + +- `transitionLabel` — Progress text (optional, e.g. "Part 2 of 3", "Next: The Solution") + +**Slots:** + +- `title` — Main transition text +- `subtitle` — Supporting context + +```yaml +--- +layout: 2-statement +variant: transition +transitionLabel: "Part 2 of 3" +--- + +::title:: +Next: The Solution + +::subtitle:: +How we solved the performance challenge +``` + +**When to use:** Use `1-title variant: section` for bold section headers. Use `2-statement variant: transition` for lighter narrative connectors within a section, especially when you want a subtitle and/or progress indicator. + +--- + +### 3-screenshot + +All variants show images in a **macOS browser frame** with traffic lights. + +#### `variant: full` + +Full-width screenshot, clipped at bottom by footer. + +**Props:** + +- `image` — Screenshot URL (required) +- `url` — URL shown in browser bar +- `grayscale` — 0–100 (default: 100 = full grayscale) + +```yaml +--- +layout: 3-screenshot +variant: full +image: /screenshots/dashboard.png +url: vercel.com/dashboard +grayscale: 0 +--- +``` + +--- + +#### `variant: right-1` + +Statement on left (1/3), screenshot on right (2/3). + +**Structure:** + +- Left: 1 Statement (title + body) +- Right: Browser frame with screenshot + +**Slots:** + +- `title` — Statement title +- `body` — Statement body + +```yaml +--- +layout: 3-screenshot +variant: right-1 +image: /screenshots/deploy.png +url: vercel.com +--- + +::title:: +# One-Click Deploy + +::body:: +Push to Git and your site is live. +``` + +--- + +#### `variant: left-2` + +Screenshot on left (2/3), 2 statements stacked on right (1/3). + +**Structure:** + +- Left: Browser frame with screenshot +- Right: 2 Statement blocks stacked + +**Slots:** + +- `s1-title`, `s1-body` — First statement +- `s2-title`, `s2-body` — Second statement + +--- + +#### `variant: right-list` + +Title + 4 list items on left (1/3), screenshot on right (2/3). + +**Structure:** + +- Left top: Title +- Left bottom: 4 simple text rows +- Right: Browser frame with screenshot + +**Slots:** + +- `title` — Section title +- `item-1`, `item-2`, `item-3`, `item-4` — List items (plain text, no title/body) + +--- + +#### `variant: left-list` + +Screenshot on left (2/3), title + 4 list items on right (1/3). + +Same slots as right-list. + +--- + +### 4-image + +Images are shown **without browser frame** (full-bleed or contained). + +**Common Props (all variants):** + +- `imageMode` — How images fit: `cover` (default), `contain`, `fill`, `none`, `scale-down` +- `grayscale` — Global grayscale for all images (`0` = full color, `100` = full grayscale, default: `100`) + +#### `variant: full` + +Full-screen image. + +**Props:** + +- `image` — Image URL +- `grayscale` — 0–100 (default: 100) + +--- + +#### `variant: left-list` + +Image on left (1/2), title + 4 list items on right (1/2). + +**Props:** + +- `image` — Image URL +- `grayscale` — 0–100 (default: 100) + +**Slots:** + +- `title` — Section title +- `item-1`, `item-2`, `item-3`, `item-4` — List items + +--- + +#### `variant: title-image` + +Title at top (30%), image below (70%). + +**Props:** + +- `image` — Image URL +- `grayscale` — 0–100 (default: 100) + +**Slots:** + +- `title` — Section title + +--- + +#### `variant: grid-2` / `grid-3` / `grid-4` + +Title + 2/3/4 images, each with a statement below. + +**Structure:** + +- Top row: Title +- Middle row: 2, 3, or 4 images side by side +- Bottom row: 2, 3, or 4 Statement blocks (one per image) + +**Props:** + +- `image1`, `image2`, `image3`, `image4` — Image URLs +- `grayscale1`, `grayscale2`, `grayscale3`, `grayscale4` — Per-image grayscale overrides (0–100) +- `imageMode1`, `imageMode2`, `imageMode3`, `imageMode4` — Per-image fit mode overrides (`cover`, `contain`, `fill`, `none`, `scale-down`) + +For grid variants, `grayscale` and `imageMode` apply to all images by default. Use `grayscaleN` / `imageModeN` to override specific cells. + +**Slots:** + +- `title` — Section title +- `s1-title`, `s1-body` — Caption for image1 +- `s2-title`, `s2-body` — Caption for image2 +- `s3-title`, `s3-body` — Caption for image3 (grid-3, grid-4) +- `s4-title`, `s4-body` — Caption for image4 (grid-4 only) + +```yaml +--- +layout: 4-image +variant: grid-3 +image1: /team/alice.jpg +image2: /team/bob.jpg +image3: /team/carol.jpg +grayscale1: 0 +grayscale2: 0 +grayscale3: 0 +--- + +::title:: +# Meet the Team + +::s1-title:: +Alice Chen + +::s1-body:: +Engineering Lead + +::s2-title:: +Bob Smith + +::s2-body:: +Product Manager + +::s3-title:: +Carol Davis + +::s3-body:: +Designer +``` + +--- + +### 4-mermaid + +Mermaid charts with the same composition model as `4-image` (image areas replaced by diagrams). + +**When to use:** Architecture diagrams, process flows, system maps, and side-by-side chart comparisons. + +**Variants:** + +- `variant: full` — One full-slide chart +- `variant: left-list` — Chart left, title + list right +- `variant: title-image` — Title top, chart below +- `variant: grid-2` — 2 charts + 2 statement captions +- `variant: grid-3` — 3 charts + 3 statement captions +- `variant: grid-4` — 4 charts + 4 statement captions + +Grid variants use numbered chart props (chart1, chart2, etc.) and matching statement slots (s1-title/body, s2-title/body, etc.). Single-chart variants use a single chart prop. left-list adds title + item-1..item-4 slots. title-image adds a title slot. + +**Syntax rules (all variants):** + +- Put Mermaid code in frontmatter using YAML multiline strings (`|`). +- Start with a Mermaid diagram declaration (`flowchart LR`, `flowchart TD`, etc.). +- Prefer plain labels (`Node["Text"]`) over HTML labels if rendering is unstable. +- Use hex colors in `classDef` and `linkStyle`. + +**Example: `variant: full`** + +## \\`\\`\\`yaml + +layout: 4-mermaid +variant: full +chart: | +flowchart LR +User["User"] --> Edge["Edge Router"] +Edge -->|cache hit| Cached(["Cached"]) +Edge -->|cache miss| App["App Runtime"] +App --> DB["Primary DB"] +classDef success fill:#0b1d0f,stroke:#46a758,color:#63c174; +class Cached success; + +--- + +\\`\\`\\` + +**Example: `variant: grid-2`** + +## \\`\\`\\`yaml + +layout: 4-mermaid +variant: grid-2 +chart1: | +flowchart LR +Edge["Edge"] --> API["API"] +API -->|ok| Fast(["Low Latency"]) +classDef success fill:#0b1d0f,stroke:#46a758,color:#63c174; +class Fast success; +chart2: | +flowchart LR +API["API"] --> Guard["Rate Limit"] +Guard -->|blocked| Drop(["Dropped"]) +classDef danger fill:#2a1314,stroke:#e5484d,color:#ff6166; +class Drop danger; + +--- + +::title:: + +# Traffic Paths + +::s1-title:: +Fast path + +::s1-body:: +Edge + API success flow. + +::s2-title:: +Blocked path + +::s2-body:: +Rate limit rejects abusive traffic. +\\`\\`\\` + +--- + +### 5-open + +Flexible layouts with open content areas for custom content (iframes, demos, etc.). + +#### `variant: title-space` + +Title at top (30%), open area below (70%). + +**Slots:** + +- `title` — Section title +- `content` — Open area (any HTML/Vue) + +--- + +#### `variant: title-2-spaces` / `title-3-spaces` + +Title at top, 2 or 3 open areas side by side below. + +**Slots:** + +- `title` — Section title +- `space-1`, `space-2`, `space-3` — Open areas + +--- + +#### `variant: list-space` + +Title + 5 list items on left (1/3), open area on right (2/3). + +**Slots:** + +- `title` — Section title +- `item-1` through `item-5` — List items +- `space` — Open area + +--- + +### 6-quote + +#### `variant: center` + +Centered quote with attribution. + +**Slots:** + +- `quote` — Quote text +- `author` — Attribution (name, title, company) + +```yaml +--- +layout: 6-quote +variant: center +--- + +::quote:: +The best way to predict the future is to build it. + +::author:: +— Guillermo Rauch, CEO of Vercel +``` + +--- + +#### `variant: with-statements` + +2 statements on left (1/3), quote on right (2/3). + +**Slots:** + +- `s1-title`, `s1-body` — First statement +- `s2-title`, `s2-body` — Second statement +- `quote`, `author` — Quote content + +--- + +### 7-number + +#### `variant: one` + +Single huge metric, centered. + +**Slots:** + +- `number` — The metric (e.g., "99.9%") +- `label` — Description (e.g., "Uptime SLA") + +--- + +#### `variant: two` / `variant: three` + +2 or 3 metrics side by side. + +**Slots:** + +- `number-1`, `label-1` — First metric +- `number-2`, `label-2` — Second metric +- `number-3`, `label-3` — Third metric (three only) + +--- + +#### `variant: four-with-title` + +Statement on left (1/3), 2×2 grid of 4 metrics on right (2/3). + +**Slots:** + +- `title`, `subtitle` — Left side statement +- `number-1`, `label-1` through `number-4`, `label-4` — Metrics + +--- + +#### `variant: six` + +6 metrics in 3×2 grid. + +**Slots:** + +- `number-1`, `label-1` through `number-6`, `label-6` + +--- + +#### `variant: two-with-source` / `three-with-source` + +2 or 3 metrics with source citations below each. + +**Slots:** + +- `number-1`, `label-1`, `source-1` — First metric with citation +- `number-2`, `label-2`, `source-2` — Second metric +- `number-3`, `label-3`, `source-3` — Third metric (three-with-source only) + +--- + +### 8-special + +#### `variant: qa` + +Q&A slide with decorative left panel. + +**Structure:** + +- Left: Decorative quote bubble SVG + optional custom content +- Right: Badge + up to 4 icon points (contact info, links, etc.) + +**Props:** + +- `badge` — Badge text (default: "Q&A") +- `item1Icon`, `item2Icon`, `item3Icon`, `item4Icon` — Lucide icons + +**Slots:** + +- `left` — Optional content in left panel +- `item-1`, `item-2`, `item-3`, `item-4` — Text next to each icon + +```yaml +--- +layout: 8-special +variant: qa +badge: Questions? +item1Icon: mail +item2Icon: twitter +--- + +::item-1:: +hello@vercel.com + +::item-2:: +@vercel +``` + +--- + +#### `variant: thank-you` + +Centered Vercel triangle + "Thank you" text. No slots. + +--- + +#### `variant: splash` + +Centered Vercel triangle only. No slots. + +--- + +### 9-utility + +Blank canvases for custom content. + +- `variant: blank` — Empty with frame +- `variant: crosshairs` — Frame + crosshair decorations +- `variant: full-grid` — Dense alignment grid + +Default slot accepts any content. + +--- + +### 10-code + +All variants show code in a **code editor frame** with filename header. + +**IMPORTANT — Maximum lines of code (LOC) that fit without clipping:** + +| Size | Full variant | 2/3 variants (right-1, left-2, right-list, left-list) | +| ---- | ------------ | ----------------------------------------------------- | +| `xs` | 23 lines | 19 lines | +| `sm` | 17 lines | 14 lines | +| `md` | 14 lines | 11 lines | +| `lg` | 12 lines | 9 lines | + +**Always count your code lines (including blank lines) and pick a `codeSize` that fits.** If the code exceeds the limit, it will be clipped at the bottom. Prefer trimming code to show only the essential parts rather than cramming everything in at `xs`. + +**Scrollable mode:** Set `scrollable: true` to allow vertical scrolling for longer code. This should NOT be the default — only use it when showing the complete code is essential and trimming would lose important context. Non-scrollable (clipped) code is the standard look. + +#### `variant: full` + +Full-width code, clipped at bottom. + +**Props:** + +- `filename` — Filename shown in header (e.g., `api/hello.ts`) +- `codeSize` — `xs`, `sm` (default), `md`, `lg` +- `scrollable` — `true` to enable vertical scrolling (default: `false`, code clips at bottom) + +**Slots:** + +- `code` — Fenced code block with syntax highlighting + +```yaml +--- +layout: 10-code +variant: full +filename: api/hello.ts +--- + +::code:: +\\`\\`\\`ts +export async function GET() { + return Response.json({ hello: 'world' }) +} +\\`\\`\\` +``` + +--- + +#### `variant: right-1` + +Statement on left (1/3), code on right (2/3). + +**Slots:** + +- `title`, `body` — Statement +- `code` — Code block + +--- + +#### `variant: left-2` + +Code on left (2/3), 2 statements on right (1/3). + +**Slots:** + +- `code` — Code block +- `s1-title`, `s1-body` — First statement +- `s2-title`, `s2-body` — Second statement + +--- + +#### `variant: right-list` / `left-list` + +Code + title + 4 list items. + +**Slots:** + +- `code` — Code block +- `title` — Section title +- `item-1`, `item-2`, `item-3`, `item-4` — List items + +--- + +### 11-promo + +#### `variant: two-statements` + +Badge + decorative grid + 2 statements. + +**Structure:** + +- Top: Badge +- Middle: Decorative square grid +- Bottom: 2 Statement blocks side by side + +**Props:** + +- `badge` — Badge text +- `gridCols` — Grid columns (default: 14) +- `gridRows` — Grid rows (default: 3) + +**Slots:** + +- `s1-title`, `s1-body`, `s1-tag` — First statement +- `s2-title`, `s2-body`, `s2-tag` — Second statement + +--- + +### playful + +Animated full-screen grid layouts. + +#### `variant: welcome` + +Static "▲ W E L C O M E" display. No configuration needed. + +--- + +#### `variant: triangles` + +Random Vercel triangles fade in/out. + +**Props:** + +- `flashInterval` — ms between new triangles (default: 2500) +- `fadeDuration` — ms for fade out (default: 4000) + +--- + +#### `variant: values` + +Random Vercel values (FTW, DIG DEEP, etc.) fade in/out. + +Same props as triangles. + +--- + +#### `variant: splitflap` + +Mechanical split-flap display animation. + +Use `` components in the slide body: + +```yaml +--- +layout: playful +variant: splitflap +--- + + +``` + +**Flap props:** + +- `text` — Text to display (max 16 chars). Use `▲` for Vercel icon. +- `row` — Row number (1–9) +- `startCol` — Starting column (auto-centers if omitted) +- `mono` — Use monospace font (default: false) +- `flipDuration` — ms per character flip (default: 120) +- `staggerDelay` — ms between letters (default: 100) + +--- + +### 12-youtube + +Embedded YouTube video in a **macOS browser frame** (same chrome as 3-screenshot). The browser bar always shows `youtube.com`. Uses privacy-enhanced embedding (`youtube-nocookie.com`). + +**Timestamp support:** Include `&t=SECONDS` in the URL to start the video at a specific time. The component auto-extracts `t=` or `start=` from the URL and passes it to the embed player. + +#### `variant: full` + +Full-width browser frame with embedded video, clipped at bottom. + +**Props:** + +- `url` — YouTube video URL (supports `youtube.com/watch?v=`, `youtu.be/`, `youtube.com/embed/`). Append `&t=SECONDS` to start at a specific timestamp. + +```yaml +--- +layout: 12-youtube +variant: full +url: https://www.youtube.com/watch?v=dQw4w9WgXcQ&t=166 +--- +``` + +--- + +#### `variant: left-list` + +Video in browser frame on left (1/2), title + 4 list items on right (1/2). + +**Props:** + +- `url` — YouTube video URL. Append `&t=SECONDS` to start at a specific timestamp. + +**Slots:** + +- `title` — Section title +- `item-1`, `item-2`, `item-3`, `item-4` — List items + +```yaml +--- +layout: 12-youtube +variant: left-list +url: https://www.youtube.com/watch?v=dQw4w9WgXcQ&t=120 +--- + +::title:: +Video Walkthrough + +::item-1:: +Step 1: Setup + +::item-2:: +Step 2: Configure + +::item-3:: +Step 3: Deploy + +::item-4:: +Step 4: Monitor +``` + +--- + +## Offer Layouts + +Sales offer slides for customer proposals. All offer layouts are dark-themed with the Vercel design system. Data is passed via **frontmatter props** (not slots) for structured data like SKUs, details, and team members. + +--- + +### offer-title + +Cover slide for a customer offer with title and team members. + +**Structure:** + +- Large title at top +- 2×2 team member grid at bottom (name + role in monospace) + +**Props:** + +- `team1Name`–`team4Name` — Team member names +- `team1Role`–`team4Role` — Team member roles (shown in monospace, muted) + +**Slots:** + +- `title` — Main heading (use `
` for line breaks) + +```yaml +--- +layout: offer-title +team1Name: Jane Smith +team1Role: ACCOUNT MANAGER +team2Name: John Doe +team2Role: SOLUTIONS ENGINEER +team3Name: Alice Chen +team3Role: CSM +team4Name: Bob Wilson +team4Role: CRO +--- +::title:: +# Acme Corp's
Vercel offer +``` + +--- + +### offer-statement + +Badge + title on left, numbered points on right. + +**Structure:** + +- Left: Outline badge + large title +- Right: Up to 4 numbered points (01–04) with title + body + +**Props:** + +- `badge` — Badge text (default: "OUR PRICING PHILOSOPHY") +- `badgeColor` — Badge border color (default: "#A0A0A0") + +**Slots:** + +- `title` — Left side heading +- `point-1-title`, `point-1-body` — First numbered point +- `point-2-title`, `point-2-body` — Second numbered point +- `point-3-title`, `point-3-body` — Third point +- `point-4-title`, `point-4-body` — Fourth point + +```yaml +--- +layout: offer-statement +badge: OUR PRICING PHILOSOPHY +--- + +::title:: +Transparency
& predictability + +::point-1-title:: +Usage-based + +::point-1-body:: +Pay for only what you use + +::point-2-title:: +Product flexibility + +::point-2-body:: +One commit across products, shift spend where you need + +::point-3-title:: +Scales with you + +::point-3-body:: +The more you buy, the better your discount +``` + +--- + +### offer-product-grid + +Product portfolio grid showing all Vercel SKUs organized by category. + +**Structure:** + +- Title + subtitle at top +- Grid of product categories with individual SKU pills +- Diagonal background pattern behind grid + +**Props:** + +- `categories` — Array of `{ title, products: [{ name }], highlighted? }` (has sensible defaults with all Vercel products) +- `rows` — Array of index arrays defining grid layout (default: `[[0,1], [2,3,4], [5,6,7,8]]`) + +**Slots:** + +- `title` — Grid heading +- `subtitle` — Subtitle text + +```yaml +--- +layout: offer-product-grid +--- + +::title:: +Introducing Flexible Commitment + +::subtitle:: +Giving your team access to the entire Vercel product portfolio +``` + +--- + +### offer-timeline + +Flex commit timeline visualization with checkbox grid. + +**Structure:** + +- Badge at top +- Large headline (supports mixed white/muted text via HTML spans) +- Month 1–12 timeline with animated checkbox grid + fade overlay + +**Props:** + +- `badge` — Badge text (default: "FLEX COMMIT") +- `startLabel` — Left label (default: "MONTH 1") +- `endLabel` — Right label (default: "MONTH 12") +- `months` — 12-element array of boolean arrays for checkbox states + +**Slots:** + +- `title` — Headline (use `` and `` for mixed styling) + +```yaml +--- +layout: offer-timeline +--- + +::title:: +Swap SKUs anytime to align with
shifting needs and seasonality.
+``` + +--- + +### offer-pricing + +Discount matrix on left, rate card on right. + +**Structure:** + +- Left: Title + 2×2 discount matrix (BASE/MODERATE/HIGH/BEST) +- Right: Rate card table with diagonal background + +**Props:** + +- `badgeText` — Rate card badge (default: "FLEX COMMIT RATE CARD") +- `footerText` — Rate card footer (default: "ENTIRE RATE CARD AVAILABLE WITH FULL PROPOSAL") +- `skus` — Array of `{ name, listPrice, discountPrice }` (default: 3 placeholder SKUs) + +**Slots:** + +- `title` — Left side heading + +```yaml +--- +layout: offer-pricing +skus: + - name: Fluid Compute + listPrice: $0.18/GB-hr + discountPrice: $0.12/GB-hr + - name: Edge Requests + listPrice: $2/M + discountPrice: $1.40/M + - name: Bandwidth + listPrice: $0.15/GB + discountPrice: $0.10/GB +--- + +::title:: +Pricing that
scales with
you. +``` + +--- + +### offer-roi + +Centered card with label/value rows and dotted leaders. + +**Structure:** + +- Centered rounded card with outline badge +- Up to 3 key-value rows with dotted line between label and value + +**Props:** + +- `badge` — Card badge text (default: "ANTICIPATED QUANTIFIED VALUE") + +**Slots:** + +- `label`, `value` — First row (default: "ROI" / "$XM") +- `label-2`, `value-2` — Second row (optional) +- `label-3`, `value-3` — Third row (optional) + +```yaml +--- +layout: offer-roi +--- + +::label:: +ROI + +::value:: +$2.5M + +::label-2:: +Annual savings + +::value-2:: +$500K +``` + +--- + +### offer-details + +Header + key-value sidebar on left, rate card on right. + +**Structure:** + +- Header bar with customer name +- Left: Key-value detail rows (flexible count) +- Right: Rate card with diagonal background + +**Props:** + +- `headerText` — Header text (default: "[Customer name] offer") +- `details` — Array of `{ label, value }` for the sidebar rows +- `badgeText` — Rate card badge +- `footerText` — Rate card footer +- `skus` — Array of `{ name, listPrice, discountPrice }` + +```yaml +--- +layout: offer-details +headerText: Acme Corp offer +details: + - label: Flex commit balance + value: $120K / YR + - label: Term + value: 2 YRS + - label: Support level + value: Enterprise + - label: Start date + value: Q1 2026 +skus: + - name: Fluid Compute + listPrice: $0.18/GB-hr + discountPrice: $0.12/GB-hr + - name: Edge Requests + listPrice: $2/M + discountPrice: $1.40/M + - name: Bandwidth + listPrice: $0.15/GB + discountPrice: $0.10/GB +--- + +``` + +--- + +### offer-comparison + +Side-by-side comparison of 2–3 offer options, each with rate card. + +**Structure:** + +- Header bar with customer name +- Equal-width columns, each with: option badge, balance/term KV rows, small rate card + +**Props:** + +- `headerText` — Header text (default: "[Customer name] offer comparison") +- `options` — Array of `{ label, balance, term, skus: [{ name, listPrice, discountPrice }] }` + +```yaml +--- +layout: offer-comparison +headerText: Acme Corp offer comparison +options: + - label: OPTION 1 + balance: $100K / YR + term: 1 YR + skus: + - name: Fluid Compute + listPrice: $0.18 + discountPrice: $0.15 + - name: Edge Requests + listPrice: $2/M + discountPrice: $1.70/M + - label: OPTION 2 + balance: $120K / YR + term: 2 YRS + skus: + - name: Fluid Compute + listPrice: $0.18 + discountPrice: $0.12 + - name: Edge Requests + listPrice: $2/M + discountPrice: $1.40/M +--- + +``` + +--- + +### offer-before-after + +Side-by-side before/after comparison with colored badges and icon rows. + +**Structure:** + +- Left: Red "BEFORE STATE" badge + year + title + X-icon rows +- Right: Green "AFTER STATE" badge + year + title + check-icon rows + +**Props:** + +- `beforeBadge` — Left badge text (default: "BEFORE STATE") +- `afterBadge` — Right badge text (default: "AFTER STATE") +- `beforeYear` — Left year label (default: "2025 & EARLIER") +- `afterYear` — Right year label (default: "2026+") +- `beforeColor` — Left badge color (default: red `#DA2F36`) +- `afterColor` — Right badge color (default: green `#63C173`) + +**Slots:** + +- `before-title` — Left title (default: "MIU") +- `after-title` — Right title (default: "Flex Commit") +- `before-1` through `before-6` — Before rows (with X icon) +- `after-1` through `after-6` — After rows (with check icon) + +```yaml +--- +layout: offer-before-after +--- + +::before-title:: +MIU + +::after-title:: +Flex Commit + +::before-1:: +Separate commits per SKU + +::after-1:: +Max flexibility + +::before-2:: +Monthly burndown + +::after-2:: +Annual burndown + +::before-3:: +Shallow blanket discount + +::after-3:: +Private rate card +``` diff --git a/presentations/getting-started-workflows-webinar/.vslides-guide.md b/presentations/getting-started-workflows-webinar/.vslides-guide.md new file mode 100644 index 0000000..e7baa53 --- /dev/null +++ b/presentations/getting-started-workflows-webinar/.vslides-guide.md @@ -0,0 +1,1392 @@ +# Vercel Slidev Theme Guide + +## Presentation Setup + +Set global config in the **first slide's frontmatter**: + +```yaml +--- +theme: ./ +title: My Presentation # Shown in footer +footerLogo: wordmark # "wordmark" | "triangle" | "none" +footerTitle: true # Show title in footer +layout: 1-title +--- +``` + +--- + +## Syntax Reference + +### Frontmatter + +```yaml +--- +layout: layout-name +variant: variant-name +propName: propValue +--- +``` + +### Named Slots + +```markdown +::slot-name:: +Content with **markdown** +``` + +### Icons + +[Lucide](https://lucide.dev/icons): `rocket`, `zap`, `globe`, `code`, `mail`, `twitter`, `github`, etc. + +### Tags + +```markdown +::s1-tag:: +Coming soon +``` + +Colors: `gray`, `blue`, `purple`, `pink`, `red`, `amber`, `green`, `teal` + +Tags are used with statements in `2-statement` layouts via `s1-tag`–`s4-tag` slots. See the `2-statement` section for `tagPosition` guidance. + +**Color guidance:** Use color sparingly — Vercel guidance is <5% color on a slide. Prefer `gray` as the default tag color. Only use a colored tag as a rare accent (e.g. one highlight per slide). Overusing color dilutes its impact. + +### Images + +```yaml +image: /photos/hero.jpg # Relative to public/ +grayscale: 0 # 0 = full color, 100 = grayscale (default) +``` + +--- + +## Layout Variants — Full Reference + +### 1-title + +#### `variant: title` / `variant: section` + +Opening slide or section divider. + +**Structure:** + +- Vercel logo (title only) +- Large centered title +- 2×2 grid of subtitle points (up to 4), each with icon OR avatar image + +**Props:** + +- `subtitle1Icon`–`subtitle4Icon` — Lucide icon name +- `subtitle1Image`–`subtitle4Image` — Avatar image URL (circular, grayscale) +- `subtitleSize` — `sm` (default) or `lg` (larger avatars for presenter intros) + +**Slots:** + +- `title` — Main heading +- `subtitle-1`, `subtitle-2`, `subtitle-3`, `subtitle-4` — Text next to each icon/avatar + +```yaml +--- +layout: 1-title +variant: title +subtitle1Icon: rocket +subtitle2Image: /team/alice.jpg +--- + +::title:: +# Welcome to Vercel + +::subtitle-1:: +Fast deployments + +::subtitle-2:: +Alice Chen, Engineering +``` + +--- + +#### `variant: agenda` + +Numbered agenda/outline with up to 8 items in two columns. + +**Structure:** + +- Badge in top-left +- 8 numbered items (01–08) in two columns of 4 + +**Props:** + +- `badge` — Badge text (default: "Agenda") + +**Slots:** + +- `item-1` through `item-8` — Agenda item text + +```yaml +--- +layout: 1-title +variant: agenda +badge: Today's Agenda +--- + +::item-1:: +Introduction + +::item-2:: +Demo + +::item-3:: +Q&A +``` + +--- + +### 2-statement + +#### `variant: large` + +Single big statement, centered. + +**Structure:** + +- Large centered title +- Smaller subtitle below + +**Slots:** + +- `title` — Main statement +- `subtitle` — Supporting text + +```yaml +--- +layout: 2-statement +variant: large +--- + +::title:: +# Ship 10x faster + +::subtitle:: +Deploy in seconds, scale automatically +``` + +--- + +#### `variant: title-2` / `title-3` / `title-4` + +Title on left, 2/3/4 statements stacked vertically on right. + +**Structure:** + +- Left half: Large title +- Right half: 2, 3, or 4 Statement blocks stacked + +**Props:** + +- `tagPosition` — `inline` (default) or `above`. Use `inline` for wide statement areas (e.g. `title-2`, `cols-2`). Only use `above` when columns are narrow enough that an inline tag would wrap awkwardly (e.g. `cols-4`, `grid-4`). Don't default to `above` — it adds vertical height and leads to cramped content in rows with limited space. + +**Slots:** + +- `title` — Left side heading +- `s1-title`, `s1-body`, `s1-tag` — First statement +- `s2-title`, `s2-body`, `s2-tag` — Second statement +- `s3-title`, `s3-body`, `s3-tag` — Third statement (title-3, title-4) +- `s4-title`, `s4-body`, `s4-tag` — Fourth statement (title-4 only) + +```yaml +--- +layout: 2-statement +variant: title-3 +--- + +::title:: +# Why Vercel? + +::s1-tag:: +Fast + +::s1-title:: +Speed + +::s1-body:: +Deploy in under 10 seconds + +::s2-title:: +Scale + +::s2-body:: +Auto-scaling to millions + +::s3-title:: +Security + +::s3-body:: +Enterprise-grade by default +``` + +--- + +#### `variant: cols-2` / `cols-3` / `cols-4` + +Title at top, 2/3/4 statements in columns below. + +**Structure:** + +- Top: Title row +- Bottom: 2, 3, or 4 Statement blocks side by side + +**Slots:** Same as title-N variants above. + +--- + +#### `variant: grid-4` + +Title at top, 4 statements in 2×2 grid. + +**Structure:** + +- Top: Title row +- Bottom: 2×2 grid of Statement blocks + +**Slots:** Same as title-4. + +--- + +#### `variant: transition` + +Narrative section connector with optional progress label. Use between major content blocks to guide the audience through the presentation's narrative arc. + +**Structure:** + +- Centered title + subtitle (like `large` but lighter, using `lg` Statement) +- Optional progress indicator at bottom (e.g. "Part 2 of 3") + +**Props:** + +- `transitionLabel` — Progress text (optional, e.g. "Part 2 of 3", "Next: The Solution") + +**Slots:** + +- `title` — Main transition text +- `subtitle` — Supporting context + +```yaml +--- +layout: 2-statement +variant: transition +transitionLabel: "Part 2 of 3" +--- + +::title:: +Next: The Solution + +::subtitle:: +How we solved the performance challenge +``` + +**When to use:** Use `1-title variant: section` for bold section headers. Use `2-statement variant: transition` for lighter narrative connectors within a section, especially when you want a subtitle and/or progress indicator. + +--- + +### 3-screenshot + +All variants show images in a **macOS browser frame** with traffic lights. + +#### `variant: full` + +Full-width screenshot, clipped at bottom by footer. + +**Props:** + +- `image` — Screenshot URL (required) +- `url` — URL shown in browser bar +- `grayscale` — 0–100 (default: 100 = full grayscale) + +```yaml +--- +layout: 3-screenshot +variant: full +image: /screenshots/dashboard.png +url: vercel.com/dashboard +grayscale: 0 +--- +``` + +--- + +#### `variant: right-1` + +Statement on left (1/3), screenshot on right (2/3). + +**Structure:** + +- Left: 1 Statement (title + body) +- Right: Browser frame with screenshot + +**Slots:** + +- `title` — Statement title +- `body` — Statement body + +```yaml +--- +layout: 3-screenshot +variant: right-1 +image: /screenshots/deploy.png +url: vercel.com +--- + +::title:: +# One-Click Deploy + +::body:: +Push to Git and your site is live. +``` + +--- + +#### `variant: left-2` + +Screenshot on left (2/3), 2 statements stacked on right (1/3). + +**Structure:** + +- Left: Browser frame with screenshot +- Right: 2 Statement blocks stacked + +**Slots:** + +- `s1-title`, `s1-body` — First statement +- `s2-title`, `s2-body` — Second statement + +--- + +#### `variant: right-list` + +Title + 4 list items on left (1/3), screenshot on right (2/3). + +**Structure:** + +- Left top: Title +- Left bottom: 4 simple text rows +- Right: Browser frame with screenshot + +**Slots:** + +- `title` — Section title +- `item-1`, `item-2`, `item-3`, `item-4` — List items (plain text, no title/body) + +--- + +#### `variant: left-list` + +Screenshot on left (2/3), title + 4 list items on right (1/3). + +Same slots as right-list. + +--- + +### 4-image + +Images are shown **without browser frame** (full-bleed or contained). + +**Common Props (all variants):** + +- `imageMode` — How images fit: `cover` (default), `contain`, `fill`, `none`, `scale-down` +- `grayscale` — Global grayscale for all images (`0` = full color, `100` = full grayscale, default: `100`) + +#### `variant: full` + +Full-screen image. + +**Props:** + +- `image` — Image URL +- `grayscale` — 0–100 (default: 100) + +--- + +#### `variant: left-list` + +Image on left (1/2), title + 4 list items on right (1/2). + +**Props:** + +- `image` — Image URL +- `grayscale` — 0–100 (default: 100) + +**Slots:** + +- `title` — Section title +- `item-1`, `item-2`, `item-3`, `item-4` — List items + +--- + +#### `variant: title-image` + +Title at top (30%), image below (70%). + +**Props:** + +- `image` — Image URL +- `grayscale` — 0–100 (default: 100) + +**Slots:** + +- `title` — Section title + +--- + +#### `variant: grid-2` / `grid-3` / `grid-4` + +Title + 2/3/4 images, each with a statement below. + +**Structure:** + +- Top row: Title +- Middle row: 2, 3, or 4 images side by side +- Bottom row: 2, 3, or 4 Statement blocks (one per image) + +**Props:** + +- `image1`, `image2`, `image3`, `image4` — Image URLs +- `grayscale1`, `grayscale2`, `grayscale3`, `grayscale4` — Per-image grayscale overrides (0–100) +- `imageMode1`, `imageMode2`, `imageMode3`, `imageMode4` — Per-image fit mode overrides (`cover`, `contain`, `fill`, `none`, `scale-down`) + +For grid variants, `grayscale` and `imageMode` apply to all images by default. Use `grayscaleN` / `imageModeN` to override specific cells. + +**Slots:** + +- `title` — Section title +- `s1-title`, `s1-body` — Caption for image1 +- `s2-title`, `s2-body` — Caption for image2 +- `s3-title`, `s3-body` — Caption for image3 (grid-3, grid-4) +- `s4-title`, `s4-body` — Caption for image4 (grid-4 only) + +```yaml +--- +layout: 4-image +variant: grid-3 +image1: /team/alice.jpg +image2: /team/bob.jpg +image3: /team/carol.jpg +grayscale1: 0 +grayscale2: 0 +grayscale3: 0 +--- + +::title:: +# Meet the Team + +::s1-title:: +Alice Chen + +::s1-body:: +Engineering Lead + +::s2-title:: +Bob Smith + +::s2-body:: +Product Manager + +::s3-title:: +Carol Davis + +::s3-body:: +Designer +``` + +--- + +### 4-mermaid + +Mermaid charts with the same composition model as `4-image` (image areas replaced by diagrams). + +**When to use:** Architecture diagrams, process flows, system maps, and side-by-side chart comparisons. + +**Variants:** + +- `variant: full` — One full-slide chart +- `variant: left-list` — Chart left, title + list right +- `variant: title-image` — Title top, chart below +- `variant: grid-2` — 2 charts + 2 statement captions +- `variant: grid-3` — 3 charts + 3 statement captions +- `variant: grid-4` — 4 charts + 4 statement captions + +Grid variants use numbered chart props (chart1, chart2, etc.) and matching statement slots (s1-title/body, s2-title/body, etc.). Single-chart variants use a single chart prop. left-list adds title + item-1..item-4 slots. title-image adds a title slot. + +**Syntax rules (all variants):** + +- Put Mermaid code in frontmatter using YAML multiline strings (`|`). +- Start with a Mermaid diagram declaration (`flowchart LR`, `flowchart TD`, etc.). +- Prefer plain labels (`Node["Text"]`) over HTML labels if rendering is unstable. +- Use hex colors in `classDef` and `linkStyle`. + +**Example: `variant: full`** + +## \\`\\`\\`yaml + +layout: 4-mermaid +variant: full +chart: | +flowchart LR +User["User"] --> Edge["Edge Router"] +Edge -->|cache hit| Cached(["Cached"]) +Edge -->|cache miss| App["App Runtime"] +App --> DB["Primary DB"] +classDef success fill:#0b1d0f,stroke:#46a758,color:#63c174; +class Cached success; + +--- + +\\`\\`\\` + +**Example: `variant: grid-2`** + +## \\`\\`\\`yaml + +layout: 4-mermaid +variant: grid-2 +chart1: | +flowchart LR +Edge["Edge"] --> API["API"] +API -->|ok| Fast(["Low Latency"]) +classDef success fill:#0b1d0f,stroke:#46a758,color:#63c174; +class Fast success; +chart2: | +flowchart LR +API["API"] --> Guard["Rate Limit"] +Guard -->|blocked| Drop(["Dropped"]) +classDef danger fill:#2a1314,stroke:#e5484d,color:#ff6166; +class Drop danger; + +--- + +::title:: + +# Traffic Paths + +::s1-title:: +Fast path + +::s1-body:: +Edge + API success flow. + +::s2-title:: +Blocked path + +::s2-body:: +Rate limit rejects abusive traffic. +\\`\\`\\` + +--- + +### 5-open + +Flexible layouts with open content areas for custom content (iframes, demos, etc.). + +#### `variant: title-space` + +Title at top (30%), open area below (70%). + +**Slots:** + +- `title` — Section title +- `content` — Open area (any HTML/Vue) + +--- + +#### `variant: title-2-spaces` / `title-3-spaces` + +Title at top, 2 or 3 open areas side by side below. + +**Slots:** + +- `title` — Section title +- `space-1`, `space-2`, `space-3` — Open areas + +--- + +#### `variant: list-space` + +Title + 5 list items on left (1/3), open area on right (2/3). + +**Slots:** + +- `title` — Section title +- `item-1` through `item-5` — List items +- `space` — Open area + +--- + +### 6-quote + +#### `variant: center` + +Centered quote with attribution. + +**Slots:** + +- `quote` — Quote text +- `author` — Attribution (name, title, company) + +```yaml +--- +layout: 6-quote +variant: center +--- + +::quote:: +The best way to predict the future is to build it. + +::author:: +— Guillermo Rauch, CEO of Vercel +``` + +--- + +#### `variant: with-statements` + +2 statements on left (1/3), quote on right (2/3). + +**Slots:** + +- `s1-title`, `s1-body` — First statement +- `s2-title`, `s2-body` — Second statement +- `quote`, `author` — Quote content + +--- + +### 7-number + +#### `variant: one` + +Single huge metric, centered. + +**Slots:** + +- `number` — The metric (e.g., "99.9%") +- `label` — Description (e.g., "Uptime SLA") + +--- + +#### `variant: two` / `variant: three` + +2 or 3 metrics side by side. + +**Slots:** + +- `number-1`, `label-1` — First metric +- `number-2`, `label-2` — Second metric +- `number-3`, `label-3` — Third metric (three only) + +--- + +#### `variant: four-with-title` + +Statement on left (1/3), 2×2 grid of 4 metrics on right (2/3). + +**Slots:** + +- `title`, `subtitle` — Left side statement +- `number-1`, `label-1` through `number-4`, `label-4` — Metrics + +--- + +#### `variant: six` + +6 metrics in 3×2 grid. + +**Slots:** + +- `number-1`, `label-1` through `number-6`, `label-6` + +--- + +#### `variant: two-with-source` / `three-with-source` + +2 or 3 metrics with source citations below each. + +**Slots:** + +- `number-1`, `label-1`, `source-1` — First metric with citation +- `number-2`, `label-2`, `source-2` — Second metric +- `number-3`, `label-3`, `source-3` — Third metric (three-with-source only) + +--- + +### 8-special + +#### `variant: qa` + +Q&A slide with decorative left panel. + +**Structure:** + +- Left: Decorative quote bubble SVG + optional custom content +- Right: Badge + up to 4 icon points (contact info, links, etc.) + +**Props:** + +- `badge` — Badge text (default: "Q&A") +- `item1Icon`, `item2Icon`, `item3Icon`, `item4Icon` — Lucide icons + +**Slots:** + +- `left` — Optional content in left panel +- `item-1`, `item-2`, `item-3`, `item-4` — Text next to each icon + +```yaml +--- +layout: 8-special +variant: qa +badge: Questions? +item1Icon: mail +item2Icon: twitter +--- + +::item-1:: +hello@vercel.com + +::item-2:: +@vercel +``` + +--- + +#### `variant: thank-you` + +Centered Vercel triangle + "Thank you" text. No slots. + +--- + +#### `variant: splash` + +Centered Vercel triangle only. No slots. + +--- + +### 9-utility + +Blank canvases for custom content. + +- `variant: blank` — Empty with frame +- `variant: crosshairs` — Frame + crosshair decorations +- `variant: full-grid` — Dense alignment grid + +Default slot accepts any content. + +--- + +### 10-code + +All variants show code in a **code editor frame** with filename header. + +**IMPORTANT — Maximum lines of code (LOC) that fit without clipping:** + +| Size | Full variant | 2/3 variants (right-1, left-2, right-list, left-list) | +| ---- | ------------ | ----------------------------------------------------- | +| `xs` | 23 lines | 19 lines | +| `sm` | 17 lines | 14 lines | +| `md` | 14 lines | 11 lines | +| `lg` | 12 lines | 9 lines | + +**Always count your code lines (including blank lines) and pick a `codeSize` that fits.** If the code exceeds the limit, it will be clipped at the bottom. Prefer trimming code to show only the essential parts rather than cramming everything in at `xs`. + +**Scrollable mode:** Set `scrollable: true` to allow vertical scrolling for longer code. This should NOT be the default — only use it when showing the complete code is essential and trimming would lose important context. Non-scrollable (clipped) code is the standard look. + +#### `variant: full` + +Full-width code, clipped at bottom. + +**Props:** + +- `filename` — Filename shown in header (e.g., `api/hello.ts`) +- `codeSize` — `xs`, `sm` (default), `md`, `lg` +- `scrollable` — `true` to enable vertical scrolling (default: `false`, code clips at bottom) + +**Slots:** + +- `code` — Fenced code block with syntax highlighting + +```yaml +--- +layout: 10-code +variant: full +filename: api/hello.ts +--- + +::code:: +\\`\\`\\`ts +export async function GET() { + return Response.json({ hello: 'world' }) +} +\\`\\`\\` +``` + +--- + +#### `variant: right-1` + +Statement on left (1/3), code on right (2/3). + +**Slots:** + +- `title`, `body` — Statement +- `code` — Code block + +--- + +#### `variant: left-2` + +Code on left (2/3), 2 statements on right (1/3). + +**Slots:** + +- `code` — Code block +- `s1-title`, `s1-body` — First statement +- `s2-title`, `s2-body` — Second statement + +--- + +#### `variant: right-list` / `left-list` + +Code + title + 4 list items. + +**Slots:** + +- `code` — Code block +- `title` — Section title +- `item-1`, `item-2`, `item-3`, `item-4` — List items + +--- + +### 11-promo + +#### `variant: two-statements` + +Badge + decorative grid + 2 statements. + +**Structure:** + +- Top: Badge +- Middle: Decorative square grid +- Bottom: 2 Statement blocks side by side + +**Props:** + +- `badge` — Badge text +- `gridCols` — Grid columns (default: 14) +- `gridRows` — Grid rows (default: 3) + +**Slots:** + +- `s1-title`, `s1-body`, `s1-tag` — First statement +- `s2-title`, `s2-body`, `s2-tag` — Second statement + +--- + +### playful + +Animated full-screen grid layouts. + +#### `variant: welcome` + +Static "▲ W E L C O M E" display. No configuration needed. + +--- + +#### `variant: triangles` + +Random Vercel triangles fade in/out. + +**Props:** + +- `flashInterval` — ms between new triangles (default: 2500) +- `fadeDuration` — ms for fade out (default: 4000) + +--- + +#### `variant: values` + +Random Vercel values (FTW, DIG DEEP, etc.) fade in/out. + +Same props as triangles. + +--- + +#### `variant: splitflap` + +Mechanical split-flap display animation. + +Use `` components in the slide body: + +```yaml +--- +layout: playful +variant: splitflap +--- + + +``` + +**Flap props:** + +- `text` — Text to display (max 16 chars). Use `▲` for Vercel icon. +- `row` — Row number (1–9) +- `startCol` — Starting column (auto-centers if omitted) +- `mono` — Use monospace font (default: false) +- `flipDuration` — ms per character flip (default: 120) +- `staggerDelay` — ms between letters (default: 100) + +--- + +### 12-youtube + +Embedded YouTube video in a **macOS browser frame** (same chrome as 3-screenshot). The browser bar always shows `youtube.com`. Uses privacy-enhanced embedding (`youtube-nocookie.com`). + +**Timestamp support:** Include `&t=SECONDS` in the URL to start the video at a specific time. The component auto-extracts `t=` or `start=` from the URL and passes it to the embed player. + +#### `variant: full` + +Full-width browser frame with embedded video, clipped at bottom. + +**Props:** + +- `url` — YouTube video URL (supports `youtube.com/watch?v=`, `youtu.be/`, `youtube.com/embed/`). Append `&t=SECONDS` to start at a specific timestamp. + +```yaml +--- +layout: 12-youtube +variant: full +url: https://www.youtube.com/watch?v=dQw4w9WgXcQ&t=166 +--- +``` + +--- + +#### `variant: left-list` + +Video in browser frame on left (1/2), title + 4 list items on right (1/2). + +**Props:** + +- `url` — YouTube video URL. Append `&t=SECONDS` to start at a specific timestamp. + +**Slots:** + +- `title` — Section title +- `item-1`, `item-2`, `item-3`, `item-4` — List items + +```yaml +--- +layout: 12-youtube +variant: left-list +url: https://www.youtube.com/watch?v=dQw4w9WgXcQ&t=120 +--- + +::title:: +Video Walkthrough + +::item-1:: +Step 1: Setup + +::item-2:: +Step 2: Configure + +::item-3:: +Step 3: Deploy + +::item-4:: +Step 4: Monitor +``` + +--- + +## Offer Layouts + +Sales offer slides for customer proposals. All offer layouts are dark-themed with the Vercel design system. Data is passed via **frontmatter props** (not slots) for structured data like SKUs, details, and team members. + +--- + +### offer-title + +Cover slide for a customer offer with title and team members. + +**Structure:** + +- Large title at top +- 2×2 team member grid at bottom (name + role in monospace) + +**Props:** + +- `team1Name`–`team4Name` — Team member names +- `team1Role`–`team4Role` — Team member roles (shown in monospace, muted) + +**Slots:** + +- `title` — Main heading (use `
` for line breaks) + +```yaml +--- +layout: offer-title +team1Name: Jane Smith +team1Role: ACCOUNT MANAGER +team2Name: John Doe +team2Role: SOLUTIONS ENGINEER +team3Name: Alice Chen +team3Role: CSM +team4Name: Bob Wilson +team4Role: CRO +--- +::title:: +# Acme Corp's
Vercel offer +``` + +--- + +### offer-statement + +Badge + title on left, numbered points on right. + +**Structure:** + +- Left: Outline badge + large title +- Right: Up to 4 numbered points (01–04) with title + body + +**Props:** + +- `badge` — Badge text (default: "OUR PRICING PHILOSOPHY") +- `badgeColor` — Badge border color (default: "#A0A0A0") + +**Slots:** + +- `title` — Left side heading +- `point-1-title`, `point-1-body` — First numbered point +- `point-2-title`, `point-2-body` — Second numbered point +- `point-3-title`, `point-3-body` — Third point +- `point-4-title`, `point-4-body` — Fourth point + +```yaml +--- +layout: offer-statement +badge: OUR PRICING PHILOSOPHY +--- + +::title:: +Transparency
& predictability + +::point-1-title:: +Usage-based + +::point-1-body:: +Pay for only what you use + +::point-2-title:: +Product flexibility + +::point-2-body:: +One commit across products, shift spend where you need + +::point-3-title:: +Scales with you + +::point-3-body:: +The more you buy, the better your discount +``` + +--- + +### offer-product-grid + +Product portfolio grid showing all Vercel SKUs organized by category. + +**Structure:** + +- Title + subtitle at top +- Grid of product categories with individual SKU pills +- Diagonal background pattern behind grid + +**Props:** + +- `categories` — Array of `{ title, products: [{ name }], highlighted? }` (has sensible defaults with all Vercel products) +- `rows` — Array of index arrays defining grid layout (default: `[[0,1], [2,3,4], [5,6,7,8]]`) + +**Slots:** + +- `title` — Grid heading +- `subtitle` — Subtitle text + +```yaml +--- +layout: offer-product-grid +--- + +::title:: +Introducing Flexible Commitment + +::subtitle:: +Giving your team access to the entire Vercel product portfolio +``` + +--- + +### offer-timeline + +Flex commit timeline visualization with checkbox grid. + +**Structure:** + +- Badge at top +- Large headline (supports mixed white/muted text via HTML spans) +- Month 1–12 timeline with animated checkbox grid + fade overlay + +**Props:** + +- `badge` — Badge text (default: "FLEX COMMIT") +- `startLabel` — Left label (default: "MONTH 1") +- `endLabel` — Right label (default: "MONTH 12") +- `months` — 12-element array of boolean arrays for checkbox states + +**Slots:** + +- `title` — Headline (use `` and `` for mixed styling) + +```yaml +--- +layout: offer-timeline +--- + +::title:: +Swap SKUs anytime to align with
shifting needs and seasonality.
+``` + +--- + +### offer-pricing + +Discount matrix on left, rate card on right. + +**Structure:** + +- Left: Title + 2×2 discount matrix (BASE/MODERATE/HIGH/BEST) +- Right: Rate card table with diagonal background + +**Props:** + +- `badgeText` — Rate card badge (default: "FLEX COMMIT RATE CARD") +- `footerText` — Rate card footer (default: "ENTIRE RATE CARD AVAILABLE WITH FULL PROPOSAL") +- `skus` — Array of `{ name, listPrice, discountPrice }` (default: 3 placeholder SKUs) + +**Slots:** + +- `title` — Left side heading + +```yaml +--- +layout: offer-pricing +skus: + - name: Fluid Compute + listPrice: $0.18/GB-hr + discountPrice: $0.12/GB-hr + - name: Edge Requests + listPrice: $2/M + discountPrice: $1.40/M + - name: Bandwidth + listPrice: $0.15/GB + discountPrice: $0.10/GB +--- + +::title:: +Pricing that
scales with
you. +``` + +--- + +### offer-roi + +Centered card with label/value rows and dotted leaders. + +**Structure:** + +- Centered rounded card with outline badge +- Up to 3 key-value rows with dotted line between label and value + +**Props:** + +- `badge` — Card badge text (default: "ANTICIPATED QUANTIFIED VALUE") + +**Slots:** + +- `label`, `value` — First row (default: "ROI" / "$XM") +- `label-2`, `value-2` — Second row (optional) +- `label-3`, `value-3` — Third row (optional) + +```yaml +--- +layout: offer-roi +--- + +::label:: +ROI + +::value:: +$2.5M + +::label-2:: +Annual savings + +::value-2:: +$500K +``` + +--- + +### offer-details + +Header + key-value sidebar on left, rate card on right. + +**Structure:** + +- Header bar with customer name +- Left: Key-value detail rows (flexible count) +- Right: Rate card with diagonal background + +**Props:** + +- `headerText` — Header text (default: "[Customer name] offer") +- `details` — Array of `{ label, value }` for the sidebar rows +- `badgeText` — Rate card badge +- `footerText` — Rate card footer +- `skus` — Array of `{ name, listPrice, discountPrice }` + +```yaml +--- +layout: offer-details +headerText: Acme Corp offer +details: + - label: Flex commit balance + value: $120K / YR + - label: Term + value: 2 YRS + - label: Support level + value: Enterprise + - label: Start date + value: Q1 2026 +skus: + - name: Fluid Compute + listPrice: $0.18/GB-hr + discountPrice: $0.12/GB-hr + - name: Edge Requests + listPrice: $2/M + discountPrice: $1.40/M + - name: Bandwidth + listPrice: $0.15/GB + discountPrice: $0.10/GB +--- + +``` + +--- + +### offer-comparison + +Side-by-side comparison of 2–3 offer options, each with rate card. + +**Structure:** + +- Header bar with customer name +- Equal-width columns, each with: option badge, balance/term KV rows, small rate card + +**Props:** + +- `headerText` — Header text (default: "[Customer name] offer comparison") +- `options` — Array of `{ label, balance, term, skus: [{ name, listPrice, discountPrice }] }` + +```yaml +--- +layout: offer-comparison +headerText: Acme Corp offer comparison +options: + - label: OPTION 1 + balance: $100K / YR + term: 1 YR + skus: + - name: Fluid Compute + listPrice: $0.18 + discountPrice: $0.15 + - name: Edge Requests + listPrice: $2/M + discountPrice: $1.70/M + - label: OPTION 2 + balance: $120K / YR + term: 2 YRS + skus: + - name: Fluid Compute + listPrice: $0.18 + discountPrice: $0.12 + - name: Edge Requests + listPrice: $2/M + discountPrice: $1.40/M +--- + +``` + +--- + +### offer-before-after + +Side-by-side before/after comparison with colored badges and icon rows. + +**Structure:** + +- Left: Red "BEFORE STATE" badge + year + title + X-icon rows +- Right: Green "AFTER STATE" badge + year + title + check-icon rows + +**Props:** + +- `beforeBadge` — Left badge text (default: "BEFORE STATE") +- `afterBadge` — Right badge text (default: "AFTER STATE") +- `beforeYear` — Left year label (default: "2025 & EARLIER") +- `afterYear` — Right year label (default: "2026+") +- `beforeColor` — Left badge color (default: red `#DA2F36`) +- `afterColor` — Right badge color (default: green `#63C173`) + +**Slots:** + +- `before-title` — Left title (default: "MIU") +- `after-title` — Right title (default: "Flex Commit") +- `before-1` through `before-6` — Before rows (with X icon) +- `after-1` through `after-6` — After rows (with check icon) + +```yaml +--- +layout: offer-before-after +--- + +::before-title:: +MIU + +::after-title:: +Flex Commit + +::before-1:: +Separate commits per SKU + +::after-1:: +Max flexibility + +::before-2:: +Monthly burndown + +::after-2:: +Annual burndown + +::before-3:: +Shallow blanket discount + +::after-3:: +Private rate card +``` diff --git a/presentations/getting-started-workflows-webinar/slides.md b/presentations/getting-started-workflows-webinar/slides.md new file mode 100644 index 0000000..33a3631 --- /dev/null +++ b/presentations/getting-started-workflows-webinar/slides.md @@ -0,0 +1,679 @@ +--- +theme: ./ +title: Getting Started with Vercel Workflows +footerLogo: wordmark +footerTitle: true +layout: 1-title +variant: title +subtitle1Image: https://vercel.com/api/www/avatar/d86685cc0ac958594071ba9cf94e5c50a3551011 +subtitle2Icon: code +subtitle3Icon: cake +subtitle4Icon: bot +subtitleSize: lg +--- + +::title:: +# Getting Started with Vercel Workflows + +::subtitle-1:: +Pranay Prakash, Head of Workflows + +::subtitle-2:: +Long-running backends in plain TypeScript + +::subtitle-3:: +Demo-first walkthrough + +::subtitle-4:: +Streaming, agents, and GA + + +--- +layout: 2-statement +variant: large +--- + +::title:: +# The code you run on day one can be the code you ship + +::subtitle:: +Workflow is about removing the whole “now productionize it with queues, workers, schedulers, and state machines” phase. + + +--- +layout: 2-statement +variant: transition +transitionLabel: "Demo First" +--- + +::title:: +Let's jump straight into the birthday card app + +::subtitle:: +Then we will break apart the code, the runtime model, and how that extends to agents. + + +--- +layout: 2-statement +variant: title-4 +tagPosition: inline +--- + +::title:: +# Birthday card generator + +::s1-tag:: +Input + +::s1-title:: +One request + +::s1-body:: +Describe the card, pick a birthday, add guests, and send. + +::s2-tag:: +Parallel + +::s2-title:: +Image + message + +::s2-body:: +Generate both pieces together, then stream progress to the UI. + +::s3-tag:: +Human + +::s3-title:: +RSVP signatures + +::s3-body:: +Guests click a webhook link to sign the card and resume the workflow. + +::s4-tag:: +Time + +::s4-title:: +Deliver later + +::s4-body:: +Sleep until the birthday, then send the final postcard. + + +--- +layout: 4-mermaid +variant: title-image +chart: | + flowchart LR + A["User submits form"] --> B["POST /api/generate"] + B --> C["start(generateBirthdayCard)"] + C --> D["generate prompts"] + D --> E["generate image"] + D --> F["generate message"] + C --> G["send RSVP emails"] + G --> H["wait for webhook clicks"] + H --> I["sleep until birthday"] + I --> J["send final postcard"] + classDef accent fill:#111111,stroke:#666666,color:#ffffff; + class C,E,F,G,H,I,J accent; +--- + +::title:: +# One workflow, multiple async patterns + + +--- +layout: 2-statement +variant: cols-4 +tagPosition: above +--- + +::title:: +# What to watch during the live demo + +::s1-tag:: +1 + +::s1-title:: +Live progress + +::s1-body:: +The UI updates as the workflow runs, before the final result is done. + +::s2-tag:: +2 + +::s2-title:: +Parallel steps + +::s2-body:: +Image and message generation happen at the same time. + +::s3-tag:: +3 + +::s3-title:: +Suspension + +::s3-body:: +The workflow actually stops running while it waits for RSVP clicks. + +::s4-tag:: +4 + +::s4-title:: +Resumption + +::s4-body:: +The workflow wakes back up exactly where it left off. + + +--- +layout: 2-statement +variant: transition +transitionLabel: "After The Demo" +--- + +::title:: +Now let's break apart what actually happened + +::subtitle:: +The interesting part is how little infrastructure code is needed to model a multi-step, multi-day process. + + +--- +layout: 10-code +variant: left-2 +filename: app/api/generate/generate-birthday-card.ts +codeSize: xs +--- + +::code:: +```ts +export const generateBirthdayCard = async (...) => { + 'use workflow'; + + const { textPrompt, imagePrompt } = await generatePrompts(prompt); + + const [image, text] = await Promise.all([ + generateImage(imagePrompt), + generateMessage(textPrompt), + ]); + + const webhooks = rsvpEmails.map(() => createWebhook()); + await Promise.all(rsvpEmails.map((email, i) => + requestRsvp(email, webhooks[i].url, image, text) + )); + + const rsvpReplies = await Promise.all(webhooks); + await sleep(birthday!); + await sendRecipientEmail({ recipientEmail, cardImage: image, cardText: text, rsvpReplies }); +}; +``` + +::s1-title:: +This reads like product logic + +::s1-body:: +It is top-to-bottom business logic, not a map of workers, queues, and schedulers. + +::s2-title:: +This is the main abstraction + +::s2-body:: +A workflow is just long-running code with explicit async boundaries. + + +--- +layout: 10-code +variant: right-list +filename: app/api/generate/route.ts + app/api/generate/[runId]/stream/route.ts +codeSize: xs +--- + +::code:: +```ts +const run = await start(generateBirthdayCard, [...]); + +return new Response(run.readable, { + headers: { + 'x-workflow-run-id': run.runId, + }, +}); + +const stream = run.getReadable({ startIndex }); +return new Response(stream); +``` + +::title:: +# Two routes, not an orchestration system + +::item-1:: +One route starts the workflow + +::item-2:: +One route resumes the stream + +::item-3:: +No webhook correlation database + +::item-4:: +No worker app to coordinate separately + + +--- +layout: 2-statement +variant: cols-3 +tagPosition: inline +--- + +::title:: +# Hooks and webhooks are the most powerful primitive here + +::s1-tag:: +Webhook + +::s1-title:: +One-time public URL + +::s1-body:: +Perfect for email links, approvals, signatures, and “click to continue” flows. + +::s2-tag:: +Hook + +::s2-title:: +Resume from your own code + +::s2-body:: +Use a token to resume from Slack, GitHub, internal events, or another route. + +::s3-tag:: +Pattern + +::s3-title:: +Event bus without extra state + +::s3-body:: +One workflow can naturally map to one Slack thread, one PR, one task, or one onboarding flow. + + +--- +layout: 2-statement +variant: cols-2 +tagPosition: inline +--- + +::title:: +# Sleep is more than a timer + +::s1-tag:: +Delay + +::s1-title:: +Wait for days or weeks + +::s1-body:: +The workflow suspends fully and resumes later. No server is sitting there “waiting.” + +::s2-tag:: +Timeout + +::s2-title:: +Race hooks vs sleep + +::s2-body:: +“Wait for a webhook, but only for 24 hours” becomes a simple pattern instead of custom infrastructure. + + +--- +layout: 2-statement +variant: cols-3 +tagPosition: inline +--- + +::title:: +# What `use workflow` and `use step` are actually doing + +::s1-tag:: +Workflow + +::s1-title:: +Deterministic replay + +::s1-body:: +The workflow function replays quickly using the event log instead of redoing side effects. + +::s2-tag:: +Step + +::s2-title:: +Full Node.js boundary + +::s2-body:: +Steps run the real work, serialize inputs and outputs, and become retryable execution units. + +::s3-tag:: +Result + +::s3-title:: +Code stays simple + +::s3-body:: +You get durability without turning your code into a state machine DSL. + + +--- +layout: 4-mermaid +variant: left-list +chart: | + flowchart TD + A["Workflow starts"] --> B["Call step"] + B --> C["Step runs"] + C --> D["Inputs + outputs logged"] + D --> E["Workflow replays"] + E --> F["Resume from event log"] + F --> G["Wait on hook / sleep"] + G --> H["Replay and continue"] + classDef accent fill:#111111,stroke:#666666,color:#ffffff; + class B,C,D,E,F,G,H accent; +--- + +::title:: +# The event log powers both durability and observability + +::item-1:: +Every step has inputs and outputs + +::item-2:: +Parallel steps show up naturally + +::item-3:: +Replay and recovery come from the same system + +::item-4:: +You get the audit trail without building it yourself + + +--- +layout: 2-statement +variant: cols-3 +tagPosition: inline +--- + +::title:: +# Streaming is durable too + +::s1-tag:: +UI + +::s1-title:: +Live progress + +::s1-body:: +Steps write progress to a workflow stream so the client sees updates before the run finishes. + +::s2-tag:: +Recovery + +::s2-title:: +Resume from chunk index + +::s2-body:: +If the page reloads or the connection drops, the client can reconnect and continue from where it left off. + +::s3-tag:: +Scale + +::s3-title:: +Same primitive for agents + +::s3-body:: +Simple apps send progress JSON; agents can stream every token, tool call, and result. + + +--- +layout: 10-code +variant: left-2 +filename: flight-booking-app/workflows/chat/index.ts +codeSize: xs +--- + +::code:: +```ts +export async function chat(initialMessages: UIMessage[]) { + 'use workflow'; + + const writable = getWritable(); + + const agent = new DurableAgent({ + model: 'bedrock/claude-haiku-4-5-20251001-v1', + system: FLIGHT_ASSISTANT_PROMPT, + tools: flightBookingTools, + }); + + await agent.stream({ + messages, + writable, + preventClose: true, + }); +} +``` + +::s1-title:: +An agent is basically just a workflow + +::s1-body:: +LLM calls, tool calls, retries, and long-running state all fit the same model. + +::s2-title:: +Why DurableAgent matters + +::s2-body:: +It gives agent builders a native workflow abstraction instead of a fragile loop around `streamText`. + + +--- +layout: 2-statement +variant: cols-4 +tagPosition: above +--- + +::title:: +# Getting started is intentionally small + +::s1-tag:: +Setup + +::s1-title:: +Wrap the framework + +::s1-body:: +In Next.js, the setup is basically `withWorkflow(nextConfig)`. + +::s2-tag:: +Adoption + +::s2-title:: +Start with one painful path + +::s2-body:: +Retries, approvals, delayed sends, or multi-step AI are perfect first candidates. + +::s3-tag:: +Breadth + +::s3-title:: +Many frameworks + +::s3-body:: +Next.js, Astro, Express, Fastify, Hono, Nitro, Nuxt, SvelteKit, Vite, and more. + +::s4-tag:: +Launch + +::s4-title:: +Python too + +::s4-body:: +GA is about broadening where the model applies, not just polishing one API. + + +--- +layout: 2-statement +variant: grid-4 +tagPosition: above +--- + +::title:: +# Why GA matters + +::s1-tag:: +Internal + +::s1-title:: +Used across Vercel agents + +::s1-body:: +This is no longer a niche experiment. It is part of how Vercel runs long-lived agent systems. + +::s2-tag:: +Customer + +::s2-title:: +Flora + +::s2-body:: +A creative agent workflow where the core product experience is built on workflows. + +::s3-tag:: +Customer + +::s3-title:: +Durable + +::s3-body:: +Workflow-powered onboarding and generated customer sites as part of a real production flow. + +::s4-tag:: +Scale + +::s4-title:: +5,000+ step runs + +::s4-body:: +The model is not limited to toy demos. It is designed for serious, high-step, high-parallelism workloads. + + +--- +layout: 2-statement +variant: large +--- + +::title:: +# Durable systems should be easy to explain from the code itself + +::subtitle:: +If your team can read the workflow top to bottom, they can build it, debug it, and trust it in production. + + +--- +layout: 8-special +variant: qa +badge: Questions? +item1Icon: mail +item2Icon: github +item3Icon: book-open +item4Icon: bot +--- + +::left:: +## Thank you + +Let's talk about the workflows you want to make durable next. + +::item-1:: +pranay@vercel.com + +::item-2:: +github.com/vercel/workflow-examples + +::item-3:: +useworkflow.dev + +::item-4:: +Hooks, streaming, agents, and GA + + diff --git a/presentations/getting-started-workflows-webinar/slides.with-notes.bak b/presentations/getting-started-workflows-webinar/slides.with-notes.bak new file mode 100644 index 0000000..598738a --- /dev/null +++ b/presentations/getting-started-workflows-webinar/slides.with-notes.bak @@ -0,0 +1,793 @@ +--- +theme: ./ +title: Getting Started with Vercel Workflows +footerLogo: wordmark +footerTitle: true +layout: 1-title +variant: title +subtitle1Image: https://vercel.com/api/www/avatar/d86685cc0ac958594071ba9cf94e5c50a3551011 +subtitle2Icon: code +subtitleSize: lg +--- + +::title:: +# Getting Started with Vercel Workflows + +::subtitle-1:: +Pranay Prakash, Head of Workflows + +::subtitle-2:: +Durable backends in plain TypeScript + + +--- +--- +layout: 2-statement +variant: large +--- + +::title:: +# Long-running backends should feel like writing application code + +::subtitle:: +Not stitching together queues, retries, schedulers, and state machines by hand. + + +--- +--- +layout: 1-title +variant: agenda +badge: Today's Flow +--- + +::item-1:: +Why async systems get messy fast + +::item-2:: +The Workflow mental model + +::item-3:: +Live demo: birthday card generator + +::item-4:: +What durability looks like in code + +::item-5:: +Human input, waiting, and resuming + +::item-6:: +Patterns beyond one demo app + +::item-7:: +How to adopt Workflow incrementally + +::item-8:: +Q&A + + +--- +--- +layout: 2-statement +variant: title-3 +tagPosition: inline +--- + +::title:: +# Why this matters + +::s1-tag:: +Today + +::s1-title:: +Async code is everywhere + +::s1-body:: +Background jobs, approval flows, delayed sends, retries, and agent loops show up in almost every modern app. + +::s2-tag:: +Pain + +::s2-title:: +The usual stack sprawls + +::s2-body:: +Queues, workers, cron, retry logic, and state tracking get split across services and become harder to reason about. + +::s3-tag:: +Shift + +::s3-title:: +Durability moves into your code + +::s3-body:: +Workflow turns plain TypeScript functions into resilient, observable processes without forcing a rearchitecture. + + +--- +--- +layout: 2-statement +variant: cols-3 +tagPosition: inline +--- + +::title:: +# What you'll learn in this session + +::s1-tag:: +Core + +::s1-title:: +Two directives + +::s1-body:: +How `use workflow` and `use step` convert ordinary functions into durable execution boundaries. + +::s2-tag:: +Practical + +::s2-title:: +One real product flow + +::s2-body:: +We will build the mental model through a birthday card app with generation, approvals, waiting, and delivery. + +::s3-tag:: +Adoption + +::s3-title:: +One migration recipe + +::s3-body:: +How to take an existing async path or AI agent and convert it into a workflow in minutes. + + +--- +--- +layout: 2-statement +variant: transition +transitionLabel: "Part 1 of 4" +--- + +::title:: +The fastest way to explain Workflow is through one product story + +::subtitle:: +We are going to use a playful app so the system design stays memorable. + + +--- +--- +layout: 2-statement +variant: title-3 +tagPosition: inline +--- + +::title:: +# Demo app: birthday card generator + +::s1-tag:: +Parallel + +::s1-title:: +Two AI tasks at once + +::s1-body:: +Generate an image and a message in parallel from one user prompt. + +::s2-tag:: +Human Input + +::s2-title:: +RSVPs as workflow events + +::s2-body:: +Invite friends, wait for replies, and resume the flow when real people interact. + +::s3-tag:: +Scheduling + +::s3-title:: +Deliver at the right moment + +::s3-body:: +Pause until the birthday, then send the final card and RSVP summary to the recipient. + + +--- +--- +layout: 4-mermaid +variant: title-image +chart: | + flowchart LR + A["User submits prompt"] --> B["POST /api/generate"] + B --> C["start(generateBirthdayCard)"] + C --> D["generatePrompts()"] + D --> E["generateImage()"] + D --> F["generateMessage()"] + C --> G["requestRsvp()"] + G --> H["createWebhook() waits for replies"] + H --> I["sleep(until birthday)"] + I --> J["sendRecipientEmail()"] + classDef accent fill:#111111,stroke:#666666,color:#ffffff; + class C,E,F,G,H,I,J accent; +--- + +::title:: +# One workflow, multiple real-world async patterns + + +--- +--- +layout: 10-code +variant: right-list +filename: app/api/generate/route.ts +codeSize: sm +--- + +::code:: +```ts +export const POST = async (request: Request) => { + const body = await request.json(); + + const run = await start(generateBirthdayCard, [ + body.prompt, + body.recipientEmail, + body.rsvpEmails, + new Date(body.eventDate), + ]); + + const values = await run.returnValue; + return NextResponse.json(values); +}; +``` + +::title:: +# Workflow starts from a normal route + +::item-1:: +No separate worker app + +::item-2:: +No custom queue producer + +::item-3:: +Your API returns workflow output + +::item-4:: +The call site stays easy to read + + +--- +--- +layout: 10-code +variant: left-2 +filename: app/api/generate/generate-birthday-card.ts +codeSize: xs +--- + +::code:: +```ts +export const generateBirthdayCard = async (...) => { + 'use workflow'; + + const { textPrompt, imagePrompt } = await generatePrompts(prompt); + + const [image, text] = await Promise.all([ + generateImage(imagePrompt), + generateMessage(textPrompt), + ]); + + const webhooks = rsvpEmails.map(() => createWebhook()); + await Promise.all(rsvpEmails.map((email, i) => + requestRsvp(email, webhooks[i].url) + )); + + const rsvpReplies = await Promise.all(webhooks); + await sleep(birthday!); + await sendRecipientEmail({ recipientEmail, cardImage: image, cardText: text, rsvpReplies }); +}; +``` + +::s1-title:: +`use workflow` + +::s1-body:: +This function becomes the durable orchestrator for the whole flow. + +::s2-title:: +Read it top to bottom + +::s2-body:: +The control flow stays understandable even though the work spans time, models, emails, and human actions. + + +--- +--- +layout: 10-code +variant: right-list +filename: app/api/generate/generate-image.ts + generate-message.ts +codeSize: sm +--- + +::code:: +```ts +export const generateImage = async (prompt: string) => { + 'use step'; + const { files } = await generateText({ + model: 'google/gemini-2.5-flash-image', + prompt, + }); + return files.at(0); +}; + +export const generateMessage = async (prompt: string) => { + 'use step'; + const { text } = await generateText({ + model: 'openai/gpt-5-nano', + prompt, + }); + return text; +}; +``` + +::title:: +# Each async boundary becomes explicit + +::item-1:: +Model calls are isolated into steps + +::item-2:: +Retries happen at the right boundary + +::item-3:: +Outputs are captured per step + +::item-4:: +Observability matches the code you wrote + + +--- +--- +layout: 2-statement +variant: transition +transitionLabel: "Live Demo" +--- + +::title:: +Switch from slides to the app + +::subtitle:: +The goal is not just to generate a card. The goal is to watch a durable workflow move through real states. + + +--- +--- +layout: 2-statement +variant: cols-4 +tagPosition: above +--- + +::title:: +# What to watch during the demo + +::s1-tag:: +1 + +::s1-title:: +Start + +::s1-body:: +Submit one prompt and kick off the workflow from the UI. + +::s2-tag:: +2 + +::s2-title:: +Parallel work + +::s2-body:: +Image and message generation can happen together. + +::s3-tag:: +3 + +::s3-title:: +Pause + +::s3-body:: +The workflow waits for RSVP clicks and for the birthday delay. + +::s4-tag:: +4 + +::s4-title:: +Resume + +::s4-body:: +The final email sends only after the required external events complete. + + +--- +--- +layout: 2-statement +variant: transition +transitionLabel: "Part 2 of 4" +--- + +::title:: +What just happened is the product story + +::subtitle:: +Now let's turn that story into a reusable mental model you can apply to your own app. + + +--- +--- +layout: 2-statement +variant: title-4 +tagPosition: inline +--- + +::title:: +# Four primitives to remember + +::s1-tag:: +Entrypoint + +::s1-title:: +`use workflow` + +::s1-body:: +Marks the durable orchestrator. + +::s2-tag:: +Boundary + +::s2-title:: +`use step` + +::s2-body:: +Marks retryable units of work. + +::s3-tag:: +External Event + +::s3-title:: +`createWebhook` + +::s3-body:: +Lets humans or systems resume a waiting workflow. + +::s4-tag:: +Time + +::s4-title:: +`sleep` + +::s4-body:: +Lets a workflow pause and continue later without custom schedulers. + + +--- +--- +layout: 4-mermaid +variant: left-list +chart: | + flowchart TD + A["Workflow begins"] --> B["Step succeeds"] + B --> C["State + output recorded"] + C --> D["Next step runs"] + D --> E{"Failure?"} + E -->|yes| F["Retry only failed step"] + E -->|no| G["Continue forward"] + G --> H["Pause on webhook or sleep"] + H --> I["Resume without replaying completed work"] + classDef accent fill:#111111,stroke:#666666,color:#ffffff; + class B,C,D,F,H,I accent; +--- + +::title:: +# The event log is the superpower + +::item-1:: +Completed work is persisted + +::item-2:: +Retries target the failing boundary + +::item-3:: +Pauses become first-class state + +::item-4:: +Recovery does not mean starting over + + +--- +--- +layout: 2-statement +variant: cols-3 +tagPosition: inline +--- + +::title:: +# Why this feels different from the usual stack + +::s1-tag:: +Less Glue + +::s1-title:: +Fewer moving parts + +::s1-body:: +You are not wiring queue producers, workers, retry policies, and schedulers as separate systems. + +::s2-tag:: +More Clarity + +::s2-title:: +Code matches runtime + +::s2-body:: +The observable units in the platform line up with the functions and steps in your source code. + +::s3-tag:: +Safer AI + +::s3-title:: +Agents become practical + +::s3-body:: +Long-running tool loops, approvals, and resumable streams stop being one-off infrastructure projects. + + +--- +--- +layout: 2-statement +variant: transition +transitionLabel: "Part 3 of 4" +--- + +::title:: +The birthday card app is one example, not the whole story + +::subtitle:: +Once the mental model clicks, the same approach applies to agent patterns, framework integrations, and production workflows. + + +--- +--- +layout: 2-statement +variant: cols-3 +tagPosition: inline +--- + +::title:: +# More examples from this repo + +::s1-tag:: +Patterns + +::s1-title:: +AI SDK workflow patterns + +::s1-body:: +Sequential, parallel, routing, orchestrator-worker, and evaluator loop patterns built on top of Workflow. + +::s2-tag:: +Agents + +::s2-title:: +Flight booking app + +::s2-body:: +A durable AI agent with tools, multi-turn state, retries, and stream reconnection. + +::s3-tag:: +Reach + +::s3-title:: +Framework integrations + +::s3-body:: +The examples repo already shows Workflow across Next.js, Astro, Hono, Nitro, Nuxt, SvelteKit, and Vite. + + +--- +--- +layout: 2-statement +variant: title-3 +tagPosition: inline +--- + +::title:: +# How to adopt Workflow incrementally + +::s1-tag:: +Start Small + +::s1-title:: +Pick one async path + +::s1-body:: +Choose a flow that already hurts: approvals, retries, delayed sends, or multi-step AI processing. + +::s2-tag:: +Draw Boundaries + +::s2-title:: +Mark the expensive edges + +::s2-body:: +Put model calls, external APIs, email sends, and side effects behind `use step`. + +::s3-tag:: +Replace Glue + +::s3-title:: +Keep your app, remove orchestration + +::s3-body:: +Start the workflow from your existing route or handler and let the platform manage durability underneath. + + +--- +--- +layout: 10-code +variant: right-list +filename: next.config.ts +codeSize: sm +--- + +::code:: +```ts +import { withWorkflow } from 'workflow/next'; + +const nextConfig = {}; + +export default withWorkflow(nextConfig); +``` + +::title:: +# The setup story is intentionally small + +::item-1:: +Bring Workflow into the framework you already use + +::item-2:: +Keep routes, components, and deployment flow familiar + +::item-3:: +Add workflows where you need durability + +::item-4:: +Expand from one use case to many + + +--- +--- +layout: 2-statement +variant: cols-3 +tagPosition: inline +--- + +::title:: +# Migration recipe you can use tomorrow + +::s1-tag:: +1 + +::s1-title:: +Find the workflow + +::s1-body:: +Look for the place where one request turns into a multi-step process. + +::s2-tag:: +2 + +::s2-title:: +Isolate the steps + +::s2-body:: +Move each durable boundary into a small, testable function with `use step`. + +::s3-tag:: +3 + +::s3-title:: +Add time and humans + +::s3-body:: +Once the flow is durable, webhook waits, approvals, scheduling, and agent loops feel natural instead of scary. + + +--- +--- +layout: 2-statement +variant: large +--- + +::title:: +# Durable systems should be easy to explain from the code itself + +::subtitle:: +That is the bar. If your team can read the workflow top to bottom, they can own it in production. + + +--- +--- +layout: 8-special +variant: qa +badge: Q&A +item1Icon: mail +item2Icon: github +item3Icon: book-open +item4Icon: cake +--- + +::left:: +## Thank you + +Let's talk about the workflows you want to make durable next. + +::item-1:: +pranay@vercel.com + +::item-2:: +github.com/vercel/workflow-examples + +::item-3:: +useworkflow.dev + +::item-4:: +Birthday card demo + repo tour + + diff --git a/presentations/getting-started-workflows-webinar/speaker-notes.md b/presentations/getting-started-workflows-webinar/speaker-notes.md new file mode 100644 index 0000000..1e5dec5 --- /dev/null +++ b/presentations/getting-started-workflows-webinar/speaker-notes.md @@ -0,0 +1,130 @@ +# Speaker Notes + +Short presenter notes based on the actual mock walkthrough. + +## Slide 1 - Getting Started with Vercel Workflows + +- Quick intro. +- Demo first, then code, then the broader story. +- Focus today: long-running backends in plain TypeScript. + +## Slide 2 - The code you run on day one can be the code you ship + +- This is the real thesis. +- We are used to demo first, then “productionize” later. +- Workflow is trying to remove that whole second phase. + +## Slide 3 - Let's jump straight into the birthday card app + +- I want to go directly into the demo. +- Then I will break apart the code and runtime. + +## Slide 4 - Birthday card generator + +- One request. +- Parallel image + message generation. +- Guests sign with RSVP links. +- Wait until the birthday. +- Send the final postcard. + +## Slide 5 - One workflow, multiple async patterns + +- Immediate work. +- Human input. +- Waiting on time. +- Final delivery. + +## Slide 6 - What to watch during the live demo + +- Live progress updates. +- Parallel steps. +- Real suspension. +- Real resumption. + +## Slide 7 - Now let's break apart what actually happened + +- After the demo, move into code. +- The key message is how little infrastructure code is here. + +## Slide 8 - Workflow code + +- This reads like product logic. +- It matches how I would whiteboard the feature. +- That is the abstraction I want people to remember. + +## Slide 9 - Two routes, not an orchestration system + +- One route starts the workflow. +- One route resumes the stream. +- No extra DB for webhook correlation. +- No separate worker service. + +## Slide 10 - Hooks and webhooks + +- This is one of the most powerful primitives. +- Webhooks for public one-time URLs. +- Hooks for internal resume logic. +- Great for Slack, GitHub, approvals, and event-driven systems. + +## Slide 11 - Sleep is more than a timer + +- This is a real long-running primitive. +- Sleep for days or weeks. +- Also mention timeout patterns with hook vs sleep. + +## Slide 12 - What `use workflow` and `use step` are actually doing + +- Workflow replays. +- Steps do the real work. +- Event log resolves prior results. +- This is why the code stays simple. + +## Slide 13 - The event log powers both durability and observability + +- Observability is not bolted on. +- Same event log powers replay, recovery, and the audit trail. +- Inputs and outputs are already there. + +## Slide 14 - Streaming is durable too + +- New important piece in this version of the talk. +- Not just live streaming, resumable streaming. +- Same primitive works for simple apps and for agents. + +## Slide 15 - An agent is basically just a workflow + +- This is the framing I used in the mock. +- LLM calls and tool calls fit naturally into the workflow model. +- DurableAgent is the concrete example. + +## Slide 16 - Getting started is intentionally small + +- Setup is light. +- Start with one painful async path. +- Mention framework breadth and Python at GA. + +## Slide 17 - Why GA matters + +- Internal usage across Vercel agents. +- Customer stories: Flora and Durable. +- This is the proof slide, not the hype slide. +- Mention 5,000-step runs and serious workloads. + +## Slide 18 - Durable systems should be easy to explain from the code itself + +- This is the closing thought. +- Reliability plus clarity. +- If the team can read it, they can own it. + +## Slide 19 - Q&A + +- Invite questions on adoption. +- Invite questions on hooks, streaming, and agents. +- Seed with “what is a good first workflow candidate?” if needed. + +## Optional talking points if you need extra time + +- One workflow can map naturally to one Slack thread or one GitHub PR. +- Timeout pattern: race a hook against sleep. +- Streams are resumable, which matters for real production UIs. +- DurableAgent is a clean bridge from workflows into AI agents. From 6535f3d588057624def7efdbce4db76c1077b369 Mon Sep 17 00:00:00 2001 From: Pranay Prakash Date: Wed, 22 Apr 2026 13:57:30 -0700 Subject: [PATCH 2/2] Ignore .vslides.json (contains API tokens) Co-Authored-By: Claude Opus 4.7 --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index c8a7336..ba8c66d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .vercel .env*.local +.vslides.json