
Brand System
- 9 installs
- 53 repo stars
- Updated July 9, 2026
- appautomaton/presentation
brand-system is a Claude skill that creates a visual identity system (palette, typography, style direction) as a brand brief, JS config, and preview PDF.
About
This skill creates a visual identity system consisting of a color palette, font pairings, and style direction. A developer or consultant uses it before building a deck, presentation, or document that needs a unified visual language. It produces three artifacts: a markdown brand brief with rationale, a machine-readable identity.js config for downstream build scripts, and a three-slide preview PDF proving the palette, typography, and a combination demo.
- Creates a visual identity system: palette, typography, style direction
- Outputs a markdown brand brief, a JS config, and a preview PDF
- Uses an 8-role color palette and a pairing lookup for typography
Brand System by the numbers
- 9 all-time installs (skills.sh)
- Ranked #1,469 of 1,880 Design & UI/UX skills by installs in the Skillselion catalog
- Data as of Jul 28, 2026 (Skillselion catalog sync)
brand-system capabilities & compatibility
- Capabilities
- branding · ui design · design system
- Use cases
- ui design · web design · presentations
What brand-system says it does
Eight roles. One accent + three signals. Every color traces to a reason.
npx skills add https://github.com/appautomaton/presentation --skill brand-systemAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 9 |
|---|---|
| repo stars | ★ 53 |
| Last updated | July 9, 2026 |
| Repository | appautomaton/presentation ↗ |
What it does
Produce a color palette, font pairing, and style direction as a brand brief, JS config, and preview PDF before building a deck.
Who is it for?
Establishing a cohesive design system before building a deck, presentation, or document.
Skip if: Producing finished slides or laying out every page, which downstream skills handle.
When should I use this skill?
You need a unified palette, typography, and style direction before a visual project.
What you get
A brand brief, identity.js config, and preview PDF define the palette, fonts, and style direction.
- brand-brief.md
- identity.js config
- brand-preview.pdf
By the numbers
- 8-role color palette structure
- 3-slide preview PDF
- 6 active design directions in the design landscape
Files
Brand System
Create a visual identity system: palette, typography, and style direction — documented in a markdown brief, extracted into a JS config, and rendered as a preview PDF.
Artifacts
All output goes to the user's working directory. Ask for the path if not provided.
| File | What it is |
|---|---|
brand-brief.md | The primary artifact. Research, rationale, and every design decision in one readable document. |
identity.js | A machine-readable extraction of the brief. Importable by downstream build scripts. |
brand-preview.pdf | A 3-slide rendered proof: palette, typography, and a combination demo. |
Intermediate HTML goes to /tmp/brand-system/ and is cleaned up after rendering.
Reference files
Load these on demand — not all are needed for every engagement.
| File | When to load |
|---|---|
| references/palette-guide.md | When building the color palette — 8-role structure, derivation from anchor color, contrast testing |
| references/typography-guide.md | When selecting fonts — pairing lookup table, weight discipline, same-vs-different family guidance |
| references/design-landscape.md | When proposing a visual style — 6 active design directions with full specifications (2025-2026) |
---
How to work
This is a conversational design process. The brief is built through dialogue — research, propose, refine.
1. Start from what you know
Read the user's request. Most of the direction is already implied — the subject, the context, the tone. Don't ask questions the request already answers.
If genuine ambiguity remains, ask the one or two questions that would most change your direction. Good questions resolve a fork in the road.
Worth asking (only if truly unclear):
- "Should this feel institutional and restrained, or is there room for something more contemporary?"
- "Is there an existing brand I should align with, or are we starting fresh?"
Decide yourself (don't ask the user):
- What colors to use — research the brand and propose
- What font to use — match the context and recommend
- How many palette roles — the structure is defined, follow it
2. Research
Before choosing colors or fonts, look at what exists.
Known brand → search for their actual colors, typeface, and visual direction. Extract the primary brand color, the typeface family, and the tone.
New project → search for what works in the space — peer palettes, industry conventions, tone-appropriate examples.
3. Build the palette
Load references/palette-guide.md for the full role structure and derivation rules.
The palette grows from an anchor color:
- User provides one → build around it
- Known brand → extract from research
- Fresh → choose based on tone and context
Eight roles. One accent + three signals. Every color traces to a reason.
4. Choose typography
Load references/typography-guide.md for the pairing lookup table and rules.
Find the Google Font that echoes the subject's typographic character. One family for both heading and body is the default — a second family must earn its place.
5. Propose a style direction
Load references/design-landscape.md for the full catalog of six active design directions.
The style defines shape language, surface treatment, and compositional rules. It is recorded in the brief and the config, but it is un-opinionated in application — the style direction is a vocabulary that downstream skills interpret for their own medium. This skill defines what the visual language is, not how every slide or page should look.
Recommend the best fit based on context and offer 1-2 alternatives. Filter by context — don't present all six.
Anti-patterns regardless of style:
- Rounded cards with shadows → SaaS product UI
- Cards nested inside cards → visual noise
- Gradient fills on content → marketing, not professional
- Inconsistent radius within a single composition
6. Draft the brief, then refine
Write brand-brief.md with everything so far. Present it and ask the user 2-3 targeted questions — the decisions most likely to benefit from their input.
Identify what to surface based on what you're least certain about. If the accent color was a clear derivation from the brand, don't ask about it. If you had to choose between two equally good fonts, surface that choice.
Structure of the brief:
# Brand Brief — [Project Name]
## Context
[Who, what, for whom]
## Research
[What was found — colors, typeface, tone, sources]
## Color Palette
| Role | Hex | Rationale |
|---|---|---|
| ... | ... | ... |
## Typography
| Role | Font | Weights | Rationale |
|---|---|---|---|
| ... | ... | ... | ... |
## Visual Style
[Which direction, why, what it means in practice]
## Identity (if applicable)
[Project or firm name, tagline, positioning — only when the project calls for it]Iterate until the user approves. Typically 1-3 rounds. Each round should narrow.
Do not generate `identity.js` or render the preview until the brief is finalized. The preview confirms decisions already made — it is not a design exploration tool.
7. Generate the config
Extract the approved brief into identity.js. Every value comes from the brief.
module.exports = {
// 'institutional' | 'modern' | 'dark' | 'bento' | 'editorial' | 'data-forward'
style: 'modern',
palette: {
name: 'project-name',
surfaceDark: '#...', accent: '#...', accentLight: '#...',
surfaceMuted: '#...', surface: '#...',
text: '#...', textMuted: '#...', textFine: '#...', textOnDark: '#FFFFFF',
chartPrimary: '#...', chartSecondary: '#...',
positive: '#...', warning: '#...', negative: '#...',
},
fonts: {
heading: { family: '...', weights: [600, 700], stack: "'...', system-ui, sans-serif" },
body: { family: '...', weights: [400, 600], stack: "'...', system-ui, sans-serif" },
mono: null,
},
// Optional — include when the project has a brand identity
firm: null, // or { name: '...', tagline: '...', logoSVG: null }
toCSS() { /* generates [data-palette="..."] { --accent: ...; --radius: ...; ... } */ },
// Style tokens are included in the CSS output alongside palette colors.
// Downstream skills use var(--radius), var(--card-border), etc.
};8. Render the preview
Write a short Node script to the user's working directory. The script requires the engine from this skill's base directory (provided in context) and the identity config from the working directory:
const { createPreview } = require('<this-skill>/engine');
const brand = require('./identity');
createPreview(brand, { output: './brand-preview.pdf' });<this-skill> = this skill's base directory path. Node resolves playwright from the skill's own node_modules/ — no install needed in the user's project.
Three slides: palette swatches, typography specimens + combo demo, cover on dark surface. Present to the user — if anything is off, update the brief first, then regenerate.
---
Architecture
brand-system/
SKILL.md ← always loaded (this file)
references/
palette-guide.md ← load when building palette
typography-guide.md ← load when choosing fonts
design-landscape.md ← load when proposing style
engine/
index.js ← createPreview(brand) → PDF
render.js ← Playwright HTML → PDF
package.json ← playwright only
node_modules/ ← self-containedSelf-contained: package.json + node_modules/ are inside the skill folder. All require() calls resolve locally — no workspace-level install needed. Run npm install inside this skill folder if node_modules/ is missing.
The identity.js this skill produces is an input to downstream skills, not an instruction. Downstream skills interpret the style direction for their own medium — this skill defines the vocabulary, not the implementation.
/**
* Brand System — Preview Builder
*
* Takes a brand identity config and renders a 2-3 slide preview PDF
* showing color palette, font specimens, and a combination demo.
*/
const path = require('path');
const fs = require('fs');
const { renderPDF } = require('./render');
const { getStyleTokens, styleToCSS } = require('./styles');
function buildHTML(brand, options = {}) {
const { width = 1280, height = 720 } = options;
const p = brand.palette;
const f = brand.fonts;
const firm = brand.firm || { name: p.name || 'Brand Preview', tagline: '', logoSVG: null };
const style = brand.style || 'modern';
const rawTokens = getStyleTokens(style);
// Map CSS custom property tokens to inline-friendly values
const st = {
radius: rawTokens['--radius'],
kpiRadius: rawTokens['--radius-kpi'],
cardBorder: rawTokens['--card-border'],
cardBg: rawTokens['--card-bg'] === 'var(--surface-muted)' ? p.surfaceMuted : rawTokens['--card-bg'],
cardShadow: rawTokens['--card-shadow'],
headerBorder: rawTokens['--header-rule'] + ' solid',
};
// Google Fonts CDN link
const fontFamilies = [f.heading, f.body, f.mono].filter(Boolean);
const fontLinks = fontFamilies.map(font => {
const weights = font.weights.join(';');
const family = font.family.replace(/ /g, '+');
return `<link href="https://fonts.googleapis.com/css2?family=${family}:wght@${weights}&display=swap" rel="stylesheet">`;
}).join('\n ');
const pageCSS = `
@page { size: ${width}px ${height}px; margin: 0; }
*, *::before, *::after { box-sizing: border-box; print-color-adjust: exact; -webkit-print-color-adjust: exact; }
html, body { margin: 0; padding: 0; width: ${width}px; }
body { font-family: ${f.body.stack}; color: ${p.text}; background: ${p.surface}; }
.slide { width: ${width}px; height: ${height}px; overflow: hidden; position: relative;
page-break-after: always; break-after: page; }
.slide:last-child { page-break-after: auto; break-after: auto; }
`;
// ── Slide 1: Color Palette ────────────────────────────────────────
const paletteRoles = [
{ name: 'Primary / Dark', hex: p.surfaceDark, light: true },
{ name: 'Accent', hex: p.accent, light: true },
{ name: 'Accent Light', hex: p.accentLight, light: false },
{ name: 'Surface Muted', hex: p.surfaceMuted, light: false },
{ name: 'Positive', hex: p.positive, light: true },
{ name: 'Warning', hex: p.warning, light: false },
{ name: 'Negative', hex: p.negative, light: true },
{ name: 'Surface', hex: p.surface, light: false },
];
const swatches = paletteRoles.map(role => `
<div style="flex: 1; display: flex; flex-direction: column; border-radius: 8px; overflow: hidden;">
<div style="flex: 1; background: ${role.hex}; display: flex; align-items: flex-end; padding: 12px;">
<span style="font-size: 11px; font-weight: 600; color: ${role.light ? '#fff' : p.text};">${role.name}</span>
</div>
<div style="padding: 8px 12px; background: #fff; border: 1px solid #e5e5e5; border-top: 0; border-radius: 0 0 8px 8px;">
<span style="font-size: 12px; font-family: monospace; color: ${p.textMuted};">${role.hex}</span>
</div>
</div>
`).join('');
const slide1 = `
<section class="slide" style="background: ${p.surface}; display: flex; flex-direction: column; padding: 48px 56px;">
<div style="margin-bottom: 24px;">
<p style="font-size: 11px; font-weight: 700; letter-spacing: 0.2em; text-transform: uppercase; color: ${p.accent}; margin: 0 0 8px;">
Brand System
</p>
<h2 style="font-size: 32px; font-weight: 700; letter-spacing: -0.03em; color: ${p.text}; margin: 0; font-family: ${f.heading.stack};">
Color Palette — ${firm.name}
</h2>
<div style="height: 3px; width: 60px; background: ${p.accent}; border-radius: 2px; margin-top: 12px;"></div>
</div>
<div style="flex: 1; display: flex; gap: 12px;">
${swatches}
</div>
<div style="margin-top: 16px; display: flex; gap: 12px;">
<div style="flex: 1; display: flex; align-items: center; gap: 8px; padding: 10px 14px; border-radius: 6px; background: ${p.surfaceMuted};">
<span style="font-size: 11px; font-weight: 600; color: ${p.textMuted};">Text: ${p.text}</span>
</div>
<div style="flex: 1; display: flex; align-items: center; gap: 8px; padding: 10px 14px; border-radius: 6px; background: ${p.surfaceMuted};">
<span style="font-size: 11px; font-weight: 600; color: ${p.textMuted};">Text Muted: ${p.textMuted}</span>
</div>
<div style="flex: 1; display: flex; align-items: center; gap: 8px; padding: 10px 14px; border-radius: 6px; background: ${p.surfaceMuted};">
<span style="font-size: 11px; font-weight: 600; color: ${p.textMuted};">Text Fine: ${p.textFine}</span>
</div>
<div style="flex: 1; display: flex; align-items: center; gap: 8px; padding: 10px 14px; border-radius: 6px; background: ${p.surfaceMuted};">
<span style="font-size: 11px; font-weight: 600; color: ${p.textMuted};">Chart Primary: ${p.chartPrimary}</span>
</div>
</div>
</section>`;
// ── Slide 2: Typography Specimens ─────────────────────────────────
const slide2 = `
<section class="slide" style="background: ${p.surface}; display: flex; flex-direction: column; padding: 48px 56px;">
<div style="margin-bottom: 24px;">
<p style="font-size: 11px; font-weight: 700; letter-spacing: 0.2em; text-transform: uppercase; color: ${p.accent}; margin: 0 0 8px;">
Brand System
</p>
<h2 style="font-size: 32px; font-weight: 700; letter-spacing: -0.03em; color: ${p.text}; margin: 0; font-family: ${f.heading.stack};">
Typography — ${f.heading.family}${f.body.family !== f.heading.family ? ' + ' + f.body.family : ''}
</h2>
<div style="height: 3px; width: 60px; background: ${p.accent}; border-radius: 2px; margin-top: 12px;"></div>
</div>
<div style="flex: 1; display: grid; grid-template-columns: 1fr 1fr; gap: 32px;">
<!-- Left: Heading specimens -->
<div>
<p style="font-size: 11px; font-weight: 700; letter-spacing: 0.18em; text-transform: uppercase; color: ${p.accent}; margin: 0 0 16px;">
${f.heading.family} — Headings
</p>
<p style="font-size: 40px; font-weight: 700; letter-spacing: -0.03em; color: ${p.text}; margin: 0 0 12px; font-family: ${f.heading.stack}; line-height: 1.05;">
Bold 700 — Aa Bb Cc
</p>
<p style="font-size: 26px; font-weight: 600; letter-spacing: -0.02em; color: ${p.text}; margin: 0 0 12px; font-family: ${f.heading.stack}; line-height: 1.1;">
Semibold 600 — Aa Bb Cc Dd Ee
</p>
<p style="font-size: 18px; font-weight: 400; color: ${p.text}; margin: 0 0 12px; font-family: ${f.body.stack}; line-height: 1.5;">
Regular 400 — The quick brown fox jumps over the lazy dog. Clarity at every scale.
</p>
<p style="font-size: 15px; font-weight: 400; color: ${p.textMuted}; margin: 0; font-family: ${f.body.stack}; line-height: 1.6;">
Body text at 15px — Analysis reveals a 340bps margin improvement opportunity across five cost levers, capturing $180M in annualized savings within 18 months.
</p>
</div>
<!-- Right: Combination demo -->
<div>
<p style="font-size: 11px; font-weight: 700; letter-spacing: 0.18em; text-transform: uppercase; color: ${p.accent}; margin: 0 0 6px;">
Combination Demo
</p>
<p style="font-size: 10px; color: ${p.textFine}; margin: 0 0 12px; font-family: ${f.body.stack};">
Style: ${style.charAt(0).toUpperCase() + style.slice(1)} · Radius: ${st.radius} · ${st.cardBorder !== 'none' ? 'Bordered' : 'Borderless'}
</p>
<div style="background: #fff; ${st.cardBorder !== 'none' ? `border: ${st.cardBorder} ${p.accentLight}40;` : ''} border-radius: ${st.radius}; overflow: hidden; ${st.cardShadow !== 'none' ? `box-shadow: ${st.cardShadow};` : ''}">
<div style="padding: 20px 20px 10px; border-top: ${st.headerBorder} ${p.accent};">
<p style="font-size: 9px; font-weight: 700; letter-spacing: 0.18em; text-transform: uppercase; color: ${p.accent}; margin: 0 0 4px;">Market Position</p>
<p style="font-size: 17px; font-weight: 700; letter-spacing: -0.02em; color: ${p.text}; margin: 0; font-family: ${f.heading.stack}; line-height: 1.15;">
Cloud revenue grew 48% to $17.7B, outpacing AWS and Azure
</p>
</div>
<div style="padding: 12px 20px; display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 8px;">
<div style="background: ${st.cardBg || p.surfaceMuted}; border-radius: ${st.kpiRadius}; padding: 12px; ${st.cardBorder !== 'none' && !st.cardBg ? `border: 1px solid ${p.accentLight}30;` : ''}">
<p style="font-size: 20px; font-weight: 700; color: ${p.accent}; margin: 0; font-family: ${f.heading.stack};">$70B+</p>
<p style="font-size: 10px; font-weight: 600; color: ${p.text}; margin: 4px 0 0;">Cloud ARR</p>
<p style="font-size: 9px; color: ${p.positive}; margin: 2px 0 0;">+48% YoY</p>
</div>
<div style="background: ${st.cardBg || p.surfaceMuted}; border-radius: ${st.kpiRadius}; padding: 12px; ${st.cardBorder !== 'none' && !st.cardBg ? `border: 1px solid ${p.accentLight}30;` : ''}">
<p style="font-size: 20px; font-weight: 700; color: ${p.accent}; margin: 0; font-family: ${f.heading.stack};">$240B</p>
<p style="font-size: 10px; font-weight: 600; color: ${p.text}; margin: 4px 0 0;">Backlog</p>
<p style="font-size: 9px; color: ${p.positive}; margin: 2px 0 0;">+55% QoQ</p>
</div>
<div style="background: ${st.cardBg || p.surfaceMuted}; border-radius: ${st.kpiRadius}; padding: 12px; ${st.cardBorder !== 'none' && !st.cardBg ? `border: 1px solid ${p.accentLight}30;` : ''}">
<p style="font-size: 20px; font-weight: 700; color: ${p.accent}; margin: 0; font-family: ${f.heading.stack};">200%+</p>
<p style="font-size: 10px; font-weight: 600; color: ${p.text}; margin: 4px 0 0;">GenAI Growth</p>
<p style="font-size: 9px; color: ${p.textMuted}; margin: 2px 0 0;">YoY revenue</p>
</div>
</div>
<div style="padding: 8px 20px; border-top: 1px solid ${p.accentLight}20; display: flex; justify-content: space-between;">
<span style="font-size: 9px; color: ${p.textFine};">Source: Analysis, 2026</span>
<span style="font-size: 9px; color: ${p.textFine};">Confidential</span>
</div>
</div>
${f.mono ? `
<div style="margin-top: 16px;">
<p style="font-size: 11px; font-weight: 700; letter-spacing: 0.18em; text-transform: uppercase; color: ${p.accent}; margin: 0 0 8px;">
${f.mono.family} — Data / Monospace
</p>
<div style="background: ${st.cardBg || p.surfaceMuted}; border-radius: ${st.kpiRadius}; padding: 14px 16px; font-family: ${f.mono.stack};">
<p style="font-size: 24px; font-weight: 500; color: ${p.text}; margin: 0;">$4.2B</p>
<p style="font-size: 14px; font-weight: 500; color: ${p.textMuted}; margin: 4px 0 0;">+12.4% YoY | 2,847 bps</p>
</div>
</div>
` : ''}
</div>
</div>
</section>`;
// ── Slide 3: Cover Demo ───────────────────────────────────────────
const slide3 = `
<section class="slide" style="background: ${p.surfaceDark}; display: flex; flex-direction: column; justify-content: space-between; padding: 48px 56px;">
<div style="display: flex; align-items: center; gap: 12px;">
${firm.logoSVG || ''}
<span style="font-size: 13px; font-weight: 600; letter-spacing: 0.2em; text-transform: uppercase; color: ${p.accent}; font-family: ${f.heading.stack};">
${firm.name}
</span>
</div>
<div>
<h1 style="font-size: 48px; font-weight: 700; letter-spacing: -0.03em; color: ${p.textOnDark}; margin: 0 0 16px; font-family: ${f.heading.stack}; line-height: 1.05;">
Cover Slide Preview
</h1>
<p style="font-size: 19px; color: ${p.accentLight}; margin: 0; font-family: ${f.body.stack}; line-height: 1.5; max-width: 640px;">
${firm.tagline}
</p>
<div style="height: 3px; width: 60px; background: ${p.accent}; border-radius: 2px; margin-top: 20px;"></div>
</div>
<div style="display: flex; justify-content: space-between; align-items: center;">
<span style="font-size: 13px; color: ${p.textFine}; font-family: ${f.body.stack};">Prepared for Client Leadership</span>
<span style="font-size: 13px; color: ${p.textFine}; font-family: ${f.body.stack};">2026 · Confidential</span>
</div>
</section>`;
return `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>${firm.name} — Brand Preview</title>
${fontLinks}
<style>${pageCSS}</style>
</head>
<body>
${slide1}
${slide2}
${slide3}
</body>
</html>`;
}
async function createPreview(brand, options = {}) {
const { output = 'brand-preview.pdf', width = 1280, height = 720 } = options;
const html = buildHTML(brand, { width, height });
await renderPDF(html, output, { width, height });
console.log(`Wrote: ${output} (3 slides, ${width}x${height})`);
return output;
}
module.exports = { createPreview, buildHTML };
/**
* Brand System — Lightweight PDF renderer
*
* Renders HTML to PDF via Playwright. No Tailwind, no ECharts, no vendored fonts.
* Uses Google Fonts via CDN and plain CSS for color swatches and font specimens.
*
* Intermediate HTML is written to system tmp and cleaned up after rendering.
*/
const fs = require('fs');
const path = require('path');
const os = require('os');
const { chromium } = require('playwright');
const TMP_DIR = path.join(os.tmpdir(), 'brand-system');
function ensureTmpDir() {
if (!fs.existsSync(TMP_DIR)) {
fs.mkdirSync(TMP_DIR, { recursive: true });
}
}
async function renderPDF(html, outputPath, options = {}) {
const { width = 1280, height = 720, slug = 'preview' } = options;
// Write intermediate HTML to system tmp
ensureTmpDir();
const tmpHtml = path.join(TMP_DIR, `${slug}-preview.html`);
fs.writeFileSync(tmpHtml, html, 'utf8');
const browser = await chromium.launch({ headless: true });
const page = await browser.newPage();
await page.setViewportSize({ width, height });
await page.emulateMedia({ media: 'screen', colorScheme: 'light' });
// Load from file:// so relative paths resolve if needed
await page.goto(`file://${tmpHtml}`, { waitUntil: 'networkidle' });
// Wait for Google Fonts to load
await page.waitForTimeout(800);
await page.pdf({
path: outputPath,
width: `${width}px`,
height: `${height}px`,
printBackground: true,
preferCSSPageSize: false,
});
await browser.close();
// Clean up intermediate HTML
try { fs.unlinkSync(tmpHtml); } catch (_) {}
}
module.exports = { renderPDF };
/**
* Style tokens — shape language definitions for each visual direction.
*
* These are CSS custom properties that downstream skills consume.
* The brand-system preview engine uses them directly.
* The deck build script gets them via identity.toCSS() which includes them.
*/
const STYLE_TOKENS = {
institutional: {
'--radius': '2px',
'--radius-card': '2px',
'--radius-kpi': '2px',
'--card-border': '1px solid',
'--card-bg': 'transparent',
'--card-shadow': 'none',
'--header-rule': '4px',
'--divider-style': 'sharp', // sharp | soft | editorial
},
modern: {
'--radius': '8px',
'--radius-card': '8px',
'--radius-kpi': '8px',
'--card-border': '1px solid',
'--card-bg': 'var(--surface-muted)',
'--card-shadow': 'none',
'--header-rule': '4px',
'--divider-style': 'soft',
},
dark: {
'--radius': '6px',
'--radius-card': '6px',
'--radius-kpi': '6px',
'--card-border': 'none',
'--card-bg': 'rgba(255,255,255,0.06)',
'--card-shadow': 'none',
'--header-rule': '4px',
'--divider-style': 'soft',
},
bento: {
'--radius': '12px',
'--radius-card': '12px',
'--radius-kpi': '12px',
'--card-border': '1px solid',
'--card-bg': 'var(--surface-muted)',
'--card-shadow': '0 1px 3px rgba(0,0,0,0.06)',
'--header-rule': '4px',
'--divider-style': 'soft',
},
editorial: {
'--radius': '0px',
'--radius-card': '0px',
'--radius-kpi': '0px',
'--card-border': 'none',
'--card-bg': 'transparent',
'--card-shadow': 'none',
'--header-rule': '3px',
'--divider-style': 'editorial',
},
'data-forward': {
'--radius': '2px',
'--radius-card': '2px',
'--radius-kpi': '2px',
'--card-border': '1px solid',
'--card-bg': 'transparent',
'--card-shadow': 'none',
'--header-rule': '3px',
'--divider-style': 'sharp',
},
};
function getStyleTokens(style) {
return STYLE_TOKENS[style] || STYLE_TOKENS.modern;
}
/**
* Generate CSS custom properties for a style direction.
* Injected into the palette CSS so downstream skills can use var(--radius), etc.
*/
function styleToCSS(style) {
const tokens = getStyleTokens(style);
return Object.entries(tokens)
.map(([prop, value]) => ` ${prop}: ${value};`)
.join('\n');
}
module.exports = { STYLE_TOKENS, getStyleTokens, styleToCSS };
{
"name": "brand-system",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "brand-system",
"version": "1.0.0",
"dependencies": {
"playwright": "^1.52.0"
}
},
"node_modules/fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/playwright": {
"version": "1.58.2",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.58.2.tgz",
"integrity": "sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==",
"license": "Apache-2.0",
"dependencies": {
"playwright-core": "1.58.2"
},
"bin": {
"playwright": "cli.js"
},
"engines": {
"node": ">=18"
},
"optionalDependencies": {
"fsevents": "2.3.2"
}
},
"node_modules/playwright-core": {
"version": "1.58.2",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.58.2.tgz",
"integrity": "sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==",
"license": "Apache-2.0",
"bin": {
"playwright-core": "cli.js"
},
"engines": {
"node": ">=18"
}
}
}
}
{
"name": "brand-system",
"version": "1.0.0",
"private": true,
"description": "Visual identity system — palettes, fonts, style direction, preview rendering",
"dependencies": {
"playwright": "^1.52.0"
}
}
Presentation Design Landscape — 2025-2026
Reference material for visual style selection. Updated March 2026.
Active design directions
Six distinct visual languages coexist in professional presentations today. Each serves a different audience and purpose.
1. MBB Institutional
The consulting standard. McKinsey, BCG, and Bain have refined this over decades.
- Surface: white or near-white, minimal variation
- Typography: one sans-serif family, moderate weight contrast (regular body, bold titles)
- Color: one accent color plus signal colors (green/amber/red). Navy and charcoal for text. Never more than 4 colors in a chart.
- Layout: structured grids, generous margins (56px), clear three-zone hierarchy (header → body → footer). Data charts fill the body zone — no decorative wrappers.
- Shape language: sharp corners or minimal radius (≤4px). No shadows, no gradients on content. Thin 1px rules to separate zones.
- Charts: clean, annotation-heavy. Waterfall bridges, horizontal sorted bars, evaluation grids. The chart IS the slide — no sidebar commentary needed.
- Fonts commonly used: Helvetica, Calibri, Arial (legacy). Modern equivalents via Google Fonts: Inter, Source Sans 3.
- Best for: board presentations, steering committees, due diligence, regulatory submissions
2. Modern Corporate
The dominant style for tech-sector strategy and corporate communications. Cleaner than MBB, more expressive than institutional.
- Surface: off-white (
#F8F9FA) with subtle surface-muted zones for cards and panels - Typography: geometric sans-serif, two weights visible (semibold titles, regular body). Slightly larger metrics.
- Color: brand-derived accent with carefully muted signal colors. Accent appears in headers, chart primaries, and callout borders.
- Layout: CSS Grid / Bento-inspired modules. Asymmetric splits (3fr + 2fr). KPI card grids. Combination slides with chart + metric cards.
- Shape language: consistent 8px radius. Cards use either a light fill OR a thin border — never both. No shadows.
- Charts: ECharts/SVG with consulting-quality axes. Radar, clustered bars, stacked area, waterfall bridges. Annotation callouts explain the data.
- Fonts commonly used: Inter, Manrope, DM Sans, Plus Jakarta Sans
- Best for: tech strategy, growth-stage analysis, investor updates, product roadmaps
3. Dark Premium
High-contrast, cinematic. Popularized by Sequoia's crisis memos and tech keynotes.
- Surface: deep charcoal or near-black (
#0A0E14) with electric accent pops - Typography: bold sans-serif headings (often oversized, 44-60px). Regular weight body in light gray.
- Color: one vivid accent (electric green, neon blue, vivid coral) against dark background. Minimal signal colors — the accent does most of the work.
- Layout: full-bleed dark slides, generous whitespace, one message per slide. Metrics land as oversized numbers on dark surfaces.
- Shape language: minimal radius on accent elements. No card borders — elements float on the dark surface.
- Charts: light-colored series on dark backgrounds. Simplified — donut over pie, single-line trends over multi-series. Glow effects on accent elements.
- Fonts commonly used: Inter (bold), Georgia (serif accent), SF Pro Display equivalents
- Best for: crisis communications, high-stakes fundraising, keynote stage decks, tech leadership
4. Bento Modular
The 2025-2026 breakout trend, inspired by Apple's product pages. Modular grid of content blocks.
- Surface: white base with varied-size rectangular modules. Each module is a self-contained content unit.
- Typography: module titles in bold sans, content in regular. Metrics and data in large bold treatment within their module.
- Color: accent used for highlight modules and primary data. Most modules are neutral (white or light gray fill) with one or two accent-colored "hero" modules.
- Layout: CSS Grid with varying column/row spans. A 3×3 or 4×3 grid where some cells span 2 columns or 2 rows. Hero cell for the primary message, supporting cells for metrics/evidence.
- Shape language: consistent medium radius (8-12px). Modules have subtle shadows or 1px borders. Gap between modules is part of the design (8-12px).
- Charts: embedded within modules — smaller, more numerous. Sparklines, mini bar charts, progress indicators. Data is distributed across modules rather than concentrated in one chart.
- Fonts commonly used: Inter, Manrope, SF Pro Display equivalents
- Best for: product launches, feature overviews, dashboard-style updates, OKR reviews
5. Editorial / Publication
High-contrast, typography-forward. Feels like a branded magazine, not a slide deck.
- Surface: stark contrast — dark covers, white content pages. No mid-tone backgrounds.
- Typography: serif headings (Instrument Serif, Playfair Display) with sans body (DM Sans, Inter). Large type hierarchy — cover titles 48-64px, section markers as oversized decorative elements.
- Color: extremely restrained accent usage. One accent color appears in one element per slide. The typography and whitespace do the visual work, not color.
- Layout: editorial grid — wide columns with narrow margin annotations. Pullout quotes. Large photographic or typographic hero zones.
- Shape language: zero radius. Horizontal rules separate content zones. No card boxes — content sections are defined by whitespace and rules.
- Charts: minimal framing. Charts float in generous whitespace. Annotation is typographic (callout text) rather than graphic (boxes and arrows).
- Fonts commonly used: Instrument Serif, Playfair Display, Crimson Pro + DM Sans, Inter
- Best for: annual reports, premium advisory, thought leadership, brand positioning decks
6. Data-Forward / Engineering
Dense, functional, information-maximizing. Used when the audience expects technical depth.
- Surface: light gray or white, uniform. No decorative elements.
- Typography: mono or mono-adjacent for data (JetBrains Mono, IBM Plex Mono). Sans-serif for text (Inter, Source Code Pro). Small body text (13-14px) is acceptable — the audience reads closely.
- Color: muted palette. Blues and grays for data series. Green/red only for directional signals. Avoid warm colors.
- Layout: dense grids. Multi-panel slides with 3-4 exhibits per slide. Tables with many rows. Comparison matrices.
- Shape language: sharp corners. Thin 1px borders everywhere. Monospace alignment. Tab-like section navigation.
- Charts: complex — multi-axis, dual-scale, indexed time series, heatmaps. Dense annotation. The chart is the argument.
- Fonts commonly used: JetBrains Mono, IBM Plex Mono + Inter, Source Sans 3
- Best for: engineering reviews, data science readouts, technical due diligence, system architecture decks
Color trends — 2025-2026
| Direction | Palette character | When to use |
|---|---|---|
| Warm neutrals | Earthy browns, natural greens, muted terracotta | Sustainability, culture, org design |
| Neo-mint / teal | Calm blue-green as anchor, soft pastels as support | Innovation, health tech, forward-looking strategy |
| High-contrast neon | Electric accent (lime, blue, coral) on dark background | Pitch decks, tech demos, high-stakes fundraising |
| Monochrome depth | Single hue at 4-5 lightness levels | Institutional, data-heavy, minimalist |
| Brand-derived | Client's primary color → muted for density, with harmonious signals | Consulting engagements, client-facing strategy |
Typography trends — 2025-2026
| Direction | Character | Examples |
|---|---|---|
| Oversized sans | Headlines as visual elements, 50%+ of slide area | Startup pitches, keynotes |
| Variable weight | Single font family, weight variation creates hierarchy | Corporate strategy, modern consulting |
| Serif + sans pairing | Serif headings for gravitas, sans body for readability | Editorial, premium advisory |
| Monospace accent | Mono for data/metrics, sans for narrative | Engineering, fintech, technical |
| Single family | One font, hierarchy through size/weight only | MBB institutional, clean corporate |
Layout trends — 2025-2026
| Pattern | What it is | Best for |
|---|---|---|
| Bento Grid | Modular rectangular blocks, varied sizes | Product overviews, multi-metric slides |
| Asymmetric split | 60/40 or 55/45 column split | Chart + commentary, data + insight |
| Full-bleed hero | One large element fills the slide | Cover slides, single-metric impact |
| Three-zone shell | Header (title) → body (content) → footer (source) | All analytical content slides |
| Editorial column | Wide main column with narrow sidebar annotation | Reports, editorial-style decks |
| Dense grid | 2×3 or 3×3 uniform cells | KPI dashboards, comparison matrices |
Sources
- SlideRabbit, "Presentation Design Trends Shaping Modern Slides in 2026"
- Slidesgo, "Presentation Design Trends 2026: 10 Styles Shaping Your Slides"
- Design Shack, "25+ Presentation Design Trends for 2025"
- Adobe, "Design Trends for 2026"
- SlideEgg, "7 Top Presentation Design Trends for 2026"
- Zeenesia, "Color and Typography Trends in 2026"
- Analyst Academy, "600+ Real Consulting Presentations from McKinsey, BCG, Bain"
- Ink Bot Design, "Bento Grid Design: Modular Hierarchy for Conversion in 2026"
- Extended Frames, "Bento Grid Layouts for Slides"
Palette Guide
How to build a color palette for a visual identity system.
Starting positions
The palette always grows from an anchor color. Three ways to find it:
User provides a color — build around it directly. Derive dark, light, muted, and text variants by adjusting lightness and saturation. Pick harmonious signal colors that don't clash.
Known brand — extract the primary brand color from research (their website, press kit, brand page). Mute it 10-15% for slide density — brand colors are designed for screens, not data-dense slides.
Starting fresh — choose a color that fits the project's tone. Research what colors dominate the space (tech → blues, health → greens/teals, finance → navies, creative → warm/bold). Avoid choosing a color that makes the project look like a competitor.
The eight palette roles
Every palette needs exactly these eight roles. No more, no fewer.
| Role | Purpose | How to derive |
|---|---|---|
| Surface Dark | Cover backgrounds, section dividers, dark-mode slides | Darken the accent to ~15-20% lightness. Or use a neutral navy (#1A2744) / charcoal (#0A0E14). Must pass WCAG contrast ≥ 4.5:1 against white text. |
| Accent | Headers, highlights, chart primary, action elements, brand marker | The anchor color — the brand's primary or the user's chosen color. Mute saturation 10-15% from the pure brand version for print legibility. |
| Accent Light | Borders, gridlines, subtle tints, hover states | Lighten accent to 40-50% lightness. Should feel like a whisper of the accent, not a second accent. |
| Surface Muted | Card fills, evidence panels, alternate row backgrounds | Lighten accent to 90-95% lightness — nearly white but with a perceptible tint. Should be distinguishable from pure white on screen. |
| Surface | Default slide background | White (#FFFFFF) or off-white (#F8F9FA). Off-white is warmer and reduces eye strain; pure white is crisper and more institutional. |
| Positive | Growth indicators, favorable deltas, success states | A green that harmonizes with the accent. Avoid traffic-light green — use muted, natural greens (#0D9D58, #2E9E5A, #788C5D). |
| Warning | Attention callouts, neutral caution, in-progress states | Amber or gold (#F4B400, #E6A800). Never pure yellow — poor contrast on white backgrounds. |
| Negative | Risk indicators, unfavorable deltas, error states | Red or coral (#DB4437, #C46686, #A43C35). Should feel serious but not alarming. Must not clash with the accent. |
Text colors
Derived from the palette, not chosen independently:
| Role | How to derive |
|---|---|
| Text (primary) | A dark variant of the accent or surface-dark color. Never pure #000000 — use #1A2744 or #141413 or similar. |
| Text Muted (secondary) | 40-50% lightness, neutral or slightly warm. For annotations, labels, secondary copy. |
| Text Fine (tertiary) | 55-65% lightness. For footnotes, source lines, metadata. Should be readable but recessive. |
| Text on Dark | White (#FFFFFF) or near-white. Used on surface-dark backgrounds. |
| Chart Primary | Usually the same as accent. |
| Chart Secondary | A neutral gray (#999999 or #9AA0A6). For comparator series, secondary data. |
Derivation from a single anchor color
Given one hex value (the anchor), derive the full palette:
1. Accent = the anchor, muted 10-15% if needed 2. Surface Dark = anchor at 15-20% lightness (darken dramatically) 3. Accent Light = anchor at 70-80% lightness (lighten significantly) 4. Surface Muted = anchor at 95% lightness (barely tinted white) 5. Text = anchor at 10-15% lightness (very dark variant) 6. Text Muted = neutral at 35-40% lightness 7. Text Fine = neutral at 55-60% lightness 8. Positive/Warning/Negative = choose signal colors that harmonize (check they don't clash with accent in a chart context)
Anti-patterns
- Full-saturation brand colors on dense slides → mute them
- More than one accent color → dilutes authority
- Pure black (#000000) text → harsh, use a dark variant instead
- Signal colors that match consumer UI (traffic-light bright) → too loud for analytical slides
- Accent and negative colors that are too close in hue → confusing in charts
Contrast testing
Before finalizing, mentally test the palette in three contexts:
1. KPI card: accent metric number + text label + positive/negative trend arrow on surface-muted background. Is it readable? 2. Chart: accent bars + chart-secondary bars + positive/negative delta indicators on white. Do the colors separate clearly? 3. Cover slide: white title + accent-light subtitle on surface-dark background. Does the contrast work?
Typography Guide
How to choose and pair fonts for a visual identity system.
The decision process
1. Identify the subject's typeface family from research (geometric, humanist, serif, mono) 2. Find the closest Google Font that echoes that DNA 3. Decide whether heading and body should be the same family or different 4. Select weights: 400 (regular), 600 (semibold), 700 (bold) — load only what you need 5. Optionally add a monospace for data and metrics
Pairing lookup table
From the subject's brand typeface to a Google Fonts recommendation:
| Their typeface feels like... | Google Fonts to try | Why this pairing works |
|---|---|---|
| Geometric sans (Product Sans, Roboto, Circular, Futura) | Inter, Manrope | Same geometric DNA — circular 'o', even stroke width, neutral personality |
| Humanist sans (Söhne, Helvetica Now, Gill Sans, Frutiger) | DM Sans, Source Sans 3 | Warmer, more approachable. Open apertures, slight stroke contrast |
| Neo-grotesque (Suisse, Aktiv Grotesk, SF Pro) | Inter, Plus Jakarta Sans | Clean, contemporary, slightly geometric. Good at many weights. |
| Slab serif (Roboto Slab, Zilla Slab) | Roboto Slab + Inter | Authoritative headlines with clean body text |
| Serif / editorial (Canela, GT Sectra, Tiempos) | Instrument Serif + DM Sans | Premium gravitas. Serif heading creates instant editorial tone. |
| Monospace-forward (SF Mono, JetBrains Mono, Fira Code) | JetBrains Mono + Inter | Technical identity. Mono for data/metrics, sans for narrative. |
| No clear reference (new project, no existing brand) | Inter | The universal default. Works for every context. Add a second family only if the project needs more personality. |
Font roles
| Role | What it controls | Weight guidance |
|---|---|---|
| Heading | Cover titles, action titles, section headers, card metric values | 600 (semibold) for action titles, 700 (bold) for cover titles and big metrics |
| Body | Paragraphs, bullet text, table cells, callout content, card labels | 400 (regular) for body copy, 600 (semibold) for emphasis and labels |
| Mono (optional) | Data tables, KPI numbers, code blocks, technical annotations | 400 or 500 (medium). Used sparingly — only when tabular alignment or technical tone matters. |
Heading + body: same or different?
Same family (e.g., Inter for both):
- Simpler, more cohesive
- Hierarchy comes from size and weight, not font contrast
- Best default for most projects
- Works for: consulting, corporate, technical
Different families (e.g., Instrument Serif heading + DM Sans body):
- More distinctive, more editorial
- Creates stronger visual hierarchy through typographic contrast
- Requires careful pairing — the families must share proportions (x-height, cap height)
- Works for: premium advisory, annual reports, thought leadership, brand-forward decks
Rule of thumb: start with one family. Add a second only if the project needs more personality than size/weight variation can provide.
Google Fonts availability
Fonts commonly vendored in deck engines (available offline):
- Inter
- DM Sans
- Plus Jakarta Sans
- Source Sans 3
Fonts available via CDN only (require network at render time):
- Manrope
- Instrument Serif
- Playfair Display
- JetBrains Mono
- IBM Plex Mono
- Crimson Pro
- Any other Google Font
When using CDN fonts, add the link via headExtra in the build script or in the engine's HTML generation.
Weight loading discipline
Load only the weights you use. Every extra weight adds network latency and render time.
| Project type | Typical weights needed |
|---|---|
| Single-family (Inter) | 400, 600, 700 |
| Serif + sans pairing | Heading: 400, 700. Body: 400, 600 |
| With mono | Add mono at 400 or 500 only |
Never load italic weights unless the design specifically uses italics (rare in presentations).
Anti-patterns
- Loading 8+ weights "just in case" → slow renders, wasted bandwidth
- Serif body with sans heading → feels inverted, hard to read at body sizes
- Decorative/display fonts for body text → poor readability at 13-15px
- Mixing 3+ font families → looks chaotic, not intentional
- Using the brand's proprietary font directly → licensing issues; find the Google Fonts equivalent instead
Related skills
FAQ
What artifacts does it produce?
A markdown brand brief, a machine-readable identity.js config, and a three-slide preview PDF.
How is the palette structured?
Eight roles built from an anchor color: one accent plus three signals, each traced to a reason.