Skip to main content

RingProgress

Overview​

The RingProgress component draws a value as a ring that fills as it grows, with anything you pass shown in the middle.

Reach for it where one number wants a picture that takes little room: an upload or an import on its way, a goal on a dashboard, storage used, or a score. With sections it draws several values around one ring, such as storage by kind of file.

It's a progress bar for a task moving toward done, or a meter with isMeter for a level within a range. A screen reader reads its label with its value. For work with no value yet, use Loader.

info

Bulma has no ring, so RingProgress brings its own styles, in bestax.css and its variants, or in extras.css next to your own Bulma.


Import​

import { RingProgress } from '@allxsmith/bestax-bulma';

Usage​

Basic Usage​

Give it a value from 0 to 100 and a label, and put the number in the middle as its children.

<RingProgress value={72} label="Upload" color="success">
  72%
</RingProgress>

Out of Something Else​

max sets the value that fills the ring. A screen reader reads the value as a percentage, so say it in your own words with aria-valuetext.

<RingProgress
  value={7}
  max={10}
  label="Weekly goal"
  aria-valuetext="7 of 10 tasks"
>
  7/10
</RingProgress>

A Level, Not a Task​

isMeter makes it a meter: a level within a range, such as storage used or a score, rather than a task moving toward done. A screen reader says which.

<RingProgress isMeter value={64} label="Storage used" color="info" size="large">
  64%
</RingProgress>

Sections​

sections draws several values around one ring, clockwise from the top, each with its own color and label. A screen reader can't take several values from one element, so the ring is an image whose name lists them: this one is "Storage: Documents 40%, Photos 15%, Video 15%". A legend built from Tags says the same on the page.

<>
  <RingProgress
    label="Storage"
    size="medium"
    sections={[
      { value: 40, color: 'info', label: 'Documents' },
      { value: 15, color: 'warning', label: 'Photos' },
      { value: 15, color: 'danger', label: 'Video' },
    ]}
  >
    70%
  </RingProgress>
  <Tags mt="3">
    <Tag color="info">Documents 40%</Tag>
    <Tag color="warning">Photos 15%</Tag>
    <Tag color="danger">Video 15%</Tag>
  </Tags>
</>

Colors​

color takes the colors Progress does. Without one, the arc is the primary color.

<>
  <RingProgress value={70} label="Primary" color="primary" mr="3" />
  <RingProgress value={70} label="Link" color="link" mr="3" />
  <RingProgress value={70} label="Info" color="info" mr="3" />
  <RingProgress value={70} label="Success" color="success" mr="3" />
  <RingProgress value={70} label="Warning" color="warning" mr="3" />
  <RingProgress value={70} label="Danger" color="danger" />
</>

Sizes and Rounded Ends​

size is small, medium or large, and isRounded rounds the ends of the arc.

<>
  <RingProgress value={60} label="Small" size="small" isRounded mr="4" />
  <RingProgress value={60} label="Normal" isRounded mr="4" />
  <RingProgress value={60} label="Medium" size="medium" isRounded mr="4" />
  <RingProgress value={60} label="Large" size="large" isRounded />
</>

A Value That Changes​

The arc slides to a new value, and doesn't when the user has asked for less motion. A ring doesn't announce a change on its own: say when an upload finishes in a role="status" region.

function example() {
  const [value, setValue] = useState(20);
  return (
    <>
      <RingProgress value={value} label="Upload" color="success" mb="3">
        {value}%
      </RingProgress>
      <Paragraph role="status">
        {value >= 100 ? 'Upload complete' : ''}
      </Paragraph>
      <Button size="small" onClick={() => setValue(v => Math.min(100, v + 20))}>
        Move it on
      </Button>
    </>
  );
}


Accessibility​

  • Roles: role="progressbar" by default, role="meter" with isMeter, and role="img" with sections, on the ring itself, named by label (or aria-label, or aria-labelledby with a visible heading's id). A development build warns when it has no name.
  • Values: a progressbar or a meter has aria-valuenow, aria-valuemin and aria-valuemax, read as a percentage unless aria-valuetext says otherwise. With sections, use label, since a name from aria-labelledby would replace the one that lists the values.
  • The drawing and the middle are hidden from assistive tech, which reads the value from the ring, so nothing is heard twice.
  • Contrast: against the track, the primary and link arcs clear WCAG 1.4.11's 3:1 on a light page and the info, success, warning and danger arcs on a dark one. The others fall short in that scheme, as they do in Bulma's progress bar, so show the value as text in the middle where it matters.
  • Forced colors: the arcs take the system's text color and the track its gray, and sections get gaps between them, since they're one color there.
  • Motion: the arc doesn't slide under prefers-reduced-motion: reduce.
  • Not live: a changing value announces nothing on its own.

  • Progress: The same value as a bar.
  • Loader: For work with no value yet.
  • Tags: A legend for a ring in sections.
  • Helper Props: Bulma helper props for spacing, color, etc.

Additional Resources​


Props​

PropTypeDefaultDescription
classNamestring—Additional CSS classes to apply.
valuenumber—How far along it is, from 0 to max. Values outside that range are drawn, and read, at the nearest end. Ignored with sections.
maxnumber100The value that fills the ring.
sections{ value: number; color?: RingProgressColor; label: string }[]—Several values around one ring, in order clockwise from the top, each with its own color and label. The ring is then an image whose name writes them out, such as "Storage: Documents 40%, Photos 15%", since a screen reader can't take several values from one element. A total over max fills the ring and stops.
labelstring—What it shows, such as "Upload" or "Storage". It names the ring, so it's read with the value. A development build warns when the ring has no name.
isMeterbooleanfalseA level within a range, such as storage used or a score, rather than a task moving toward done: role="meter" in place of role="progressbar". Ignored with sections.
colorRingProgressColor—The arc's color, and the color of sections without one.
size'small' | 'medium' | 'large'—The ring's size.
isRoundedbooleanfalseRounds the ends of the arc.
childrenReact.ReactNode—What sits in the middle of the ring, such as the value as text. It's hidden from assistive tech, which reads the value from the ring itself, so the value isn't heard twice. Say anything more, such as "7 of 10 tasks", in aria-valuetext.
textColorBulma color | 'inherit' | 'current'—Text color helper.
bgColorBulma color | 'inherit' | 'current'—Background color helper.
refReact.Ref<HTMLDivElement>—Ref forwarded to the root <div>.
aria-labelledbystring—The id of a visible heading that names the ring, in place of label. With sections, use label, since a name from here replaces the one that lists the values.
aria-valuetextstring—What a screen reader says for the value, such as "7 of 10 tasks". Without it, the value is read as a percentage.
...All standard <div> attributes and Bulma helper props—See Helper Props

Types:

  • RingProgressColor: 'primary' | 'link' | 'info' | 'success' | 'warning' | 'danger' | 'black' | 'dark' | 'light' | 'white' — The Bulma colors an arc takes, the ones Progress takes.

CSS & Sass Variables​

RingProgress registers these variables on its own .ring-progress element. Override them there (or via className) — a value set on an ancestor is only inherited, and loses to the component-level declaration. See Theme.

CSS VariableSass VariableDefault
--bulma-ring-progress-size$ring-progress-size5rem
--bulma-ring-progress-thickness$ring-progress-thickness10
--bulma-ring-progress-track-color$ring-progress-track-colorvar(--bulma-border-weak)
--bulma-ring-progress-arc-color$ring-progress-arc-colorvar(--bulma-primary)
--bulma-ring-progress-duration$ring-progress-durationvar(--bulma-duration)
--bulma-ring-progress-label-color$ring-progress-label-colorvar(--bulma-text-strong)
--bulma-ring-progress-label-weight$ring-progress-label-weightvar(--bulma-weight-semibold)
--bulma-ring-progress-label-size$ring-progress-label-sizecalc(var(--bulma-ring-progress-size) / 5)