diff --git a/Cargo.lock b/Cargo.lock index 0c70453..8a47e71 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2948,7 +2948,7 @@ dependencies = [ [[package]] name = "portal" -version = "0.3.23" +version = "0.3.24" dependencies = [ "anyhow", "arc-swap", diff --git a/src/app.rs b/src/app.rs index 1ef756a..5758cb8 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,5 +1,5 @@ use leptos::prelude::*; -use leptos_meta::{provide_meta_context, HashedStylesheet, Html, MetaTags, Title}; +use leptos_meta::{provide_meta_context, HashedStylesheet, Html, MetaTags, Stylesheet, Title}; use leptos_router::{ components::{Route, Router, Routes}, hooks::{use_location, use_navigate, use_query_map}, @@ -79,8 +79,20 @@ pub fn App() -> impl IntoView { }); provide_context(Lang(lang)); + // A content-shipped stylesheet (site.yaml `stylesheet:`) layered + // after the default one - leptos_meta appends it at the MetaTags + // slot, which the shell places after HashedStylesheet, so content + // rules win at equal specificity. + let custom_css = Memo::new(move |_| { + site.get() + .and_then(|r| r.ok()) + .and_then(|s| s.stylesheet) + .map(|p| format!("/site/{p}")) + }); + view! { + {move || custom_css.get().map(|href| view! { })} {move || { site.get() diff --git a/src/content.rs b/src/content.rs index bb7a70b..abc6dfd 100644 --- a/src/content.rs +++ b/src/content.rs @@ -527,6 +527,11 @@ pub struct SiteConfig { /// `None` means "en"; unknown codes fall back to English per key. #[serde(default)] pub lang: Option, + /// A content-repo-relative CSS file (e.g. "custom.css") layered + /// *after* the default stylesheet, so it overrides. Served + /// same-origin at `/site/`; plain path only. + #[serde(default)] + pub stylesheet: Option, #[serde(default)] pub hero: HeroConfig, } @@ -582,6 +587,13 @@ impl SiteConfig { } other => anyhow::bail!("site.yaml: hero.kind {other:?} is not one of plain | module"), } + if let Some(sheet) = &self.stylesheet { + if !is_safe_site_path(sheet) || !sheet.ends_with(".css") { + anyhow::bail!( + "site.yaml: stylesheet {sheet:?} must be a plain repo-relative .css path" + ); + } + } Ok(()) } }