Skip to main content

max / makenotwork

2.3 KB · 43 lines History Blame Raw
1 {# Composable click-through carousel.
2
3 One widget, used everywhere. A carousel is just an ordered list of frames;
4 every surface (app product pages, landing) calls this same macro with a
5 different frame list. Do not fork it for bespoke layouts -- if a surface
6 needs more, extend the macro.
7
8 Click-through, not animated: the viewer advances frames with the prev/next
9 controls, the dots, or the arrow keys. No autoplay, no auto-advance, no
10 slide animation -- frames swap instantly.
11
12 Each frame is a `CarouselFrame { image, alt, caption: Option<_> }`
13 (see `src/templates/mod.rs`). Usage:
14
15 {%- import "partials/carousel.html" as carousel -%}
16 {% call carousel::carousel("af-shots", frames) %}
17
18 Progressive enhancement: with JS off, the first frame shows and the
19 controls are inert -- you still see a screenshot, nothing breaks.
20 #}
21
22 {% macro carousel(id, frames) -%}
23 <div class="carousel" id="{{ id }}" role="group" aria-roledescription="carousel" aria-label="Screenshots" tabindex="0">
24 <div class="carousel-viewport">
25 {% for f in frames %}
26 <figure class="carousel-frame{% if loop.first %} is-active{% endif %}" data-index="{{ loop.index0 }}" role="group" aria-roledescription="slide" aria-label="{{ loop.index }} of {{ frames.len() }}"{% if !loop.first %} aria-hidden="true"{% endif %}>
27 <img class="carousel-img" src="{{ f.image }}" alt="{{ f.alt }}" loading="lazy">
28 {% if let Some(cap) = f.caption %}<figcaption class="carousel-caption">{{ cap }}</figcaption>{% endif %}
29 </figure>
30 {% endfor %}
31 </div>
32 {% if frames.len() > 1 %}
33 <button type="button" class="carousel-nav carousel-prev" data-carousel-prev aria-label="Previous screenshot">&#8249;</button>
34 <button type="button" class="carousel-nav carousel-next" data-carousel-next aria-label="Next screenshot">&#8250;</button>
35 <div class="carousel-dots" role="tablist" aria-label="Choose screenshot">
36 {% for f in frames %}
37 <button type="button" class="carousel-dot{% if loop.first %} is-active{% endif %}" data-carousel-dot="{{ loop.index0 }}" role="tab" aria-label="Screenshot {{ loop.index }}" aria-selected="{% if loop.first %}true{% else %}false{% endif %}"></button>
38 {% endfor %}
39 </div>
40 {% endif %}
41 </div>
42 {%- endmacro %}
43