use renderreport::components::advanced::TableOfContents;
use renderreport::prelude::*;
fn main() -> renderreport::Result<()> {
println!("Generating SEO Audit report...");
let engine = Engine::new()?;
let report = engine
.report("seo-audit")
.metadata("author", "Casoon")
.add_component(
CoverPage::new("Website Audit Report", "example.com", 78, "C+")
.with_brand("Casoon")
.with_date("March 2026")
.with_subtitle("SEO, Accessibility, Performance & Security Analysis")
.with_modules(vec![
"SEO".into(),
"Accessibility".into(),
"Performance".into(),
"Security".into(),
])
.with_issues(15, 3),
)
.add_component(
TableOfContents::new()
.with_title("Table of Contents")
.with_depth(2),
)
.add_component(
HeroSummary::new(78, "C+", "example.com")
.with_date("March 2026")
.with_verdict("The website shows solid fundamentals but has significant SEO and performance issues that need attention.")
.add_metric(HeroMetric { title: "Critical Issues".into(), value: "3".into(), accent_color: Some("#ef4444".into()) })
.add_metric(HeroMetric { title: "Warnings".into(), value: "7".into(), accent_color: Some("#f59e0b".into()) })
.add_metric(HeroMetric { title: "Passed Checks".into(), value: "45".into(), accent_color: Some("#22c55e".into()) })
.add_metric(HeroMetric { title: "Overall Score".into(), value: "78 / 100".into(), accent_color: Some("#3b82f6".into()) })
.with_top_actions(vec![
"Fix missing meta descriptions on 12 pages".into(),
"Optimize Largest Contentful Paint (currently 3.2s)".into(),
"Add alt text to 8 images".into(),
])
.with_positive_aspects(vec![
"Valid SSL certificate with HSTS enabled".into(),
"Responsive design passes all mobile tests".into(),
"robots.txt and XML sitemap correctly configured".into(),
"Structured data (JSON-LD) present on key pages".into(),
]),
)
.add_component(Section::new("Module Overview").with_level(1))
.add_component(
CardDashboard::new(vec![
DashboardCard { name: "SEO".into(), score: 72, interpretation: "Several meta tags missing, thin content on subpages".into(), good_threshold: 80, warn_threshold: 50 },
DashboardCard { name: "Accessibility".into(), score: 92, interpretation: "Excellent WCAG 2.1 AA compliance".into(), good_threshold: 80, warn_threshold: 50 },
DashboardCard { name: "Performance".into(), score: 68, interpretation: "LCP too slow, render-blocking resources".into(), good_threshold: 80, warn_threshold: 50 },
DashboardCard { name: "Security".into(), score: 85, interpretation: "Strong HTTPS setup, minor header improvements needed".into(), good_threshold: 80, warn_threshold: 50 },
])
.with_title("Module Scores"),
)
.add_component(
SeverityOverview::new(3, 4, 5, 3)
.with_title("Issue Severity Breakdown"),
)
.add_component(Section::new("SEO Analysis").with_level(1))
.add_component(
Finding::new("Missing Meta Descriptions", Severity::High,
"12 pages lack meta descriptions, reducing click-through rates from search results. Pages without meta descriptions see 20-30% lower CTR on average.")
.with_recommendation("Add unique, compelling meta descriptions (150-160 characters) to all pages. Prioritize high-traffic landing pages first.")
.with_affected("example.com/products, example.com/about, example.com/blog/*")
.with_category("On-Page SEO"),
)
.add_component(
Finding::new("Duplicate Title Tags", Severity::Medium,
"5 pages share the same title tag. This confuses search engines and dilutes ranking potential.")
.with_recommendation("Create unique, descriptive title tags for each page. Include primary keywords and keep titles under 60 characters.")
.with_affected("example.com/services/*, example.com/solutions/*")
.with_category("On-Page SEO"),
)
.add_component(
Finding::new("Missing Canonical Tags", Severity::Medium,
"URL parameters create duplicate content (e.g., ?sort=price, ?page=2). Without canonical tags, search engines may index multiple versions.")
.with_recommendation("Implement self-referencing canonical tags on all pages. Add canonical tags pointing to the main version for parameterized URLs.")
.with_category("Technical SEO"),
)
.add_component(Section::new("Performance Analysis").with_level(1))
.add_component(
Finding::new("Largest Contentful Paint Too Slow", Severity::High,
"LCP measured at 3.2 seconds (target: <2.5s). The hero image on the homepage is 2.4 MB and loads without optimization.")
.with_recommendation("Compress hero image to WebP format (saves ~60%), implement lazy loading for below-fold images, and add width/height attributes.")
.with_affected("example.com/ (homepage)")
.with_category("Core Web Vitals"),
)
.add_component(
Finding::new("Render-Blocking Resources", Severity::Medium,
"3 CSS files and 2 JavaScript files block initial rendering. Total blocking time: 850ms.")
.with_recommendation("Inline critical CSS, defer non-essential JavaScript, and use async loading for third-party scripts.")
.with_category("Loading Performance"),
)
.add_component(Section::new("Accessibility Analysis").with_level(1))
.add_component(
Finding::new("Missing Alt Text on Images", Severity::High,
"8 images lack alternative text, making content inaccessible to screen reader users. This also impacts image SEO.")
.with_recommendation("Add descriptive alt text to all informational images. Use empty alt attributes (alt=\"\") only for decorative images.")
.with_affected("example.com/products (5 images), example.com/team (3 images)")
.with_category("WCAG 2.1 AA"),
)
.add_component(
Finding::new("Keyboard Navigation Complete", Severity::Info,
"All interactive elements are reachable via keyboard. Focus indicators are visible and follow a logical tab order.")
.with_category("WCAG 2.1 AA"),
)
.add_component(Section::new("Security Analysis").with_level(1))
.add_component(
Finding::new("Missing Content-Security-Policy Header", Severity::Medium,
"No CSP header detected. This leaves the site vulnerable to cross-site scripting (XSS) attacks.")
.with_recommendation("Implement a Content-Security-Policy header. Start with report-only mode to identify issues, then enforce.")
.with_category("HTTP Headers"),
)
.add_component(
Finding::new("SSL/TLS Configuration Strong", Severity::Info,
"TLS 1.3 supported, HSTS enabled with 1-year max-age, certificate valid until December 2026. No mixed content detected.")
.with_category("Transport Security"),
)
.add_component(Section::new("Technical SEO Checklist").with_level(1))
.add_component(
AuditTable::new(vec![
TableColumn::new("Check").with_width("35%"),
TableColumn::new("Status").with_width("15%"),
TableColumn::new("Details").with_width("50%"),
])
.with_title("Technical Audit Results")
.add_row(vec!["robots.txt", "Pass", "Correctly configured, allows search engine crawling"])
.add_row(vec!["XML Sitemap", "Pass", "Found at /sitemap.xml, 156 URLs indexed"])
.add_row(vec!["HTTPS", "Pass", "TLS 1.3, valid certificate, no mixed content"])
.add_row(vec!["Canonical Tags", "Fail", "Missing on parameterized URLs"])
.add_row(vec!["Structured Data", "Pass", "JSON-LD schema on product and organization pages"])
.add_row(vec!["Mobile Friendly", "Pass", "Responsive design, passes mobile-friendly test"])
.add_row(vec!["Core Web Vitals", "Warning", "LCP: 3.2s (needs improvement), FID: 45ms, CLS: 0.05"])
.add_row(vec!["Open Graph Tags", "Pass", "Present on all main pages"]),
)
.add_component(Section::new("Score Comparison").with_level(1))
.add_component(
ModuleComparison::new(vec![
ComparisonModule::new("SEO", 72).with_color("#3b82f6"),
ComparisonModule::new("Accessibility", 92).with_color("#22c55e"),
ComparisonModule::new("Performance", 68).with_color("#f59e0b"),
ComparisonModule::new("Security", 85).with_color("#8b5cf6"),
])
.with_title("Module Scores Compared"),
)
.add_component(Section::new("Recommended Actions").with_level(1))
.add_component(RoadmapBlock::new(vec![
RoadmapColumn {
title: "Immediate (Week 1-2)".into(),
accent_color: Some("#ef4444".into()),
items: vec![
ActionItem { action: "Add missing meta descriptions".into(), role: "Content Team".into(), priority: "High".into(), effort: Some("4 hours".into()), benefit: "Higher CTR from search results".into() },
ActionItem { action: "Add alt text to images".into(), role: "Content Team".into(), priority: "High".into(), effort: Some("2 hours".into()), benefit: "Accessibility compliance, image SEO".into() },
ActionItem { action: "Compress hero image".into(), role: "Developer".into(), priority: "High".into(), effort: Some("1 hour".into()), benefit: "Faster LCP, better Core Web Vitals".into() },
],
},
RoadmapColumn {
title: "Short-term (Week 3-4)".into(),
accent_color: Some("#f59e0b".into()),
items: vec![
ActionItem { action: "Implement canonical tags".into(), role: "Developer".into(), priority: "Medium".into(), effort: Some("3 hours".into()), benefit: "Eliminate duplicate content issues".into() },
ActionItem { action: "Defer render-blocking resources".into(), role: "Developer".into(), priority: "Medium".into(), effort: Some("4 hours".into()), benefit: "Faster initial page load".into() },
ActionItem { action: "Add CSP header".into(), role: "DevOps".into(), priority: "Medium".into(), effort: Some("2 hours".into()), benefit: "XSS protection".into() },
],
},
RoadmapColumn {
title: "Long-term (Month 2-3)".into(),
accent_color: Some("#22c55e".into()),
items: vec![
ActionItem { action: "Fix duplicate title tags".into(), role: "Content Team".into(), priority: "Medium".into(), effort: Some("3 hours".into()), benefit: "Better search differentiation".into() },
ActionItem { action: "Implement image CDN".into(), role: "DevOps".into(), priority: "Low".into(), effort: Some("8 hours".into()), benefit: "Faster global image delivery".into() },
],
},
]))
.add_component(
Callout::info("This report was generated automatically. Scores are based on automated checks and may not capture all aspects of your website. A manual review is recommended for critical findings.")
.with_title("Note"),
)
.build();
let pdf = engine.render_pdf(&report)?;
let output_path = "examples/output/seo_audit_report.pdf";
std::fs::write(output_path, &pdf)?;
println!("✓ SEO Audit report generated successfully!");
println!(" Output: {}", output_path);
println!(" Size: {} KB", pdf.len() / 1024);
Ok(())
}