First-run guide for branding a fresh copy of deck-forge. The fastest path is the
/setup agent in Cursor or Claude Code; everything it does can also be done by
hand. Both routes are below.
npm install
npm run dev # http://localhost:8080 — example deck at /d/exampleOpen the project in Cursor or Claude Code and run:
/setup
(No slash command? Just tell the agent: "set up this template for my brand.")
The agent asks a short set of questions, with sensible defaults, and confirms before writing anything:
- Product / company name
- One-line tagline
- Primary brand color (hex, e.g.
#2563EB) - Secondary / accent color (hex, optional)
- Landing background —
darkorlight - Logo — a path to a file you will drop in
/public(e.g./logo.svg), or skip for a wordmark - Fonts — heading/sans and mono (default Inter + JetBrains Mono)
- Canonical URL
| File | What gets written |
|---|---|
src/index.css |
Brand tokens (hex → HSL channels, light tints + their *-dark variants), the font @import, and the body font-family |
tailwind.config.ts |
fontFamily.sans / fontFamily.mono |
src/brand.config.ts |
brand.name, tagline, url, logo, landingTheme |
index.html |
<title> and the meta/OG description |
package.json |
The kebab-case name |
It then offers to delete the example deck so you start clean (see below for exactly what that removes). After applying:
npm install # if you haven't already
npm run dev
npm run build # verify it still compilesEdit the same files yourself. The mapping from interview answer to file:
This file is the brand surface. Colors are stored as raw HSL channels in
the form H S% L% (e.g. 221 83% 53%) so they compose with opacity:
hsl(var(--brand-primary) / 0.4). Never store hex in a token.
Edit these first, in the :root block:
--brand-primary: 221 83% 53%; /* your primary accent */
--brand-primary-light: 221 83% 96%; /* tint for light surfaces */
--brand-primary-light-dark: 221 70% 16%; /* tint used on dark slides */
--brand-accent: 215 16% 47%; /* secondary / muted brand line */Then, if you want to tune them, the surfaces (--bg-page/-dark,
--bg-surface*, --bg-code*), text (--text-primary/secondary/muted/
faint/on-brand and their *-dark counterparts), borders, and status colors.
Keep the editor-UI tokens in sync with your primary so the app chrome matches:
--primary: 221 83% 53%; /* = --brand-primary */
--ring: 221 83% 53%; /* = --brand-primary */Dark slides need no extra work. The .slide-dark block (used when a slide
declares <SlideFrame theme="dark">) remaps surfaces and text to their *-dark
counterparts automatically. Just set the token values.
When you have a hex color, convert it before storing. Example for #2563EB:
- Normalize to 0–1: R = 0x25/255 = 0.145, G = 0x63/255 = 0.388, B = 0xEB/255 = 0.922.
- max = 0.922 (B), min = 0.145 (R), delta = 0.777.
- L = (max + min) / 2 = 0.533 → 53%.
- S = delta / (1 − |2L − 1|) = 0.777 / 0.933 = 0.833 → 83%.
- H (max is B): H = 60 × (((R − G) / delta) + 4) = 60 × (3.69) ≈ 221°.
So #2563EB → 221 83% 53%. Sanity-check by pasting hsl(221 83% 53%) into
any color picker and comparing to the original hex.
Fonts live in two places, and both must agree:
src/index.css— change the Google Fonts@importat the top of the file and thebody { font-family: ... }rule lower down.tailwind.config.ts— changefontFamily.sansandfontFamily.monoundertheme.extend.
Default is Inter + JetBrains Mono.
Set the non-color identity in the brand object:
export const brand = {
name: 'Your Name',
tagline: 'Your one-liner.',
url: 'https://yourdomain.com',
logo: '/logo.svg', // path under /public; '' renders a wordmark
landingTheme: 'dark', // 'dark' | 'light'
}Update <title> and the meta/OG description (name="description",
og:title, og:description, twitter:*).
Set the kebab-case "name" field to your project, e.g. "name": "your-name".
Once you have your own deck (or as the last step of setup), delete the demo so the app still builds:
- Delete the folder
src/slides/example-deck/(allSlideNN*.tsxplusindex.ts). - Delete the page
src/pages/ExampleDeck.tsx. - Delete
src/slides/_shared/data.ts(it only holds example copy — keep it if you reuse the pattern). - In
src/brand.config.ts, remove theexample-deckentry from thedecksarray. - In
src/App.tsx, removeimport ExampleDeck from './pages/ExampleDeck'and the'example-deck': ExampleDeckentry in thedeckPagesmap.
The app builds with an empty decks array — the landing simply lists nothing.
Adding your own deck is a separate, four-step flow. See
docs/AUTHORING.md.
Before you commit, run the same checks CI runs:
npm run build && npm run lint && npx prettier --check .Formatting is Prettier with semi: false, singleQuote: true,
printWidth: 120, trailingComma: es5. Auto-fix with npm run format. Tests:
npm test.