use anyhow::{Context, Result}; use maud::{html, Markup, DOCTYPE}; use minify_html::{minify, Cfg}; pub trait Page: Sized { fn render_content(self) -> Markup; fn page_title() -> String; fn render_head() -> Markup { html!( head { (DOCTYPE) meta charset="utf-8"; meta name="viewport" content="width=device-width,initial-scale=1"; title { (Self::page_title()) } } ) } fn render_navigation() -> Markup { html!( div class="navbar" { header { h1 { "V2 Tales"; } } nav { a href="/index.html" { "HOME"; } a href="/stories.html" { "STORIES"; } } } ) } fn style() -> Markup { html!( style { """ * { padding: 0; margin: 0; } h1 { padding: 0.3rem; letter-spacing: 0.8rem; font-size: 1.5rem; color: #2F3C57; display: flex; justify-content: center; align-items: center; gap: 2rem; } h2 { font-size: 1.8rem; margin-bottom: 2rem; color: #2F3C57; } body { margin: 2rem 1rem; background-color: #ffc994; text-align: center; } hr { margin: 1rem; } img { max-width: 3rem; max-height: 3rem; } .navbar { display: flex; justify-content: space-between; align-items: center; padding: 0 3rem; } nav { display: flex; justify-content: space-around; width: 50%; a { text-decoration: none; color: #2F3C57; &:hover { text-decoration: underline; } } } .story-card { text-align: left; font-size: 1.2rem; ul { display: flex; justify-content: start; list-style: none; li { font-size: 1rem; padding: 0.8rem; margin: 0.8rem; background-color: #2F3C57; color: #ffd1a3; outline: 0.2rem solid #2F3C57; a { text-decoration: underline #E1CCEB; color: #E1CCEB; } } } hr { margin: 2rem 0; } padding: 3rem; margin: 3rem; background-color: #ffd1a3; border-bottom: 0.1rem dotted black; } """ } ) } fn render_page(self) -> Markup { html!((Self::render_head())(Self::style())( Self::render_navigation() )(Self::render_content(self))) } fn write_page(mut self) -> Result<()> { self.prepare_content()?; let page_html = self.render_page().into_string(); let mut cfg = Cfg::new(); cfg.minify_css = true; let minified_html = minify(page_html.as_bytes(), &cfg); let html_path = format!("dist/{}.html", Self::page_title().to_lowercase()); std::fs::write(&html_path, minified_html) .with_context(|| format!("Failed to write {}", html_path))?; Ok(()) } // use this to validate and prepare the content for the page, e.g. files fn prepare_content(&mut self) -> Result<()> { Ok(()) } }