Leaderboard

Top sellers

Revenue this month

  1. Amelia Brooks Enterprise $84,200 –No change
  2. Noah Fischer Mid-market $72,450 ▲ 2 places up
  3. Priya Raman Enterprise $66,900 ▼ 1 places down
  4. Lucas Moreau SMB $53,800 ▲ 1 places up
  5. Hana Sato Mid-market $48,700 ▼ 2 places down
@use 'sprucecss/scss/spruce' as *;

.leaderboard {
  background-color: color('surface');
  border: config('border-width-base', $display) solid color('border');
  border-radius: radius('lg');
  padding: spacer('m');
  @include layout-stack('m');

  &__header {
    @include layout-stack('xxs');
  }

  &__title {
    font-size: font-size('h5');
  }

  &__metric {
    color: color('muted');
    font-size: config('font-size-sm', $typography);
  }

  &__list {
    list-style: none;
    padding: 0;
    @include layout-stack('s');
  }

  &__item {
    align-items: center;
    column-gap: spacer('s');
    display: grid;
    grid-template-columns: 1.75rem auto minmax(0, 1fr) auto;
    row-gap: spacer('xxs');
  }

  &__rank {
    align-items: center;
    background-color: color('surface-muted');
    block-size: 1.75rem;
    border-radius: radius('pill');
    color: color('muted');
    display: flex;
    font-size: config('font-size-sm', $typography);
    font-variant-numeric: tabular-nums;
    font-weight: 700;
    justify-content: center;
  }

  &__item--top &__rank {
    background-color: color-alpha('primary', 0.14);
    color: color('primary');
  }

  &__person,
  &__result {
    display: flex;
    flex-direction: column;
    line-height: config('line-height-md', $typography);
    min-inline-size: 0;
  }

  &__result {
    align-items: flex-end;
    font-variant-numeric: tabular-nums;

    strong {
      color: color('heading');
    }
  }

  &__name {
    color: color('heading');
    font-weight: 600;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  &__team,
  &__change {
    color: color('muted');
    font-size: config('font-size-sm', $typography);
  }

  &__change {
    &--up {
      color: color('success', 'alert');
    }

    &--down {
      color: color('danger', 'alert');
    }
  }

  // The result as a share of the leader's.
  &__bar {
    background-color: color('surface-muted');
    block-size: 0.3rem;
    border-radius: radius('pill');
    grid-column: 3 / -1;
    overflow: hidden;

    &::before {
      background-color: color('primary');
      block-size: 100%;
      border-radius: inherit;
      content: '';
      display: block;
      inline-size: var(--value);
    }
  }
}
<section class="leaderboard" aria-labelledby="leaderboard-title">
    <header class="leaderboard__header">
        <h2 class="leaderboard__title" id="leaderboard-title">Top sellers</h2>
        <p class="leaderboard__metric">Revenue this month</p>
    </header>
    <ol class="leaderboard__list">
        <li class="leaderboard__item leaderboard__item--top" style="--value: 100%;">
            <span class="leaderboard__rank" aria-hidden="true">1</span>
            <span class="avatar avatar--sm"><img src="https://images.unsplash.com/photo-1494790108377-be9c29b29330?auto=format&amp;fit=crop&amp;w=96&amp;h=96&amp;q=70" alt="" width="40" height="40" loading="lazy"></span>
            <span class="leaderboard__person">
                <span class="leaderboard__name">Amelia Brooks</span>
                <span class="leaderboard__team">Enterprise</span>
            </span>
            <span class="leaderboard__result">
                <strong>$84,200</strong>
                <span class="leaderboard__change">–<span class="sr-only">No change</span></span>
            </span>
            <span class="leaderboard__bar" aria-hidden="true"></span>
        </li>
        <li class="leaderboard__item leaderboard__item--top" style="--value: 86%;">
            <span class="leaderboard__rank" aria-hidden="true">2</span>
            <span class="avatar avatar--sm"><img src="https://images.unsplash.com/photo-1500648767791-00dcc994a43e?auto=format&amp;fit=crop&amp;w=96&amp;h=96&amp;q=70" alt="" width="40" height="40" loading="lazy"></span>
            <span class="leaderboard__person">
                <span class="leaderboard__name">Noah Fischer</span>
                <span class="leaderboard__team">Mid-market</span>
            </span>
            <span class="leaderboard__result">
                <strong>$72,450</strong>
                <span class="leaderboard__change leaderboard__change--up">▲ 2<span class="sr-only"> places up</span></span>
            </span>
            <span class="leaderboard__bar" aria-hidden="true"></span>
        </li>
        <li class="leaderboard__item leaderboard__item--top" style="--value: 79%;">
            <span class="leaderboard__rank" aria-hidden="true">3</span>
            <span class="avatar avatar--sm"><img src="https://images.unsplash.com/photo-1438761681033-6461ffad8d80?auto=format&amp;fit=crop&amp;w=96&amp;h=96&amp;q=70" alt="" width="40" height="40" loading="lazy"></span>
            <span class="leaderboard__person">
                <span class="leaderboard__name">Priya Raman</span>
                <span class="leaderboard__team">Enterprise</span>
            </span>
            <span class="leaderboard__result">
                <strong>$66,900</strong>
                <span class="leaderboard__change leaderboard__change--down">▼ 1<span class="sr-only"> places down</span></span>
            </span>
            <span class="leaderboard__bar" aria-hidden="true"></span>
        </li>
        <li class="leaderboard__item" style="--value: 64%;">
            <span class="leaderboard__rank" aria-hidden="true">4</span>
            <span class="avatar avatar--sm"><img src="https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?auto=format&amp;fit=crop&amp;w=96&amp;h=96&amp;q=70" alt="" width="40" height="40" loading="lazy"></span>
            <span class="leaderboard__person">
                <span class="leaderboard__name">Lucas Moreau</span>
                <span class="leaderboard__team">SMB</span>
            </span>
            <span class="leaderboard__result">
                <strong>$53,800</strong>
                <span class="leaderboard__change leaderboard__change--up">▲ 1<span class="sr-only"> places up</span></span>
            </span>
            <span class="leaderboard__bar" aria-hidden="true"></span>
        </li>
        <li class="leaderboard__item" style="--value: 58%;">
            <span class="leaderboard__rank" aria-hidden="true">5</span>
            <span class="avatar avatar--sm"><img src="https://images.unsplash.com/photo-1544005313-94ddf0286df2?auto=format&amp;fit=crop&amp;w=96&amp;h=96&amp;q=70" alt="" width="40" height="40" loading="lazy"></span>
            <span class="leaderboard__person">
                <span class="leaderboard__name">Hana Sato</span>
                <span class="leaderboard__team">Mid-market</span>
            </span>
            <span class="leaderboard__result">
                <strong>$48,700</strong>
                <span class="leaderboard__change leaderboard__change--down">▼ 2<span class="sr-only"> places down</span></span>
            </span>
            <span class="leaderboard__bar" aria-hidden="true"></span>
        </li>
    </ol>
</section>

Leaderboard

A ranked list of people (or products, teams) by one metric, with the rank change and a bar against the leader's result.

Anatomy

  • .leaderboard__list is an ordered list: the rank comes from the order.
  • Every __item has a __rank badge (the top three are highlighted with --top), an Avatar, the __person (name and team), the __result (value and rank __change) and a __bar.

Accessibility and Usability

  • The visible rank number is decorative (aria-hidden), because the ordered list already announces "1 of 5"; without that, the rank would be read twice.
  • The rank change arrows are characters with hidden text ("2 places up"), and the colors only repeat what the words say.
  • Long names are truncated with an ellipsis, so the results stay aligned.

Technical Details

  • The bar width is the --value custom property: the result as a share of the leader's, in percent.
  • The result column aligns the numbers with font-variant-numeric: tabular-nums.

Documentation

Learn about Spruce CSS through our extensive documentation.

Components

Explore our UI library of accessible components built with Spruce CSS.

Blocks and Pages

Full-width page blocks and complete page templates, ready to copy.

Find us on GitHub

Did you find a bug? Have an idea or a question? Please open an issue to help us develop the project.