diff --git a/src/app.rs b/src/app.rs index ce345a7..93c3f7b 100644 --- a/src/app.rs +++ b/src/app.rs @@ -945,7 +945,16 @@ fn AlternativeCard( let name = feature.name.clone(); move || !name.is_empty() }> -

{feature.name.clone()}

+

+ {match feature.link.clone() { + Some(href) => view! { + + {feature.name.clone()} + + }.into_any(), + None => feature.name.clone().into_any(), + }} +

, + /// An https URL the card's name links to - an announcement's + /// programme page, a venue. Plain-text descriptions can't carry + /// links, so this is the one place a card points elsewhere. + #[serde(default)] + pub link: Option, #[serde(default)] pub requirements: Vec, /// Live data this feature pulls in. Read-only unless `transitions` @@ -1017,6 +1022,16 @@ pub fn validate_questions( } } for alternative in &question.alternatives { + for feature in &alternative.features { + if let Some(link) = &feature.link { + if !link.starts_with("https://") { + anyhow::bail!( + "question {:?} feature {:?}: link {:?} must be an https:// URL", + question.id, feature.name, link + ); + } + } + } // A dangling action is a literal dead end: the submit // button navigates to "Nothing here". if let Some(action) = &alternative.action { diff --git a/style/main.css b/style/main.css index bca5bdd..3f79551 100644 --- a/style/main.css +++ b/style/main.css @@ -448,6 +448,17 @@ main.not-found { margin-bottom: 0.15em; } +.feature h3 a { + color: inherit; + text-decoration: none; + border-bottom: 0.06rem solid var(--line); +} + +.feature h3 a:hover { + color: var(--accent); + border-bottom-color: var(--accent); +} + .feature p { color: var(--ink-dim); font-size: 0.95rem;