The Marp slide theme for CPU (Computer Psycho Union) at UNNC.
To make slides, read docs/usage.md. To let an agent do it, install the skill:
npx skills add CompPsyUnion/cpu-style-marp-templateThat is the skills CLI — it symlinks the skill into every agent it detects (Claude Code, Cursor, Codex, …). Add -g for all projects, -a claude to target one agent, --copy if you prefer real files over symlinks. This README is about how the theme itself is put together, for whoever maintains it.
tree -a --dirsfirst -I .git
.
├── .agents
│ └── skills
│ └── cpu-marp
│ └── SKILL.md # agent skill body, cross-agent location
├── .claude
│ └── skills -> ../.agents/skills # symlink so Claude Code picks it up in-repo
├── .vscode
│ └── settings.json # Marp theme registration + cSpell dictionary
├── assets
│ └── poster.png # placeholder poster for the side-image demo
├── docs
│ └── usage.md # usage guide
├── themes
│ └── cpu.css # the theme — all styles live in this one file
├── .editorconfig # documentation standards
├── .markdownlint.json # (paired with the line above)
├── .gitignore
├── LICENSE
├── README.md
└── template.md # sample deck, one page per slide typethemes/cpu.css is a standalone Marpit theme (first line /* @theme cpu */), in four layers from top to bottom:
:rootvariables. Colors and fonts are defined here and nowhere else;- Base layer. Canvas size, guide lines, page numbers, plus default styles for paragraphs and inline elements. A standalone theme has no Marp default theme to fall back on, so all of this has to be written out;
- Shared elements. Things every page runs into: the
h2title (yellow, dashed underline, a</>hanging in the top-right corner),h3yellow tags, square list markers, tables; - Page classes. Layout rules under
section.<class>, one class per slide type.
A few techniques worth knowing:
- Sparse pages (
side,todo) usedisplay: flex; flex-direction: columnwithmargin-top: autoon the info blocks, so leftover space is split evenly between blocks instead of piling everything at the top and leaving a hole in the middle; - The mega words are hollow letters made with
-webkit-text-strokeandcolor: transparent(coverh3, dividerh2, side-imageh4); the coverh4is solid yellow; - Side images go through
background: pinned right, full height, original ratio, taking no layout space at all. The text column keeps clear via a--side-gapvariable set by each slide's scoped style — the theme owns layout, the image belongs to the deck; - The org name in the cover's top-right sits on
section.lead h2::after. Why notsection::after— see the pitfalls below.
| Variable | Value | Used for |
|---|---|---|
--yellow |
#f7d447 |
Brand yellow: banner, tags, list markers, table heads, divider pages |
--yellow-soft |
#f9dd74 |
Secondary yellow: nested list markers, agenda numbers |
--ink |
#111111 |
Body text |
--gray |
#a3a3a3 |
Secondary text (italics redefined as gray upright text) |
--grid |
#e3d5a8 |
Dashed guide lines, the rule under titles |
--sans |
Noto Sans SC, PingFang SC, … | Body |
--display |
Archivo Black, Arial Black, … | Mega words, big numbers |
--mono |
ui-monospace, SF Mono, … | Emails, page numbers, </> |
Recolors and font swaps only touch :root.
Worth knowing before changing the theme — each of these was tracked down in actual renders:
- Marpit reserves
section::before/::afterfor pagination and strips literalcontentfrom::after. So text like the org name never paints if written intosection::after; it has to ride on another pseudo-element — the theme usesh2::after. The old slides.md had its CSS inlined at the end of the file, and the empty rules left behind after content stripping took the page numbers and the cover banner down with it — they all vanished from PDF exports. Loading the theme as a file via--theme-setmakes the whole problem go away. - Any
section::afterrule without acontentproperty kills page-number rendering. To restyle the number on a specific page, followsection.yellow::afterand repeat the attr() content; the cover hides its number withcolor: transparent, again with content present. - CLI exports referencing local images must pass
--allow-local-files, otherwise the images are silently blocked by the security policy with nothing but a warning to tip you off. - Fonts load from Google Fonts; offline you get the system fallbacks, with
Arial Blackstanding in whenArchivo Blackis unavailable.
Add a section.<class> rule block to themes/cpu.css — copy todo for a plain layout, side for one with an image. Any ::after rule must carry content (see above). Then sync three places: an example page in template.md, a row in the docs/usage.md table, a row in the SKILL.md quick reference. Run the regression below before committing.
marp template.md --theme-set themes/cpu.css --allow-local-files -o /tmp/tpl.pdfCheck page by page (pixel sampling beats squinting at a clean export):
- Cover: yellow band
#f7d447on top, org name top-right, mega words bottom-right; - Agenda: first row highlighted yellow;
- Roster: yellow table head;
- Side-image page: image full height, text clear of it;
- Both divider pages solid
#f7d447; - Page number bottom-right from page 2 on, none on the cover;
npx markdownlint-cli README.md docs/usage.md template.mdpasses.