Master slide guide

How to build a new slide master

Every master lives in src/components/slideLayouts.tsx as one entry in the layouts array. Follow the conventions below so new masters compose cleanly with the existing ones and with the renderer's shared behaviors (bleed, params, theming).

01

Respect the safe area and bleed

Box coordinates { x, y, w, h } are percentages of the inner safe area — the region inside --slide-bleed, not the full 1920×1080 canvas. x:0, y:0 is the top-left of the safe area; x:100, y:100 is the bottom-right. The slide shell (.slide-safe) applies the bleed padding for you.

  • Hang titles and body copy off x:0 so a single vertical baseline runs through the whole deck.
  • Kickers pin flush to top-left (y:0); footer labels pin to y:95. Do not invent new baselines for these.
  • To render an edge-to-edge background, use the full-bleed sentinel { x:0, y:0, w:100, h:100, role:"media" }. The renderer expands it past the safe-area padding automatically.
  • Never hard-code pixel offsets to try to "add padding" — that fights the bleed system. Adjust percentages instead.
02

Use configurable parameters for on/off toggles

When a master has an element that authors may want to hide (a category pill, a footer badge, a decorative accent), add a ParamSpec rather than forking the master into two variants. See the Editorial title master for the canonical example.

{
  id: "editorial-title",
  name: "Editorial title",
  boxes: [
    { x: 0, y: 0,  w: 55,  h: 5,  role: "kicker" },
    { x: 0, y: 14, w: 100, h: 34, role: "title"  },
    // ...
  ],
  params: [
    {
      id: "showKicker",
      label: "Category pill",
      description: "Show the small category label above the title.",
      type: "toggle",
      default: true,
      hidesSlots: ["kicker"],
    },
  ],
}
  • id — stable key stored on SlideData.paramValues. Never rename after shipping.
  • type"toggle" (Switch), "text" (Input, overrides appliesToSlot text), "icon" (picker from KICKER_ICONS), or "media" (image / video URL used as a full-bleed background layer behind slot content).
  • default — value applied when a slide has no explicit override. Match the "safe" state (usually true = shown).
  • hidesSlots — slot keys removed when the toggle is off. Slot keys follow the same rule the renderer uses: role for the first box of that role, role2, role3… for subsequent ones.

The renderer resolves defaults via resolveParams() and hides boxes via hiddenSlots() — you don't need to branch in the layout itself, just declare the param.

02b

Text containers hug their content

Boxes with a text role — title, subtitle, body, kicker, pricing — render at height: auto. Their h value is a reservation, not a fixed frame: the box shrinks to fit the text, and the visible gap to the next block is next.y − current.y − actual text height.

  • Control the gap between two text blocks by setting the next block's y, never by padding an oversized h. Padding a fixed frame gives you a giant top gap once the text is shorter than the frame.
  • Keep h generous enough that the auto text can't overflow into the next box at max content — think of it as the worst-case ceiling.
  • Fixed-height roles are visuals (device, media, video, gallery), content grids (features), and thin structural strips (label). Those render at exactly the declared h.
03

Group related variants under a family

When a master has several visual variants that share intent (e.g. the Proposal family), give each layout the same family id plus a short variantLabel. The nav sidebar collapses them under one chevron row automatically.

{
  id: "proposal-pricing-split",
  name: "Proposal · Pricing split",
  family: "proposal",
  familyLabel: "Proposal",   // set on the FIRST variant only
  variantLabel: "Pricing split",
  boxes: [ /* ... */ ],
}
04

Pick roles, don't invent primitives

The renderer knows how to draw a fixed set of roles: title, subtitle, body, kicker, label, card, stat, quote, media, pricing, video, gallery. Compose masters from these. Only add a new BoxRole when a truly new content type appears — and update roleClasses, hintForRole, and the render branch in the same change.

05

Superbar design DNA

Every master should feel like it came off superbar.ai. Pull from the same small kit — don't invent parallel systems.

  • Type stack. One family only: InterDisplay for headings, Inter for body — both loaded through font-display / font-sans. No serif, no italic accent family. The site ships zero Instrument Serif; don't introduce one here.
  • Headline weight. Hero titles ship at 900 (black) with very tight tracking (-0.085em) and near-solid leading (0.98). Section heads (h2) drop to 700 at -0.02em/1.15; sub-heads (h3) to 600 at -0.015em/1.2. Body/lede stays 400–500, tracking normal, leading 1.28–1.4. Emphasize with weight (600/700), never with italics.
  • Scale. Oversized hero (titleSize(), cap width driven), 44px subtitle, 32px body, 22px kicker/label. Kickers and chrome are uppercase with 0.14–0.16em tracking; nothing else is.
  • Palette. Neutrals do all the layout work — neutral-100 as the page ground, neutral-900/neutral-950 (near-black #111) for type and dark surfaces. In this deck those map to --color-cream and --color-plum. One accent per slide, never two: emerald (emerald-400) is the primary bright hit; amber and red are reserved for status/alerts.
  • Radii. Base radius is --radius: 0.625rem (10px). Cards and media surfaces use rounded-lg/rounded-xl — not rounded-2xl/3xl. Buttons, pills, and chips are the only rounded-full shapes.
  • Surfaces. Cards ride bg-white (or bg-neutral-900 when inverted) with a hairline border-plum/10 and no shadow beyond a soft shadow-sm on hero cards. The featured card in a pair flips to bg-plum with white text.
  • Accents. Kicker pills use a subtle border + tinted background (border-plum/15 bg-plum/[0.06]) with a 14px lucide glyph at 70% opacity. Rules and dividers are border-plum/10, never full-opacity black.
  • Imagery. Full-bleed media uses the sentinel box and carries a black-to-transparent overlay so the headline holds contrast. Product/UI shots sit inside rounded surfaces with the same hairline border — no drop shadows.
  • Motion (when we add it). Text enters with a short (600ms) 16px rise + fade on the cubic-bezier(0.22, 1, 0.36, 1) curve superbar.ai uses, staggered by role: kicker → title → body. No scale, no blur, no bounce.
  • Restraint rule. One hero idea, one accent color, one CTA. If a slide has more than one of any of these, split it.
06

Component kit — buttons, pills, borders

Copy these primitives into new masters instead of re-styling from scratch. Each pairs the rendered example with its exact class list.

Buttons & CTAs

// Primary
<button className="inline-flex items-center gap-2 rounded-full bg-plum text-cream px-5 py-2.5 text-[14px] font-medium">
  Primary action →
</button>

// Secondary
<button className="inline-flex items-center gap-2 rounded-full border border-plum/20 bg-white text-plum px-5 py-2.5 text-[14px] font-medium">
  Secondary
</button>

// Ghost link
<button className="text-plum text-[14px] font-medium underline underline-offset-4 decoration-plum/30">
  Ghost link →
</button>

Kicker pills

Category
Inverted
// Light surface
<div className="inline-flex items-center gap-2 rounded-full border border-plum/15 bg-plum/[0.06] text-slate px-4 py-1.5 text-[12px] uppercase tracking-[0.14em]">
  Category
</div>

// On dark slide
<div className="... border-white/25 bg-white/10 text-white/90 ...">Inverted</div>

Box borders & surfaces

Card
Hairline border, 10px radius, no shadow.
Card · shadow-sm
Reserve for pricing / hero cards only.
Featured
Flip to plum for the emphasized card in a pair.
// Default card
<div className="rounded-xl bg-white border border-plum/10 p-6" />

// Elevated (pricing, hero)
<div className="rounded-xl bg-white border border-plum/10 p-10 shadow-sm" />

// Featured (dark)
<div className="rounded-xl bg-plum text-cream border border-plum p-6" />

Dividers & rules

border-plum/10 — hairline
<div className="h-px bg-plum/10" />          // hairline
<div className="border-t border-plum/10" />   // block divider
<div className="border-l border-plum/10" />   // vertical rail

Accent chips (use sparingly)

Amber accentSuperbar red

One accent per slide, never both. Use for a status, a delta, or a single word inside a headline — not for standard body copy.

07

Default spacing between text elements

Text elements read as one unit when the gaps between them stay in a tight range (12–48px). Use the defaults below; deviate only for a deliberate compositional reason.

  • Pill → title24px (mt-6). Close enough to feel attached, loose enough that the kicker doesn't crowd the cap height.
  • Title → subtitle16px (mt-4). Subtitles are a continuation of the headline; keep them tight.
  • Title → body32px (mt-8). A clear break between the hero idea and the supporting paragraph.
  • Subtitle → body24px (mt-6). Slightly tighter than title→body because the subtitle already bridged the gap.
  • Body → body (paragraph stack) — 20px (mt-5) between paragraphs.
  • Body → CTA / footer note48px (mt-12). Actions and metadata sit in their own zone.

Hard rules: never go below 12px (elements collide) or above 48px (they stop reading as a unit). When two elements must feel like one voice, use the low end; when you want a beat of silence, use the high end.

More sections land here as we agree on new conventions. Keep entries short and paired with a canonical example from an existing master. Back to masters.