Price

Regular

$89.00

Sale

Sale price:$189.00Regular price:$229.00−17%

Starting price

From$49.00

Unit price

$12.90$2.58 / 100 g

Subscription

$24permonth Billed $288 yearly

With tax note

89,00 €inkl. MwSt., zzgl. Versand

Large

$1,299.00

Member price

$79.00Member price Regular price: $89.00

@use 'sprucecss/scss/spruce' as *;

.price {
  align-items: baseline;
  color: color('text');
  display: flex;
  flex-wrap: wrap;
  gap: spacer('xxs') spacer('xs');
  margin: 0;

  &__amount {
    color: color('heading');
    font-family: config('font-family-heading', $typography);
    font-size: font-size('h4');
    font-variant-numeric: tabular-nums;
    font-weight: 700;
    line-height: 1.2;
  }

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

  &__compare {
    color: color('muted');
    text-decoration-thickness: 1px;
  }

  &__badge {
    align-self: center;
    background-color: color-alpha('danger', 0.12, 'alert');
    border-radius: radius('pill');
    color: color('heading');
    font-size: 0.75rem;
    font-weight: 700;
    line-height: 1;
    padding: 0.35em 0.65em;
  }

  &__unit {
    color: color('muted');
    flex-basis: 100%;
    font-size: config('font-size-sm', $typography);

    abbr {
      border: 0;
      cursor: default;
      text-decoration: none;
    }
  }

  &__cents {
    font-size: 0.55em;
    vertical-align: super;
  }

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

  &--member &__badge {
    background-color: color-alpha('primary', 0.12);
    color: color('primary');
  }

  &--lg &__amount {
    font-size: font-size('h1');
  }
}

.price-demo {
  display: grid;
  gap: spacer('m');
  grid-template-columns: repeat(auto-fill, minmax(12rem, 1fr));

  &__item {
    border: config('border-width-base', $display) solid color('border');
    border-radius: radius('md');
    padding: spacer('s');
    @include layout-stack('xs');
  }

  &__label {
    color: color('muted');
    font-size: 0.75rem;
    font-weight: 700;
    letter-spacing: 0.05em;
    margin: 0;
    text-transform: uppercase;
  }
}
<div class="price-demo">
    <div class="price-demo__item">
        <p class="price-demo__label">Regular</p>
        <p class="price">
            <data class="price__amount" value="89.00">$89.00</data>
        </p>
    </div>
    <div class="price-demo__item">
        <p class="price-demo__label">Sale</p>
        <p class="price price--sale">
            <span class="sr-only">Sale price:</span>
            <data class="price__amount" value="189.00">$189.00</data>
            <s class="price__compare"><span class="sr-only">Regular price:</span> <data value="229.00">$229.00</data></s>
            <span class="price__badge">−17%</span>
        </p>
    </div>
    <div class="price-demo__item">
        <p class="price-demo__label">Starting price</p>
        <p class="price">
            <span class="price__prefix">From</span>
            <data class="price__amount" value="49.00">$49.00</data>
        </p>
    </div>
    <div class="price-demo__item">
        <p class="price-demo__label">Unit price</p>
        <p class="price">
            <data class="price__amount" value="12.90">$12.90</data>
            <span class="price__unit">$2.58 <abbr title="per">/</abbr> 100&nbsp;g</span>
        </p>
    </div>
    <div class="price-demo__item">
        <p class="price-demo__label">Subscription</p>
        <p class="price">
            <data class="price__amount" value="24.00">$24</data>
            <span class="price__suffix"><span class="sr-only">per</span><span aria-hidden="true">/</span>month</span>
            <span class="price__unit">Billed $288 yearly</span>
        </p>
    </div>
    <div class="price-demo__item">
        <p class="price-demo__label">With tax note</p>
        <p class="price" lang="de">
            <data class="price__amount" value="89.00">89,00&nbsp;€</data>
            <span class="price__unit">inkl. MwSt., zzgl. Versand</span>
        </p>
    </div>
    <div class="price-demo__item">
        <p class="price-demo__label">Large</p>
        <p class="price price--lg">
            <data class="price__amount" value="1299.00">$1,299<span class="price__cents">.00</span></data>
        </p>
    </div>
    <div class="price-demo__item">
        <p class="price-demo__label">Member price</p>
        <p class="price price--member">
            <data class="price__amount" value="79.00">$79.00</data>
            <span class="price__badge">Member price</span>
            <span class="price__unit">Regular price: $89.00</span>
        </p>
    </div>
</div>

Price

Price display patterns for shops and pricing pages: regular, sale, starting price, unit price, subscription, tax note, large and member prices.

Anatomy

  • .price: the wrapper (--sale, --member, --lg).
  • .price__amount: the price in a data element; __prefix ("From") and __suffix ("/month") go around it.
  • .price__compare: the original price in an s element; __badge for the discount or a label; __unit for the unit price or a note on its own line; __cents for raised decimals.
  • .price-demo only lays out the examples.

Accessibility and Usability

  • A strikethrough is not announced by most screen readers, so a sale price has hidden "Sale price:" and "Regular price:" labels; otherwise "$189 $229" is ambiguous.
  • The slash in "/month" is hidden and replaced with a hidden "per", because "slash month" sounds odd. In the unit price, the slash is an abbr with the "per" title.
  • The discount is text ("−17%"), and the sale color only reinforces it.
  • Use the lang attribute when the price format belongs to another language than the page (89,00 € in German).

Technical Details

  • The data element's value holds the machine-readable amount (89.00) next to the formatted text, which is handy for scripts, sorting and structured data.
  • Format the prices on the server or with Intl.NumberFormat (new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' })); don't build the currency format by hand, the symbol position and the separators depend on the locale.
  • In the EU, show the lowest price of the last 30 days next to a discount (the Omnibus directive); the __unit line fits it.
  • font-variant-numeric: tabular-nums keeps the digits aligned in lists and tables.

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.