Generate beautifully designed PDF reports with Nordic/Scandinavian aesthetic. Uses Nutrient DWS API for HTML-to-PDF conversion.
58
73%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
Generate minimalist, elegant PDF reports inspired by Scandinavian design principles.
cd ~/clawd-nuri-internal/skills/elegant-reports
# Generate a report (dense layout)
node generate.js report.md output.pdf --template report
# Generate a presentation (bold slides)
node generate.js data.md slides.pdf --template presentation
# Dark mode
node generate.js report.md --template report --theme dark
# List templates
node generate.js --list| Template | Style | Use Case |
|---|---|---|
report | Dense, multi-column | Deep dives, analysis, competitive intel |
presentation | Big & bold, one idea/page | Exec briefings, board decks |
Each template supports --theme light (default) or --theme dark.
Add YAML frontmatter to control output:
---
title: Q4 Competitive Analysis
subtitle: Market Intelligence Report
author: Nuri
template: report
theme: dark
---
Your content here...Based on Nordic/Scandinavian design principles:
See NORDIC_DESIGN_RESEARCH.md for complete design documentation.
This is how to create new templates with visual feedback:
# Use browser tool to study design examples
browser navigate https://www.canva.com/templates/...
browser screenshot
# Look for:
# - Typography choices (font, size, weight)
# - Color palette (backgrounds, text, accents)
# - Layout patterns (grids, spacing)
# - Component styles (cards, tables, callouts)Create a new CSS file in themes/:
/* themes/my-theme.css */
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@600;700;800&family=Inter:wght@400;500&display=swap');
:root {
/* Color tokens */
--color-bg: #FAFAFA;
--color-surface: #FFFFFF;
--color-text-primary: #0A0A0A;
--color-text-secondary: #404040;
--color-accent: #2563EB;
/* Typography tokens */
--font-display: 'Poppins', sans-serif;
--font-body: 'Inter', sans-serif;
/* Spacing tokens */
--space-4: 1rem;
--space-6: 1.5rem;
--space-8: 2rem;
}
/* Component styles... */Create HTML in templates/:
<!-- templates/my-template.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{{title}}</title>
<style>
{{styles}}
</style>
</head>
<body>
<div class="page">
<h1>{{title}}</h1>
<p>{{subtitle}}</p>
{{content}}
</div>
</body>
</html>Available variables: {{title}}, {{subtitle}}, {{author}}, {{date}}, {{content}}, {{styles}}
# Generate test HTML manually
node -e "
const fs = require('fs');
const css = fs.readFileSync('themes/my-theme.css', 'utf8');
let html = fs.readFileSync('templates/my-template.html', 'utf8');
html = html.replace('{{styles}}', css);
html = html.replace(/\{\{title\}\}/g, 'Test Title');
html = html.replace(/\{\{subtitle\}\}/g, 'Test Subtitle');
html = html.replace(/\{\{date\}\}/g, 'January 2026');
html = html.replace(/\{\{author\}\}/g, 'Nuri');
html = html.replace(/\{\{content\}\}/g, '<p>Test content</p>');
fs.writeFileSync('test-output.html', html);
"
# Preview in browser
browser navigate file://$(pwd)/test-output.html
browser screenshot
# See what's wrong, fix it, repeatAdd to TEMPLATES object in generate.js:
const TEMPLATES = {
// ...existing templates...
'my-template': {
description: 'My custom template description',
template: 'my-template.html',
themes: {
light: 'my-theme.css',
dark: 'my-theme-dark.css'
}
}
};node generate.js test.md output.pdf --template my-template --output-html| Token | Size | Use |
|---|---|---|
--text-xs | 11-12px | Labels, captions |
--text-sm | 13-14px | Body (dense) |
--text-base | 14-16px | Body (normal) |
--text-lg | 16-18px | Lead paragraphs |
--text-xl | 18-20px | Section headers |
--text-2xl | 20-24px | H3 |
--text-3xl | 24-32px | H2 |
--text-4xl | 32-40px | H1 (report) |
--text-5xl | 48-56px | H1 (presentation) |
--text-6xl | 64-72px | Hero text |
| Token | Size | Use |
|---|---|---|
--space-1 | 2-4px | Tight gaps |
--space-2 | 4-8px | Inline spacing |
--space-3 | 8-12px | Component padding |
--space-4 | 12-16px | Card padding |
--space-6 | 20-24px | Section gaps |
--space-8 | 28-32px | Major sections |
--space-10 | 36-40px | Page sections |
--space-12 | 44-48px | Page margins |
Light Mode:
--color-bg: #FAFAFA;
--color-surface: #FFFFFF;
--color-text-primary: #0A0A0A;
--color-text-secondary: #404040;
--color-text-muted: #737373;
--color-accent: #2563EB;Dark Mode:
--color-bg: #09090B;
--color-surface: #18181B;
--color-text-primary: #FAFAFA;
--color-text-secondary: #D4D4D8;
--color-text-muted: #A1A1AA;
--color-accent: #3B82F6;<div class="kpi-strip">
<div class="kpi-item">
<div class="kpi-value">$10.8M</div>
<div class="kpi-label">Revenue</div>
<div class="kpi-change positive">+12%</div>
</div>
<!-- more items -->
</div><div class="key-findings">
<div class="key-findings-title">Key Points</div>
<ul>
<li><strong>Point 1</strong> — Details</li>
<li><strong>Point 2</strong> — Details</li>
</ul>
</div><div class="two-col">
<div>Left column content</div>
<div>Right column content</div>
</div><div class="callout callout-tip">
<div class="callout-title">Tip</div>
<p>Content here</p>
</div>Types: callout-tip (green), callout-warning (amber), callout-danger (red)
<table class="no-break">
<thead>
<tr><th>Header</th><th class="num">Value</th></tr>
</thead>
<tbody>
<tr><td>Row</td><td class="num">123</td></tr>
</tbody>
</table>Add these classes to prevent awkward breaks:
<div class="no-break">This won't split across pages</div>
<div class="page-break">Forces new page before this</div>Tables, cards, callouts, and KPI strips have page-break-inside: avoid by default.
elegant-reports/
├── SKILL.md # This file
├── NORDIC_DESIGN_RESEARCH.md # Design principles documentation
├── generate.js # Main generator script
├── package.json
├── themes/
│ ├── nordic-v2.css # Presentation light
│ ├── nordic-report.css # Report light
│ └── nordic-report-dark.css # Report dark
├── templates/
│ ├── executive-v2.html # Presentation template
│ └── report-v2.html # Report template
└── examples/
└── sample-executive.md # Example inputnpm install)const { generateReport } = require('./generate.js');
await generateReport({
input: 'report.md',
output: 'report.pdf',
template: 'report',
theme: 'dark',
title: 'My Report',
author: 'Nuri'
});