Skip to content
txbrendan/ui

Scale Question

beta

A question answered on a short worded scale, for check-ins, like "Poor, Broken, OK, Good, Great". One answer, as cards in a row with the dot above each word. All ink, so no end of the scale looks better.

Preview

check-in

Sleep

Last three nights

Workload

This week

Sleep not answered · Workload 3

states

Effort

This task

Mood
Sleep

Last three nights

size

Sleep, sm
Sleep, md
Sleep, lg

Install

  1. 1. Add the registry to components.json, once per app. Without it, the shadcn CLI looks up @txbrendan in a public list that txbrendan/ui does not control.

    npx shadcn@latest registry add "@txbrendan=https://txbrendan.dev/r/{name}.json"
  2. 2. Add the theme, once per app. It replaces your color tokens with ones checked for contrast.

    npx shadcn@latest add @txbrendan/theme
  3. 3. Add the component.

    npx shadcn@latest add @txbrendan/scale-question

Use it for

  • Check-ins: sleep, workload, mood, how hard a task felt.
  • Name the question in label, and say over what time in hint, like "Last three nights".
  • Give up to five steps, lowest first. Words, not numbers: "None, Light, Some, Busy, Too much".
  • The answer is the step's number, 1 to the number of steps.
  • Keep each word short. Five cards share a phone's width, and a long word wraps.

Avoid it for

  • A scale with an end that is good or bad by color. This one stays ink on purpose.
  • More than five steps, or a number scale like 1 to 10. Use a slider or a number stepper.
  • Options that need a hint or a price each. Use selection cards.

Props

  • steps

    The words, lowest first. Up to five.

    string[]

  • value

    The step's number, 1 to steps.length, or null for no answer. Use with onValueChange to control it.

    number | null

  • defaultValue

    The step's number at first render, when you do not control it.

    number | null

  • onValueChange

    Called with the step's number when someone picks one.

    (value: number) => void

  • label

    Names the question. TypeScript requires this, aria-label, or aria-labelledby.

    ReactNode

  • hint

    Over what time, like "Last three nights". It describes the group.

    ReactNode

  • error

    Replaces the hint, in red, and turns every card's edge red.

    ReactNode

  • size

    Text and dot size.

    "sm" | "md" | "lg" · default "md"

  • disabled

    Blocks choosing.

    boolean · default false

  • ref

    Reaches the group. Focusing it focuses the answer, or the first step.

    Ref<HTMLDivElement>

  • id

    Lands on the group, with aria-* and data-*. Its aria-describedby joins the note's.

    string

  • className

    Classes for the frame around the label, the cards, and the note.

    string

Accessibility

  • A radio group named by the label, with a radio in each card named by its word.
  • Tab lands on the answer, or the first step. The arrow keys move and choose.
  • Screen readers hear the word and where it sits, like "Some, 3 of 5".
  • The hint or the error describes the group. An error is announced once when it appears.
  • A chosen card shows a dot and a doubled edge, so the answer does not rest on color.

Examples

Answered, with a hint

<ScaleQuestion label="Sleep" hint="Last three nights" steps={["Poor", "Broken", "OK", "Good", "Great"]} defaultValue={4} />

Unanswered, with a hint

<ScaleQuestion label="Workload" hint="This week" steps={["None", "Light", "Some", "Busy", "Too much"]} />