
Openpress Create Slide
- 27 installs
- 11 repo stars
- Updated August 4, 2026
- quan0715/open-press
Helps with ai & agent building tasks.
About
openpress-create-slide is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted coding.
- openpress-create-slide
- AI & Agent Building
- AI-coding skill
Openpress Create Slide by the numbers
- 27 all-time installs (skills.sh)
- +4 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #9,601 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/quan0715/open-press --skill openpress-create-slideAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 27 |
|---|---|
| repo stars | ★ 11 |
| Last updated | August 4, 2026 |
| Repository | quan0715/open-press ↗ |
What it does
Helps with ai & agent building tasks.
Files
OpenPress Create Slide
openpress-create-slide owns artifact creation. The openpress skill owns CLI lifecycle: build, render, PDF, image, deploy, doctor, upgrade.
Boundary
| Owner | Scope |
|---|---|
openpress-create-slide | Create, add, edit, or revise slide decks: structure, template registry, theme tokens/CSS, Tailwind semantic styling, UI primitives, assets, narrative. |
openpress-create-pages | Page-based documents. |
openpress | CLI lifecycle. |
openpress-deploy | Public deploy after explicit user confirmation. |
openpress-apply-comments | Pending @openpress-comment markers. |
---
Delivery Workflow
Every deck follows four delivery phases. Do not jump straight to JSX unless the user asks for a narrow code edit.
TOPIC → STORY → STYLE → PRODUCE1. TOPIC
Lock the presentation intent.
Output a short working brief, then proceed with reasonable assumptions unless a missing answer would materially change the deck.
| Decision | What to capture |
|---|---|
| Topic | What the deck is about |
| Audience | Who must understand or decide |
| Objective | Inform, sell, align, teach, report, or persuade |
| Core promise | The one sentence the deck must make believable |
| Constraints | page count, deadline, assets, brand, export target |
Do not ask the user to write YAML before seeing anything. If details are missing, state assumptions and keep moving.
2. STORY
Create the content outline and narrative arc before designing individual slides.
Output a slide plan table:
| Field | Purpose |
|---|---|
id | Stable semantic slide id |
kind | cover, context, problem, insight, proof, agenda, comparison, close, etc. |
message | The one thing this slide says |
evidence | Source, asset, metric, quote, or placeholder needed |
template | Registered template to use or template to create |
Narrative rules:
- Start with audience context, not decorative cover copy.
- Give each slide one job. Split slides when a message needs two unrelated visual hierarchies.
- Keep the deck coherent: every slide should support the core promise.
- Mark missing evidence explicitly; do not invent material business numbers, legal claims, or public commitments.
- Prefer fewer, stronger slides over dense slide dumps.
3. STYLE
Design the visual system before bulk production.
Output:
- Visual direction: one chosen direction with a short rationale.
- Theme tokens: colors, fonts, typography roles, and whether they belong in
slide-style/theme/default.css, activetheme/default.css, or both. - Template registry plan: templates to create or reuse, with each template's intended slide roles.
- One or two representative slides when visual approval matters.
Style rules:
- Use
@open-press/core/themefor portable color/font/typography tokens when a deck style should move across workspaces. - Keep layout in
boxandFrame layout; keep typography and color in theme CSS. - Build templates from
Slide, nestedFrame,Text,Line,MediaObject,Media, andMediaCaption. - Make the style inspectable and reusable before generating many slides.
4. PRODUCE
Create the deck, add slides from templates, tune the result, then verify.
- New deck: scaffold
press.tsxas an ordered marker-only<Slide id />index, create or select aslide-style/template registry, create theme tokens/CSS, then create eachslides/<id>/slide.tsxfrom a registered template withexport const meta. - New slide: use
open-press slide add <id> --press <slug> --template <name>when a registered template fits, or omit--templateto use the manifest default. Then editslides/<id>/slide.tsxdirectly. - New template: create
press/<slug>/slide-style/templates/<template>/slide.tsx, register it inpress/<slug>/slide-style/manifest.json, and verify it renders in the Workbench template browser before relying on it for new slides. - New theme: define color/font/typography tokens using
@open-press/core/themewhen the deck needs a portable style package; emit or mirror the variables inpress/<slug>/slide-style/theme/default.css, and keep the active deck CSS inpress/<slug>/theme/default.css. - Choose
ui/primitives based on content type; do not force the user to specify them. - If the user provides a reference image, read it and generate a brief visual inventory before writing JSX — the image is the source of truth for that slide.
Production loop:
create template/theme → add slides with CLI → edit copied slide source → build/preview → visual tune → document metadataAuthoring constraints during PRODUCE:
- Fixed 1920 × 1080 canvas. Think in absolute pixels for type, spacing, image slots.
- 100–160 px content padding unless deliberately full-bleed.
- Keep body text large enough for projection. Do vertical budget math before writing dense slides.
- Never use scrollable slide content.
- One coherent visual direction across the deck.
- Prefer explicit repeated JSX over
array.mapwhen inspector editability matters. - Prefer
slides/<id>/slide.tsxcopied from a registered template, then edited as explicit inline content, over hidden data arrays or empty proxy components. - Prefer copied template slides built from
Slide, nestedFramelayout props,Text,Line,MediaObject,Media, andMediaCaptionover shared protocol layout components or generic HTML layout wrappers.Slideacceptslayoutdirectly, and nestedFrameregions can replace most wrapper elements under the slide. - Use stable
labelonText,Line, andMediaObjectin templates and copied slide source. UseframeKeyforFrameidentity. Do not hand-write generated locator values. - Do not create or depend on legacy protocol layout files in new workspaces. Existing workspaces may keep local layouts as user source.
- Do not create slide-local CSS files by default. Use
slide-style/theme/default.cssfor portable style packages andtheme/default.cssfor the active Press theme. Add reusableop-*classes only when a pattern repeats. - Use
lucide-reactfor icons by default. Hand-draw SVG only for structural diagrams or flow arrows that no library covers. - Static decks are valid. Use motion sparingly with one transition family.
Tuning
Iterate until layout and content are aligned.
- Take feedback on composition, density, hierarchy, or wording.
- Edit JSX directly. Do not regenerate the whole file for small changes.
- Keep semantic
idvalues stable during reorder — do not renumber.
Use open-press search to locate content within the deck before editing:
# 找哪張 slide 提到某個關鍵字,回傳 page id + 行號
open-press search . "<query>" --json
# 含 components / theme / design.md 一起搜
open-press search . "<query>" --scope all --jsonEditing an existing slide deck:
- Reorder: use
open-press slide reorder ...; keep semanticidstable. - Insert: choose a new semantic
id, useopen-press slide add <id> --press <slug> --template <name>when a registered template fits. - Hide/show: use
open-press slide skip <id>andopen-press slide unskip <id>. - Edit content: edit
slides/<id>/slide.tsxdirectly, updateexport const metaif intent changed. - Convert data-prop layout: replace
items={[...]}with explicit JSX children before further edits. - Add a template: when a visual pattern should be reusable or portable across slide packages. Register it in
slide-style/manifest.jsonbefore using it with the CLI. - Add a layout/component: only when a pattern is reused across multiple templates or slides and cannot be expressed cleanly with
Frameregions. - *Add a `ui/` primitive**: only when a content block is reused across multiple templates or slides.
Documentation Gate
After the user confirms a slide or a batch, read the current JSX and update export const meta in each confirmed slides/<id>/slide.tsx. Write it from observation, not prediction.
export const meta = {
layout: "title-slide",
description: "一句話描述這張的構圖與視覺主軸",
keypoints: ["要傳達的訊息或視覺規則"],
visuals: ["asset-filename"],
} satisfies SlideMetaSpeaker notes live in export const notes in the same slide.tsx; they are workbench-only and never rendered in the slide frame.
Alignment Gate
Read the meta and notes back to the user and ask: 「這樣描述對嗎?」
If corrected, update meta / notes in slide.tsx first, then update JSX to match if needed.
---
Slide Metadata
Lives in press/<slug>/slides/<id>/slide.tsx. SlideMeta is strict; workspace-specific fields require an explicit extension type.
import type { SlideMeta } from "@open-press/core"
export const meta = {
layout: "<layout-name>",
description: "<一句話說明這張在做什麼>",
keypoints: ["<要傳達的訊息>"],
visuals: ["<asset-filename>"],
} satisfies SlideMeta| 欄位 | 必填 | 說明 |
|---|---|---|
layout | 建議 | 版型家族名,對應 template name 或 family |
description | 建議 | 一句話說明用途 |
keypoints | 建議 | 要傳達的訊息,Agent 決定排版 |
visuals | 選填 | 已存在的素材檔名 |
Use stable label for editable object primitives (Text, Line, MediaObject) when authoring templates or copied slide source. Do not write generated locator values.
---
Setup
1. Environment check:
node -v && npm -v && npx -vNode ≥ 20 required. If missing or outdated, stop and ask the user to install Node.js LTS.
2. Detect workspace branch:
find press -mindepth 2 -maxdepth 2 -name press.tsx -print -quit 2>/dev/null | grep -q . && echo EXISTING || echo FRESH3a. Fresh workspace:
npm create @open-press <target> -- --type slides --title "<title>"Use . only when the user explicitly wants the current directory. The create package rejects non-empty targets — do not use a force flag.
3b. Existing workspace:
Read press/*/press.tsx to identify existing slugs, geometries, componentsDir, mediaDir, theme/, and any slide-style/manifest.json. Run open-press create <slug> --type slides --title "<title>", then edit the generated press/<slug>/ source. Do not touch sibling Press folders unless the user asks.
For dogfood or disposable verification, use a temporary slug like slide-dogfood and remove it after.
Create/Core Alignment Check
Before changing release docs, create templates, or slide authoring rules, confirm @open-press/create and open-press create still match the current core slides architecture:
- Both
packages/create/src/slides-template.tsandpackages/cli/src/slides-template.tsscaffold the same structure:press/<slug>/press.tsx,slide-style/manifest.json, registeredslide-style/templates/*/slide.tsx,slide-style/theme/default.css,slides/intro/slide.tsx, and activetheme/default.css. - The scaffold must not create
themes/; core discovers per-Press theme files frompress/<slug>/theme/**. press.tsxmust be marker-only:<Press type="slides" page="slide-16-9">with self-closing<Slide id />children and no slide content, CSS imports, data arrays, or layout components.- The generated slide file must import core primitives directly from
@open-press/core, export literalmeta satisfies SlideMeta, exportnotes, and default-export a slide component built fromSlide, nestedFrame, and content primitives. - Template slide
Text,Line, andMediaObjectregions must hand-author stable locallabelvalues. They must not hand-author generated locator values. - After any template change, run
pnpm --filter @open-press/create testandpnpm --filter @open-press/cli test; these tests must cover the file tree and a generated workspace build.
Template Registry Contract
Portable slide style lives under press/<slug>/slide-style/.
press/<slug>/slide-style/
manifest.json
templates/
cover/slide.tsx
agenda/slide.tsx
theme/default.cssmanifest.json is the source of truth for what the CLI and Workbench can add:
{
"id": "source-deck-style",
"name": "Source Deck Style",
"defaultTemplate": "cover",
"templates": {
"cover": {
"source": "templates/cover/slide.tsx",
"description": "Date, large serif title, and red rule"
},
"agenda": {
"source": "templates/agenda/slide.tsx",
"description": "Two-column agenda with divider"
}
}
}Template files are complete slides. Use __SLIDE_ID__ and __SLIDE_COMPONENT__ placeholders when authoring reusable template source:
import { Frame, Line, Slide, Text, type SlideMeta } from "@open-press/core";
export const meta = {
layout: "cover",
description: "Date, large serif title, and red rule",
} satisfies SlideMeta;
export default function __SLIDE_COMPONENT__() {
return (
<Slide id="__SLIDE_ID__" className="op-source-deck-slide op-template-cover">
<Frame frameKey="canvas" chrome={false}>
<Text as="p" label="date" box={{ x: 100, y: 80 }} className="op-source-deck-date">
26 JUNE 2024
</Text>
<Text as="h1" label="title" box={{ x: 100, y: 360, w: 1155 }} className="op-source-deck-title">
A line is length without breadth.
</Text>
<Line label="red-rule" box={{ x: 100, y: 774, w: 270, h: 4 }} className="op-source-deck-red-rule" />
</Frame>
</Slide>
);
}Then create slides through the CLI:
open-press slide add intro --press slide --template cover
open-press slide add agenda --press slide --template agenda
open-press slide add appendix --press slide # uses manifest defaultTemplate when presentTheme Contract
When a deck needs a portable visual system, define a theme token graph before generating many templates.
import { defineTheme, themeToCssText } from "@open-press/core/theme";
export const deckTheme = defineTheme({
name: "Source Deck",
colors: {
bg: "#f8f2e6",
ink: "#111217",
accent: { value: "#d7332f", label: "Red rule" },
muted: "#8a8881",
},
fonts: {
serif: "Georgia, 'Times New Roman', serif",
sans: "Inter, system-ui, sans-serif",
},
typography: {
title: { font: "serif", size: 124, lineHeight: 1.02, weight: 400, color: "ink" },
body: { font: "sans", size: 40, lineHeight: 1.35, color: "ink" },
meta: { font: "sans", size: 24, lineHeight: 1.2, weight: 700, color: "muted" },
},
});
export const deckThemeCss = themeToCssText(deckTheme, ".op-source-deck-slide");Theme rules:
slide-style/theme/default.cssbelongs to the portable template package.theme/default.cssis the active Press theme used by the deck.- Keep typography and colors in theme tokens/CSS. Keep layout in
boxandFrame layout. - Do not redefine font sizes or colors inside every template unless a source deck requires an intentional exception.
- Add optional
theme-colorsortheme-typographytemplates only when the user needs visual QA slides for the package.
---
Intake
Gather only what the current phase needs. Do not block production on optional details.
| Phase | Intake |
|---|---|
| TOPIC | topic, audience, objective, title, page count, target slug |
| STORY | key messages, source material, required evidence, desired call to action |
| STYLE | brand/reference images, visual register, density, motion level, export target |
| PRODUCE | assets, existing press path, template names, verification expectations |
Defaults:
- Geometry:
slide-16-9. - Page count: 6–10 for a new narrative deck unless the user says otherwise.
- Text density: light to standard.
- Motion: static or subtle.
- Visual direction: choose one coherent direction; offer alternatives only when the user asks for exploration.
- Theme direction: define colors, fonts, and typography scale before creating many templates; make it portable through
slide-style/theme/default.csswhen the deck style may be reused. - Reference images: read them and generate a brief visual inventory before writing JSX.
Ask before using custom geometry.
---
Verify
Before claiming a deck is deliverable, check all four phases produced artifacts:
- TOPIC: working brief exists or assumptions were stated.
- STORY: slide plan has stable ids, slide roles, messages, evidence needs, and templates.
- STYLE: theme tokens/CSS and template registry match the chosen direction.
- PRODUCE: slide source exists, build passes, and visual issues were inspected when preview/image/PDF matters.
Draft marker scan before build:
# 確認沒有未完成標記殘留
open-press search . "[TODO:" --scope all --json
open-press search . "[DRAFT:" --scope all --jsonnpm run buildWhen image/PDF export matters:
npm run openpress:image
npm run openpress:pdfAfter build passes, run the Documentation Gate for slides confirmed in this session:
1. Read current JSX for each confirmed slide. 2. Update export const meta and export const notes in the same slides/<id>/slide.tsx. 3. Present metadata to the user: 「這樣描述對嗎?」 4. If corrected, update meta / notes first, then JSX.
Report: working brief, slide plan, chosen style, Press slug, title, geometry, templates/theme touched, files written, assets still needed, verification result, and metadata entries written.
---
When to Read References
- Press Tree & folder layout: read
references/press-tree.md— canonical folder structure, Press Tree TSX example, path resolution rules. - Templates, layout, UI primitives, slots: read
references/layout-contract.md— template registry, core Object API primitives, layout props, UI primitive list, boundary tables. - Tailwind/theme styling contract: read
references/css-colocate.md— semanticop-*classes, theme API/CSS split, allowed utilities, and the no slide-local CSS default. - Typography role system: read
references/typography-roles.md— theme-backed semantic role classes, base CSS, inline extension rules, and layout override rules. Read this before writing any text styles. - Icon libraries: read
references/icons.md— recommended packages, size and weight guidelines for slides.
---
Do Not
- Do not ask the user to write YAML before seeing any slides.
- Do not put component names, CSS class names, or verbatim copy into
export const meta. - Do not omit stable
labelfromText,Line, orMediaObjectin reusable templates. - Do not write generated locators.
- Do not generate one empty component per slide when inline layout composition is clearer.
- Do not use
npm create @open-pressoropen-press createas an upgrade tool. - Do not edit generated output.
- Do not publish.
- Do not install dependencies for slide authoring.
- Do not hand-draw SVG for icons available in
lucide-reactor@phosphor-icons/react.
Tailwind And Theme Styling Rules
Slide styling is Tailwind-first and theme-token-aware. New slide decks, templates, UI primitives, and generated slide stubs should use op-* semantic classes, @open-press/core/theme tokens when a portable style package is needed, and approved Tailwind layout utilities instead of authoring slide-local CSS files.
Source Of Truth
The framework provides the shared slide style layer:
- Tailwind v4 Vite plugin is enabled by OpenPress.
tailwindcss/theme.cssandtailwindcss/utilities.cssare imported.- Preflight is intentionally not imported, so existing reader/workbench CSS is not globally reset.
- Slide tokens and component classes live in
packages/core/src/styles/openpress/slide-design-system.css.
Downstream slide source uses those classes through JSX.
Portable deck style is owned by:
press/<slug>/slide-style/theme/default.cssfor a template package's source theme.press/<slug>/theme/default.cssfor the active Press theme.- Optional theme token helpers from
@open-press/core/theme(defineTheme,themeToCssText) when the style needs reusable colors/fonts/typography.
File Responsibility Split
| File / Layer | What goes here | Rule |
|---|---|---|
packages/core/src/styles/openpress/slide-design-system.css | Shared @theme tokens and stable op-* component classes | Framework-owned |
slide-style/manifest.json | Registered template names and source paths | Source of truth for CLI and Workbench template add |
slide-style/templates/*/slide.tsx | Portable complete slide templates | Core objects + semantic classes; no local CSS import |
slide-style/theme/default.css | Portable template package theme | Colors, fonts, typography variables and repeated op-* classes |
theme/default.css | Active per-Press theme | Copied/synced from slide-style theme, then deck-specific overrides |
ui/*.tsx | Small primitives using op-* classes | No local CSS import |
slides/<id>/slide.tsx | Explicit slide content using copied template source and core objects | No slide-local CSS by default |
theme/*.css, layouts/*.css, ui/*.css, slides/<id>/style.css | Legacy / escape hatch only | Avoid unless a user explicitly asks for custom CSS |
Allowed Class Families
Use:
- Semantic components:
op-title,op-body,op-card,op-card-muted,op-panel,op-callout,op-chip. - Slide structures:
op-slide-page,op-slide-shell,op-slide-title-layout,op-slide-card-grid,op-slide-process-map,op-slide-blank-layout. - Semantic colors:
bg-bg,bg-surface,text-text,text-accent,border-border. - OpenPress spacing:
p-op-md,gap-op-lg,mt-op-sm,px-op-xl. - Simple layout utilities:
grid,flex,items-center,justify-between,grid-cols-2,min-w-0.
Avoid in slide content:
- Arbitrary values:
text-[37px],bg-[#123456],mt-[19px]. - Raw Tailwind palette:
bg-blue-500,text-gray-900. - Web text scale:
text-xs,text-base,text-2xl. - Free positioning:
absolute,fixed,top-*,inset-*. - Transform / z-index / animation utilities:
scale-*,z-*,animate-*. - Inline style objects.
Template implementation files may use tightly-scoped arbitrary values only when they are hidden behind semantic op-slide-* classes in the shared style layer.
Import Pattern
Do not import CSS from slide, layout, or UI TSX files.
// Good
export function CardGridSlide(...) {
return <section className="op-slide-card-grid-layout">...</section>;
}// Avoid
import "./card-grid-slide.css";Token Vocabulary Contract
Use the existing Tailwind token names instead of inventing deck-local CSS variables.
| Token Family | Examples |
|---|---|
| Surface | bg-bg, bg-surface, bg-surface-muted, bg-surface-inverse |
| Text | text-text, text-text-muted, text-text-subtle, text-text-inverse |
| Accent | text-accent, bg-accent, bg-accent-muted |
| Border | border-border, border-border-strong |
| Type | op-display, op-title, op-section, op-lead, op-body, op-caption |
| Spacing | op-2xs, op-xs, op-sm, op-md, op-lg, op-xl, op-2xl |
If a needed style repeats across templates or slides, add or reuse an op-* semantic class in the active theme. If it is one-off, prefer changing the layout composition before adding CSS.
Theme API Pattern
When style must be portable, define the token vocabulary first:
import { defineTheme, themeToCssText } from "@open-press/core/theme";
const deckTheme = defineTheme({
name: "Source Deck",
colors: {
bg: "#f8f2e6",
ink: "#111217",
accent: "#d7332f",
muted: "#8a8881",
},
fonts: {
serif: "Georgia, 'Times New Roman', serif",
sans: "Inter, system-ui, sans-serif",
},
typography: {
title: { font: "serif", size: 124, lineHeight: 1.02, color: "ink" },
body: { font: "sans", size: 40, lineHeight: 1.35, color: "ink" },
},
});
const css = themeToCssText(deckTheme, ".op-source-deck-slide");Use the generated CSS variables in theme CSS:
.op-source-deck-title {
font-family: var(--op-theme-type-title-font-family);
font-size: var(--op-theme-type-title-font-size);
line-height: var(--op-theme-type-title-line-height);
color: var(--op-theme-type-title-color);
}Do not put theme values in every slide.tsx. Keep JSX structural; keep typography and colors in theme CSS.
Deck-Level Visual Customisation
To change the visual style of an entire deck without touching JSX, override the theme-backed values in the portable slide-style theme or the active Press theme. Keep Tailwind v4 @theme names stable and generic; do not hardcode deck-specific values into a shared global @theme block in a multi-Press workspace.
/* press/<slug>/theme/default.css or slide-style/theme/default.css */
.op-source-deck-slide {
--op-theme-color-bg: #f8f2e6;
--op-theme-color-ink: #111217;
--op-theme-color-accent: #d7332f;
--op-theme-color-muted: #8a8881;
--op-theme-type-title-font-family: Georgia, "Times New Roman", serif;
--op-theme-type-title-font-size: 124px;
--op-theme-type-title-line-height: 1.02;
--op-theme-type-body-font-family: Inter, system-ui, sans-serif;
}This is the right level for branding a deck. Do not add raw CSS classes to slide content files, and do not put slide-only values in a shared theme that also feeds A4 pages or social formats.
Icon Libraries for Slides
Prefer open-source React icon packages over hand-drawn SVG. Hand-draw SVG only for structural diagrams, flow arrows, or architecture lines that no icon library covers.
Recommended Libraries
lucide-react (default)
npm install lucide-react- 1500+ icons, consistent 24 px grid, stroke-based
- Already used in OpenPress workbench — safe to use in slides without style conflict
- Best for: UI actions, status indicators, general content icons
import { ArrowRight, CheckCircle, Users } from "lucide-react";
<CheckCircle size={32} strokeWidth={1.5} />@phosphor-icons/react (when weight variety matters)
npm install @phosphor-icons/react- 9000+ icons, 6 weights:
thin/light/regular/bold/fill/duotone - Useful when a slide needs visually heavier or lighter icons to match density
- Best for: decks with mixed weight intentionality, cover icons, chapter markers
import { Storefront, Robot } from "@phosphor-icons/react";
<Storefront size={48} weight="thin" />
<Robot size={48} weight="bold" />@tabler/icons-react (large set, stroke-consistent)
npm install @tabler/icons-react- 5000+ icons, all stroke-based, very consistent line weight
- Best for: technical slides, dashboards, when you need niche icons not in lucide
import { IconBrandLine, IconDatabase } from "@tabler/icons-react";
<IconBrandLine size={32} stroke={1.5} />What NOT to Use
- `react-icons` — aggregates many sets with inconsistent styles; mixing sets breaks slide visual coherence
- Inline `<svg>` for general UI icons — hard to maintain and usually looks heavier than library icons at the same size
When to Hand-Draw SVG
- Flow diagrams with custom arrows or connection lines
- Architecture diagrams with spatial layout
- Brand-specific shapes not in any library
Keep hand-drawn SVG in ui/<name>.tsx, not inline in press.tsx.
Size & Weight Guidelines for Slides (1920×1080 canvas)
| Context | Size | strokeWidth / weight |
|---|---|---|
| Large feature icon (hero, chapter) | 48–80 px | thin or 1 |
| Card or list icon | 24–32 px | regular or 1.5 |
| Inline text icon | 16–20 px | regular or 2 |
| CTA or action button icon | 20–24 px | regular or 1.5 |
Layout & Component Contract
Slide authoring is template-first, frame-first, and Tailwind-semantic.
Import Paths
New slide workspaces scaffold portable style source under:
press/<slug>/slide-style/manifest.json
press/<slug>/slide-style/templates/<template>/slide.tsx
press/<slug>/slide-style/theme/default.cssCopied slide files live at:
press/<slug>/slides/<id>/slide.tsxTemplates and copied slides should import core primitives directly:
import { Frame, Line, Media, MediaCaption, MediaObject, Slide, Text, type SlideMeta } from "@open-press/core";layouts/ and deck-local chrome components may exist in older or heavily customized workspaces, but they are no longer the default scaffolded slide style boundary.
Template Slide Contract
A template slide is a complete slide.tsx source file registered in press/<slug>/slide-style/manifest.json.
The CLI copies it into press/<slug>/slides/<id>/slide.tsx, substitutes __SLIDE_ID__ and __SLIDE_COMPONENT__, and leaves theme files untouched. Templates should import core primitives directly from @open-press/core.
Template files should be complete slides built from Slide / nested Frame layout props, Text, Line, MediaObject, Media, and MediaCaption. Slide is the slide-friendly page Frame wrapper and accepts layout directly; nested Frame regions should own copy groups, cards, grids, and visual regions.
Use plain HTML elements only for tiny visual wrappers when no primitive fits. Do not use generic HTML as the main template skeleton.
Frame Boundary
| Region | Preferred primitive | Rule |
|---|---|---|
| Slide page and canvas | Slide | Owns id, className, and page-level layout |
| Copy groups, cards, grids, visual regions | Frame | Give each region a stable, semantic frameKey |
| Visible title, lead, caption, label | Text | Use stable label and semantic op-* typography classes |
| Divider, rule, axis, timeline mark | Line | Use stable label; style color/weight through theme CSS |
| Image or media with caption | MediaObject, Media, MediaCaption | Keep asset references portable |
| One-off visual wrapper | Plain HTML | Allowed only when a core primitive does not fit |
Layout Props
Use layout on Slide and nested Frame regions for stable composition:
<Slide
id="pricing"
className="op-slide-page bg-bg text-text"
layout={{ mode: "grid", columns: "minmax(0,1fr) 520px", gap: 64, padding: 96, width: "fill", height: "fill" }}
>
<Frame frameKey="copy" layout={{ mode: "stack", gap: 24 }}>
<Text as="h1" label="title" className="op-display">Pricing model</Text>
</Frame>
</Slide>Avoid props-heavy layout APIs such as items, metrics, steps, blocks, logo, footerLabel, showFolio, pageNumber, or totalPages. Use explicit JSX children so inspector and source editing can target real authoring nodes.
UI Primitive Set
Reusable slide UI primitives, if a workspace needs them, belong in workspace source such as press/<slug>/ui/ or press/<slug>/components/. They do not wrap the page-level Slide.
| Primitive | Typical use |
|---|---|
Card | Repeated card surfaces |
Badge | Labels and chips |
Callout | Strong highlighted note, preferably implemented with Frame |
KpiCard | Metric plus label |
ImageFrame | Decorated image container |
QuoteBlock | Pull quote |
Timeline | Sequence or process |
CompareTable | Small comparison table |
BaseCallout is deprecated for new authoring guidance. It remains exported for compatibility, but portable slide templates should use Frame regions and Text instead. A future framework cleanup may either remove it from examples or replace it with a stronger Callout primitive that participates in the same object/frame semantics as other authoring primitives.
Text Rule
Use Text from @open-press/core or local wrappers that accept and forward TextProps.
Do hand-write stable local label values for Text, Line, and MediaObject in templates and copied slide source. Do not write generated locator values. The engine derives build-local locators from the object tree.
Template Add CLI
Use the public CLI for slide creation:
open-press slide add <id> --press <slug> --template <template>
open-press slide add <id> --press <slug> # uses manifest defaultTemplate when availableDo not manually create a slide folder when the desired template is registered; the CLI updates both slides/<id>/slide.tsx and the marker-only press.tsx.
Press Tree & Folder Contract
Recommended Folder Layout
press/<slug>/press.tsx ← canonical entry, only supported entry
press/<slug>/slides/<id>/slide.tsx ← slide content + meta + notes
press/<slug>/slide-style/manifest.json
press/<slug>/slide-style/templates/<template>/slide.tsx
press/<slug>/slide-style/theme/default.css
press/<slug>/theme/default.css ← active per-Press theme copied/synced from slide-style/theme/default.css
press/<slug>/ui/text.tsx
press/<slug>/ui/card.tsx
press/<slug>/ui/badge.tsx
press/<slug>/ui/callout.tsx
press/<slug>/ui/kpi-card.tsx
press/<slug>/ui/image-frame.tsx
press/<slug>/ui/quote-block.tsx
press/<slug>/ui/timeline.tsx
press/<slug>/ui/compare-table.tsx
press/<slug>/media/Do not create slides/<id>/style.css, layouts/*.css, ui/*.css, or extra theme/*.css by default. Slide styling should use the shared Tailwind op-* semantic layer plus the active Press theme. Portable style package CSS belongs in slide-style/theme/default.css; active deck CSS belongs in theme/default.css. Local CSS is an escape hatch only after a user explicitly asks for custom CSS that cannot be represented with template composition and semantic classes.
Press Tree Default Shape
import { Press, Slide } from "@open-press/core";
export default function SlidePress() {
return (
<Press slug="slide" title="Deck Title" type="slides" page="slide-16-9">
<Slide id="cover" />
<Slide id="agenda" />
<Slide id="closing" />
</Press>
);
}For Slides-type workspaces, press.tsx is an ordered index only. Do not put slide content, CSS imports, data arrays, or layout components in press.tsx.
Component & Media Path Resolution
- Default authoring: pass folder-local
./componentsand./mediaon<Press> - Custom path: set
<Press componentsDir>or<Press mediaDir>to a string or string array - Paths starting with
./resolve relative to the owning Press folder - Bare paths resolve relative to
press/
Rules
- Do not generate one empty component per slide just to hide content
- Compose visible content inside
slides/<id>/slide.tsx, usingSlide, nestedFrameregions,Text, and media primitives - Create reusable visual starting points in
slide-style/templates/<template>/slide.tsx, then register them inslide-style/manifest.json - Extract a component only when it is a reusable
ui/*primitive or a repeated workspace-specific pattern - Write stable local
labelvalues onText,Line, andMediaObject; do not write generated locators - Do not use inline style objects for slide layout or text styling; use
op-*classes
Typography Role System
Slides use a semantic role system. Roles define what a text element is, not how it looks. Visual values come from theme tokens; structure comes from Frame / box / layout CSS.
Three-Layer Rule
@open-press/core/theme → portable token graph
theme/default.css → active CSS variables and semantic classes
Text / Frame source → structure and editable contentLayout CSS must not redefine font sizes or colors. It only overrides when a layout genuinely needs a structural deviation.
---
Token Dependencies
When using the public Theme API, define typography tokens with defineTheme before writing many templates:
import { defineTheme } from "@open-press/core/theme";
export const deckTheme = defineTheme({
colors: {
ink: "#111217",
muted: "#8a8881",
accent: "#d7332f",
},
fonts: {
serif: "Georgia, 'Times New Roman', serif",
sans: "Inter, system-ui, sans-serif",
},
typography: {
title: { font: "serif", size: 124, lineHeight: 1.02, weight: 400, color: "ink" },
lead: { font: "sans", size: 48, lineHeight: 1.24, color: "ink" },
body: { font: "sans", size: 40, lineHeight: 1.35, color: "ink" },
caption: { font: "sans", size: 24, lineHeight: 1.3, color: "muted" },
},
});The emitted variables are --op-theme-type-<role>-font-family, --op-theme-type-<role>-font-size, --op-theme-type-<role>-line-height, --op-theme-type-<role>-font-weight, --op-theme-type-<role>-letter-spacing, and --op-theme-type-<role>-color.
---
Role Definitions
| Class | Role | Typical use |
|---|---|---|
.eyebrow | Chapter / category label | Section tag above heading |
.h1 | Main title | Core message of the slide |
.h2 | Supporting title | Subtitle, secondary heading |
.h3 | Item heading | List item title, sub-section label |
.body | Primary body text | Main explanatory content |
.body-sm | Secondary body text | Supporting detail, list item description |
.caption | Figure label | Text below image or diagram |
.note | Supplementary text | Source citation, fine print |
.marker | Numbered / sequence marker | Step numbers, agenda numbers (01, 02, 03) |
---
Base CSS
.eyebrow {
font-size: var(--op-theme-type-caption-font-size);
font-weight: 700;
color: var(--op-theme-color-accent);
letter-spacing: 0.08em;
line-height: 1.2;
}
.h1 {
font-family: var(--op-theme-type-title-font-family);
font-size: var(--op-theme-type-title-font-size);
font-weight: var(--op-theme-type-title-font-weight);
color: var(--op-theme-type-title-color);
line-height: var(--op-theme-type-title-line-height);
}
.h2 {
font-family: var(--op-theme-type-lead-font-family);
font-size: var(--op-theme-type-lead-font-size);
color: var(--op-theme-type-lead-color);
line-height: var(--op-theme-type-lead-line-height);
}
.h3 {
font-size: var(--op-theme-type-body-font-size);
font-weight: 700;
color: var(--op-theme-color-ink);
line-height: 1.25;
}
.body {
font-family: var(--op-theme-type-body-font-family);
font-size: var(--op-theme-type-body-font-size);
color: var(--op-theme-type-body-color);
line-height: var(--op-theme-type-body-line-height);
}
.body-sm {
font-size: var(--op-theme-type-caption-font-size);
color: var(--op-theme-color-muted);
line-height: var(--op-theme-type-caption-line-height);
}
.caption {
font-size: var(--op-theme-type-caption-font-size);
color: var(--op-theme-type-caption-color);
line-height: var(--op-theme-type-caption-line-height);
}
.note {
font-size: 15px;
font-weight: 400;
color: var(--op-theme-color-muted);
line-height: 1.4;
opacity: 0.72;
}
.marker {
font-size: var(--op-theme-type-lead-font-size);
color: var(--op-theme-color-accent);
line-height: 1;
}---
Inline Extension
Every role class supports these inline HTML elements without extra classes:
/* applies to all role classes */
.h1 strong, .h2 strong, .h3 strong,
.body strong, .body-sm strong { font-weight: 700; }
.h1 em, .h2 em, .h3 em,
.body em, .body-sm em {
color: var(--op-theme-color-accent);
font-style: normal;
}Usage in JSX:
<h2 className="h1">
打造 <strong>7 天</strong>無休的數位員工
</h2>
<p className="body">
AI 客服不是讓機器<em>取代</em>真人,而是讓真人專注在<em>更有價值的事</em>。
</p><mark> is reserved but not defined by default. Add only when highlight-style emphasis is needed:
.h1 mark { background: none; color: var(--op-theme-color-accent); }---
Layout Override Rules
Layout CSS may override a role only for layout-driven reasons:
/* ✓ — overriding max-width is structural */
.agenda-layout .h1 { max-width: 560px; }
/* ✓ — overriding font-size with a token when layout demands a different scale */
.agenda-layout .h1 { font-size: var(--op-theme-type-lead-font-size); }
/* ✗ — never hardcode values */
.agenda-layout .h1 { font-size: 92px; color: #172d4d; }---
Extension Pattern
Add a modifier class when a role needs a visual variant:
.h1--light { color: #fff7e8; } /* dark background variant */
.body--muted { color: var(--op-theme-color-muted); } /* de-emphasized body */
.marker--large { font-size: var(--op-theme-type-lead-font-size); } /* oversized marker */Never create a new role class for something that is already a variant of an existing role.