Showcase
Each example shows real output produced by renderreport itself, from files in the project repository. Input on the left, output on the right.
Website health check from JSON
examples/requests/health-check.jsonDetail: Website health check from JSON →{
"template_id": "default",
"title": "Website Health Check",
"subtitle": "shop.example.com — October 2026",
"metadata": {
"date": "2026-10-12",
"author": "Web Team"
},
"components": [
{
"type": "section",
"data": {
"title": "Summary",
"level": 1
}
},
{
"type": "grid-component",
"data": {
"columns": 3,
"items": [
{
"content": {
"type": "score-card",
"data": {
"title": "Performance",
"score": 78,
"description": "Lighthouse, mobile"
}
}
},
{
"content": {
"type": "score-card",
"data": {
"title": "Accessibility",
"score": 91,
"description": "axe, 42 pages"
}
}
},
{
"content": {
"type": "score-card",
"data": {
"title": "SEO",
"score": 64,
"description": "Crawl of 1,280 URLs",
"good_threshold": 80,
"warn_threshold": 60
}
}
}
]
}
},
{
"type": "progress-bar",
"data": {
"label": "Home: pages passing Core Web Vitals",
"value": 96.0
}
},
{
"type": "progress-bar",
"data": {
"label": "Product: pages passing Core Web Vitals",
"value": 88.0
}
},
{
"type": "progress-bar",
"data": {
"label": "Checkout: pages passing Core Web Vitals",
"value": 81.0
}
},
{
"type": "progress-bar",
"data": {
"label": "Category: pages passing Core Web Vitals",
"value": 54.0
}
},
{
"type": "section",
"data": {
"title": "Findings",
"level": 1
}
},
{
"type": "finding",
"data": {
"title": "Category pages load hero images without size hints",
"severity": "high",
"description": "Images on category pages have no width and height, so the layout shifts while they load.",
"recommendation": "Set width and height on every hero image and serve them as AVIF.",
"category": "Performance"
}
},
{
"type": "finding",
"data": {
"title": "212 URLs return a redirect chain",
"severity": "medium",
"description": "Old product URLs redirect twice before reaching the current page.",
"recommendation": "Point each old URL directly at its final target.",
"category": "SEO"
}
},
{
"type": "callout",
"data": {
"variant": "info",
"title": "About this report",
"content": "Generated with the renderreport CLI from examples/requests/health-check.json."
}
}
]
}Open health-check.pdf · Download health-check.pdf (34 KiB)
Same request, brand theme
examples/requests/health-check-themed.jsonDetail: Same request, brand theme →{
"template_id": "default",
"title": "Website Health Check",
"subtitle": "Same request, brand theme from the JSON",
"metadata": {
"date": "2026-10-12",
"author": "Web Team"
},
"theme": {
"id": "brand",
"name": "Brand",
"tokens": {
"color.primary": "#0F766E",
"color.ok": "#15803D",
"color.warn": "#B45309",
"color.info": "#0F766E",
"color.info-soft": "#ECFDF5",
"color.accent-soft": "#ECFDF5",
"page.margin": "22mm"
}
},
"components": [
{
"type": "section",
"data": {
"title": "Summary",
"level": 1
}
},
{
"type": "grid-component",
"data": {
"columns": 3,
"items": [
{
"content": {
"type": "score-card",
"data": {
"title": "Performance",
"score": 78,
"description": "Lighthouse, mobile"
}
}
},
{
"content": {
"type": "score-card",
"data": {
"title": "Accessibility",
"score": 91,
"description": "axe, 42 pages"
}
}
},
{
"content": {
"type": "score-card",
"data": {
"title": "SEO",
"score": 64,
"description": "Crawl of 1,280 URLs",
"good_threshold": 80,
"warn_threshold": 60
}
}
}
]
}
},
{
"type": "progress-bar",
"data": {
"label": "Home: pages passing Core Web Vitals",
"value": 96.0
}
},
{
"type": "progress-bar",
"data": {
"label": "Product: pages passing Core Web Vitals",
"value": 88.0
}
},
{
"type": "progress-bar",
"data": {
"label": "Checkout: pages passing Core Web Vitals",
"value": 81.0
}
},
{
"type": "progress-bar",
"data": {
"label": "Category: pages passing Core Web Vitals",
"value": 54.0
}
},
{
"type": "section",
"data": {
"title": "Findings",
"level": 1
}
},
{
"type": "finding",
"data": {
"title": "Category pages load hero images without size hints",
"severity": "high",
"description": "Images on category pages have no width and height, so the layout shifts while they load.",
"recommendation": "Set width and height on every hero image and serve them as AVIF.",
"category": "Performance"
}
},
{
"type": "finding",
"data": {
"title": "212 URLs return a redirect chain",
"severity": "medium",
"description": "Old product URLs redirect twice before reaching the current page.",
"recommendation": "Point each old URL directly at its final target.",
"category": "SEO"
}
},
{
"type": "callout",
"data": {
"variant": "info",
"title": "About this report",
"content": "Generated with the renderreport CLI from examples/requests/health-check-themed.json: the health check request plus a theme block."
}
}
]
}Project status report
examples/simple_report.rsDetail: Project status report →//! Simple Report Example
//!
//! A beginner-friendly example showcasing the most common RenderReport components.
//! Great starting point to understand the API.
//!
//! Run with: cargo run --example simple_report
use renderreport::components::advanced::*;
use renderreport::components::charts::{Chart, Gauge};
use renderreport::components::Component;
use renderreport::prelude::*;
fn main() -> renderreport::Result<()> {
let engine = Engine::new()?;
let report = engine
.report("default")
.title("Project Status Report")
.subtitle("Sprint 24 — March 2026")
.metadata("author", "Engineering Team")
// ── Overview Section ────────────────────────────────────────
.add_component(Section::new("Overview").with_level(1))
.add_component(
SummaryBox::new("Sprint Summary")
.add_item_with_status("Sprint Goal", "Feature Complete", ScoreStatus::Good)
.add_item("Story Points", "47 / 52")
.add_item("Velocity", "48 avg")
.add_item_with_status("Release Readiness", "On Track", ScoreStatus::Good),
)
// ── Score Cards in Grid ─────────────────────────────────────
.add_component(
Grid::new(3)
.add_item(
ScoreCard::new("Code Quality", 92)
.with_description("SonarQube analysis")
.as_item(),
)
.add_item(
ScoreCard::new("Test Coverage", 87)
.with_description("Unit + Integration")
.with_thresholds(80, 60)
.as_item(),
)
.add_item(
ScoreCard::new("Performance", 95)
.with_description("Lighthouse score")
.as_item(),
),
)
// ── Progress ────────────────────────────────────────────────
.add_component(Section::new("Sprint Progress").with_level(1))
.add_component(ProgressBar::new("Backend API", 95.0).with_color("#22c55e"))
.add_component(ProgressBar::new("Frontend UI", 82.0).with_color("#3b82f6"))
.add_component(ProgressBar::new("Documentation", 60.0).with_color("#f59e0b"))
.add_component(ProgressBar::new("QA Testing", 70.0).with_color("#8b5cf6"))
// ── Chart ───────────────────────────────────────────────────
.add_component(Section::new("Velocity Trend").with_level(1))
.add_component(
Chart::bar("Story Points per Sprint")
.add_series("Completed", vec![
("S20".into(), 38.0),
("S21".into(), 42.0),
("S22".into(), 45.0),
("S23".into(), 51.0),
("S24".into(), 47.0),
])
.with_labels("Sprint", "Story Points"),
)
// ── KPI Gauges ──────────────────────────────────────────────
.add_component(
Grid::new(3)
.add_item(Gauge::new("Uptime", 99.8).with_range(95.0, 100.0).as_item())
.add_item(Gauge::new("Response Time", 72.0).with_range(0.0, 100.0).as_item())
.add_item(Gauge::new("Error Rate", 95.0).with_range(0.0, 100.0).as_item()),
)
// ── Findings ────────────────────────────────────────────────
.add_component(Section::new("Open Issues").with_level(1))
.add_component(
Finding::new("Database Connection Pool Exhaustion", Severity::High,
"Under peak load (>500 concurrent users), the connection pool is exhausted causing 503 errors.")
.with_recommendation("Increase pool size from 20 to 50 and implement connection timeout of 5s.")
.with_category("Infrastructure"),
)
.add_component(
Finding::new("Deprecated API Endpoints", Severity::Medium,
"3 API endpoints marked for deprecation in v2.0 are still being consumed by mobile clients.")
.with_recommendation("Coordinate with mobile team to migrate to v2 endpoints before the April release.")
.with_category("API"),
)
// ── Metadata ────────────────────────────────────────────────
.add_component(Divider::new())
.add_component(
KeyValueList::new()
.with_title("Project Details")
.add("Project", "RenderReport")
.add("Version", "0.1.0-alpha")
.add("Sprint", "24 (March 3 – March 17)")
.add_highlighted("Next Release", "v0.2.0 — April 1, 2026"),
)
// ── Closing ─────────────────────────────────────────────────
.add_component(
Callout::info("This report was auto-generated from sprint data. Review detailed metrics in Jira.")
.with_title("Note"),
)
.build();
let pdf = engine.render_pdf(&report)?;
std::fs::write("examples/output/simple_report.pdf", &pdf)?;
println!(
"✓ Simple report generated: examples/output/simple_report.pdf ({} KB)",
pdf.len() / 1024
);
Ok(())
}Open simple-report.pdf · Download simple-report.pdf (55 KiB)
Dark theme
examples/custom_theme.rsDetail: Dark theme →//! Custom Theme Example
//!
//! Shows how to create and apply custom themes with full reports.
//!
//! Run with: cargo run --example custom_theme
use renderreport::components::advanced::*;
use renderreport::components::charts::{Chart, Gauge};
use renderreport::components::Component;
use renderreport::prelude::*;
use renderreport::theme::{Theme, TokenValue};
fn main() -> renderreport::Result<()> {
let engine = Engine::new()?;
// ── Corporate Blue Theme ────────────────────────────────────
let mut corporate_theme = Theme::new("corporate", "Corporate Blue");
corporate_theme
.tokens
.set("color.primary", TokenValue::Color("#003366".into()));
corporate_theme
.tokens
.set("color.secondary", TokenValue::Color("#336699".into()));
corporate_theme
.tokens
.set("color.text", TokenValue::Color("#333333".into()));
corporate_theme
.tokens
.set("color.ok", TokenValue::Color("#006633".into()));
corporate_theme
.tokens
.set("color.warn", TokenValue::Color("#cc6600".into()));
corporate_theme
.tokens
.set("color.bad", TokenValue::Color("#cc0000".into()));
corporate_theme
.tokens
.set("font.heading", TokenValue::Font("Helvetica".into()));
corporate_theme
.tokens
.set("font.body", TokenValue::Font("Helvetica".into()));
let report = engine
.report("default")
.title("Quarterly Business Review")
.subtitle("Q1 2025 — Corporate Theme Demo")
.metadata("author", "Finance Department")
.theme(corporate_theme)
// Overview
.add_component(Section::new("Executive Summary").with_level(1))
.add_component(
SummaryBox::new("Key Metrics")
.add_item_with_status("Revenue", "$1.2M", ScoreStatus::Good)
.add_item_with_status("Growth YoY", "+15%", ScoreStatus::Good)
.add_item_with_status("Churn Rate", "2.1%", ScoreStatus::Warning)
.add_item("Active Customers", "842")
.add_item("NPS Score", "72"),
)
// Score Cards in Grid
.add_component(Section::new("Department Scores").with_level(1))
.add_component(
Grid::new(3)
.add_item(
ScoreCard::new("Customer Satisfaction", 89)
.with_description("CSAT survey results")
.as_item(),
)
.add_item(
ScoreCard::new("Employee Engagement", 82)
.with_description("Quarterly pulse survey")
.as_item(),
)
.add_item(
ScoreCard::new("Operational Efficiency", 91)
.with_description("Process automation index")
.as_item(),
),
)
// Chart
.add_component(Section::new("Revenue Trend").with_level(1))
.add_component(
Chart::bar("Monthly Revenue ($K)")
.add_series("2024", vec![
("Jan".into(), 380.0), ("Feb".into(), 395.0), ("Mar".into(), 420.0),
])
.add_series("2025", vec![
("Jan".into(), 410.0), ("Feb".into(), 435.0), ("Mar".into(), 465.0),
])
.with_labels("Month", "Revenue ($K)"),
)
.add_component(
Chart::pie("Revenue by Segment").add_series("Revenue", vec![
("Enterprise".into(), 52.0),
("Mid-Market".into(), 28.0),
("SMB".into(), 15.0),
("Consulting".into(), 5.0),
]),
)
// KPIs
.add_component(Section::new("Key Performance Indicators").with_level(1))
.add_component(
Grid::new(3)
.add_item(Gauge::new("Target Achievement", 87.0).with_range(0.0, 100.0).as_item())
.add_item(Gauge::new("Budget Utilization", 78.0).with_range(0.0, 100.0).as_item())
.add_item(Gauge::new("Pipeline Health", 92.0).with_range(0.0, 100.0).as_item()),
)
// Findings
.add_component(Section::new("Highlights & Risks").with_level(1))
.add_component(
Finding::new("APAC Market Expansion Ahead of Schedule", Severity::Info,
"The APAC team closed 3 enterprise deals in Q1, exceeding the annual target by 40%. Revenue from the region grew 62% YoY.")
.with_category("Sales"),
)
.add_component(
Finding::new("Rising Customer Acquisition Costs", Severity::Medium,
"CAC increased 18% QoQ due to competitive pressure in paid channels. Current CAC: $342 (target: $290).")
.with_recommendation("Shift 20% of paid budget to organic content marketing. Invest in referral program expansion.")
.with_category("Marketing"),
)
.add_component(
Finding::new("Infrastructure Cost Optimization Needed", Severity::High,
"Cloud spending exceeded budget by 22% ($48K overage). Primary driver: unoptimized staging environments running 24/7.")
.with_recommendation("Implement auto-scaling for staging environments. Schedule non-production resources to shut down outside business hours.")
.with_category("Operations"),
)
// Progress
.add_component(Section::new("OKR Progress").with_level(1))
.add_component(ProgressBar::new("Revenue Target ($1.4M)", 86.0).with_color("#006633"))
.add_component(ProgressBar::new("New Customers (50)", 70.0).with_color("#336699"))
.add_component(ProgressBar::new("Product Launch", 95.0).with_color("#006633"))
.add_component(ProgressBar::new("Hiring Plan (12 roles)", 58.0).with_color("#cc6600"))
// Metadata
.add_component(Divider::new())
.add_component(
KeyValueList::new()
.with_title("Report Details")
.add("Prepared by", "Finance Department")
.add("Review Date", "April 5, 2025")
.add("Distribution", "Executive Team, Board of Directors")
.add_highlighted("Next Review", "July 2025"),
)
.add_component(
Callout::info("All financial figures are preliminary and subject to final audit review. Corporate theme uses Helvetica and a dark blue color palette.")
.with_title("Disclaimer"),
)
.build();
let pdf = engine.render_pdf(&report)?;
std::fs::write("examples/output/corporate_report.pdf", &pdf)?;
println!("✓ Created corporate_report.pdf ({} KB)", pdf.len() / 1024);
// ── Dark Theme ──────────────────────────────────────────────
let mut dark_theme = Theme::new("dark", "Dark Mode");
dark_theme
.tokens
.set("color.background", TokenValue::Color("#1a1a2e".into()));
dark_theme
.tokens
.set("color.surface", TokenValue::Color("#16213e".into()));
dark_theme
.tokens
.set("color.surface-soft", TokenValue::Color("#1a2744".into()));
dark_theme
.tokens
.set("color.text", TokenValue::Color("#eaeaea".into()));
dark_theme
.tokens
.set("color.text-muted", TokenValue::Color("#a0a0a0".into()));
dark_theme
.tokens
.set("color.border", TokenValue::Color("#0f3460".into()));
dark_theme
.tokens
.set("color.primary", TokenValue::Color("#e94560".into()));
dark_theme
.tokens
.set("color.ok", TokenValue::Color("#06d6a0".into()));
dark_theme
.tokens
.set("color.ok-soft", TokenValue::Color("#0a2e23".into()));
dark_theme
.tokens
.set("color.warn", TokenValue::Color("#ffd166".into()));
dark_theme
.tokens
.set("color.warn-soft", TokenValue::Color("#2e2510".into()));
dark_theme
.tokens
.set("color.bad", TokenValue::Color("#ef476f".into()));
dark_theme
.tokens
.set("color.bad-soft", TokenValue::Color("#2e101a".into()));
dark_theme
.tokens
.set("color.info", TokenValue::Color("#4ea8de".into()));
dark_theme
.tokens
.set("color.info-soft", TokenValue::Color("#0f2540".into()));
dark_theme
.tokens
.set("color.accent-soft", TokenValue::Color("#1a1a3e".into()));
// Table colors for dark theme
dark_theme
.tokens
.set("table.header-bg", TokenValue::Color("#0f2040".into()));
dark_theme
.tokens
.set("table.row-alt-bg", TokenValue::Color("#121d33".into()));
dark_theme
.tokens
.set("table.border", TokenValue::Color("#1a3055".into()));
let dark_report = engine
.report("default")
.title("System Monitoring Dashboard")
.subtitle("Infrastructure Health Report — Dark Theme")
.metadata("author", "DevOps Team")
.theme(dark_theme)
// Status Overview
.add_component(Section::new("System Status").with_level(1))
.add_component(
SummaryBox::new("Infrastructure Overview")
.add_item_with_status("API Gateway", "Healthy", ScoreStatus::Good)
.add_item_with_status("Database Cluster", "Degraded", ScoreStatus::Warning)
.add_item_with_status("Cache Layer", "Healthy", ScoreStatus::Good)
.add_item_with_status("Queue Service", "Healthy", ScoreStatus::Good)
.add_item("Uptime (30d)", "99.94%"),
)
.add_component(
Grid::new(3)
.add_item(
ScoreCard::new("API Health", 98)
.with_description("Avg response <120ms")
.as_item(),
)
.add_item(
ScoreCard::new("Database", 72)
.with_description("Replication lag detected")
.with_thresholds(90, 60)
.as_item(),
)
.add_item(
ScoreCard::new("CDN Hit Rate", 95)
.with_description("Global edge caching")
.as_item(),
),
)
// Performance Charts
.add_component(Section::new("Performance Metrics").with_level(1))
.add_component(
Chart::line("Request Latency (ms)")
.add_series("P50", vec![
("00:00".into(), 45.0), ("04:00".into(), 38.0), ("08:00".into(), 62.0),
("12:00".into(), 85.0), ("16:00".into(), 78.0), ("20:00".into(), 52.0),
])
.add_series("P99", vec![
("00:00".into(), 120.0), ("04:00".into(), 95.0), ("08:00".into(), 180.0),
("12:00".into(), 245.0), ("16:00".into(), 210.0), ("20:00".into(), 145.0),
])
.with_labels("Time", "Latency (ms)"),
)
.add_component(
Chart::bar("Requests per Service (K)")
.add_series("Requests", vec![
("Auth".into(), 245.0), ("Users".into(), 180.0), ("Orders".into(), 320.0),
("Search".into(), 410.0), ("Media".into(), 95.0),
])
.with_labels("Service", "Requests (K)"),
)
// Gauges
.add_component(Section::new("Resource Utilization").with_level(1))
.add_component(
Grid::new(3)
.add_item(Gauge::new("CPU Usage", 62.0).with_range(0.0, 100.0).as_item())
.add_item(Gauge::new("Memory", 78.0).with_range(0.0, 100.0).as_item())
.add_item(Gauge::new("Disk I/O", 45.0).with_range(0.0, 100.0).as_item()),
)
// Incidents
.add_component(Section::new("Active Incidents").with_level(1))
.add_component(
Finding::new("Database Replication Lag", Severity::High,
"Primary-replica lag increased to 3.2 seconds (threshold: 1s). Read queries on replicas may return stale data. Started 2 hours ago.")
.with_recommendation("Investigate slow queries on primary. Consider temporarily routing read traffic to primary until lag resolves.")
.with_category("Database"),
)
.add_component(
Finding::new("Elevated Error Rate on Search Service", Severity::Medium,
"Error rate at 2.1% (baseline: 0.3%). Correlated with new deployment v2.4.1 rolled out at 14:30 UTC.")
.with_recommendation("Prepare rollback to v2.4.0 if error rate exceeds 5%. Check Elasticsearch cluster health.")
.with_category("Search"),
)
.add_component(
Finding::new("SSL Certificate Renewal", Severity::Info,
"Certificate for api.example.com expires in 14 days. Auto-renewal is configured via Let's Encrypt.")
.with_category("Security"),
)
// Deployment Table
.add_component(Section::new("Recent Deployments").with_level(1))
.add_component(
AuditTable::new(vec![
TableColumn::new("Service").with_width("20%"),
TableColumn::new("Version").with_width("15%"),
TableColumn::new("Deployed").with_width("20%"),
TableColumn::new("Status").with_width("15%"),
TableColumn::new("Notes").with_width("30%"),
])
.with_title("Last 24 Hours")
.add_row(vec!["Search", "v2.4.1", "Today 14:30", "Monitoring", "New ranking algorithm"])
.add_row(vec!["Auth", "v3.1.0", "Today 09:15", "Stable", "OAuth2 PKCE support"])
.add_row(vec!["Orders", "v5.8.2", "Yesterday", "Stable", "Hotfix: tax calculation"])
.add_row(vec!["Media", "v1.12.0", "Yesterday", "Stable", "WebP auto-conversion"])
.add_row(vec!["Gateway", "v2.0.3", "2 days ago", "Stable", "Rate limit tuning"]),
)
.add_component(
Callout::warning("Database replication lag is being actively monitored. On-call engineer has been notified. Dark theme demonstrates custom color tokens.")
.with_title("Ops Status"),
)
.build();
let dark_pdf = engine.render_pdf(&dark_report)?;
std::fs::write("examples/output/dark_report.pdf", &dark_pdf)?;
println!("✓ Created dark_report.pdf ({} KB)", dark_pdf.len() / 1024);
Ok(())
}Open dark-report.pdf · Download dark-report.pdf (69 KiB)
Word search puzzle
examples/wordsearch_demo.rsDetail: Word search puzzle →//! WordSearch Component Example
//!
//! Demonstrates parameter-driven word search puzzle generation and solution rendering.
//!
//! Run with: cargo run --example wordsearch_demo
use renderreport::components::WordSearch;
use renderreport::prelude::*;
fn main() -> renderreport::Result<()> {
let engine = Engine::new()?;
// 1. Generate Puzzle Page (for players to solve)
let puzzle = WordSearch::builder()
.title("Rätselspaß: Software & Open Source")
.description("Finde alle 10 versteckten Begriffe im Buchstabengitter (waagerecht, senkrecht & diagonal)!")
.words(vec![
"RUST",
"TYPST",
"RENDER",
"REPORT",
"ENGINE",
"CARGO",
"STENCIL",
"LAYOUT",
"PARSER",
"VECTOR",
])
.grid_size(14, 14)
.allow_diagonal(true)
.allow_reverse(false)
.seed(42)
.show_solution(false)
.columns_word_list(3)
.build()?;
let report_puzzle = engine
.report("default")
.title("WordSearch Rätselseite")
.subtitle("Generiert mit renderreport")
.add_component(puzzle)
.build();
std::fs::create_dir_all("examples/output")?;
let pdf_puzzle = engine.render_pdf(&report_puzzle)?;
let path_puzzle = "examples/output/wordsearch_puzzle.pdf";
std::fs::write(path_puzzle, pdf_puzzle)?;
println!("✓ Generated {}", path_puzzle);
// 2. Generate Solution Page (with highlighted words)
let solution = WordSearch::builder()
.title("Lösungsblatt: Software & Open Source")
.description("Alle versteckten Wörter sind farbig hervorgehoben.")
.words(vec![
"RUST", "TYPST", "RENDER", "REPORT", "ENGINE", "CARGO", "STENCIL", "LAYOUT", "PARSER",
"VECTOR",
])
.grid_size(14, 14)
.allow_diagonal(true)
.allow_reverse(false)
.seed(42)
.show_solution(true)
.columns_word_list(3)
.build()?;
let report_solution = engine
.report("default")
.title("WordSearch Lösungsblatt")
.subtitle("Generiert mit renderreport")
.add_component(solution)
.build();
let pdf_solution = engine.render_pdf(&report_solution)?;
let path_solution = "examples/output/wordsearch_solution.pdf";
std::fs::write(path_solution, pdf_solution)?;
println!("✓ Generated {}", path_solution);
Ok(())
}Open word-search.pdf · Download word-search.pdf (82 KiB)
Component catalog
examples/component_catalog.rsDetail: Component catalog →//! Component Catalog
//!
//! A visual reference of every registered component with realistic example content.
//! This file is the living documentation — a CI test verifies that every component
//! registered in the engine appears here.
//!
//! Run with: cargo run --example component_catalog
//!
//! Stable download URL (latest release):
//! https://github.com/casoon/renderreport/releases/latest/download/component-catalog.pdf
use renderreport::components::advanced::*;
use renderreport::components::barcode::Barcode;
use renderreport::components::charts::{Chart, Gauge, Sparkline};
use renderreport::components::crosstab::{Crosstab, PivotTable};
use renderreport::components::text::{
DateField, Eyebrow, Label, NumberField, ResourceField, TextBlock,
};
use renderreport::components::wordsearch::WordSearch;
use renderreport::prelude::*;
use std::collections::HashMap;
fn main() -> renderreport::Result<()> {
let engine = Engine::new()?;
let report = engine
.report("default")
.title("renderreport — Component Catalog")
.subtitle("Visual reference of all registered components")
.metadata("author", "renderreport")
.metadata("date", env!("CARGO_PKG_VERSION"))
.metadata("footer_prefix", "renderreport")
.metadata("footer_link_url", "https://github.com/casoon/renderreport")
// ── Cover ────────────────────────────────────────────────────────────
// @id: cover-page (not rendered in this catalog, but component exists)
// @id: table-of-contents
.add_component(TableOfContents::new().with_title("Contents").with_depth(2))
// @id: page-break
.add_component(PageBreak::new())
// ── 1. Layout ────────────────────────────────────────────────────────
.add_component(Section::new("1. Layout").with_level(1)) // @id: section
.add_component(Section::new("1.1 Section").with_level(2))
.add_component(TextBlock::new(
"Organises content into titled blocks. Supports levels 1–3.",
)) // @id: textblock
.add_component(Section::new("Level 3").with_level(3))
.add_component(Section::new("1.2 SectionHeaderSplit").with_level(2))
.add_component(TextBlock::new(
"Two-column header: title on the left, body text on the right.",
))
.add_component(
// @id: section-header-split
SectionHeaderSplit::new("Performance Analysis", "Covers load time, Core Web Vitals, and resource budget across all crawled pages.")
.with_eyebrow("Module Report")
.with_level(2),
)
.add_component(Section::new("1.3 Divider").with_level(2))
.add_component(TextBlock::new("Horizontal rule."))
.add_component(Divider::new()) // @id: divider
.add_component(Section::new("1.4 Grid").with_level(2))
.add_component(TextBlock::new(
"Multi-column layout. item_min_height for uniform cell heights.",
))
.add_component(
Grid::new(3) // @id: grid-component
.with_item_min_height("80pt")
.add_item(MetricCard::new("Columns", "3").as_item()) // @id: metric-card
.add_item(MetricCard::new("Min-Height", "80pt").as_item()) // @id: metric-card
.add_item(MetricCard::new("Gap", "16pt").as_item()), // @id: metric-card
)
.add_component(Section::new("1.5 FlowGroup").with_level(2))
.add_component(TextBlock::new(
"Soft keep-together wrapper: stays on one page if it fits within keep_together_if_under.",
))
.add_component(
FlowGroup::new() // @id: flow-group
.with_spacing("8pt")
.with_keep_together_if_under("200pt")
.add_item(ScoreCard::new("Group Item A", 88).as_item()) // @id: score-card
.add_item(ScoreCard::new("Group Item B", 74).as_item()), // @id: score-card
)
.add_component(Section::new("1.6 Watermark").with_level(2))
.add_component(TextBlock::new(
"Diagonal background text. Built-in presets: draft(), confidential(). \
Placed as overlay on the current page.",
))
.add_component({ // @id: watermark
let mut w = Watermark::new("DRAFT");
w.opacity = 0.15;
w.size = "72pt".into();
w
})
// ── 2. Score & Metrics ───────────────────────────────────────────────
.add_component(Section::new("2. Score & Metrics").with_level(1))
.add_component(Section::new("2.1 ScoreCard").with_level(2))
.add_component(TextBlock::new(
"Score 0–100 with automatic Good/Warn/Bad status. Optional fixed height.",
))
.add_component(
Grid::new(3).with_item_min_height("110pt")
.add_item(ScoreCard::new("Accessibility", 94).with_description("WCAG 2.1 AA").as_item()) // @id: score-card
.add_item(ScoreCard::new("Performance", 67).with_description("Lighthouse").as_item()) // @id: score-card
.add_item(ScoreCard::new("Security", 41).with_description("OWASP Top 10").as_item()), // @id: score-card
)
.add_component(Section::new("2.2 MetricCard").with_level(2))
.add_component(TextBlock::new(
"Key/value highlight with optional accent colour and fixed height.",
))
.add_component(
Grid::new(4).with_item_min_height("90pt")
.add_item(MetricCard::new("Issues", "142").with_subtitle("total").as_item()) // @id: metric-card
.add_item(MetricCard::new("Critical", "8").with_accent_color("#e53e3e").as_item()) // @id: metric-card
.add_item(MetricCard::new("Pages", "24").with_subtitle("crawled").as_item()) // @id: metric-card
.add_item(MetricCard::new("Score", "79/100").with_accent_color("#38a169").as_item()), // @id: metric-card
)
.add_component(Section::new("2.3 Gauge").with_level(2))
.add_component(
Grid::new(3)
.add_item(Gauge::new("Overall", 79.0).as_item()) // @id: gauge
.add_item(Gauge::new("Accessibility", 94.0).as_item()) // @id: gauge
.add_item(Gauge::new("Performance", 52.0).as_item()), // @id: gauge
)
.add_component(Section::new("2.4 ProgressBar").with_level(2))
.add_component(ProgressBar::new("Accessibility", 94.0)) // @id: progress-bar
.add_component(ProgressBar::new("Performance", 67.0))
.add_component(ProgressBar::new("Security", 41.0))
.add_component(Section::new("2.5 ScoreBand").with_level(2))
.add_component(ScoreBand::new(82).with_label("Overall Score")) // @id: score-band
.add_component(Section::new("2.6 Stat").with_level(2))
.add_component(TextBlock::new("Single stat with optional unit, trend and accent."))
.add_component(
Grid::new(3)
.add_item(Stat::new("Total Issues", "142").with_unit("findings").as_item()) // @id: stat
.add_item(Stat::new("Score Delta", "+21").with_trend("up", true).as_item()) // @id: stat
.add_item(Stat::new("Revenue", "€2.4M").with_accent("#22c55e").as_item()), // @id: stat
)
.add_component(Section::new("2.7 StatPair").with_level(2))
.add_component(TextBlock::new("Two stats side by side."))
.add_component(
Grid::new(2)
.add_item(
StatPair::new(
StatPairEntry::new("Before", "58"),
StatPairEntry::new("After", "79"),
)
.as_item(), // @id: stat-pair
)
.add_item(
StatPair::new(
StatPairEntry::new("Score", "79").with_unit("/100"),
StatPairEntry::new("Delta", "+21").with_accent("#22c55e"),
)
.as_item(), // @id: stat-pair
),
)
.add_component(Section::new("2.8 MetricStrip").with_level(2))
.add_component(TextBlock::new(
"Compact horizontal strip of key metrics.",
))
.add_component(MetricStrip::new(vec![ // @id: metric-strip
MetricStripItem::new("Score", "79").with_unit("/100"),
MetricStripItem::new("Issues", "142").with_status("bad"),
MetricStripItem::new("Critical", "8").with_accent("#e53e3e"),
MetricStripItem::new("Pages", "24").with_unit("crawled"),
]))
.add_component(Section::new("2.9 TrendTile").with_level(2))
.add_component(
Grid::new(3)
.add_item(TrendTile::up("Accessibility", "+7").with_reference("was 87").as_item()) // @id: trend-tile
.add_item(TrendTile::down("Performance", "-4").with_reference("was 71").as_item()) // @id: trend-tile
.add_item(TrendTile::stable("SEO", "0").as_item()), // @id: trend-tile
)
.add_component(Section::new("2.10 ModuleComparison").with_level(2))
.add_component(ModuleComparison::new(vec![ // @id: module-comparison
ComparisonModule::new("Accessibility", 94),
ComparisonModule::new("Performance", 67),
ComparisonModule::new("SEO", 88),
ComparisonModule::new("Security", 41),
]))
.add_component(Section::new("2.11 PortfolioSummary").with_level(2))
.add_component(TextBlock::new(
"Portfolio-level overview card for batch benchmark reports.",
))
.add_component( // @id: portfolio-summary
PortfolioSummary::new(8, 74)
.with_best("shop.example.com", 93)
.with_worst("legacy.example.com", 41)
.with_issues(89, 12),
)
.add_component(PageBreak::new())
// ── 3. Narrative & Hero ──────────────────────────────────────────────
.add_component(Section::new("3. Narrative & Hero").with_level(1))
.add_component(Section::new("3.1 HeroSummary").with_level(2))
.add_component(TextBlock::new(
"Executive summary block with score, grade, verdict and key metrics.",
))
.add_component( // @id: hero-summary
HeroSummary::new(79, "B", "example.com")
.with_date("2026-04-02")
.with_verdict(
"The site performs well in accessibility but needs work on performance \
and security to reach the next tier.",
)
.add_metric(HeroMetric {
title: "Issues".into(),
value: "142".into(),
accent_color: Some("#e53e3e".into()),
})
.add_metric(HeroMetric {
title: "Critical".into(),
value: "8".into(),
accent_color: None,
})
.with_top_actions(vec![
"Add alt text to 14 images".into(),
"Fix colour contrast on hero section".into(),
])
.with_positive_aspects(vec![
"No keyboard trap violations".into(),
"Valid SSL certificate".into(),
]),
)
.add_component(Section::new("3.2 CardDashboard").with_level(2))
.add_component(TextBlock::new(
"Grid of module score cards with interpretation text.",
))
.add_component(CardDashboard::new(vec![ // @id: card-dashboard
DashboardCard {
name: "Accessibility".into(),
score: 94,
interpretation: "Excellent. Only minor contrast issues remain.".into(),
good_threshold: 80,
warn_threshold: 60,
},
DashboardCard {
name: "Performance".into(),
score: 67,
interpretation: "Below target. LCP and CLS need improvement.".into(),
good_threshold: 80,
warn_threshold: 60,
},
DashboardCard {
name: "Security".into(),
score: 41,
interpretation: "Critical: outdated headers, open redirects.".into(),
good_threshold: 75,
warn_threshold: 50,
},
]))
.add_component(Section::new("3.3 StatusPill").with_level(2))
.add_component(TextBlock::new(
"Compact colour-coded status badge. Built-in presets: good, warn, bad, neutral, info.",
))
.add_component(
Grid::new(5)
.add_item(StatusPill::good("Passed").as_item()) // @id: status-pill
.add_item(StatusPill::warn("Review").as_item()) // @id: status-pill
.add_item(StatusPill::bad("Failed").as_item()) // @id: status-pill
.add_item(StatusPill::info("Info").as_item()) // @id: status-pill
.add_item(StatusPill::neutral("Pending").as_item()), // @id: status-pill
)
.add_component(Section::new("3.4 Eyebrow").with_level(2))
.add_component(TextBlock::new(
"Small uppercase label above a heading, typical in editorial design.",
))
.add_component(Eyebrow::new("Module Report")) // @id: eyebrow
.add_component(Eyebrow::new("Confidential").with_color("#e53e3e"))
.add_component(PageBreak::new())
// ── 4. Charts ────────────────────────────────────────────────────────
.add_component(Section::new("4. Charts").with_level(1))
.add_component(Section::new("4.1 Chart — Bar").with_level(2))
.add_component(TextBlock::new("Types: bar, line, pie, area. Data via add_series(name, Vec<(label, value)>).")) // @id: chart
.add_component(Chart::bar("Issues by Category").add_series(
"Count",
vec![
("Contrast".into(), 34.0),
("Alt Text".into(), 28.0),
("Focus".into(), 19.0),
("Landmarks".into(), 15.0),
("Forms".into(), 12.0),
],
))
.add_component(Section::new("4.2 Chart — Line (multi-series)").with_level(2))
.add_component(
Chart::line("Score Trend")
.add_series(
"Score",
vec![
("Jan".into(), 58.0), ("Feb".into(), 63.0), ("Mar".into(), 71.0),
("Apr".into(), 79.0), ("May".into(), 85.0), ("Jun".into(), 91.0),
],
)
.add_series(
"Target",
vec![
("Jan".into(), 70.0), ("Feb".into(), 72.0), ("Mar".into(), 75.0),
("Apr".into(), 80.0), ("May".into(), 85.0), ("Jun".into(), 90.0),
],
),
)
.add_component(Section::new("4.3 Chart — Pie").with_level(2))
.add_component(Chart::pie("Issues by Severity").add_series(
"Count",
vec![
("Critical".into(), 8.0),
("High".into(), 23.0),
("Medium".into(), 41.0),
("Low".into(), 70.0),
],
))
.add_component(Section::new("4.4 Sparkline").with_level(2))
.add_component(TextBlock::new("Compact inline trend chart. Types: bar, line."))
.add_component({ // @id: sparkline
let mut s = Sparkline::bar(vec![62.0, 65.0, 70.0, 68.0, 74.0, 79.0, 83.0]);
s.width = "200pt".into();
s.height = "40pt".into();
s
})
.add_component(PageBreak::new())
// ── 5. Findings & Callouts ───────────────────────────────────────────
.add_component(Section::new("5. Findings & Callouts").with_level(1))
.add_component(Section::new("5.1 Finding").with_level(2))
.add_component(TextBlock::new(
"Structured audit finding with severity, description and recommendation.",
))
.add_component( // @id: finding
Finding::new(
"Images missing alternative text",
Severity::Critical,
"14 <img> elements have no alt attribute.",
)
.with_recommendation("Add descriptive alt text to every meaningful image.")
.with_category("WCAG 1.1.1"),
)
.add_component(Finding::new(
"Colour contrast ratio below 4.5:1",
Severity::High,
"Hero text uses #888888 on white — contrast ratio 3.5:1.",
))
.add_component(Section::new("5.2 SpotlightCard").with_level(2))
.add_component(TextBlock::new(
"Full-width spotlight block for the single most impactful finding.",
))
.add_component( // @id: spotlight-card
SpotlightCard::new(
"Missing alternative text",
"14 images across 8 pages have no alt attribute, blocking screen reader users \
from perceiving visual content.",
)
.with_variant("critical")
.with_eyebrow("Top Finding")
.with_metric("14")
.with_detail("Affects all screen reader users — estimated 15% of visitors.")
.with_action("Add descriptive alt attributes to all meaningful images"),
)
.add_component(Section::new("5.3 ImpactGrid").with_level(2))
.add_component(TextBlock::new(
"Three-panel impact overview: user, risk, conversion.",
))
.add_component( // @id: impact-grid
ImpactGrid::new(
ImpactGridCard::new("User Impact", "Screen readers blocked", "14 images with no alt text prevent assistive technology users from accessing content.").with_status("bad"),
ImpactGridCard::new("Risk Level", "Compliance risk", "WCAG 2.1 AA non-compliance may trigger legal obligations in the EU and US.").with_status("warn"),
ImpactGridCard::new("Conversion", "Trust signal", "Accessibility issues correlate with higher bounce rates on mobile.").with_status("warn"),
)
.with_title("Issue Impact"),
)
.add_component(Section::new("5.4 ChecklistPanel").with_level(2))
.add_component(TextBlock::new(
"Checklist-style diagnosis rows with status indicators.",
))
.add_component( // @id: checklist-panel
ChecklistPanel::new(vec![
ChecklistRow::new("Alt text", "14 violations found").with_status("bad"),
ChecklistRow::new("Colour contrast", "7 violations found").with_status("warn"),
ChecklistRow::new("Keyboard focus", "All interactive elements reachable").with_status("good"),
ChecklistRow::new("ARIA labels", "No violations detected").with_status("good"),
ChecklistRow::new("Skip links", "Missing on 3 pages").with_status("warn"),
])
.with_title("Accessibility Diagnosis"),
)
.add_component(Section::new("5.5 ComparisonBlock").with_level(2))
.add_component(TextBlock::new(
"Before/after comparison showing incorrect vs correct implementation.",
))
.add_component( // @id: comparison-block
ComparisonBlock::new(
r#"<img src="hero.jpg">"#,
r#"<img src="hero.jpg" alt="Team collaborating in a modern office">"#,
)
.code()
.with_note("Always provide meaningful alt text for informative images."),
)
.add_component(Section::new("5.6 Callout").with_level(2))
.add_component(TextBlock::new("Highlighted note block. Types: info, warning, success, error."))
.add_component(Callout::info("All automated findings should be verified manually.")) // @id: callout
.add_component(Callout::warning("3 critical issues require attention before release."))
.add_component(Callout::success("No WCAG 1.3.1 violations detected."))
.add_component(Callout::error("SSL certificate expires in 7 days."))
.add_component(Section::new("5.7 SummaryBox").with_level(2))
.add_component( // @id: summary-box
SummaryBox::new("Audit Summary")
.add_item("URL", "https://example.com")
.add_item("Crawled Pages", "24")
.add_item_with_status("WCAG Level", "AA", ScoreStatus::Good)
.add_item_with_status("Mobile Friendly", "Issues found", ScoreStatus::Warning),
)
.add_component(Section::new("5.8 SeverityOverview").with_level(2))
.add_component(SeverityOverview::new(8, 23, 41, 70)) // @id: severity-overview
.add_component(PageBreak::new())
// ── 6. Tables ────────────────────────────────────────────────────────
.add_component(Section::new("6. Tables").with_level(1))
.add_component(Section::new("6.1 AuditTable").with_level(2))
.add_component( // @id: audit-table
AuditTable::new(vec![
TableColumn::new("Rule"),
TableColumn::new("Severity"),
TableColumn::new("Occurrences"),
TableColumn::new("WCAG"),
])
.add_row(vec!["Missing alt text", "critical", "14", "1.1.1"])
.add_row(vec!["Low contrast", "high", "7", "1.4.3"])
.add_row(vec!["Missing label", "medium", "3", "1.3.1"])
.add_row(vec!["Skip link missing", "low", "1", "2.4.1"]),
)
.add_component(Section::new("6.2 BenchmarkTable").with_level(2))
.add_component( // @id: benchmark-table
BenchmarkTable::new(vec![
BenchmarkRow::new(1, "example.com", 79, 94, 8),
BenchmarkRow::new(2, "competitor-a.com", 65, 71, 14),
BenchmarkRow::new(3, "competitor-b.com", 88, 91, 2),
BenchmarkRow::new(4, "industry-avg", 72, 78, 9),
])
.with_title("Benchmark Comparison"),
)
.add_component(Section::new("6.3 PivotTable").with_level(2))
.add_component(TextBlock::new("Pre-aggregated matrix of row × column values."))
.add_component( // @id: pivot-table
PivotTable::new(
vec!["Accessibility".into(), "Performance".into(), "SEO".into()],
vec!["Q1".into(), "Q2".into(), "Q3".into(), "Q4".into()],
vec![
vec!["72".into(), "79".into(), "85".into(), "94".into()],
vec!["61".into(), "63".into(), "65".into(), "67".into()],
vec!["80".into(), "82".into(), "85".into(), "88".into()],
],
)
.with_title("Module Scores by Quarter"),
)
.add_component(Section::new("6.4 Crosstab").with_level(2))
.add_component(TextBlock::new(
"Aggregates raw row data across row and column dimensions.",
))
.add_component({ // @id: crosstab
let mut rows: Vec<HashMap<String, serde_json::Value>> = Vec::new();
for (region, module, score) in [
("North", "Accessibility", 92), ("North", "Performance", 71),
("South", "Accessibility", 88), ("South", "Performance", 64),
("East", "Accessibility", 95), ("East", "Performance", 73),
] {
let mut row = HashMap::new();
row.insert("Region".into(), serde_json::json!(region));
row.insert("Module".into(), serde_json::json!(module));
row.insert("Score".into(), serde_json::json!(score));
rows.push(row);
}
Crosstab::new("Region", "Module", "Score")
.with_title("Scores by Region × Module")
.with_aggregation("avg")
.with_data(rows)
})
.add_component(PageBreak::new())
// ── 7. Lists & Structure ─────────────────────────────────────────────
.add_component(Section::new("7. Lists & Structure").with_level(1))
.add_component(Section::new("7.1 List").with_level(2))
.add_component( // @id: list
List::new()
.with_title("Quick Wins")
.add_item("Add alt text to 14 images — 30 min effort")
.add_item("Increase hero text contrast from 3.5:1 to 4.5:1")
.add_item("Associate form labels with input IDs")
.add_item("Add visible skip-to-content link"),
)
.add_component(Section::new("7.2 KeyValueList").with_level(2))
.add_component( // @id: key-value-list
KeyValueList::new()
.add("Framework", "React 18")
.add("Hosting", "Vercel")
.add("CDN", "Cloudflare")
.add("Analytics", "Plausible"),
)
.add_component(Section::new("7.3 RoadmapBlock").with_level(2))
.add_component(RoadmapBlock::new(vec![ // @id: roadmap-block
RoadmapColumn {
title: "Quick Wins".into(),
accent_color: Some("#38a169".into()),
items: vec![
ActionItem {
action: "Add alt text to images".into(),
role: "Developer".into(),
priority: "high".into(),
effort: Some("2h".into()),
benefit: "Fixes 14 critical findings".into(),
},
],
},
RoadmapColumn {
title: "Short-term".into(),
accent_color: Some("#d69e2e".into()),
items: vec![
ActionItem {
action: "Increase colour contrast".into(),
role: "Designer".into(),
priority: "medium".into(),
effort: Some("4h".into()),
benefit: "Fixes 7 high findings".into(),
},
],
},
RoadmapColumn {
title: "Long-term".into(),
accent_color: None,
items: vec![
ActionItem {
action: "Redesign navigation for keyboard users".into(),
role: "Design + Dev".into(),
priority: "low".into(),
effort: Some("2w".into()),
benefit: "Full keyboard accessibility".into(),
},
],
},
]))
.add_component(Section::new("7.4 ComparisonCluster").with_level(2))
.add_component(TextBlock::new("Grid of comparison items with values and status."))
.add_component( // @id: comparison-cluster
ComparisonCluster::new(vec![
ComparisonClusterItem::new("example.com", "79").with_status("warn"),
ComparisonClusterItem::new("competitor-a.com", "65").with_status("bad"),
ComparisonClusterItem::new("competitor-b.com", "88").with_status("good"),
ComparisonClusterItem::new("industry avg", "72").with_sub("120 sites"),
])
.with_title("Score Comparison")
.with_columns(4),
)
.add_component(Section::new("7.5 SideLabel").with_level(2))
.add_component(TextBlock::new(
"Sidebar heading with optional subheading and bullet items.",
))
.add_component( // @id: side-label
SideLabel::new("What we checked")
.with_subheading("Scope of this audit")
.add_item("All public pages (24 crawled)")
.add_item("WCAG 2.1 Level AA criteria")
.add_item("Keyboard and screen reader behaviour")
.with_divider(),
)
.add_component(Section::new("7.6 PhaseBlock").with_level(2))
.add_component(TextBlock::new(
"Numbered phase in a multi-step remediation plan.",
))
.add_component( // @id: phase-block
PhaseBlock::new(1, "Triage", "Fix all critical and high findings.")
.with_items(vec![
"Add alt text to 14 images".into(),
"Fix colour contrast on hero section".into(),
"Associate form labels with inputs".into(),
])
.with_total(3)
.with_color("#e53e3e"),
)
.add_component(Section::new("7.7 DiagnosisPanel").with_level(2))
.add_component(TextBlock::new("Label–diagnosis rows with optional status indicators."))
.add_component( // @id: diagnosis-panel
DiagnosisPanel::new(vec![
DiagnosisRow::new("Meta Descriptions", "12 pages missing").with_status("bad"),
DiagnosisRow::new("Alt Text", "All images covered").with_status("good"),
DiagnosisRow::new("HTTPS", "Valid TLS 1.3").with_status("good"),
DiagnosisRow::new("Core Web Vitals", "LCP needs improvement").with_status("warn"),
])
.with_title("Technical Health Check"),
)
.add_component(Section::new("7.8 DominantIssueSpotlight").with_level(2))
.add_component(TextBlock::new("Full-width spotlight for a single dominant issue."))
.add_component( // @id: dominant-issue-spotlight
DominantIssueSpotlight::new(
"Missing Meta Descriptions",
"high",
"12 of 24 pages have no meta description, reducing click-through rates from search results by up to 30%.",
"Users see generic snippets in search results, leading to lower engagement.",
"Add unique, compelling meta descriptions (150–160 characters) to all pages.",
)
.with_eyebrow("Dominant Issue")
.with_affected_count(12),
)
.add_component(Section::new("7.9 WrongRightBlock").with_level(2))
.add_component(TextBlock::new("Before/after comparison — wrong vs. right."))
.add_component( // @id: wrong-right-block
WrongRightBlock::new(
"<img src=\"photo.jpg\">",
"<img src=\"photo.jpg\" alt=\"Team meeting in conference room\">",
)
.code()
.with_labels("Incorrect", "Correct")
.with_note("Always provide descriptive alt text for informational images."),
)
.add_component(PageBreak::new())
// ── 8. Text & Labels ─────────────────────────────────────────────────
.add_component(Section::new("8. Text & Labels").with_level(1))
.add_component(Section::new("8.1 Label").with_level(2))
.add_component(Label::new("Default label")) // @id: label
.add_component(Label::new("Bold label").bold())
.add_component(Label::new("Centred label").center())
.add_component(Label::new("Coloured label").with_color("#3182ce"))
.add_component(Section::new("8.2 TextBlock").with_level(2))
.add_component(TextBlock::new(
"renderreport is a Rust library for building data-driven PDF reports using \
Typst as the embedded render engine. Components are declared in Rust, serialised \
to JSON, and rendered via Typst templates.",
))
.add_component(Section::new("8.3 StatusPill (inline)").with_level(2))
.add_component(TextBlock::new("Already shown in section 3.3 — referenced for completeness."))
// ── 9. Numbers, Dates & Barcodes ─────────────────────────────────────
.add_component(PageBreak::new())
.add_component(Section::new("9. Numbers, Dates & Barcodes").with_level(1))
.add_component(Section::new("9.1 NumberField").with_level(2))
.add_component(TextBlock::new(
"Formatted number with optional prefix, suffix and locale.",
))
.add_component(
Grid::new(4)
.add_item(NumberField::new(42000.0).as_item()) // @id: number-field
.add_item(NumberField::currency(2400.0, "€").as_item()) // @id: number-field
.add_item(NumberField::percentage(79.0).as_item()) // @id: number-field
.add_item(NumberField::new(1234.56).with_prefix("Score: ").as_item()), // @id: number-field
)
.add_component(Section::new("9.2 DateField").with_level(2))
.add_component(
Grid::new(3)
.add_item(DateField::new("2026-04-02").as_item()) // @id: date-field
.add_item(DateField::european("2026-04-02").as_item()) // @id: date-field
.add_item(DateField::us("2026-04-02").as_item()), // @id: date-field
)
.add_component(Section::new("9.3 ResourceField").with_level(2))
.add_component(TextBlock::new("i18n resource lookup by key with locale."))
.add_component( // @id: resource-field
ResourceField::new("report.title")
.with_default("Audit Report")
.with_locale("de"),
)
.add_component(Section::new("9.4 Barcode — 1D").with_level(2))
.add_component(TextBlock::new("Code128, EAN-13, QR, Data Matrix and more."))
.add_component(
Grid::new(2)
.add_item(Barcode::code128("PROD-2026-XYZ-001").as_item()) // @id: barcode
.add_item(Barcode::ean13("5901234123457").as_item()), // @id: barcode
)
.add_component(Section::new("9.5 Barcode — 2D").with_level(2))
.add_component(
Grid::new(2)
.add_item(
Barcode::qr_code("https://github.com/casoon/renderreport")
.with_size("80pt", "80pt")
.as_item(), // @id: barcode
)
.add_item(
Barcode::data_matrix("ASSET-2026-001")
.with_size("80pt", "80pt")
.as_item(), // @id: barcode
),
)
// ── 10. Image ────────────────────────────────────────────────────────
.add_component(Section::new("10. Image").with_level(1))
.add_component(TextBlock::new(
"Embeds an image from the assets map. Centred by default. \
Supports caption and width.",
))
.asset("placeholder.svg", "examples/assets/placeholder.svg")
.add_component( // @id: image
Image::new("placeholder.svg")
.with_caption("Example image with caption")
.with_width("60%"),
)
.add_component(PageBreak::new())
// ── 11. Marketing & Narrative ─────────────────────────────────────────
.add_component(Section::new("11. Marketing & Narrative").with_level(1))
.add_component(Section::new("11.1 ProductHero").with_level(2))
.add_component(TextBlock::new("Full-page product/project introduction with highlights and CTA."))
.add_component( // @id: product-hero
ProductHero::new("renderreport", "Type-safe PDF reports from Rust")
.with_tagline("Declare components as Rust structs. Render to PDF in milliseconds.")
.with_highlights(vec![
"Full type safety — catch errors at compile time".into(),
"Theme system — customize every visual token".into(),
"Component-driven — build complex layouts from reusable blocks".into(),
"Fast rendering — 20-page PDFs in under 500ms".into(),
])
.with_cta("View on GitHub", "https://github.com/casoon/renderreport"),
)
.add_component(Section::new("11.2 FeatureGrid").with_level(2))
.add_component(TextBlock::new("Marketing feature/benefit grid with optional icons."))
.add_component( // @id: feature-grid
FeatureGrid::new(vec![
FeatureGridItem { title: "Fast Rendering".into(), description: Some("PDF output in under a second.".into()), icon: Some("⚡".into()), status: Some("highlight".into()) },
FeatureGridItem { title: "Type-safe".into(), description: Some("All components are Rust structs.".into()), icon: Some("🦀".into()), status: Some("positive".into()) },
FeatureGridItem { title: "Themeable".into(), description: Some("Custom tokens per report.".into()), icon: Some("🎨".into()), status: None },
FeatureGridItem { title: "Extensible".into(), description: Some("Add custom Typst templates.".into()), icon: Some("🔧".into()), status: None },
])
.with_columns(2)
.with_title("Why renderreport?"),
)
.add_component(Section::new("11.3 CTABox").with_level(2))
.add_component(TextBlock::new("Call-to-action block. Tones: primary, urgent, neutral."))
.add_component( // @id: cta-box
CTABox::new("Start your first report today")
.with_body("renderreport is open source and ready to embed in your pipeline.")
.with_action("View on GitHub", "https://github.com/casoon/renderreport")
.with_tone("primary"),
)
.add_component(Section::new("11.4 Testimonial").with_level(2))
.add_component( // @id: testimonial
Testimonial::new(
"renderreport saved us hours every sprint — our PDF reports now build in CI automatically.",
"Engineering Lead",
)
.with_company("Example Corp"),
)
.add_component(Section::new("11.5 ProcessFlow").with_level(2))
.add_component(TextBlock::new("Linear process with numbered steps. Direction: horizontal or vertical."))
.add_component( // @id: process-flow
ProcessFlow::new(vec![
ProcessStep { label: "Collect Data".into(), description: Some("Pull metrics from APIs.".into()), icon: None },
ProcessStep { label: "Run renderreport".into(), description: Some("Render Rust structs to PDF.".into()), icon: None },
ProcessStep { label: "Deliver PDF".into(), description: Some("Email or S3 upload.".into()), icon: None },
])
.with_title("Report Pipeline"),
)
.add_component(Section::new("11.6 Timeline").with_level(2))
.add_component( // @id: timeline
Timeline::new(vec![
TimelineItem { date: "Q1 2026".into(), title: "v0.1 alpha".into(), description: Some("Core engine + standard components.".into()), status: Some("good".into()) },
TimelineItem { date: "Q2 2026".into(), title: "v0.2 beta".into(), description: Some("Pack system + theming.".into()), status: Some("warn".into()) },
TimelineItem { date: "Q3 2026".into(), title: "v1.0 stable".into(), description: None, status: None },
])
.with_title("Release Timeline"),
)
.add_component(Section::new("11.7 Funnel").with_level(2))
.add_component( // @id: funnel
Funnel::new(vec![
FunnelStep { label: "Visitors".into(), value: "10,000".into(), unit: Some("sessions".into()), color: None },
FunnelStep { label: "Signups".into(), value: "1,200".into(), unit: Some("users".into()), color: None },
FunnelStep { label: "Activated".into(), value: "430".into(), unit: Some("users".into()), color: None },
FunnelStep { label: "Paying".into(), value: "88".into(), unit: Some("users".into()), color: None },
])
.with_title("Conversion Funnel"),
)
.add_component(Section::new("11.8 ProblemSolution").with_level(2))
.add_component( // @id: problem-solution
ProblemSolution::new(
"Generating PDFs manually is slow, error-prone, and hard to maintain.",
"renderreport lets you declare report components as Rust structs and render to PDF automatically.",
)
.with_labels("The Problem", "The Solution"),
)
.add_component(Section::new("11.9 BeforeAfter").with_level(2))
.add_component( // @id: before-after
BeforeAfter::new(
"Hand-written PDF layout in LaTeX — 300 lines of fragile code.",
"renderreport: 30 lines of Rust, fully type-safe, CI-friendly.",
)
.with_labels("Before", "After"),
)
.add_component(Section::new("11.10 WhyItMatters").with_level(2))
.add_component( // @id: why-it-matters
WhyItMatters::new(
"Automated, reproducible reports eliminate human error and free engineers \
to focus on insights rather than formatting.",
)
.with_title("Why automation matters"),
)
.add_component(Section::new("11.11 FactBox").with_level(2))
.add_component( // @id: fact-box
FactBox::new("renderreport renders a 20-page PDF in under 500 ms on a standard CI runner.")
.with_label("Performance fact"),
)
.add_component(Section::new("11.12 QuoteBlock").with_level(2))
.add_component( // @id: quote-block
QuoteBlock::new(
"Good tools shape good reports. renderreport gives your data the presentation it deserves.",
)
.with_author("renderreport team"),
)
// ── 12. Phase 2 — Advanced Extensions ─────────────────────────────────
.add_component(Section::new("12. Phase 2 — Advanced Extensions").with_level(1))
.add_component(Section::new("12.1 BenefitStrip").with_level(2))
.add_component(TextBlock::new("Horizontal benefit/feature strip for marketing intro and value prop."))
.add_component( // @id: benefit-strip
BenefitStrip::new()
.add_benefit_with_icon("⚡", "Fast", "Sub-second PDF rendering")
.add_benefit_with_icon("🔒", "Safe", "Type-safe Rust API")
.add_benefit_with_icon("🎨", "Themeable", "Customize all tokens")
.with_columns(3),
)
.add_component(Section::new("12.2 PricingCard").with_level(2))
.add_component(TextBlock::new("Single pricing/plan card with features and highlight option."))
.add_component( // @id: pricing-card
PricingCard::new("Professional", "$299")
.with_billing_period("/month")
.with_description("For growing teams")
.add_feature("Up to 50 reports/month")
.add_feature("Custom themes")
.add_feature("Priority support")
.with_cta("Start Free Trial"),
)
.add_component(PricingCard::new("Enterprise", "Custom")
.with_description("For high-volume needs")
.add_feature("Unlimited reports")
.add_feature("Dedicated SLA")
.add_feature("On-premise deployment")
.with_cta("Contact Sales")
.highlighted(),
)
.add_component(Section::new("12.3 RecommendationCard").with_level(2))
.add_component(TextBlock::new("Lightweight recommendation: title + description + impact/effort/priority badges."))
.add_component( // @id: recommendation-card
RecommendationCard::new(
"Enable HTTPS for all endpoints",
"Encrypt traffic to prevent man-in-the-middle attacks.",
)
.with_impact("high")
.with_effort("low")
.with_priority("critical"),
)
.add_component(Section::new("12.4 StepCardRow").with_level(2))
.add_component(TextBlock::new("Numbered steps displayed horizontally for process overview."))
.add_component( // @id: step-card-row
StepCardRow::new()
.add_step("Collect Data", "Pull metrics from your systems")
.add_step("Analyze", "Identify patterns and insights")
.add_step("Report", "Generate and distribute PDF")
.with_columns(3),
)
.add_component(Section::new("12.5 Columns").with_level(2))
.add_component(TextBlock::new("Asymmetric two-column layout with flexible width ratio."))
.add_component( // @id: columns
Columns::new(
"Your image or component would go here on the left (60% width).",
"Right column (40% width) for text, description, or complementary content.",
)
.with_ratio(0.6),
)
.add_component(Section::new("12.6 FaqList").with_level(2))
.add_component(TextBlock::new("Question-answer pairs for FAQs and knowledge bases."))
.add_component( // @id: faq-list
FaqList::new()
.with_title("Frequently Asked Questions")
.add_item("How does renderreport work?", "It takes Rust structs as input, serializes them to JSON, and renders them via an embedded Typst engine.")
.add_item("Can I customize the styling?", "Yes, the theme system lets you override every visual token globally.")
.add_item("What output formats are supported?", "Currently PDF via Typst. HTML and other formats are planned for future releases."),
)
.add_component(Section::new("12.7 UseCaseCard").with_level(2))
.add_component(TextBlock::new("Single use case with context, problem, solution, and optional outcome."))
.add_component( // @id: use-case-card
UseCaseCard::new(
"Enterprise Audit Automation",
"Large organizations with 50+ websites",
"Manual PDF audits take weeks and require coordination across teams.",
"renderreport generates consistent, type-safe audit reports in CI—eliminating manual steps and reducing turnaround to hours.",
)
.with_outcome("Reduced audit time from 2 weeks to 2 hours per run."),
)
.add_component(Section::new("12.8 LogoStrip").with_level(2))
.add_component(TextBlock::new("Display logos of customers, partners, or certifications."))
.add_component( // @id: logo-strip
LogoStrip::new()
.with_title("Trusted by")
.add_logo("TechCorp")
.add_logo("InnovateLabs")
.add_logo("CloudSystems")
.add_logo("DataFlow Inc")
.with_columns(4),
)
.add_component(Section::new("12.9 PullQuote").with_level(2))
.add_component(TextBlock::new("Large, visually prominent full-width quote."))
.add_component( // @id: pull-quote
PullQuote::new(
"renderreport transformed our reporting pipeline from a weeks-long manual process into a fully automated CI/CD workflow.",
)
.with_attribution("Engineering Director, FinTech Startup"),
)
.add_component(Section::new("12.10 BigNumber").with_level(2))
.add_component(TextBlock::new("Large metric display for impact statistics."))
.add_component( // @id: big-number
BigNumber::new("10x", "Faster report generation")
.with_context("From 2 weeks to 2 days"),
)
.add_component(Section::new("12.11 GlossaryList").with_level(2))
.add_component(TextBlock::new("Term-definition pairs for reference and terminology."))
.add_component( // @id: glossary-list
GlossaryList::new()
.with_title("Glossary of Terms")
.add_item("Component", "A reusable, type-safe building block for reports.")
.add_item("Theme", "A set of design tokens controlling visual appearance.")
.add_item("Typst", "A markup-based typesetting language used for rendering."),
)
.add_component(Section::new("12.12 TagCloud").with_level(2))
.add_component(TextBlock::new("Coloured tag badges for status, labels, or keyword sets."))
.add_component( // @id: tag-cloud
TagCloud::new()
.with_title("WCAG Coverage")
.add("WCAG 1.1.1", "pass")
.add("WCAG 1.3.1", "pass")
.add("WCAG 2.4.3", "warn")
.add("WCAG 4.1.2", "fail"),
)
.add_component(Section::new("12.13 DevicePreview").with_level(2))
.add_component(TextBlock::new("Side-by-side desktop / mobile screenshot comparison."))
.add_component( // @id: device-preview
DevicePreview::new("examples/assets/desktop.png", "examples/assets/mobile.png"),
)
.add_component(Section::new("12.14 WordSearch").with_level(2))
.add_component(TextBlock::new("Parameter-driven word search puzzle generator with word list and solution highlighting."))
.add_component( // @id: word-search
WordSearch::builder()
.title("Finde die Begriffe")
.words(vec!["RUST", "TYPST", "REPORT"])
.grid_size(8, 8)
.seed(42)
.build()?,
)
// ── 13. Report Patterns ──────────────────────────────────────────────────
.add_component(PageBreak::new())
.add_component(Section::new("13. Report Patterns").with_level(1))
.add_component(TextBlock::new("Pre-configured component sequences for common report types. Patterns orchestrate components into a logical narrative flow: context → analysis → solution → actions → CTA."))
.add_component(Section::new("13.1 Pattern Architecture").with_level(2))
.add_component(TextBlock::new("Every pattern follows BasePattern: hero (intro), context (why it matters), analysis (findings), solution (approach), actions (next steps), cta (call-to-action). Each pattern specializes this for a specific use case."))
.add_component(Section::new("13.2 AuditPattern").with_level(2))
.add_component(TextBlock::new("For security/compliance audits. Hero: score/grade. Analysis: findings, impact grid, checklist. Solution: roadmap. CTA: schedule review."))
.add_component(Section::new("13.3 MarketingPattern").with_level(2))
.add_component(TextBlock::new("For product/feature showcases. Hero: product intro. Analysis: features, benefits, comparison. Solution: process flow. Actions: testimonials, use cases. CTA: pricing/signup."))
.add_component(Section::new("13.4 ExecutivePattern").with_level(2))
.add_component(TextBlock::new("For C-level summaries. Hero: key metrics. Analysis: top findings, risk assessment. Solution: strategic recommendation. Actions: implementation plan, timeline. CTA: approval/next meeting."))
.build();
let pdf = engine.render_pdf(&report)?;
let out = "examples/output/component_catalog.pdf";
std::fs::create_dir_all("examples/output")?;
std::fs::write(out, &pdf)?;
println!(
"Component catalog written to {} ({} KB)",
out,
pdf.len() / 1024
);
Ok(())
}