On this page ▾
Writing a theme
A theme is a folder with the same shape as a deck's own folder. The rules are in Themes and addons and Directory structure. This page is a walk-through.
1. Make the folder
my-theme/
theme.json
layouts/
components/
styles/index.css
Put it next to your deck and write theme: ./my-theme in the headmatter. Start with only the parts you need.
2. Replace a layout
my-theme/layouts/quote.ts:
import { Component } from '@angular/core';
@Component({
selector: 'my-quote',
host: { class: 'slidev-layout my-quote' },
template: '<div class="my-quote-body"><ng-content /></div>',
})
export class MyQuote {}
Any slide with layout: quote now uses it. The selector must be a plain string literal and the class must be exported, because the generator finds a layout by reading the file as text.
If your layout reads frontmatter such as author: Ada, give the component an input of that name and list it in theme.json:
{ "layouts": { "quote": { "props": ["author"] } } }
A layout that replaces a built-in one keeps the built-in layout's inputs. For example cover keeps background. The frontmatter keys that are inputs become attributes of your tag, so give your component inputs of the same names.
3. Add a component
my-theme/components/callout.ts with selector: 'my-callout'. Slides can write <my-callout>text</my-callout>, and the generator imports it into every slide that uses the tag.
4. Add CSS
my-theme/styles/index.css is plain CSS and comes after the app's CSS. UnoCSS classes work in the layouts and components, and uno.config.mjs can add shortcuts:
export default { shortcuts: { 'my-muted': 'op60 text-sm' } };
5. Default settings
theme.json can set headmatter defaults and describe layouts:
{
"name": "minimal",
"headmatter": { "colorSchema": "dark", "transition": "fade" },
"layouts": { "quote": { "props": ["author"], "slots": [], "classProp": false } }
}
headmatter: defaults for the deck. The deck's own headmatter wins. This is also where fonts go, for example"fonts": { "sans": "Titillium Web", "mono": "Fira Code" }.layouts.<name>:propsare the frontmatter keys that become inputs,slotsare the names accepted after::name::, andclassProp: truesends the slide'sclassto the inputcontentClassinstead of the tag.
Pictures go in public/.
Extra: providers and setup hooks
A theme can change the shortcuts, or how code editors run code, with a setup/ file. See Setup hooks. setup/providers.ts default-exports an array of Angular providers, which are added to the application:
| Token | From | Use |
|---|---|---|
SHORTCUTS_SETUP |
slide-ng/lib/shortcuts |
A function (actions, list) => list to add, change or remove shortcuts. The last root that provides it wins. |
CODE_RUNNERS |
slide-ng/code/runners |
{ provide: CODE_RUNNERS, multi: true, useValue: { javascript: (code) => result } } replaces how {monaco-run} runs a language. |
SYNC_TRANSPORTS |
slide-ng/sync/transport |
Another way to carry presenter sync (multi). |
Share it
A theme is a folder, so publish it as an npm package with these folders at the top of the package. slide.ng looks the name up as @slide-ng/theme-<name>, then slide-ng-theme-<name>, then <name>, so name your package slide-ng-theme-<name>, or @your-scope/theme-<name> and use theme: @your-scope/theme-<name>.
{
"name": "slide-ng-theme-ocean",
"version": "1.0.0",
"license": "MIT",
"keywords": ["slide-ng-theme", "slide-ng"],
"files": ["layouts", "components", "styles", "public", "setup", "theme.json"],
"peerDependencies": { "@slide-ng/core": "*" }
}
To start from the built-in look instead of from nothing, run slide-ng theme eject.
Check it
Run the deck with slide-ng and edit the theme. The folders are watched, so an edit regenerates and reloads. Run slide-ng build to see the real build, which fails on a template error in a layout.