Descriptions are inline markdown; Feature.link withdrawn

Alternative and feature descriptions render links, emphasis and code
(pulldown-cmark, html feature only). Block structure flattens to one
paragraph, raw HTML is dropped, link targets are limited to https,
mailto and site-relative paths. A link belongs in the prose, so the
title-link field shipped in 0.3.7 goes before anyone uses it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Bendik Aagaard Lynghaug
2026-08-30 14:44:42 +02:00
co-authored by Claude Fable 5
parent 7fb0afcdc5
commit be954bc43e
5 changed files with 91 additions and 33 deletions
+5 -14
View File
@@ -9,7 +9,7 @@ use serde::{Deserialize, Serialize};
use crate::answers::{Answer, SelfTransitionAnswer, TransitionAnswers, TransitionItem};
use crate::auth::{current_user, User};
use crate::content::{is_qualified, Alternative, Question, Responsible, SiteConfig, Transition};
use crate::content::{is_qualified, render_inline_markdown, Alternative, Question, Responsible, SiteConfig, Transition};
use crate::resource::{get_requirement_binding, get_requirement_options, get_resource};
/// The visible site name/wordmark - "portal" is just this codebase's
@@ -680,7 +680,7 @@ fn AlternativeCard(
return view! {
<section class="alt-card">
<h2>{alternative.name.clone()}</h2>
<p class="alt-description">{alternative.description.clone()}</p>
<p class="alt-description" inner_html=render_inline_markdown(&alternative.description)></p>
{move || {
let question_id_for_action = question_id_for_action.clone();
let alt_name_for_action = alt_name_for_action.clone();
@@ -902,7 +902,7 @@ fn AlternativeCard(
<section class="alt-card">
<AltImages images=alternative.images.clone() />
<h2>{alternative.name.clone()}</h2>
<p class="alt-description">{alternative.description.clone()}</p>
<p class="alt-description" inner_html=render_inline_markdown(&alternative.description)></p>
<div class="features">
<For
each={
@@ -945,22 +945,13 @@ fn AlternativeCard(
let name = feature.name.clone();
move || !name.is_empty()
}>
<h3>
{match feature.link.clone() {
Some(href) => view! {
<a href=href target="_blank" rel="noopener noreferrer">
{feature.name.clone()}
</a>
}.into_any(),
None => feature.name.clone().into_any(),
}}
</h3>
<h3>{feature.name.clone()}</h3>
</Show>
<Show when={
let description = feature.description.clone();
move || !description.is_empty()
}>
<p>{feature.description.clone()}</p>
<p inner_html=render_inline_markdown(&feature.description)></p>
</Show>
{resource.map(|spec| {
view! {