Alternative.images: array of urls, rendered as a Swiper card deck
Deploy / deploy (push) Successful in 1m0s

image: Option<String> becomes images: Vec<String> (nothing in live
content used the old field). One url renders as the plain banner it
was; several become a swipeable cards-effect deck via Swiper Element
12.2.0, vendored into public/ (MIT) like prosekit-editor.js/yes.js
rather than pulled from a CDN - the bundle only loads on pages where
some alternative actually declares more than one image.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Bendik Aagaard Lynghaug
2026-08-12 22:11:07 +02:00
co-authored by Claude Sonnet 5
parent 40694a8f3f
commit 3233d9a3db
5 changed files with 80 additions and 9 deletions
+34 -3
View File
@@ -163,7 +163,13 @@ fn QuestionView(
.into_any();
}
// Swiper Element only registers its custom elements when its
// (vendored, ~180KB) script runs - loaded once per page, and only
// on pages where some alternative actually declares a deck.
let needs_swiper = question.alternatives.iter().any(|a| a.images.len() > 1);
view! {
{needs_swiper.then(|| view! { <leptos_meta::Script src="/swiper-element-bundle.min.js"/> })}
<Hero
title=question.name.clone()
description=question.description.clone()
@@ -370,6 +376,33 @@ fn Hero(title: String, description: String, show_yes: bool) -> impl IntoView {
}
}
/// An alternative's banner imagery: nothing, a plain image, or - for
/// several urls - a Swiper Element card deck. The `swiper-container`/
/// `swiper-slide` custom elements render inert server-side and upgrade
/// once the vendored bundle registers them (loaded per-page by
/// `QuestionView` only when some alternative actually needs it).
#[component]
fn AltImages(images: Vec<String>) -> impl IntoView {
match images.len() {
0 => ().into_any(),
1 => view! { <img class="alt-image" src=images[0].clone() alt="" /> }.into_any(),
_ => view! {
<swiper-container class="alt-image-deck" effect="cards" grab-cursor="true">
<For
each=move || images.clone()
key=|src| src.clone()
children=|src: String| view! {
<swiper-slide>
<img class="alt-image" src=src alt="" />
</swiper-slide>
}.into_any()
/>
</swiper-container>
}
.into_any(),
}
}
#[component]
fn AlternativeCard(
question_id: String,
@@ -593,9 +626,7 @@ fn AlternativeCard(
view! {
<section class="alt-card">
{alternative.image.clone().map(|src| view! {
<img class="alt-image" src=src alt="" />
})}
<AltImages images=alternative.images.clone() />
<h2>{alternative.name.clone()}</h2>
<p class="alt-description">{alternative.description.clone()}</p>
<div class="features">