Dots

Tier Ladder

A vertical ladder of levels with the current one marked, checks on reached ones, and limits, perks or progress per level.

  1. Basic, Completed

    Phone number · $250 a month
  2. Verified, Current level

    ID card and selfie
    Current
    Spend a month
    $10,000
    Send a day
    $2,500
    Withdraw a day
    $1,000
  3. Extended, Not reached

    Proof of income and address
    Next
    Steps done
"use client"

import { Button } from "@/components/ui/button"

The TierLadder component lays out levels in order, lowest first: verification tiers, referral levels or plans. Each level shows a number or a check, a name, one line of detail and an optional tag, and can hold its limits, perks, progress and an action. Use it when the order of the levels matters and the user needs to see where they are. For one limit on its own, use UsageMeter; for a pick between plans, use a choice tile group.

Installation

pnpm dlx shadcn@latest add @dots/tier-ladder

Usage

import {
  TierLadder,
  TierLadderItem,
  TierLadderLimit,
  TierLadderLimits,
} from "@/components/ui/tier-ladder"
<TierLadder aria-label="Verification levels">
  <TierLadderItem status="done" name="Basic" description="Phone number" />
  <TierLadderItem
    status="current"
    name="Verified"
    description="ID card and selfie"
    badge="Current"
  >
    <TierLadderLimits>
      <TierLadderLimit label="Spend a month" value="$10,000" />
      <TierLadderLimit label="Send a day" value="$2,500" />
    </TierLadderLimits>
  </TierLadderItem>
  <TierLadderItem name="Extended" description="Proof of income" />
</TierLadder>

Composition

Use the following composition to build a tier ladder:

TierLadder
└── TierLadderItem
    ├── TierLadderProgress
    ├── TierLadderLimits
    │   └── TierLadderLimit
    ├── TierLadderPerks
    │   └── TierLadderPerk
    └── any action, such as a Button

Features

  • Three statuses: done shows a check, current marks the user's level, upcoming shows the level number
  • A tag at the end of each header, filled on the current level
  • Limits as a grid of label and value tiles, perks as a short list
  • TierLadderProgress for steps or referrals toward a level, without the warning color of a spending limit
  • A level with no children stays a single collapsed row
  • Customizable styling through the className prop on every part

Perks and progress

Use TierLadderPerks for what a level adds, and TierLadderProgress for how far the user is from it.

  1. Friend, Completed

    1 invite
  2. Ambassador, Current level

    5 invites
    Current
    • $10 for each friend who joins
    • Free transfers abroad
  3. Partner, Not reached

    20 invites
    Invites12 more to reach Partner
    • $20 for each friend who joins
    • Priority support
"use client"

import {

In review

When the next level is waiting on a check, keep it upcoming, tag it with badge, and set statusLabel so screen readers hear the same thing.

  1. Verified, Current level

    ID card and selfie
    Current
  2. Extended, In review

    Proof of income and address
    In review

    Documents received. Usually takes up to 1 day.

  3. Business, Not reached

    Company documents
"use client"

import {

Level numbers

The marker shows the position in the list by default. Pass level when the list starts mid-ladder, for example plans from the user's current one up.

  1. Plus, Current level

    $4.99 a month
    Current
  2. Premium, Not reached

    $9.99 a month
  3. Metal, Not reached

    $19.99 a month
"use client"

import {

Accessibility

TierLadder renders an ordered list and each TierLadderItem a list item, so the order and count of levels are announced.

Name the ladder

The list has no visible title of its own. Give TierLadder an aria-label, or point aria-labelledby at a nearby heading.

<TierLadder aria-label="Verification levels">{/* ... */}</TierLadder>

Status

The marker is hidden from assistive tech. The level name is a heading that ends with its status for screen readers only: "Completed", "Current level" or "Not reached". The current level also gets aria-current="step". When badge says something else, such as "In review", pass the same text as statusLabel.

Headings

Each level name is an h3 by default. Set headingLevel so it fits the page outline.

Progress

TierLadderProgress is a meter. Screen readers hear its value from value and max, such as "8 of 20", never from valueText. Pass a valueLabel to add what the numbers lack, such as "8 of 20 invites" or "1 of 3 steps done".

API Reference

TierLadder

The ordered list of levels. Accepts all props of <ol>.

PropTypeDefaultDescription
aria-labelstring-Names the ladder for screen readers.
classNamestring-Additional classes to apply to the list.

TierLadderItem

One level. Accepts all props of <li> except title.

PropTypeDefaultDescription
nameReactNode-Name of the level. Required.
descriptionReactNode-One line under the name.
status"done" | "current" | "upcoming""upcoming"Reached, the user's level, or ahead.
levelReactNodeposition in the listWhat the marker shows for a level not yet reached.
badgeReactNode-Short tag at the end of the header.
statusLabelstringfrom statusOverrides the status read by screen readers.
headingLevel2 | 3 | 4 | 5 | 63Heading level of the name.
childrenReactNode-Limits, perks, progress or an action. Leave empty for a single row.

TierLadderLimits

A grid of key figures for a level. Accepts all props of <dl>.

PropTypeDefaultDescription
classNamestring-Additional classes to apply to the grid.

TierLadderLimit

One figure. Accepts all props of <div> except children.

PropTypeDefaultDescription
labelReactNode-What the figure is, such as "Spend a month". Required.
valueReactNode-The figure. Pass a MoneyAmount for money. Required.

TierLadderPerks

A list of what a level adds. Accepts all props of <ul>.

PropTypeDefaultDescription
classNamestring-Additional classes to apply to the list.

TierLadderPerk

One perk. Accepts all props of <li>.

PropTypeDefaultDescription
classNamestring-Additional classes to apply to the item.

TierLadderProgress

Progress toward a level. Renders a UsageMeter and accepts all of its props, with different defaults.

PropTypeDefaultDescription
size"default" | "sm""sm"The meter size.
warnAtnumber | falsefalseThe meter does not turn to warning by default.
...propsUsageMeterProps-Props spread to the underlying UsageMeter.