Odometer

A Web Component that displays a numeric value with a smooth rolling animation — similar to a mechanical odometer or number counter. It’s designed for use in badges, notifications, dashboards, or any place where numbers change dynamically.

Code
<bl-odometer></bl-odometer>

<div class="btn-group" role="group">
    <button
        class="btn blue-btn-soft-secondary"
        onclick="this.parentNode.previousElementSibling.value--"
        aria-label="Decrement"
    >
        ➖
    </button>
    <button
        class="btn blue-btn-soft-secondary"
        onclick="this.parentNode.previousElementSibling.value++"
        aria-label="Increment"
    >
        ➕
    </button>
</div>

Styled as badge

Code
<button type="button" class="btn btn-outline-primary">
    <span aria-hidden="true">🔔</span> Notifications
    <span class="badge bg-primary-subtle text-primary-emphasis">
        <bl-odometer value="2"></bl-odometer>
    </span>
</button>

<div class="btn-group" role="group">
    <button
        class="btn blue-btn-soft-secondary"
        onclick="this.parentNode.previousElementSibling.querySelector('bl-odometer').value--"
        aria-label="Decrement"
    >
        ➖
    </button>
    <button
        class="btn blue-btn-soft-secondary"
        onclick="this.parentNode.previousElementSibling.querySelector('bl-odometer').value++"
        aria-label="Increment"
    >
        ➕
    </button>
</div>

Custom max number

Code
<bl-odometer value="89" max="99"></bl-odometer>

<div class="btn-group" role="group">
    <button
        class="btn blue-btn-soft-secondary"
        onclick="this.parentNode.previousElementSibling.value--"
        aria-label="Decrement"
    >
        ➖
    </button>
    <button
        class="btn blue-btn-soft-secondary"
        onclick="this.parentNode.previousElementSibling.value++"
        aria-label="Increment"
    >
        ➕
    </button>
</div>

Style based on change

When the value changes, the element will get attribute data-change="up" or data-change="down" depending on if the number increased or decreased. This allows you to add styling based on the new state. After 3 seconds the attribute will be reset.

Code
<span class="badge rounded-pill bg-body-tertiary text-body border">
    <style>
        @scope {
            :scope {
                transition:
                    background-color 0.3s,
                    color 0.4s;
            }

            :scope:has([data-change="up"]) {
                background-color: var(--bs-success-bg-subtle) !important;
                color: var(--bs-success-text-emphasis) !important;
                border-color: var(--bs-success-bg-subtle) !important;
            }

            :scope:has([data-change="down"]) {
                background-color: var(--bs-danger-bg-subtle) !important;
                color: var(--bs-danger-text-emphasis) !important;
                border-color: var(--bs-danger-bg-subtle) !important;
            }

            bl-odometer[data-change="up"]::after {
                content: "↑";
            }
            bl-odometer[data-change="down"]::after {
                content: "↓";
            }
        }
    </style>

    <bl-odometer></bl-odometer>
</span>

<div class="btn-group" role="group">
    <button
        class="btn blue-btn-soft-secondary"
        onclick="this.parentNode.previousElementSibling.querySelector('bl-odometer').value--"
        aria-label="Decrement"
    >
        ➖
    </button>
    <button
        class="btn blue-btn-soft-secondary"
        onclick="this.parentNode.previousElementSibling.querySelector('bl-odometer').value++"
        aria-label="Increment"
    >
        ➕
    </button>
</div>

Change transition duration

You can change the duration using CSS variable --bl-odometer-duration. Default value is 0.4s. In this example it is set to 5s:

Code
<bl-odometer style="--bl-odometer-duration: 5s"></bl-odometer>

<div class="btn-group" role="group">
    <button
        class="btn blue-btn-soft-secondary"
        onclick="this.parentNode.previousElementSibling.value--"
        aria-label="Decrement"
    >
        ➖
    </button>
    <button
        class="btn blue-btn-soft-secondary"
        onclick="this.parentNode.previousElementSibling.value++"
        aria-label="Increment"
    >
        ➕
    </button>
</div>

Grab the code

When you don't use the entire Blue Web library, you can also just copy and paste the required code into your own project.

  1. Copy and paste the following code into your project. Source: @/src/js/odometer.ts