Add optional Alternative.image and Feature.color/icon for richer layouts
Deploy / deploy (push) Successful in 1m6s
Deploy / deploy (push) Successful in 1m6s
image: a banner url rendered as <img>, client-fetched directly (never server-side, so none of ResourceSource::Url's SSRF concern applies). color: any CSS color, set as the feature's own --feature-accent custom property (never interpolated into a stylesheet) - draws a left-border accent; unset means no border, not a fallback to the global --accent. icon: an Iconify name (lucide:star etc.), rendered via Iconify's public SVG API - no icon library bundled, matching this app's zero-JS- dependency content otherwise keeps to. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
a29c025a85
commit
3ccc50248f
+17
-1
@@ -600,6 +600,9 @@ fn AlternativeCard(
|
||||
|
||||
view! {
|
||||
<section class="alt-card">
|
||||
{alternative.image.clone().map(|src| view! {
|
||||
<img class="alt-image" src=src alt="" />
|
||||
})}
|
||||
<h2>{alternative.name.clone()}</h2>
|
||||
<p class="alt-description">{alternative.description.clone()}</p>
|
||||
<div class="features">
|
||||
@@ -625,8 +628,13 @@ fn AlternativeCard(
|
||||
let alt_name = alt_name.clone();
|
||||
let resource = feature.resource.clone();
|
||||
let feature_name = feature.name.clone();
|
||||
let color = feature.color.clone();
|
||||
let icon = feature.icon.clone();
|
||||
view! {
|
||||
<div class="feature">
|
||||
<div class="feature" style:--feature-accent=color>
|
||||
{icon.filter(|i| is_safe_icon_name(i)).map(|icon| view! {
|
||||
<img class="feature-icon" src=format!("https://api.iconify.design/{icon}.svg") alt="" />
|
||||
})}
|
||||
<Show when={
|
||||
let name = feature.name.clone();
|
||||
move || !name.is_empty()
|
||||
@@ -1274,6 +1282,14 @@ fn AnswerRow(
|
||||
}
|
||||
}
|
||||
|
||||
/// Iconify names are `{prefix}:{name}`, both lowercase-alphanumeric-
|
||||
/// with-hyphens by convention - this is just a character allow-list
|
||||
/// before the value goes into a URL, not a lookup against Iconify's
|
||||
/// real prefix/name list.
|
||||
fn is_safe_icon_name(s: &str) -> bool {
|
||||
!s.is_empty() && s.chars().all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_' || c == ':')
|
||||
}
|
||||
|
||||
fn format_ms(ms: i64) -> String {
|
||||
#[cfg(feature = "ssr")]
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user