name: slidev-theme-ustc description: Use when creating, editing, converting, exporting, or designing academic Slidev decks with slidev-theme-ustc, including group-meeting reports, literature surveys, conference talks, PPT-to-Slidev conversion, figure/PDF asset ingestion, layout density fixes, and ugly/empty/cramped slide repair.
slidev-theme-ustc
A clean academic Slidev theme for USTC-style presentations.
First classify the deck
Before drafting or editing, classify the task and read the matching reference:
| Scenario | Read |
|---|---|
| Internal group progress / weekly report | references/scenarios/group-progress.md |
| Group literature survey / paper reading | references/scenarios/group-literature.md |
| Conference oral / formal paper talk | references/scenarios/conference-talk.md |
| PPT or PowerPoint conversion | references/workflows/ppt-to-slidev.md |
| Paper figures, PDFs, large images, videos | references/workflows/asset-ingestion.md |
| Final package for another presenter | references/workflows/handover-export.md |
If the user gives a PPT, distinguish materials-only PPT from drafted deck PPT. Materials-only PPTs can be reorganized. Drafted PPTs with titles/order/notes should be treated as structural references unless the user asks for a redesign.
Group meeting and conference defaults differ: group meeting decks normally omit affiliations; formal conference talks normally include authors, affiliations, venue, date, and presenter. Do not let one scenario's cover metadata leak into the other.
Designing a deck, not just operating the template? Read references/authoring-academic-decks.md first. It covers how to make slides that are logically clear, rigorous, and not ugly: the argument spine, the layout decision tree, the density rubric, figure handling, academic taboos, citation discipline, and the render → measure → fix audit loop (
scripts/audit-layout.mjs). This file below is the API reference — what knobs exist; that file is which to turn and why.
Designing a deck — 10 rules (start here)
The load-bearing rules, condensed. The why/how (layout decision tree, density rubric, taboos, citations) is in references/authoring-academic-decks.md.
- A deck is an argument, not a document — pick one claim; rest →
backup. - One job per slide — one clear point each; split if it does two things.
- Structure, not prose — short bullet fragments, never paragraphs (walls of text are the #1 tell of a bad deck).
- Motivation before formalism — "Why?" before "What?"; task + motivation before method/results.
- Dense content, not dense mode — cover a lot via structure and terse wording, not by reaching for
density: denseto shrink text.dense+ a half-empty slide is the worst combination. - Fill the canvas (~85–100%) by adding substance, never by cramming — this style dislikes empty corners, but reach the target by adding a figure / merging thin slides / rebalancing; legibility always wins over a fill number.
- Wide figures (aspect ratio > ~2.5) span full width — never in a
splitcolumn (they go small and leave a void below). - Cite on the slide, verify every citation — per-page footnotes, no references slide; credit borrowed figures; never invent a citation.
- Academic voice — no colloquialisms, filler, "not X but Y" templates, or invented abbreviations; don't oversell (no "SOTA" for older work). See references/style/wording.md.
- Measure, then commit — run
scripts/audit-layout.mjs+ eyeball a--shotscreenshot before declaring a slide done; when you cut content to fit, relocate it tobackup— never silently delete.
Density tiers: normal (default) → compact (middle) → dense (tightest); reach for the largest that fits. The visual target is clean, compact, and readable: neither sparse nor cramped. See references/style/visual-rubric.md. The end slide MUST have an # h1 (the large centered closing line).
Setup
# In your slides.md frontmatter (first slide)
theme: ./ # local dev
# or
theme: @luocfprime/slidev-theme-ustc # published
Global Frontmatter
Set in the first slide of the deck:
---
theme: ./
talkTitle: 'Your Presentation Title'
conference: 'CVPR 2025'
date: 'June 2025'
presenterName: 'Alice Chen' # name of the presenter (underlined on cover); use `presenterName`, not `presenter` — Slidev reserves `presenter` for its built-in presenter-mode option
authors:
- name: 'Alice Chen'
affiliations: ['University of Science and Technology of China']
- name: 'Bob Smith'
affiliations: ['MIT', 'USTC']
marks: ['†']
- name: 'Carol Lee'
affiliations: ['Peking University']
authorMarks:
'†': 'Equal contribution'
sectionBar: true # show section progress bar (default: true)
sectionBarMode: full # 'full' (labels + progress indicator) | 'minimal' (indicator only) | 'labels' (labels only)
figurePrefix: Figure # auto-numbering prefix for FigureBlock
tablePrefix: Table # auto-numbering prefix for TableBlock
figureNumberSuffix: ': ' # suffix between FigureBlock number and caption
tableNumberSuffix: ': ' # suffix between TableBlock number and caption
figureZoom: false # click-to-zoom lightbox for all FigureBlocks (default: false; interactive-only, no-op in PDF export)
---
Authors format: array of { name, affiliations, marks? } objects. presenterName sets who is underlined (defaults to first author). Institutions get sequential superscript numbers in order of first appearance. marks are per-author symbols (e.g. †, *) displayed as superscripts after the institute number; authorMarks maps each symbol to its legend text (rendered below the affiliations line).
Layouts
cover
Title slide with author/affiliation display.
---
layout: cover
talkTitle: '...' # overrides global
subtitle: '...'
presenterName: 'Alice Chen' # who to underline (default: first author)
authors: [...] # overrides global; see structured format above
authorMarks: # symbol → legend text
'†': 'Equal contribution'
'*': 'Corresponding author'
conference: '...'
date: '...'
showLogo: true # default: true
logoSrc: /ustc/logo.svg # default
logoAlt: USTC logo # default
background: '#1a2a4a' # CSS color or image path
---
default / content
Standard body slide. default is the fallback when no layout: is specified; content is an identical alias commonly used to signal a body slide explicitly. Both accept the same props.
---
layout: default # or 'content'
density: normal # 'normal' (default) | 'compact' | 'dense'
margin: normal # 'normal' | 'tight' | 'tighter' | 'none'
lineHeight: 1.8 # optional override; matches default body line-height
align: left # 'left' | 'center' | 'right'
footnote: overlay # 'overlay' (default) | 'flow'
footer: true
footerMode: full # 'full' (default) | 'minimal'
sectionBar: true
sectionBarMode: full
subtitle: '...' # optional, renders below h1 (supports markdown)
background: '#f5f5f5' # CSS color or image path
---
split
Two-column layout.
---
layout: split
ratio: "2:1" # column width ratio (default: "2:1")
gap: md # 'sm' (0.8rem) | 'md' (1.4rem, default) | 'lg' (2rem)
subtitle: "..." # optional, renders below h1 — same as content layout
# ...all default props also apply
---
# Slide Title
::left::
Left column content
::right::
Right column content
toc
Auto-generated table of contents from section slides.
---
layout: toc
variant: arrow # 'arrow' (default) | 'classic'
highlight: 2 # 1-indexed section to highlight (0 = no highlight)
columns: 1 # only used by variant: classic; 1 | 2
footer: true
footerMode: full
background: '#f5f5f5' # optional, CSS color or image path
---
# Contents # optional; omit for a titleless agenda page
The # Title line is optional. If omitted, the TOC has no heading. Write # Contents (or any text) when the page needs an explicit title.
Sections use h1 text from each section slide (or sectionLabel if set). The arrow variant displays 01, 02, …; classic displays §1, §2, ….
TOC variant rubric:
variant: arrow(default): use for 3–5 sections, and up to about 6 if labels are short. Best for opening agenda pages and section-transition pages; usually omit the H1 and avoid numbered section labels such as一、. UsesectionLabelon section slides when the section slide title itself needs numbering.columnshas no effect.variant: classic,columns: 1: use for 6–8 sections when a linear reading order matters.variant: classic,columns: 2: use for 9–14 sections when space matters more than a strong visual agenda.- 15+ sections: avoid listing every section automatically; merge sections, split into grouped TOCs, or hand-author a shorter structure page.
Compatibility: existing decks that set columns without variant keep the old classic appearance. New decks should set variant: classic explicitly when they want columns.
section
Section divider slide.
---
layout: section
sectionLabel: 'Short Label' # overrides h1 for TOC/section bar display
sectionBarMode: minimal # per-slide bar override
footer: true
footerMode: full
background: '/bg-section.jpg' # optional, CSS color or image path
---
# Full Section Title
end
Closing/thank-you slide (centered content).
---
layout: end
showLogo: false # default: false
footer: true
footerMode: full
background: '#1a2a4a' # optional, CSS color or image path
---
# Thank you
::contact::
name@ustc.edu.cn
The end slide MUST have an # h1 — that h1 is the large centered line (e.g.
# Thank you, or the closing line in the deck's language). A common mistake is
shipping an end slide with only body
text and no h1, which renders small and off-center. Always include the h1.
The end slide is a courtesy close; it does not replace a substantive
conclusions / takeaways slide, which must come before it. The ::contact:: slot
renders below the main content.
backup
Appendix marker. Everything after this slide gets A.N page numbering in the footer.
---
layout: backup
footer: true
background: '#f5f5f5' # optional, CSS color or image path
---
blank
Full-bleed canvas with no padding or chrome. Use with <Abs> for precise positioning.
---
layout: blank
background: '/bg-fullbleed.jpg' # optional, CSS color or image path
---
Background images on any layout
Every layout (cover, default / content, split, section, toc, end, backup, blank) accepts the same background: frontmatter — either a CSS color (#1a2a4a, rgb(...), named colors) or an image path (/bg.jpg, https://...). Images render at background-size: cover; background-position: center.
Readability — overlay behavior differs by layout:
coverautomatically adds a left-to-right white gradient (rgba(255,255,255,1)→rgba(255,255,255,0.85)) on top of the image so the left-aligned title/authors stay readable.- All other layouts render the image raw, with no overlay. If body text starts becoming hard to read on a busy image, add an overlay manually in the slide's
<style>block:
<style>
.slidev-layout {
background-image:
linear-gradient(rgba(255, 255, 255, 0.88), rgba(255, 255, 255, 0.88)),
url('/bg.jpg');
background-size: cover;
background-position: center;
}
</style>
This overrides the background-image produced by the frontmatter, so you can drop the background: prop on the slide if you're styling it manually. The same trick works for tinted color overlays (linear-gradient(rgba(30,76,144,0.4), rgba(30,76,144,0.4))) when the image is light and you want a brand-tinted wash.
Components
Grid, Block, Abs — layout helpers. Callout, FigureBlock, TableBlock, ResultBox, Takeaway — content blocks. Badge — inline pill tag. VideoBlock, PlotlyGraph, QRCode — media.
All are auto-imported by Slidev. For full prop tables see references/api/components.md.
When to use which content component
| Component | Semantic role | Typical use |
|---|---|---|
<Block> |
Generic named container | Definitions, lemmas, frameworks — any "labeled box" without emotional valence |
<Callout> |
Typed advisory notice with icon | Notes, tips, warnings, important caveats — when the type of message matters |
<Takeaway> |
The single most important point | One bold conclusion per slide; no props, forces brevity |
<ResultBox> |
Quantitative or formal result | Experimental numbers, theorem statements, final answers |
<Badge> |
Inline metadata tag (not a box) | Venue / year / status / CCF rank / dataset / links — flowing inline with text |
Rules of thumb: use <Takeaway> at most once per slide. <Callout type="warning"> is for the audience, not the presenter. <Block> is neutral — reach for it when none of the others fit.
Quick reference
<Grid cols="2" gap="md" alignY="top">...</Grid>
<Block title="Definition">
...
</Block>
<Box bg="blue-pale" borderColor="var(--ustc-blue)" radius="4px">
...
</Box>
<Callout type="tip" title="Insight">
...
</Callout>
<!-- types: note tip warning important example -->
<Takeaway>
Key point.
</Takeaway>
<ResultBox title="Result" bg="blue-pale" borderColor="var(--ustc-blue)">
...
</ResultBox>
<Badge variant="solid" color="#c0392b">CCF A</Badge> <Badge variant="soft"><mdi-github /> Code</Badge>
<!-- variants: soft solid outline; any color; href turns it into a link -->
<Badge href="https://arxiv.org/abs/2509.20358" color="#b31b1b"><mdi-school /> arXiv</Badge>
<FigureBlock src="/img.png" caption="Caption" width="80%" />
<FigureBlock src="/img.png" caption="Scheme" prefix="Scheme" />
<!-- shows "Scheme 2" if global counter is at 2 — prefix changes the label text, the global number stays -->
<FigureBlock wip src="https://placehold.co/800x600" caption="Not ready yet" />
<!-- red WIP badge; use placehold.co to hold the correct aspect ratio -->
<!-- #caption slot for footnote refs / links / Vue components — REQUIRES blank lines around <template> and its content for Slidev to process inside as markdown -->
<FigureBlock src="/img.png" width="60%">
<template #caption>
Caption with [^1] footnote ref
</template>
</FigureBlock>
<TableBlock caption="Table title">| col | ... |</TableBlock>
<TableBlock wip caption="Table title">| col | ... |</TableBlock>
<!-- red WIP badge inline after caption -->
<!-- TableBlock #caption slot — same blank-line requirement; default slot still holds the table markdown -->
<TableBlock width="70%">
<template #caption>
Cite[^1] in caption
</template>
| col | ... |
</TableBlock>
<Abs x="200" y="100" w="300" :z="10">...</Abs>
<!-- x/y/w are the prop names, NOT top/left/width -->
<VideoBlock src="/videos/demo.mp4" caption="Demo" width="80%" :controls="true" />
<PlotlyGraph filePath="/chart.json" :graphWidth="600" :graphHeight="400" />
<QRCode url="https://example.com" :size="160" caption="Scan" />
Content-bearing Vue components must be multi-line with blank lines. Write
<Block>, <Box>, <Callout>, <ResultBox>, and <Takeaway> with the opening tag,
a blank line, the body, another blank line, and the closing tag — even when the body
is just one plain-text sentence. The one-line form is syntactically possible, but it
is not the style for this skill and it fails as soon as the body grows markdown
syntax (bold **...**, lists, links, footnotes [^x], inline/block math, embedded
HTML, etc.). In those cases Slidev/markdown-it treats the body as raw HTML:
**foo** keeps the literal asterisks and [^1] is not turned into a footnote
reference.
Correct (multi-line + blank lines):
<Block title="Definition">
A **convex** function satisfies[^1] $f(\lambda x + (1-\lambda) y) \le \lambda f(x) + (1-\lambda) f(y)$.
</Block>
Wrong (all on one line — not accepted, even if the text looks simple):
<Block title="Definition">A **convex** function satisfies[^1] $f(...) \le ...$.</Block>
Do not use one-line content components such as
<Takeaway>Key point.</Takeaway> or <Callout type="tip">Plain text.</Callout>.
<Badge> is different: it is an inline pill and should stay inline when used inside
running text.
Named slots follow the same rule: <template #caption> also needs a blank line around its content, otherwise a [^x] footnote reference in the caption renders literally. For a plain-text caption the caption prop is enough; only open the slot when you need footnote references, links, or Vue components.
Before handoff, run the mechanical check on edited deck files:
node <skill>/scripts/check-component-format.mjs slides.md sections/*.md
Fix every reported one-line content component. The check intentionally ignores inline
<Badge> and self-closing media components.
Design Principles
The theme exposes many features (subtitle, dense mode, section bar, footnotes, Callout types, etc.) so a deck can match a specific need — not so every deck must use all of them. Plain markdown on a default layout is often the strongest slide.
- Subtitle is optional. Add
subtitle:only when the title is genuinely ambiguous. (default/contentare aliases — both acceptsubtitle:; pick whichever name reads better in your frontmatter.) - Don't stack components. One
<Block>or one<Callout>or one<Takeaway>reads better than all three. Reach for a component only when its semantic role fits — wrapping every paragraph in something is a smell. - Align parallel items horizontally. When a slide does hold multiple instances of the same component (two
<Block>s, three<Box>es, side-by-side<Callout>s), wrap them in<Grid cols="N">so they sit in a row, not stacked vertically. Vertical stacking reads as sequence; horizontal layout reads as comparison. For equal-height items, swap<Grid>for raw native CSS Grid (<Grid>defaults toalign-items: start; native defaults tostretch). - Use the theme rhythm first. Top-level components and media blocks already share
--ustc-component-gap, with compact/dense variants. If a single slide still needs a touch more separation at one specific point, one<br>is acceptable; for repeated spacing changes, override--ustc-component-gapon.slidev-layoutfor one slide or:rootfor the deck instead of sprinkling<br>throughout the deck. - Dense mode is for content pressure, not aesthetics. If the slide already fits in
density: normal, don't switch todense. If the problem is page padding rather than text size, trymargin: tightfirst. - Toggle, don't litter. Disable
sectionBar/footerper-slide for cover, end, blank, and full-bleed visuals — not casually elsewhere. - Climb the precedence ladder for one-off styling: frontmatter prop → CSS variable override in a slide
<style>block → wrap the component in a plain<div style="…">. Two scoping levels for CSS variable overrides: (a).my-scope { --var }on a wrapper<div>— only that subtree changes; (b).slidev-layout { --var }— the entire slide including h1, footer, and section bar. For deck-wide overrides, put:root { --var }instyles/index.css. If none of these fit, propose a feature request or PR against the theme repo — never edit the installed theme files (e.g.node_modules/@luocfprime/slidev-theme-ustc/…). Those files are outside project scope: they get wiped on every reinstall, the change does not version-control with your deck, and the deck silently forks from upstream.
Implementation boundaries for agents: auto-numbering only recognizes static FigureBlock/TableBlock tags; dynamic component aliases and v-bind object spreads require manual :number. subtitle: remains a frontmatter prop paired with a markdown # h1; the theme uses a small layout-local DOM relocation internally to preserve that authoring API, so do not extend subtitle behavior into cross-slide metadata inference. Typst code fences are bundled as advanced rendering support; keep them optional and do not make core theme behavior depend on Typst output.
See references/design-guide.md for fine-tuning recipes (width limits, gutter columns, scoped overrides) and a "when NOT to use" table per feature.
Content Budget
The slide canvas is 980 × 552 px (16:9). After the section bar, h1, title gap, and padding, the usable body height is ~23.5 rem (≈ 376 px). Numbers below assume 1 rem = 16 px.
Line budget
| Density | Height per line (font × line-height) | Hard max (single-line bullets only) | Practical target |
|---|---|---|---|
| normal | 1.4 rem × 1.8 = 2.52 rem / 40 px | 8 | 5–6 |
| compact | 1.22 rem × 1.65 = 2.01 rem / 32 px | 10 | 7 |
| dense | 1.05 rem × 1.5 = 1.58 rem / 25 px | 13 | 9–10 |
A "line" is one rendered text line — a sub-bullet, a continuation wrap, or a blank-line separator all count. Use the practical target column: real bullets often wrap, and slides usually include a heading, intro sentence, or component alongside the list. Plan for 5–6 at normal density; only approach the hard max if every item is a short single-line phrase with no other elements on the slide.
Component line cost
Add these to your line count. "Normal" = density: normal; "Dense" = density: dense.
| Element | Normal (40 px/line) | Dense (25 px/line) |
|---|---|---|
<Callout type="…" title="…"> + 1 body line |
3 | 2.5 |
<Block title="…"> + 1 body line |
2.5 | 2 |
<ResultBox> + 1 body line |
2.5 | 2 |
<Takeaway> (1 sentence) |
1.5 | 1.5 |
| Each extra body line inside any component | +1 | +1 |
<Grid cols="2"> row |
max(left, right) + 0.5 | max(left, right) + 0.5 |
$$…$$ simple (single-line, super/subscripts) |
1.5 | 2.5 |
$$…$$ complex (fraction, Σ/∫, matrix) |
2–2.5 | 3–3.5 |
Display math does not scale down in dense mode. .katex-display uses margin: 1em inherited from .slidev-layout (1.1 rem = 17.6 px, fixed), and KaTeX renders at 1.21 × that size — neither is overridden by density: dense. The same physical pixel height therefore represents more line equivalents at dense density. A slide with two display-math blocks plus bullets can overflow at dense density even if the raw line count looks safe.
Image sizing
Images in <FigureBlock> do not auto-shrink to the available body height — they render at full width by default (max-height: 38rem CSS cap). Control size via props:
| Situation | Approach |
|---|---|
| Limit width (most common) | width="60%" on <FigureBlock> — constrains the whole figure block |
| Limit image width inside the block | imageWidth="400" (px) or imageWidth="80%" |
Figure in a <Grid cols="2"> or split column |
no extra sizing needed — the column constrains it |
<FigureBlock src="/img.png" caption="…" width="60%" />
<FigureBlock src="/img.png" caption="…" imageWidth="400" />
width sets the <figure> element's width (centering in the full column). imageWidth sets only the <img> inside it. Use width when the caption should also be narrowed; use imageWidth when you want the caption to span the full column width.
Overflow rules
Check line count before writing, not after.
- If over budget at normal density by ≤ 20%: switch to
density: dense+margin: tight. - If over budget by more than 20%, or if there are ≥ 2 visually distinct ideas: split into two slides — add
(1/2)/(2/2)tosubtitle:. - Never reach for
density: denseto cram content that genuinely belongs on two slides; dense mode is a slight scale-down, not a compression tool. - When splitting, put the core claim on slide 1 and the supporting detail on slide 2 (or demote to a
backupslide).
CSS Variables
Full variable reference: references/api/theme-tokens.md.
Key overridable variables:
| Variable | Default | Role |
|---|---|---|
--ustc-blue |
#1E4C90 |
primary brand color |
--ustc-fs-body |
1.4rem |
body text (normal density) |
--ustc-fs-body-compact |
1.22rem |
body text (compact density) |
--ustc-fs-body-dense |
1.05rem |
body text (dense density) |
--ustc-fs-callout / --ustc-fs-callout-title |
1.15rem / 1.15rem |
Callout body/title |
--ustc-fs-result-title / --ustc-fs-result-body |
1.15rem / 1.15rem |
ResultBox title/body |
--ustc-fs-block-title / --ustc-fs-block-body |
1.15rem / 1.15rem |
Block title/body |
--ustc-fs-takeaway |
1.4rem |
Takeaway text |
--ustc-fs-badge |
calc(body * 0.7) |
Badge text (tracks density) |
--ustc-lh |
1.8 |
body text line-height |
--ustc-title-gap |
1.5rem |
h1 to first body element gap |
--ustc-component-gap |
0.75rem |
top-level component gap |
--ustc-px / --ustc-py |
2.8rem / 1.75rem |
slide padding |
--ustc-max-w-cover-h1 / --ustc-max-w-cover-sub |
48rem / 58rem |
cover title/subtitle width |
--ustc-footer-h |
1.75rem |
footer bar height |
Common Patterns
Dense slide (lots of content)
---
layout: content
density: dense
margin: tight
---
density: dense is a coordinated scale-down — it simultaneously shrinks body text (1.4rem → 1.05rem), Callout body/title (1.15rem → 0.95rem), ResultBox and Block text (1.15rem → 0.96rem), table cells (1.1rem → 0.96rem), h2 (drops to h3 size 1.3rem), Takeaway text (1.4rem → 1.05rem), and tightens line-height (1.8 → 1.5) plus list spacing. Use it instead of overriding font sizes in <style> because a <style> override changes one element in isolation and breaks the theme's internal proportions. Dense mode keeps the whole slide visually coherent at a smaller scale. Combine with margin: tight or margin: tighter to reclaim additional page padding.
density: compact is the middle tier — every typography token sits roughly halfway between normal and dense (body 1.4rem → 1.22rem → 1.05rem; line-height 1.8 → 1.65 → 1.5). Reach for it when a slide is slightly over budget at normal but dense shrinks things more than necessary. Same coordinated-scale mechanism as dense — don't hand-tune font sizes to fake an in-between size.
Limit a component's width
Wrap the component in a plain <div style="…"> — inline style on a raw <div> is bulletproof. Don't try to pass style or class directly to a theme component in markdown: Slidev's <style> blocks are auto-scoped (so .my-class on the component root often doesn't match), and attribute passthrough on theme components in markdown is unreliable.
<Grid cols="2" gap="lg">
<Block title="A">
…
</Block>
<div style="justify-self: center; max-width: 26rem;">
<Block title="B">
…
</Block>
</div>
</Grid>
For consistent inset width across slides, prefer gutter columns: <Grid cols="1 8 8 1"> with empty <div>s on the edges.
For equal-height items, replace the theme <Grid> with raw native CSS Grid — <Grid> defaults to align-items: start, native Grid defaults to stretch:
<div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.4rem;">
<Block>
…
</Block>
<Callout type="tip">
…
</Callout>
<ResultBox>
…
</ResultBox>
</div>
Two-column with unequal widths
---
layout: split
ratio: "3:2"
gap: lg
---
# Title
::left::
Main content
::right::
Supporting figure or code
TOC with section highlight
---
layout: toc
variant: arrow # default; columns is ignored
highlight: 2 # highlight section 2
---
Appendix slides
---
layout: backup
---
# Backup Slides
All slides after this use A.N footer numbering.
Figure + caption side by side
<Grid cols="1 1" gap="md" alignY="center">
<FigureBlock src="/img/a.png" caption="Method A" width="100%" />
<FigureBlock src="/img/b.png" caption="Method B" width="100%" />
</Grid>
Footnotes in flow (not overlaid)
---
footnote: flow
---
Content with footnote[^1]
[^1]: This appears in document flow, not overlaid at the bottom.
Override section label for TOC
---
layout: section
sectionLabel: 'Short Name'
---
# Full Long Section Title Shown on Slide
The sectionLabel value is used in the TOC and section bar; the h1 is shown on the section slide itself.
Footer Modes
Full mode (default): author · title · meeting · date · page/total
Minimal mode: author · page/total
Set globally:
footerMode: minimal
Or per-slide to override:
---
footerMode: minimal
---
Section Bar
The section bar shows the presentation structure at the top of body slides. Only content / default / split slides render the bar; cover, end, toc, section, blank, and backup slides never show it (so sectionBar: false only matters on body layouts).
- Full mode: section labels + progress indicator
- Minimal mode: progress indicator only
- Labels mode: section labels only
The "progress indicator" is per-slide dots by default. When a section has more dots than fit on one row, that section's indicator automatically switches to a horizontal progress bar (click anywhere on it to jump to the corresponding slide). Other sections keep their dots. The threshold is computed from the actual available width per section, so wide decks with few sections get dots; dense decks with many sections may show progress bars in the long sections.
Control globally:
sectionBarMode: full # or 'minimal' | 'labels'
sectionBar: false # disable entirely
Hide on specific slides:
---
sectionBar: false
---
The bar height is controlled by two overridable CSS variables (set these in your deck's styles/index.css):
:root {
--ustc-nav-h-full: 2rem; /* full mode (labels + dots) */
--ustc-nav-h-minimal: 1.5rem; /* minimal mode (dots only) */
}
--ustc-nav-h is computed automatically from these and must not be set manually.
WIP Markers
Two independent WIP signals exist; they do not interact:
Component-level wip prop — on <FigureBlock>, <TableBlock>, <VideoBlock>, <QRCode>. Shows a red "WIP" badge on the component itself. No effect on section-bar dots.
Slide-level wip: true frontmatter — on any body slide (content, default, split). Produces two effects:
- A large semi-transparent "WIP" watermark centered on the slide.
- In the section bar: if the section is showing dots, that slide's dot is tinted red with a pulsing glow. If the section is showing a progress bar (because dots overflowed), the whole bar tints red and pulses to signal "this section contains WIP content"; per-slide WIP precision is intentionally not preserved in progress-bar mode.
---
layout: content
wip: true
---
# Draft Slide
Scope limitation: wip: true on a section layout slide shows the watermark, but does not produce a red dot in the section bar. Section slides appear as section-group titles in the bar, not as body-slide dots. If you need a WIP signal on a section slide visible in the bar, add a placeholder body slide after it with wip: true.
Quick Reference
| Goal | How |
|---|---|
| Smaller body text globally | :root { --ustc-fs-body: 1.2rem } in styles/index.css, or .slidev-layout { --ustc-fs-body: 1.2rem } per slide |
| Dense text on one slide | density: dense in frontmatter |
| Middle density (between normal & dense) | density: compact in frontmatter |
| Change h1 / heading colour per slide | .slidev-layout { --ustc-blue-dark: #... } in slide <style> — reaches h1, block/takeaway/callout titles |
| Recolor section bar + footer per slide | .slidev-layout { --ustc-blue: #... } in slide <style> — both chrome bars share this token |
| Recolor only the footer (keep nav blue) | .slidev-layout { --ustc-footer-bg: #... } in slide <style> |
| Hide section bar on one slide | sectionBar: false in frontmatter |
| Indicator-only section bar (no labels) | sectionBarMode: minimal |
| Labels-only section bar (no indicator) | sectionBarMode: labels |
| Change section bar height | --ustc-nav-h-full / --ustc-nav-h-minimal in :root (labels mode uses minimal height) |
| Mark a body slide as WIP | wip: true in frontmatter (watermark + red section-bar dot) |
| Background image / color on any layout | background: '/bg.jpg' (or '#1a2a4a') in frontmatter — accepted by every layout |
| Add overlay to a background image | .slidev-layout { background-image: linear-gradient(rgba(255,255,255,0.88), rgba(255,255,255,0.88)), url('/bg.jpg') } in slide <style> |
| Custom figure/table prefix | figurePrefix: "Fig." / tablePrefix: "Tab." in global frontmatter |
| Custom number suffix | figureNumberSuffix: ": " / tableNumberSuffix: ": " globally, or numberSuffix=": " per block |
| Click-to-zoom figures (lightbox) | figureZoom: true in global frontmatter (deck-wide); per-figure zoomable / :zoomable="false" override. Interactive-only — no-op in PDF export. |
| Layer Abs elements | :z="20" on top, :z="10" behind |
| Wider left column in split | ratio: "3:1" |
| Inline footnotes | footnote: flow |
| One-off width / spacing tweak | Wrap the component in <div style="…">; don't passthrough to the theme component |
| Equal-height items | Use raw native CSS Grid instead of <Grid> — see design-guide.md |
| Absolute positioning | Use <Abs x y w :z> (%/px coords); Slidev's built-in <v-drag> also works for drag-positioned elements |
| All valid prop values | See references/api/components.md |
| All CSS variables | See references/api/theme-tokens.md |
| Re-enable typographic replacements | slidev: { markdown: { markdownOptions: { typographer: true } } } in deck's vite.config.js |
| Allow external network access to dev server | server: { allowedHosts: true } in deck's vite.config.ts |
| Ignore speaker-note-only HMR updates | Built into the theme for slides.md and sections/*.md; add deck-specific files via ustcTheme.extraNotesOnlyHmrFiles |
Vite Configuration
Use the deck's vite.config.js only for deck-local server and markdown options.
Do not copy files from the theme repo's vite.config.ts into a deck; reusable
theme behavior must come from the installed theme package itself. If a theme bug
requires changing setup/, styles/, layouts/, components/, or utils/,
update/reinstall the theme package and restart Slidev.
A deck only ever has one export default — merge everything into a single object:
// vite.config.js in your deck
// Don't import defineConfig from 'vite' — user decks don't have vite as a
// direct dependency; Slidev bundles it internally.
export default {
// Allow access from other devices on the network (disabled by default)
server: {
allowedHosts: true,
},
// Re-enable typographic replacements: (c)→©, (r)→®, --→– etc.
// (the theme disables these by default for academic decks)
slidev: {
markdown: {
markdownOptions: { typographer: true },
},
},
}
Include only the keys you actually need — the two blocks above are independent and can be used separately.
Theme-maintained HMR whitelist
The theme's default Vite plugin ignores HMR when a whitelisted markdown file changes only Slidev speaker notes (HTML comments outside fenced code blocks). This keeps presenter-note edits from refreshing the visible deck.
Default whitelist:
// setup/vite-plugins.ts in the theme repo
export const notesOnlyHmrFiles = ['slides.md', 'sections/*.md']
For a deck/project with extra markdown entry points, do not copy the theme
plugin into the project. Add the extra deck-root-relative patterns in the deck's
vite.config.js:
// vite.config.js in your deck
export default {
ustcTheme: {
// Appended to the default ['slides.md', 'sections/*.md'] whitelist.
extraNotesOnlyHmrFiles: ['chapters/*.md', 'appendix.md'],
},
}
If the deck also needs other local Vite options, merge them into the same single
export default object:
export default {
server: {
allowedHosts: true,
},
ustcTheme: {
extraNotesOnlyHmrFiles: ['chapters/*.md'],
},
}
For theme-repo changes that should affect every deck by default, update
notesOnlyHmrFiles in setup/vite-plugins.ts instead:
export const notesOnlyHmrFiles = ['slides.md', 'sections/*.md', 'chapters/*.md']
Patterns are deck-root-relative, case-insensitive, and support * inside a
single path segment only. Do not add broad patterns such as **/*.md unless the
theme intentionally wants notes-only edits in every markdown file to skip HMR.
After changing the whitelist or stripping behavior, update
tests/vitePlugins.test.mjs and run:
pnpm exec tsx --test tests/vitePlugins.test.mjs
pnpm test:unit
Additional Resources
- references/authoring-academic-decks.md — how to design good academic decks: argument spine, layout decision tree, density rubric, figure handling, academic taboos, citations, the render→measure→fix audit loop
- references/scenarios/group-progress.md — internal group meeting / weekly progress defaults, including no-affiliation covers and WIP handling
- references/scenarios/group-literature.md — literature survey / paper reading structure, metadata badges, and survey-specific pitfalls
- references/scenarios/conference-talk.md — formal conference/oral paper talks: affiliations, time budget, image-heavy text-light slides, and drafted PPT preservation
- references/workflows/ppt-to-slidev.md — classify PPTs as materials-only vs drafted deck, then convert appropriately
- references/workflows/asset-ingestion.md — bring in PDF/SVG/raster/video assets; includes
scripts/pdf2svg.py - references/workflows/handover-export.md — package PDF/PPTX/PNG/source handovers; includes
scripts/handover-export.sh - references/style/wording.md — wording preferences: avoid AI-smelling shorthand, invented acronyms, filler, and unsupported SOTA claims
- references/style/visual-rubric.md — operational check for sparse, cramped, ugly, over-boxed, or misaligned slides
- scripts/audit-layout.mjs — the layout audit ruler: renders each slide headless and reports overflow / sparse / wide-figure-in-split / image whitespace / orphan defects with fix hints (
node <skill>/scripts/audit-layout.mjs <url-or-deck.md>) - scripts/pdf2svg.py — convert PDF figures/pages to SVG (
uv run <skill>/scripts/pdf2svg.py input.pdf -o public/images) - scripts/handover-export.sh — export PDF/PPTX/PNG once and zip a handover package (
bash <skill>/scripts/handover-export.sh <deck-dir> <handover-dir> <artifact-prefix>) - scripts/check-component-format.mjs — fail on one-line content components such as
<Takeaway>...</Takeaway>(node <skill>/scripts/check-component-format.mjs slides.md sections/*.md) - references/api/components.md — full prop tables for all components
- references/api/prop-defaults.md — all prop default values (source:
utils/defaults.ts) - references/api/theme-tokens.md — complete CSS variable reference
- references/examples/full-deck.md — canonical demo deck showing all layouts and components in use
- references/design-guide.md — design principles, layout fine-tuning recipes, "when NOT to use" table per feature
Runnable demo decks under references/examples/ (each runs with slidev examples/<name>.md):
- references/examples/layouts.md — every layout's full frontmatter options
- references/examples/components.md — every component's prop demos
- references/examples/math.md — math rendering across positions and components
- references/examples/tweaks.md — fine-tuning recipes (before/after pages, companion to
design-guide.md)