Skip to content
caliper ui docs

Components

Mood slider

A satisfaction rating as a face that changes with the slider: the mouth bends from a frown to a smile, the eyes soften and the colour warms from red through amber to green, with the word for each step beneath.

Free
pnpm dlx shadcn@latest add @caliper/mood-slider

Quick check-in

Drag the slider or use the arrow keys.

Saved: Good
Installs components/ui/mood-slider.tsxDepends on none beyond ReactMotion none in component source

When to use

Use it for

  • Satisfaction and effort surveys after support, onboarding or a purchase.
  • Check-ins where a friendly control gets more answers.

Skip it when

  • Precise ratings; use `Rating` or a number scale.
  • Serious or sensitive contexts where a cartoon face is out of place.

Decisions

Why it is built this way, so you know what to keep when you change your copy.

Behaviour

Continuous, then a step
Dragging bends the face smoothly and lets go on the nearest step; arrow keys move one step.

Styling

Face and word agree
Shape, colour and the word all say the same thing, so it reads without colour.

Usage

A native range
It is a range input with each step's word as its value text.

Installation

terminal
pnpm dlx shadcn@latest add @caliper/mood-slider

Adds its 2 files to components/ui/, each with its own stylesheet, and installs their packages. No account needed. The first part also adds Caliper UI’s tokens; import them once as in Installation.

Usage

This is the code behind the preview above.

example.tsx
import { useState, type ReactNode } from "react"
import { Badge } from "@/components/ui/badge"
import { MoodSlider } from "@/components/ui/mood-slider"

function Panel({ title, context, className = "max-w-md", children }: { title: string; context: string; className?: string; children: ReactNode }) {
  return (
    <section className={`grid w-full gap-5 rounded-overlay border border-border bg-background p-6 shadow-resting max-sm:p-4 ${className}`}>
      <div className="grid gap-1">
        <p className="font-medium">{title}</p>
        <p className="text-sm text-muted-foreground">{context}</p>
      </div>
      {children}
    </section>
  );
}

export function MoodSliderDemo() {
  const [mood, setMood] = useState(3);
  return (
    <Panel title="Quick check-in" context="Drag the slider or use the arrow keys." className="max-w-sm">
      <MoodSlider label="How was your onboarding?" value={mood} onValueChange={setMood} />
      <Badge className="justify-self-center">Saved: {["Awful", "Poor", "Okay", "Good", "Great"][mood]}</Badge>
    </Panel>
  );
}

API reference

MoodSlider

component

Also accepts the native props of its root element and a ref.

PropTypeDefaultDescription
label *string—The question, e.g. "How was your onboarding?".
moodsstring[]["Awful", "Poor", "Okay", "Good", "Great"]One word per step, worst first.
valuenumber—
defaultValuenumber2
onValueChange((value: number) => void)—

Accessibility

It is built on native elements and keeps aria-hidden and aria-valuetext in step with state.

No custom key handling; Tab and the native activation keys do the work.

Motion

It declares no animation in its component source. Any transition comes from its stylesheet, which uses the duration tokens that collapse to instant under prefers-reduced-motion.

All Changing values examples