Bruno DelorenceBruno
Appearance
Appearance

Choose your reading theme.

Contact

Effort slider

New

A stepped slider for how hard a model thinks, whose last step turns the track into a twinkling night sky.

effort-slider/effort-slider.tsx
Live · interactive
Effort

The live component, numbered part by part. Every measurement is read from it as it is drawn. Point at a row to outline its part, or at a part to find its row.

Fig. 1, taken apart

The same component in 3D, its layers lifted apart. Hover or focus it to take it apart, pick a layer to read its dimensions, and drag to turn it.

When to use

  • Choosing how hard a model should think before it answers: a trade of speed for quality in a few named steps.
  • Ordered settings of 3 to 7 steps where each step has a name worth reading: Low, Medium, High.
  • A setting with a sensible default to point at: the recommended tick says where most people should stay.
  • Product moments where the top step deserves a little celebration, and the slider is seen rarely enough to enjoy it.

When not to use

  • Continuous values like volume or a price range. Use a plain slider with numbers.
  • Two or three unordered options. A segmented control shows them all at once.
  • Settings changed often in a dense form: the payoff gets old, and the steps’ names cost reading time.
  • When the steps’ effect isn’t explained anywhere. Keep the help text, or say it next to the slider.

You can add this component with the shadcn CLI or by hand:

Into components/effort-slider of any React project, with no setup:

Terminal
npx shadcn@latest add https://brunodelorence.com/r/effort-slider.json

Either way it needs React 19.2 and a bundler that supports CSS Modules, such as Next.js or Vite. It has a dark colour scheme: it follows your page’s color-scheme, light unless the page sets one, or its colorScheme prop, with nothing to configure. Browsers without light-dark() show it light.

Usage

model-settings.tsx
import { EffortSlider } from './effort-slider';export function ModelSettings() {  const [effort, setEffort] = useState(1);  return (    <EffortSlider      value={effort}      onChange={(index) => setEffort(index)}      recommended={1}      autoplay={false}    />  );}

Each state below is a live instance, locked so you can study it.

The pieces it is built from, each with its measurements read live.

Below are variations of this component, each live, with its code.

Black hole

theme="galaxy": a spiral that grows and spins faster each step, then collapses. On a light page it becomes a black hole that pulls the fill in; on a dark one, a white hole that throws light out.

Race car

theme="formula": a car on tarmac with kerbs streaming past. The last step opens its rear flap for the overtake, with light trails, sparks and a whoosh down the track.

Rocket launch

theme="missile": the flame grows from a wisp to a full burn. At the top the rocket lifts in a bright plume, with shockwave rings and smoke billowing along the track.

Sound barrier

theme="jet": the wings sweep back and the afterburner lights step by step. At Mach 1 a vapour cone blooms over the jet, ring shocks spread and the track becomes a streaked sky.

A small card for choosing effort, from Faster to Smarter in named steps. The thumb is a pink brain that glides from stop to stop while the track fills pink to violet behind it, and the step’s name changes letter by letter. Reach the last step and the brain grows blue-violet in a flash, rays of specks shoot out around it, and the track becomes a mosaic of squares that keeps twinkling.

  • The thumb glides on an exponential ease: each frame closes the same share of the gap, with a time constant of 56 ms, so a step lands in about 200 ms with no overshoot. The place is a single custom property, --pos, from 0 to 1, written once per frame.
  • Everything that follows the thumb reads --pos: the thumb’s transform, the clip that ends the fill under its middle, and the size of the mask that fades the fill in from the left. One style write moves them all.
  • The fill is one pink-to-violet gradient across the whole track, clipped at the thumb and faded over the filled length, so the further it goes the more violet its bright end.
  • The step’s name changes letter by letter: the old one rises and blurs away in 110 ms while each new letter comes up into focus over 170 ms, 22 ms after the one before.
  • The brain is one path for the left half and its mirror, drawn as a thick round stroke under its fill, so the stroke becomes the outline and fills the gap, the fold and the notches. The last step’s brain sits in the same drawing and crossfades in as the whole grows 1.3 times.
  • The last step draws on two small canvases: the track’s mosaic (5 rows of 3 px squares, 4 px apart, dimmer and sparser to the left) and 100 px around the thumb for the rays, the sparks on the brain and the ring of specks that flies out on arrival. They twinkle in steps 7.5 times a second; only the first second of the burst draws every frame.
  • The flash on arrival and the glow that stays are CSS radial gradients; nothing on the canvases runs off screen, in a hidden tab or under reduced motion.
  • While on screen and untouched, the demo climbs one step every 525 ms, rests on the last for 3.4 s and starts again from the bottom. Any key or press hands it to the reader at the step it was on.
  • Every colour is a light-dark() token on the root, so the card follows the page’s color-scheme or the colorScheme prop. The night sky, the glow and the brains are the same on both.
  • The theme prop swaps the look and keeps the slider: a galaxy, a racing car, a missile or a jet, each with its own thumb drawn in SVG, its own fill and its own payoff painted on the same two canvases. The thumb’s parts read --pos, so they change with the glide itself: the spiral grows, the flame lengthens, the wings sweep back. What changes only on the last step has a transition of its own.
  • A theme’s loops (the spin, the streaming kerbs, the flicker) speed up one step at a time through the Web Animations API: updatePlaybackRate changes their pace without a jump in their phase.
  • The galaxy’s payoff differs by scheme: a black hole on a light page, which pulls the fill into a funnel (a clip-path whose points follow a registered custom property, so only the warp transitions) while motes stream into it; a white hole on a dark one, which throws rings, rays and light out. The slider learns the scheme from a light-dark() colour it reads back.
PropTypeDefaultDescription
theme'brain' | 'galaxy' | 'formula' | 'missile' | 'jet''brain'The look: the brain and its night sky, or a spiral galaxy that collapses into a black (light) or white (dark) hole, a racing car that overtakes, a missile that launches, or a jet that breaks the sound barrier. Each names its steps to suit.
levelsstring[]['Low', 'Medium', 'High', 'Extra', 'Max', 'Galaxy']The steps’ names, from the fastest to the smartest. Without it, the theme’s own (for the brain, these).
valuenumber—The chosen step’s index, for a controlled slider.
defaultValuenumber0The step chosen at first, for an uncontrolled slider.
onChange(index: number, level: string) => void—Called with the new step’s index and name when the reader picks one.
recommendednumber1The step marked as recommended, with a taller tick and a label under it; -1 for none.
labelstring'Effort'The slider’s name, before the step’s name.
minLabelstring'Faster'The words at the left end.
maxLabelstring'Smarter'The words at the right end.
recommendedLabelstring'Recommended'The words under the recommended tick.
helpstring'How long the model thinks before it answers. Higher is slower, and smarter.'What the help button explains; an empty string leaves the button out.
galaxybooleantrueThe last step is the theme’s payoff (the brain’s night sky); false keeps it like the others.
autoplaybooleantrueWhile on screen and untouched, a demo climbs the steps and rests on the last.
colorScheme'light' | 'dark' | 'system'—Its colours: light, dark, or the reader’s system setting. Without it the component follows the page’s color-scheme, which is light unless the page sets one.
classNamestring—Extra class for the root.

The complete source behind this demo. Open source under the MIT license, © 2026 Bruno Delorence: copy it, change it, ship it.

effort-slider.tsx
// Copyright (c) 2026 Bruno Delorence. MIT License.'use client';import {  useEffect,  useId,  useRef,  useState,  type CSSProperties,  type KeyboardEvent as ReactKeyboardEvent,  type PointerEvent as ReactPointerEvent,  type RefObject,} from 'react';import { Brain } from './brain';import { Galaxy, TRACK_WIDTH } from './galaxy';import type { Engine } from './payoff';import { LOOKS, type Theme } from './themes';import { useAutoplay } from './use-autoplay';import styles from './effort-slider.module.css';import looks from './themes.module.css';/* * Effort slider: how hard a model should think, from Faster to Smarter, in a few named steps. The
effort-slider.module.css
/* Effort slider (effort-slider.tsx): a small card on a stage. At night the stage is near-black and   the card a step lighter; by day the card is white on a pale grey.   Every size is in --u, a px of the card at its own size (13 px type). The stage sets the card's   type size from its own width (a container query), so the whole card scales as one drawing.   Colours are the tokens below, light-dark(light, dark) where the two differ: the component takes   the color-scheme it inherits from the page (light unless the page says otherwise), or the one   its colorScheme prop sets (data-color-scheme). The last step's night sky, its glow and the brain   are the same on both.   Memory. The stage is one layout boundary sized from outside. The thumb moves by a transform; the   fill by a clip and a mask that read the same --pos; the two canvases are small (the track, and   100 px around the thumb) and only redraw 7.5 times a second while the last step is on. *//* A length that stays the card's px wherever it is read, even in smaller type. */@property --u {  syntax: '<length>';  inherits: true;  initial-value: 1px;}.root {  /* The stage, the card and its edge. */
brain.tsx
// Copyright (c) 2026 Bruno Delorence. MIT License./* * The thumb's drawing: a brain seen from above, two lobed halves either side of a gap, each with a * fold that branches from the middle. One path draws the left half and a mirror of it the right. * * Units are px of the slider at its own size (13 px type), around the brain's middle. Each half is * painted twice in one go: a thick round stroke first, which becomes the dark outline outside the * fill and fills the gap, the fold and the notches, then the fill over the inner half of it. *//** The left half, its edges in px from the brain's middle. */export const HALF =  'M-1.2-7.3C-1.2-8.4-2-9-3.1-9C-4.3-9-5.1-8.3-5.1-7.2L-5.1-6.2C-5.1-5.6-4.9-5.2-4.6-4.9' +  'C-5.4-4.5-6.2-5.2-7-5.1C-7.8-5-8.1-4.3-8.1-3.6L-8.1-2.6C-8.1-2-7.3-1.8-7.3-1.2' +  'C-7.3-0.6-8.1-0.3-8.6 0.3C-9 0.7-9.1 1.2-9.1 1.8L-9.1 3.4C-9.1 4.4-8.3 4.9-7.4 5' +  'C-6.6 5.1-5.6 5.6-5 6.1C-5.9 6.4-6.6 7-6.6 8C-6.6 9-5.5 9.6-4.3 9.6' +  'C-2.6 9.6-1.2 8.5-1.2 6.8L-1.2 1.4C-1.6 1.4-2.2 1.9-3 2C-3.8 2.1-4.4 2-4.7 1.6' +  'C-5 1.2-4.8 0.7-4.3 0.5C-3.5 0.1-2.6-0.6-2.2-1.2C-1.9-1.7-1.6-2.3-1.2-2.4Z';/** How much larger the brain grows at the last stop. */export const GROWN = 1.3;function Halves({ className }: { className: string }) {
galaxy.ts
// Copyright (c) 2026 Bruno Delorence. MIT License.import { GROWN, HALF } from './brain';/* * The last stop's two canvases. * * - The mosaic fills the track: a grid of small rounded squares, 4 px apart in 5 rows, each with a *   colour of its own and a brightness that steps now and then, so the track twinkles like a patch *   of sky. Squares are fewer and dimmer towards the left, where the effort is lowest. * - The burst sits on the thumb: dotted rays all around the brain that twinkle, sparks on the brain *   itself, and, as the stop is reached, a ring of coloured specks that flies out from the brain *   while the rays shoot outwards. * * Everything twinkles in steps, 7.5 times a second; only the burst, for its first second, draws * every frame. Nothing runs while the slider is off screen, the tab is hidden, or the reader asks * for less motion (then a still frame is drawn instead). The colours are the night sky's on both * schemes, so they live here rather than in the stylesheet. * * Units are px of the slider at its own size (13 px type); `unit` scales them to the page. */type Rgb = readonly [number, number, number];
themes.ts
// Copyright (c) 2026 Bruno Delorence. MIT License.import type { ComponentType } from 'react';import { Car } from './car';import { Hole, HOLE_REACH } from './hole';import { Jet } from './jet';import { Launch, LAUNCH_REACH } from './launch';import { Missile } from './missile';import { Overtake, OVERTAKE_REACH } from './overtake';import type { Payoff } from './payoff';import { Sonic, SONIC_REACH } from './sonic';import { Spiral } from './spiral';/* * The slider's themes besides the brain: each a thumb that changes step by step (its own drawing), * a fill (themes.module.css) and a last-step payoff (its own canvases' painter). */export type Theme = 'brain' | 'galaxy' | 'formula' | 'missile' | 'jet';type Look = {  /** The steps' names when none are given. */  levels: string[];  /** The last step's name on a dark page, where it differs. */
themes.module.css
/* The effort slider's themes (themes.ts): the galaxy, formula, missile and jet looks. The brain is   effort-slider.module.css alone; nothing here touches it.   The card says its theme (data-theme), whether the page is dark (data-dark, read by the slider   from its own colours) and whether the last step is on (data-sky). Each theme sets its tokens on   the card, styles the classes below that the slider adds next to its own, and draws its thumb in   its own SVG, whose parts read --pos (0 to 1, where the thumb is) to change step by step: the   change follows the thumb's glide, so it is smooth with no transitions of its own. What changes   only on the last step sits on a group of its own, with a transition.   Animations named pace… run faster step by step: the slider sets their playback rate. Inside the   SVGs, transform origins are in the drawing's units (its 0 0 is the thumb's middle). */@property --es-warp {  syntax: '<number>';  inherits: true;  initial-value: 0;}/* Tokens. */.themed[data-theme='galaxy'] {  --fill-from: light-dark(#c9bcff, #2a2070);  --fill-to: light-dark(#5a36d4, #9a5cf0);  --sky-ink: light-dark(#4527b0, #f2f4ff);
payoff.ts
// Copyright (c) 2026 Bruno Delorence. MIT License./* * What the themed payoffs share (the brain's night sky has its own, galaxy.ts): two canvases, the * track and a box around the thumb, and a loop that draws them while the last step is on. * * - A theme says how big its box is, how long its arrival lasts, and draws three things: the *   arrival (t is ms since the last step was reached), what keeps going after it, and the track. * - The loop draws every frame through the arrival, then 30 times a second. It stops while the *   slider is off screen or the tab is hidden. Under reduced motion there is no arrival: the *   theme's world is run forward a second and a half out of sight and drawn once, as a still. * - Light or dark is told by the slider (`scheme`), which reads it from the page's CSS. * * Units are px of the slider at its own size (13 px type); `unit` scales them to the page. On the * track, (0, 0) is its top left and the thumb's middle is at (THUMB_X, 10). In the box, (0, 0) is * the thumb's middle. */import { TRACK_WIDTH } from './galaxy';export const TRACK_HEIGHT = 20;/** The thumb's middle on the last step. */export const THUMB_X = TRACK_WIDTH - 8;
spiral.tsx
// Copyright (c) 2026 Bruno Delorence. MIT License.import styles from './themes.module.css';/* * The galaxy theme's thumb: a spiral galaxy seen face on, two arms winding out of a warm core, with * a few stars on them. It grows and turns faster step by step (the stylesheet reads --pos; the * slider sets the spin's rate). On the last step it collapses into what sits in the same drawing: * a black hole on a light page (a dark disc ringed by bent light, its accretion disc passing in * front and, lensed, over the top) or a white hole on a dark one (a blinding core throwing rings * and rays outwards). * * Units are px of the slider at its own size, around the thumb's middle. *//** One arm: a logarithmic spiral, r = 1.1 e^(0.3 θ), as a band that thins outwards. */function arm(turn: number) {  const steps = 36;  const end = Math.PI * 2.25;  const point = (theta: number) => {    const r = 1.1 * Math.exp(0.3 * theta);    return [Math.cos(theta + turn) * r, Math.sin(theta + turn) * r];  };  const outer: string[] = [];
hole.ts
// Copyright (c) 2026 Bruno Delorence. MIT License.import { Payoff, THUMB_X, between, clamp01, easeOut, pick, rgba, type Rgb } from './payoff';/* * The galaxy theme's last step. The spiral has collapsed, and what it became depends on the page. * * - Light: a black hole. A ring of light falls in on it as it forms; the track's sky is pulled *   towards it, every mote of it streaking to the right faster and faster as it nears, until the *   dark swallows it; specks orbit in the hole's tilted disc (hidden while behind it), and the *   ring of bent light around it breathes. * - Dark: a white hole, the same thing run backwards. Two rings and a crown of rays burst out of *   it, and from then on it throws light out: motes stream away left along the track, slowing as *   they go, specks fly out all around, and a faint ring leaves it every 1.4 s. *//** Half the box around the thumb. */export const HOLE_REACH = [44, 34] as const;const ARRIVAL = 1000;const FALLING: Rgb[] = [  [160, 132, 255],  [236, 128, 214],  [255, 255, 255],
car.tsx
// Copyright (c) 2026 Bruno Delorence. MIT License.import styles from './themes.module.css';/* * The formula theme's thumb: a single-seater racing car seen from above, nose to the right. Step * by step (the stylesheet reads --pos) speed lines stream off its back, the exhaust glows and the * car shivers on its springs. On the last step the rear wing's flap opens, the rain light blinks * and the car pulls ahead of its own place on the track. * * Units are px of the slider at its own size, around the thumb's middle. *//** The body: narrow at the gearbox, wide at the sidepods, a long tapering nose. */const BODY =  'M-11 -2.2L-6.4 -3.3C-2.6 -3.6 -0.4 -3.2 1.4 -1.9L11.9 -0.9Q13.3 0 11.9 0.9L1.4 1.9' +  'C-0.4 3.2 -2.6 3.6 -6.4 3.3L-11 2.2Z';/** The front wing, a shallow arrow under the nose. */const FRONT_WING = 'M9.4 -5.6L11.4 -5.4L12.4 -0.4L12.4 0.4L11.4 5.4L9.4 5.6L10.4 0Z';export function Car({ id }: { id: string }) {  return (    <svg className={styles.icon} viewBox="-16 -16 32 32" aria-hidden="true" focusable="false">      <defs>
overtake.ts
// Copyright (c) 2026 Bruno Delorence. MIT License.import { Payoff, THUMB_X, between, clamp01, easeOut, pick, rgba, type Rgb } from './payoff';/* * The formula theme's last step: the overtake. * * - The track runs: its kerbs stream past at the edges and speed lines rush left across the tarmac. * - The car draws light trails, a red one from its rain light and two pale ones from its wing tips, *   that shoot back down the track in a third of a second and then ripple as it weaves. * - A whoosh, a bright band of air, sweeps from the car down the whole track on arrival and again *   every 2.4 s, a lap. * - Around the car: arcs of pushed air leave its nose on arrival, and sparks spray from under its *   floor where it scrapes the tarmac. */export const OVERTAKE_REACH = [56, 26] as const;const ARRIVAL = 900;/** Where the car's rear wing is on the track, with the theme's last-step scale. */const REAR = THUMB_X - 14;const LAP = 2400;const SPARKS: Rgb[] = [  [255, 236, 170],
missile.tsx
// Copyright (c) 2026 Bruno Delorence. MIT License.import styles from './themes.module.css';/* * The missile theme's thumb: a slim rocket seen side on, nose to the right, with fins at the back * and a flame out of its nozzle. Step by step (the stylesheet reads --pos) the first wisps of vapour * give way to a flame that grows and flickers faster. On the last step it lifts: the nose tips up, * the plume blazes white with shock diamonds in it, and the body shakes. * * Units are px of the slider at its own size, around the thumb's middle. */const BODY = 'M-10 -2.2L5.6 -2.2C8.8 -2.2 11.4 -1.2 13.2 0C11.4 1.2 8.8 2.2 5.6 2.2L-10 2.2Z';const NOSE = 'M8.6 -1.85C10.6 -1.5 12 -0.8 13.2 0C12 0.8 10.6 1.5 8.6 1.85Z';const FIN = 'M-10 -2.2L-12.3 -6.2L-9.4 -6.2L-5.8 -2.2Z';const CANARD = 'M0.4 -2.2L-1.2 -3.9L-3.2 -3.9L-2.8 -2.2Z';/** The flame, from the nozzle (x 0) back to the left, and its hotter middle. */const FLAME = 'M0 -1.7C-3 -2 -8.5 -1.1 -13 0C-8.5 1.1 -3 2 0 1.7Z';const FLAME_INNER = 'M0 -1.1C-2.4 -1.2 -5.6 -0.6 -8.4 0C-5.6 0.6 -2.4 1.2 0 1.1Z';export function Missile({ id }: { id: string }) {  return (    <svg className={styles.icon} viewBox="-16 -16 32 32" aria-hidden="true" focusable="false">
launch.ts
// Copyright (c) 2026 Bruno Delorence. MIT License.import { Payoff, THUMB_X, between, clamp01, easeOut, pick, rgba, type Rgb } from './payoff';/* * The missile theme's last step: the launch. * * - On arrival a flash of plume lights the thumb and two shockwave rings spread from the nozzle, *   flattened as if over the ground. * - Smoke billows: puffs leave the nozzle down and back, hit the bottom of the track, roll left *   along it, swell and thin out. The first second throws a big cloud at once. Near the nozzle the *   smoke is lit orange by the flame. * - Embers fly back out of the plume, and a faint heat ripple leaves the nozzle every 1.8 s. */export const LAUNCH_REACH = [50, 34] as const;const ARRIVAL = 1000;/** The nozzle, around the thumb's middle, with the rocket tipped up 12° and lifted. */const NOZZLE = [-11, 0.9] as const;/** The exhaust's way: back and a little down. */const EXHAUST = [-0.978, 0.208] as const;const EMBERS: Rgb[] = [  [255, 226, 140],
jet.tsx
// Copyright (c) 2026 Bruno Delorence. MIT License.import styles from './themes.module.css';/* * The jet theme's thumb: a swing-wing fighter seen from above, nose to the right. Step by step (the * stylesheet reads --pos) its wings sweep back from spread to tucked, and from the middle steps * the afterburner lights. On the last step it breaks the sound barrier: a vapour cone blooms over * it from the nose back, and the flame shows its shock diamonds. * * Units are px of the slider at its own size, around the thumb's middle. */const FUSELAGE = 'M-12 -1.4L5.8 -1.6C9 -1.5 11.6 -0.8 13.6 0C11.6 0.8 9 1.5 5.8 1.6L-12 1.4Z';/** The upper wing, pivoting at (0.6, -1.2); the lower is its mirror. */const WING = 'M2.2 -1.4L-1.4 -10.4L-3.6 -10.4L-3.8 -1.4Z';const TAIL = 'M-7.6 -1.3L-10.8 -5.2L-12.4 -5.2L-11.6 -1.3Z';/** The vapour cone: a bell from just behind the nose that opens backwards. */const CONE =  'M7.4 0C6 -2.6 3.2 -7.2 -1 -10.6C-4 -12.8 -7.6 -13.6 -9.4 -13.4C-8.6 -11 -8.6 11 -9.4 13.4' +  'C-7.6 13.6 -4 12.8 -1 10.6C3.2 7.2 6 2.6 7.4 0Z';export function Jet({ id }: { id: string }) {  return (
sonic.ts
// Copyright (c) 2026 Bruno Delorence. MIT License.import { Payoff, THUMB_X, between, clamp01, easeOut } from './payoff';/* * The jet theme's last step: breaking the sound barrier. The vapour cone itself is part of the * thumb's drawing (jet.tsx); this draws what goes with it. * * - On arrival, the boom: three ring shocks spread from the jet, a beat apart, and a shock front, a *   hard bright edge with a glow behind it, sweeps the whole track from the jet to the left end. * - The track becomes a streaked sky: thin wind lines rush left past the jet, fast, and two *   contrails trail from its wing tips, swelling and drifting as they age. * - Specks of condensation peel off the cone's rim and fall back; a faint ring leaves the jet every *   two seconds. */export const SONIC_REACH = [46, 34] as const;const ARRIVAL = 1100;/** Where the contrails start, around the thumb's middle: the swept wings' tips. */const TIPS = [-6, 7.2] as const;type Streak = { x: number; y: number; length: number; speed: number; alpha: number; width: number };type Speck = { x: number; y: number; vx: number; vy: number; age: number; life: number };
use-autoplay.ts
'use client';import { useCallback, useEffect, useRef, useState } from 'react';/* * When a demo may play on its own: while part of it is on screen and the tab is visible, never * under reduced motion, and not while the reader is using it. `take()` hands it to the reader; it * takes back over `idle` ms after their last input. */export function useAutoplay<T extends HTMLElement>(idle = 6000) {  const ref = useRef<T>(null);  const [visible, setVisible] = useState(false);  const [reduced, setReduced] = useState(true);  const [taken, setTaken] = useState(false);  const timer = useRef(0);  useEffect(() => {    const element = ref.current;    if (!element) return;    let onScreen = false;    const update = () => setVisible(onScreen && !document.hidden);    const observer = new IntersectionObserver(      ([entry]) => {        onScreen = entry.isIntersecting;
  • The thumb is the slider (role="slider"), named by the visible label, with aria-valuenow and an aria-valuetext of the step’s name (“Medium, recommended”). Arrows, Page Up/Down, Home and End move it; a click or drag on the track does too.
  • The focus ring is drawn around the brain, and the help button shows its explanation as a tooltip on hover and keyboard focus (aria-describedby).
  • The visible step name, the end words, the stops, the rays and the canvases are hidden from screen readers, which hear the slider’s value instead.
  • Under reduced motion the thumb jumps, the name swaps at once, there is no flash or burst, the last step shows a still sky, and the demo never plays.