Digestive check
Advice on stomach, bowel and liver symptoms.
Book<xo-carousel> is a row of slides, its children, that you move through. It is made on the platform: the slides sit in a scroll container with CSS scroll snap, so touch swiping, the wheel and momentum are the browser's own, and the buttons only ask it to scroll. It follows the WAI-ARIA carousel pattern, and a slide that is out of view cannot be reached with Tab. These are the forms it covers, one element each:
items sets how many are visible; set --xo-carousel-items in CSS to change it with the screen width. counter shows the position, and the arrows stop at the ends (or wrap with loop).
<xo-carousel class="service-cards" label="Services" counter>
<article class="card"><h3>Digestive check</h3><p>Advice on stomach, bowel and liver symptoms.</p><a href="#book">Book</a></article>
<article class="card"><h3>Painless endoscopy</h3><p>Stomach and bowel, with sedation.</p><a href="#book">Book</a></article>
<article class="card"><h3>H. pylori test</h3><p>A sample taken during the endoscopy.</p><a href="#book">Book</a></article>
<article class="card"><h3>Abdominal ultrasound</h3><p>Done together with the doctor.</p><a href="#book">Book</a></article>
<article class="card"><h3>Health packages</h3><p>Checks with offers.</p><a href="#book">Book</a></article>
</xo-carousel>.service-cards { --xo-carousel-items: 1; }
@media (min-width: 40rem) { .service-cards { --xo-carousel-items: 2; } }
@media (min-width: 60rem) { .service-cards { --xo-carousel-items: 3; --xo-carousel-gap: 1.5rem; } }One slide at a time with dots and autoplay (milliseconds). It adds a pause button, pauses while the pointer or the focus is inside, does not run under prefers-reduced-motion, and the slides are announced politely only when it is not moving.
The form just worked: validation, errors and the draft came from the OpenAPI document.
We deleted our table component and kept the markup.
Strict CSP on day one, no exceptions to write.
<xo-carousel class="banner" label="What people say" dots autoplay="6000" loop>
<blockquote><p>The form just worked: validation, errors and the draft came from the OpenAPI document.</p><footer>Ada, platform team</footer></blockquote>
<blockquote><p>We deleted our table component and kept the markup.</p><footer>Grace, admin app</footer></blockquote>
<blockquote><p>Strict CSP on day one, no exceptions to write.</p><footer>Barbara, security</footer></blockquote>
</xo-carousel>A slide with data-thumb gives its picture to the button that goes to it, in place of a dot. Keys on the picture area: Left and Right, Home and End.
<xo-carousel label="Gallery" dots>
<img src="img/gallery-1.svg" alt="Clay hills" data-thumb="img/gallery-1.svg">
<img src="img/gallery-2.svg" alt="Green valley" data-thumb="img/gallery-2.svg">
<img src="img/gallery-3.svg" alt="Night over the ridge" data-thumb="img/gallery-3.svg">
</xo-carousel>The slides can come from the data layer: put xo-each on the slide. The carousel follows its children as they arrive and change.
{email}
{role}<xo-view get="listPeople" p-limit="6">
<xo-carousel class="service-cards" label="People" items="2" counter no-arrows>
<article class="card" xo-each="items"><h3>{name}</h3><p>{email}</p><span class="badge">{role}</span></article>
</xo-carousel>
</xo-view>The arrows, dots and counter are children of the carousel's controls row. To lay them out another way, make the row and the arrows' box transparent to layout (display: contents) and place each piece in the carousel's own grid. This is how this site draws them:
xo-carousel { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; column-gap: 0.75rem; align-items: center; }
xo-carousel .xo-carousel-controls, xo-carousel .xo-carousel-nav { display: contents; }
xo-carousel .xo-carousel-track { grid-column: 2; grid-row: 1; }
xo-carousel .xo-carousel-prev { grid-column: 1; grid-row: 1; }
xo-carousel .xo-carousel-next { grid-column: 3; grid-row: 1; }
xo-carousel .xo-carousel-dots { grid-column: 2; grid-row: 2; justify-self: center; }
xo-carousel .xo-carousel-count { grid-column: 2; grid-row: 2; justify-self: end; }| API | What it does |
|---|---|
items | Slides visible at once (default 1; 1.15 lets the next one peek) |
dots, counter, no-arrows | The position buttons (or thumbnails), "1 / 5", hide the arrows |
loop, autoplay | Next at the end returns to the first; move on by itself every n milliseconds |
index, count, next(), prev(), goTo(i), play(), pause() | Control it from code; index is the first visible slide |
xo-carousel-change | Fires with { index, count } |
Not included on purpose: crossfade and 3D styles, an endless loop made of cloned slides, and a continuously scrolling logo strip. They need a different mechanism than scroll snap, and the last two are bad for people who use a keyboard or a screen reader; a CSS animation does the logo strip well.