Exhibit 15 of 17

A slideshow that plays itself

It advances on its own every four seconds, stops while you hover it or use its buttons, and stays put if you pick a slide or press Pause.

The exhibit

1/4Ordinary thingA chipped enamel jug.
2/4Impossible detailIt holds the sea. Only the sea.
3/4Straight facePhotographed like evidence, on a plain table.
4/4Say it’s AILabel on the bottom: made by machine.

How it works

The strip of slides moves with one @keyframes that holds each slide, then slides on. Radio buttons pick: Play (checked to start with) runs the animation; a number stops it and translates the strip to that slide. A Pause checkbox, hovering the slides, or keyboard focus on the buttons (:has(.pick:focus-visible)) sets animation-play-state: paused.

The key CSS

Cut from this exhibit’s stylesheet as it is served.

@keyframes slides {
  0%, 20%   { translate: 0; }
  25%, 45%  { translate: -25%; }
  50%, 70%  { translate: -50%; }
  75%, 95%  { translate: -75%; }
  100%      { translate: 0; }
}
/* Play: the animation is the slideshow */
.x-slideshow #sl-play:checked ~ .frame .strip { animation: slides 16s ease-in-out infinite; }
/* …held still by a hover, keyboard focus on its controls, or the Pause box */
.x-slideshow .frame:hover .strip,
.x-slideshow .show:has(.pick:focus-visible) .strip,
.x-slideshow #sl-pause:checked ~ .frame .strip { animation-play-state: paused; }
/* a numbered radio stops the animation and shows its slide */
.x-slideshow #sl-2:checked ~ .frame .strip { translate: -25%; }
.x-slideshow #sl-3:checked ~ .frame .strip { translate: -50%; }
.x-slideshow #sl-4:checked ~ .frame .strip { translate: -75%; }
/* reduced motion: never moves on its own */
@media (prefers-reduced-motion: reduce) {
  .x-slideshow #sl-play:checked ~ .frame .strip { animation: none; }
  .x-slideshow .play, .x-slideshow .pause { display: none; }
}

Accessibility

Auto-advancing content needs a way to stop it (WCAG 2.2.2): that is the Pause box, and under reduced motion it never moves at all. The picks are labelled radios. Because the animation, not a script, chooses the slide, nothing tells a screen reader which slide is showing while it plays.

Browser support

  • :checked + ~ Works in this browser.Not in this browser: Nothing to fall back from; every browser has had it for years. Checked 2026-10-14: Chromium 149 yes, Firefox 141 yes.
  • :has() Works in this browser.Not in this browser: Without :has() it can’t work; the page says so instead of half-working. Checked 2026-10-14: Chromium 149 yes, Firefox 141 yes.
Slop at the top