CtrlK
BlogDocsLog inGet started
Tessl Logo

bap-jorkim/elegant-reports

Generate beautifully designed PDF reports with Nordic/Scandinavian aesthetic. Uses Nutrient DWS API for HTML-to-PDF conversion.

58

Quality

73%

Does it follow best practices?

Run evals on this skill

Adds up to 20 points to the overall score

View guide

SecuritybySnyk

Passed

No findings from the security scan

Overview
Quality
Evals
Security
Files
name:
elegant-reports
description:
Generate beautifully designed PDF reports with Nordic/Scandinavian aesthetic. Uses Nutrient DWS API for HTML-to-PDF conversion.

elegant-reports

Generate minimalist, elegant PDF reports inspired by Scandinavian design principles.

Quick Start

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

Templates

TemplateStyleUse Case
reportDense, multi-columnDeep dives, analysis, competitive intel
presentationBig & bold, one idea/pageExec briefings, board decks

Each template supports --theme light (default) or --theme dark.

Frontmatter

Add YAML frontmatter to control output:

---
title: Q4 Competitive Analysis
subtitle: Market Intelligence Report
author: Nuri
template: report
theme: dark
---

Your content here...

Design Philosophy

Based on Nordic/Scandinavian design principles:

  • Bold typography — Poppins for headlines, Inter for body
  • High contrast — Dark headers, clear hierarchy
  • Generous whitespace — Content breathes
  • One accent color — Blue (#2563EB) used sparingly
  • Functional beauty — Form follows function

See NORDIC_DESIGN_RESEARCH.md for complete design documentation.


Creating New Templates

The Visual Iteration Workflow

This is how to create new templates with visual feedback:

Step 1: Research References

# 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)

Step 2: Create Theme CSS

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... */

Step 3: Create Template HTML

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}}

Step 4: Test with Visual Feedback

# 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, repeat

Step 5: Register in Generator

Add 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'
    }
  }
};

Step 6: Test PDF Generation

node generate.js test.md output.pdf --template my-template --output-html

Design Tokens Reference

Typography Scale

TokenSizeUse
--text-xs11-12pxLabels, captions
--text-sm13-14pxBody (dense)
--text-base14-16pxBody (normal)
--text-lg16-18pxLead paragraphs
--text-xl18-20pxSection headers
--text-2xl20-24pxH3
--text-3xl24-32pxH2
--text-4xl32-40pxH1 (report)
--text-5xl48-56pxH1 (presentation)
--text-6xl64-72pxHero text

Spacing Scale

TokenSizeUse
--space-12-4pxTight gaps
--space-24-8pxInline spacing
--space-38-12pxComponent padding
--space-412-16pxCard padding
--space-620-24pxSection gaps
--space-828-32pxMajor sections
--space-1036-40pxPage sections
--space-1244-48pxPage margins

Color Palette

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;

Component Patterns

KPI Strip (horizontal metrics)

<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>

Key Findings Box

<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>

Two-Column Layout

<div class="two-col">
  <div>Left column content</div>
  <div>Right column content</div>
</div>

Callouts

<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)

Tables with Dark Headers

<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>

Page Break Control

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.


Files Structure

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 input

Dependencies

  • Node.js 18+
  • axios, form-data (npm install)
  • Nutrient DWS API key (configured in mcporter or NUTRIENT_DWS_API_KEY env var)

API Usage

const { generateReport } = require('./generate.js');

await generateReport({
  input: 'report.md',
  output: 'report.pdf',
  template: 'report',
  theme: 'dark',
  title: 'My Report',
  author: 'Nuri'
});
Workspace
bap-jorkim
Visibility
Public
Created
Last updated
Publish Source
CLI
Badge
bap-jorkim/elegant-reports badge