Carousel

CSS Vars — Carousel System

A dependency-free, zero-JavaScript carousel system. Everything below runs on :checked state changes and CSS animations only — no scripts, no build step. One stylesheet (cssvars-carousel-final.css) covers every variant on this page.

The rule that makes multiple instances safe

Radio button name grouping is global to the whole document, not scoped to whatever container they sit in. Every carousel below uses its own unique prefix on its name/id values (e.g. dots-, xfade-, link-) — that's the only thing you need to change per instance to safely repeat any of these on the same page. Class names (.carousel-fixed, radio-1, etc.) are always safe to reuse as-is.

1. Static — .carousel-fixed, classic dots

Manual click only. Uses a CSS variable + calc() instead of hardcoded per-slide percentages, so supporting more slides is a one-line addition (see the CSS comments) rather than a math redo.

Classes to add:
  • carousel-fixed → the outer wrapper <div>
  • slides → the <ul> holding the slides
  • slidesNavigation → the <div> wrapping the dot <label>s
  • Everything else (the <li>s, the radios, the labels) needs no class at all — just correct id/for/name pairing.

1b. Flexible links — .slidesNavigation-flex

Each link is a <label> (invisible, just the click target) wrapping a <div> you fully design — any content, any styling. The wrapper itself carries no position, so you place and lay it out however you want with ordinary utility classes.

Classes to add (both examples below):
  • carousel-fixed → the outer wrapper <div>
  • slides → the <ul>
  • slidesNavigation-flex → the <div> wrapping the <label> links, in place of slidesNavigation
  • Inside each <label>, the <div> is yours — style it however you want (below it just uses the framework's own button class)

Row of buttons, below the carousel

Column, overlaid on the right — composed from utility classes only

Additional classes on the slidesNavigation-flex wrapper for this positioning: position-absolute, top-0, bottom-0, right-1 (span the full height near the right edge), flex-column + justify-content-center (center the buttons vertically, no transform needed), gap-1, z-floating. All existing cssvars utilities — nothing new to learn.

1c. Animations — .carousel-crossfade / .zoom

Opt-in add-on classes, same mechanism underneath. Durations ride on --duration-slow/--easing, so prefers-reduced-motion is respected automatically.

Classes to add:
  • carousel-crossfade → added alongside carousel-fixed on the same wrapper <div> (i.e. class="carousel-fixed carousel-crossfade")
  • zoom → added on top of that, same wrapper, for the Ken Burns version (class="carousel-fixed carousel-crossfade zoom")
  • slide-media → required on the content <div> inside each <li> — this is what the zoom animation actually targets, so captions/text on top of it stay still

Cross-fade — no horizontal movement

Cross-fade + Ken Burns zoom

1d. Links inside slides — prev/next arrows + CTA

A <label for="..."> works anywhere, including inside a slide's own content. Prev/Next needs one pair per slide, each pointing at its actual neighbor (wraparound included) — CSS can't compute "current + 1" dynamically.

Classes to add:
  • carousel-fixed + slides → same as the base carousel, no change
  • slide-prev → on a <label> placed inside a slide's <li>, pointing (for) at the previous slide's radio
  • slide-next → same idea, pointing at the next slide's radio
  • slide-cta → on any <label> inside a slide's content that should jump to a specific other slide (combine with button for the framework's own button look, as below)

2. Autoplay — .carousel-autoplay

No radios, no clicking — a looping @keyframes animation advances the slides on its own. Hover or focus pauses it (fixed: both the animation-name and play-state now use longhand properties, so the per-count classes can never silently reset play-state back to "running").

Classes to add:
  • carousel-autoplay → the outer wrapper <div>
  • Any of the classes based on the number of slides you haveslides-2 slides-3 slides-4 slides-5 slides-6 → same wrapper, alongside carousel-autoplay — picks which pre-built timing/keyframes to use for that slide count
  • slides → the <ul>, same as always
  • No radios needed at all for this variant — nothing to click.

3. Autoplay + clickable dots — .carousel-autoplay-linked

Dots track the current slide automatically (synced @keyframes per dot, not :checked). Click one to jump there — the cycle keeps going automatically from that point, rather than freezing.

Classes to add:
  • carousel-autoplay-linked → the outer wrapper <div> (built for 4 slides — see the CSS comments for extending it)
  • slides → the <ul>
  • slidesNavigation → the <div> wrapping the dot <label>s — same class as the classic static dots, no separate name for this variant
  • Radios are needed here (unlike plain autoplay) — they're what a click switches to when jumping to a slide

Reference

ClassWhat it doesTrade-off
.carousel-fixedManual, click-drivenNo autoplay
.slidesNavigationClassic dot linksFixed dot design
.slidesNavigation-flexFully custom div-based linksNone — most flexible option
.carousel-crossfadeFade instead of slideNone
.zoomKen Burns zoom (needs .slide-media wrapper)Requires .carousel-crossfade
.slide-prev / .slide-nextPer-slide prev/next arrowsOne pair per slide, authored manually
.carousel-autoplayAutomatic, no clickingCan't also click-to-jump
.carousel-autoplay-linkedAutomatic + clickable, resumes on clickO(N²) CSS size — best for ≤4-6 slides
← Previous Next →
{C}

Built with love by Kim Majali, EITO team, and contributors.