Kousa4 Stack
ArticlesCategories
Web Development

CSS Calculations: How to Build a Live Discount Price Display Without JavaScript

Published 2026-05-16 10:57:27 · Web Development

Introduction

CSS has evolved far beyond simple colors and layout. With modern features like calc(), attr(), custom properties, and the :has() selector, you can perform real-time calculations and display dynamic information—all without a single line of JavaScript. In this article, we'll explore how to compute and show discounted prices using only CSS, a technique that can enhance e-commerce interfaces or subscription selectors while keeping performance lean.

CSS Calculations: How to Build a Live Discount Price Display Without JavaScript
Source: css-tricks.com

The Power of CSS Math

CSS math functions like calc() allow you to combine lengths, percentages, and numbers directly in stylesheets. Combined with the attr() function (which reads HTML attribute values) and custom properties, you can store numeric data in the markup and perform arithmetic in the style layer. This opens up possibilities such as showing progress percentages, computing proportions, or—as we'll see—calculating a sale price.

Building a Discount Calculator in CSS

Markup Setup

Consider a list of streaming services (Netflix, Disney+, HBO, etc.), each with a full price and a student discount offer. We store the base price and discount percentage as data-* attributes on the price element. The user can toggle the discount via a checkbox.

<li>
  <label>
    <span>Netflix</span>
    <div class="ott-price" data-price="7.99" data-discount="0.2">$7.99</div>
    <input type="checkbox" class="is-ott-selected">
  </label>
  <label>
    <span>Apply Student Discount (20%)</span>
    <input type="checkbox" class="is-ott-discounted">
  </label>
</li>

The discount toggle triggers the calculation. When the discount checkbox is checked, we want to show the original price with a strikethrough and display the new, lower price.

CSS Calculations

First, we apply the strikethrough when the discount is active:

.ott:has(.is-ott-discounted:checked) .ott-price {
  text-decoration: line-through;
}

Next, we compute the discounted price using calc() and attr(). We store the result in a custom property and then display it as a pseudo-element:

.ott:has(.is-ott-discounted:checked) .ott-price::after {
  --discount: attr(data-discount number);
  --price: attr(data-price number);
  --new-price: calc(var(--price) - (var(--price) * var(--discount)));
  content: "$" var(--new-price);
  /* additional styling to position it */
}

Note: attr() with a type (e.g., number) is part of the CSS Values Level 5 specification and may not be fully supported yet. You can also use custom properties set via inline styles or preprocessors as a fallback.

Styling the Output

Position the discounted price next to the crossed-out original. Use flexbox or grid for alignment. For example:

CSS Calculations: How to Build a Live Discount Price Display Without JavaScript
Source: css-tricks.com
.ott-price {
  display: inline-flex;
  gap: 0.5rem;
}
.ott-price::after {
  color: green;
  font-weight: bold;
}

You can also add an additional label like “Now only $X.XX” by introducing more pseudo-elements or extra markup.

Complete Demo

A full working example (as shown in the original CodePen) includes multiple services, selection checkboxes, and discount toggles. Clicking the student discount checkbox instantly updates all prices for that service. The CSS handles all calculations and styling, while JavaScript is only used for demo niceties (e.g., total sum), but the core discount logic is pure CSS.

Limitations and Browser Support

This technique relies on bleeding-edge CSS features:

  • :has() selector – supported in most modern browsers (Chrome, Safari, Firefox) as of 2024.
  • attr() with type – not yet widely supported; you may need to use data- attributes with units (e.g., data-price="7.99" read as a string) and convert within calc() using tricks like var(--price) * 1.
  • Dynamic content with attr() – only works for strings; for numbers you need the typed attr().

Because of these limitations, this approach is best suited for progressive enhancement or prototyping. For production, consider using CSS custom properties set via JavaScript for maximum compatibility. Nonetheless, the design pattern demonstrates how much logic can move into the styling layer.

Conclusion

CSS is no longer just about looks—it can actively compute and present data. By combining calc(), custom properties, and advanced selectors, you can create interactive discount displays that reduce reliance on JavaScript. While browser support for the most modern features is still maturing, experimenting with these techniques prepares you for a future where CSS takes on even more application logic. Next time you need to show a sale price, consider whether you can do it with style sheets alone.