Carousel
Slides seen one at a time through a viewer’s gate: transport keys either side of a frame counter on an LCD, lamp keys to jump, swipe on touch. Nothing moves on its own unless you ask it to.
Kit part · Carousels
A carousel as a strip of film in a machined viewer. The frames run behind a gate lit from behind, with sprocket holes along both edges, and the frames either side wait under a smoked cover. It moves a frame at a time and settles as each one registers. Turn the thumbwheel, press the keys or swipe the film itself: underneath it is a native scroller with a snap point at every frame.
Staging takes it. Smoke tests pass.
Five per cent of traffic. p95 at 180 ms.
p95 passes 800 ms. The canary comes out.
4.2.1 fixes the slow query on db-2.
Five, then 25 per cent. p95 under 200 ms.
Three regions, one at a time.
Errors flat at 0.02 per cent for an hour.
Release notes go to the whole team.
4.2.1 is live in every region.
Both finishes, side by side. Hover, pressed and focus are forced with .is-hover, .is-down and .is-focus; checked and disabled are real.
Aluminium
| Part | Rest | Hover | Pressed | Focus |
|---|---|---|---|---|
| Key | ||||
| Thumbwheel | ||||
| Counter | 01 / 12 | 04 / 12 | 11 / 12 | 12 / 12 |
Graphite
| Part | Rest | Hover | Pressed | Focus |
|---|---|---|---|---|
| Key | ||||
| Thumbwheel | ||||
| Counter | 01 / 12 | 04 / 12 | 11 / 12 | 12 / 12 |
| Name | Kind | Values | What it does |
|---|---|---|---|
| <tui-film-strip> | element | label · index | The viewer: role="region" with aria-roledescription="carousel", named by label. index is the frame to start on, from 0. |
| .tui-film-strip__viewer · __reel · __film | class | in the markup | The housing with its gate, the native scroller, and the film that carries the frames and the holes. Write them yourself or with filmStripHTML(). |
| figure.tui-film-strip__frame | element | data-name | A frame: an image or a card. data-name is what it is called; the element names it “4 of 12: Canary”. |
| .tui-film-strip__card | class | small · b · p | A frame of words, printed on paper: a small label, a title and one line. |
| --fs-w · --fs-peek | custom property | length | A frame’s width at most, 320px, and how much of each neighbour shows beside the gate, 44px. Frames are 3:2. |
| .tui-film-strip__deck | class | added by kit.mjs | Under the viewer: the LCD counter, the previous and next keys and the thumbwheel. |
| .tui-film-strip__wheel | class | role="slider" | The thumbwheel. Drag it and the film goes with its ridges; scroll on it and it clicks round a frame a notch; with focus, the arrows, Page Up and Down, Home and End. |
| index · go(i) | property · method | from 0 | The frame at rest in the gate, and a way to bring another there. The film has ends and does not wrap. |
| tui-frame | event | detail.index · frame · of · name | When a new frame comes to rest in the gate, however it got there. Bubbles. |
| Arrows · Home · End | keyboard | on the film | A frame at a time, and the ends. The film is one tab stop and the thumbwheel another. |
| filmStripHTML · filmStripCount · filmStripLabel · filmStripIndex · filmStripTurn · filmStripNotch | function | kit.mjs | The markup, and the rules under it, pure: the counter, a frame’s name, the frame at a scroll position, how far the wheel turns, and wheel notches. |
<tui-film-strip class="tui-film-strip" label="Lode 4.2, the release frame by frame">
<div class="tui-film-strip__viewer">
<div class="tui-film-strip__reel" tabindex="0" role="group" aria-label="Frames">
<div class="tui-film-strip__film">
<figure class="tui-film-strip__frame" data-name="Cut">
<img src="/release/cut.jpg" alt="The release branch, cut from main">
</figure>
<figure class="tui-film-strip__frame" data-name="Build">
<div class="tui-film-strip__card">…</div> <!-- or a card of words -->
</figure>
<!-- three to twenty frames -->
</div>
</div>
</div>
</tui-film-strip>import { filmStripHTML } from '/kit/kit.mjs'; // also defines <tui-film-strip>
// The markup above, written for you: names become "4 of 12: Canary".
box.innerHTML = filmStripHTML({ label: 'Site survey', frames: photos.map((p) => ({ name: p.title, html: `<img src="${p.src}" alt="${p.alt}">` })) });
const strip = document.querySelector('tui-film-strip');
strip.addEventListener('tui-frame', (e) => {
e.detail.frame; // 4
e.detail.name; // 'Canary'
});
strip.go(6); // bring the seventh frame into the gateNeeds kit.css and kit.mjs. How to install the kit.
aria-roledescription="carousel", named by its label, and each frame is a group named “4 of 12: Canary”, so a screen reader hears where it is and what the frame is before anything on it.aria-valuetext and the keys are real buttons named “Previous frame” and “Next frame”. At an end the key is aria-disabled, so focus is never dropped.The reel is a scroller with scroll-snap-type: x mandatory and a snap point at the middle of every frame, with scroll-snap-stop: always so a flick moves one. The film inside it is as long as its frames, padded so the first and the last can reach the gate, and carries the sprocket holes as two repeating backgrounds, so they travel with it. Frames are sized in container units: 320px at most, and never so wide that less than 44px of each neighbour shows. The gate is a pseudo-element over the middle of the viewer whose shadow, spread a long way, is the smoked cover; a second one lights it from behind in screen. The thumbwheel is 36 ridges laid round a circle with CSS sin() and cos(): each one sits at its own angle less --turn, narrows as it turns away and is gone round the back, under a light that stays put. The script sets --turn from the scroll position, 36 degrees a frame, so the wheel turns with the film however the film is moved. The settle is a Web Animations nudge on the film, four pixels past and back, as a frame arrives.
For a sequence worth going through in order, with the next frame waiting at the edge.
6 rules in this file, with the foundations they rest on and the anti-patterns that break them. Full rules ship in the brain with the Pass.
Slides seen one at a time through a viewer’s gate: transport keys either side of a frame counter on an LCD, lamp keys to jump, swipe on touch. Nothing moves on its own unless you ask it to.
A region that scrolls, with its scrollbar made as a machined rail: a groove cut into the plate and a thumb like a fader cap riding in it. Scrolling itself is the browser’s own.
A value that changes, on grey-green liquid crystal, with a ghost of the unlit segments behind it: clocks, battery, counters.