How to Compute and Display Discounts Purely in CSS: A Practical Guide
Introduction
CSS has evolved far beyond styling layouts and colors. Modern CSS now includes powerful mathematical capabilities that can handle real-world calculations—without relying on JavaScript or server-side logic. One compelling use case is displaying discounted prices on e‑commerce or subscription pages. By leveraging CSS math functions, calc(), custom properties, and the newer :has() selector, you can compute and present sale prices directly from data-* attributes. This approach reduces code complexity, eliminates network latency, and keeps the logic entirely in the browser’s rendering layer.

Setting Up the Markup
The foundation is a clean, semantic HTML structure that stores base price and discount values in data-* attributes. For example, a list of streaming services with student discounts:
<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 <br> 20%</span>
<input type="checkbox" class="is-ott-discounted">
</label>
</li>Here, data-price holds the original price (e.g., $7.99) and data-discount the discount fraction (0.2 = 20%). The checkbox toggles whether the discount is applied. This structure is simple, accessible, and keeps the business data embedded in the markup itself.
Calculating the Discounted Price with CSS
When the discount checkbox is checked, CSS takes over. Using the :has() pseudo‑class, we detect the checked state and then apply calculations via calc() and the attr() function.
Step 1: Strike Through the Original Price
First, visually indicate that the original price is no longer valid:
.ott:has(.is-ott-discounted:checked) {
.ott-price {
text-decoration: line-through;
}
}Step 2: Compute the New Price
Next, we use attr() to read the data attributes and calc() to calculate the discounted amount. However, attr() currently returns a string; to use it in calculations, we need to convert it to a number. A workaround is to use custom properties with attr() inside a calc() context, though browser support is still evolving. An alternative is to set the values as custom properties in CSS (e.g., using --price: 7.99 and --discount: 0.2) instead of data-* attributes. For the sake of this demonstration, we’ll assume a future where attr() works seamlessly with numeric types.
.ott:has(.is-ott-discounted:checked) {
.ott-price {
--n: calc(attr(data-price number) * (1 - attr(data-discount number)));
content: "$" var(--n);
}
}(Note: as of 2025, attr() with numeric type is not widely supported; this code illustrates the concept. For production, consider using CSS custom properties defined directly in the stylesheet.)
Step 3: Display the Discounted Price
To show the new price next to the struck‑through original, you might use a ::after pseudo‑element or a separate element. In the demo, the discounted price appears in a span that is conditionally shown when the checkbox is checked, using the :has() selector to toggle visibility.

Adding Interactivity
CSS alone can manage toggling states, but user interactions like checkboxes (or the :target pseudo‑class) are necessary to trigger the discount. The beauty lies in the fact that no JavaScript is needed to recalculate; the browser recomputes the CSS values automatically when the checkbox state changes. This leads to a responsive, lightweight UI that works even with JavaScript disabled.
Limitations and Browser Support
The approach demonstrated relies on cutting‑edge CSS features:
:has()– widely supported since 2023.attr()with numeric type – still experimental (Chrome 123+, behind flags).calc()with custom properties – fully supported.
Because of these gaps, the example is more of a proof of concept than a production‑ready solution. For now, developers typically rely on JavaScript to parse data attributes and update the DOM. However, as browser vendors implement the CSS Values and Units Level 4 specification, we can expect native numeric attr() to become standard.
The Future of CSS Math in E‑commerce
This technique shows that CSS can handle tasks previously reserved for scripts. As support matures, you’ll be able to:
- Calculate total cart amounts directly in CSS.
- Apply tiered discounts based on quantity.
- Show real‑time savings without a single line of JavaScript.
The result is a cleaner, more declarative front‑end that’s easier to maintain and faster to render. While we’re not there yet, experimenting with these methods today prepares us for tomorrow’s CSS.
Conclusion
Using CSS to compute and display discounted prices demonstrates the growing power of the styling layer. By combining semantic markup, custom properties, :has(), and mathematical functions, you can create interactive pricing displays that are both lightweight and resilient. Start experimenting now—because soon, CSS math will be an everyday tool for every web developer.
Related Articles
- Why the Subaru Impreza Is a Smarter Buy Than a New Honda Civic
- Web Developer Curates Top CSS Color Palettes After Abandoning Tailwind
- 5 Game-Changing Upgrades in Copilot Studio’s Switch to .NET 10 on WebAssembly
- New Open-Source Framework Plasmo Dramatically Simplifies Chrome Extension Development
- Achieving Fast Diff Line Rendering: GitHub's Performance Overhaul for Pull Requests
- Mastering JavaScript Startup Speed: How to Use V8's Explicit Compile Hints
- Developer's Quest for Alternative CSS Color Palettes Sparks Community Trend
- Build Chrome Extensions with Plasmo: Your Complete Q&A Guide