CSS-Only Discount Calculation: Display Sale Prices Without JavaScript
CSS isn't just for styling—it can also perform useful numeric calculations directly in the browser. For example, you can compute and display discounted prices on e-commerce or subscription lists using only CSS, eliminating the need for JavaScript or server-side logic. This technique relies on modern CSS features like attr(), calc(), and the :has() pseudo-class. While browser support is still evolving, it's a powerful glimpse into the future of CSS-driven interactivity. Below, we answer common questions about implementing discount calculations with pure CSS.
How can CSS calculate and display a discounted price?
CSS can perform arithmetic using the calc() function combined with attr() to read numeric values from HTML data attributes. For instance, you store the original price and discount percentage as data-price and data-discount on an element. When a user toggles a discount checkbox, the :has() pseudo-class selects the parent container and applies a new calculation: calc(attr(data-price) * (1 - attr(data-discount))). The result is displayed via a CSS custom property (--discounted-price) and shown in a pseudo-element. This way, the discounted price updates automatically without any JavaScript.

What HTML structure is needed for CSS discount pricing?
You need a container element (e.g., a list item) with two key data attributes: data-price (the base price, e.g., 7.99) and data-discount (the discount rate as a decimal, e.g., 0.2 for 20%). Inside the container, include an element to display the original price (like a div with class ott-price) and a checkbox input to toggle the discount. For example:
<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 checkbox triggers the CSS calculation when checked.
How do you strike through the original price and show the discount amount?
When the .is-ott-discounted checkbox is checked, use the :has() pseudo-class to target the parent .ott container and apply a line-through to the original price element (.ott-price). Simultaneously, calculate the discount amount in a custom property and display it in a pseudo-element (e.g., ::after) showing the saved money. Example CSS:
.ott:has(.is-ott-discounted:checked) {
.ott-price {
text-decoration: line-through;
--discount-amount: calc(attr(data-price) * attr(data-discount));
}
.ott-price::after {
content: "You save $" var(--discount-amount);
}
}This visually communicates the original price and the saving simultaneously.
What is the formula to calculate the sale price using CSS calc()?
The sale price is computed as Original Price × (1 − Discount Rate). Using CSS, you extract the numeric values from attr(data-price) and attr(data-discount) and perform the calculation inside calc(). However, note that attr() currently returns a string; to use it in arithmetic, you must apply a type hint (e.g., attr(data-price number)) which is still experimental. A practical workaround is to set the values as CSS custom properties in the HTML via inline styles. In the future, direct calc(attr(data-price number) * (1 - attr(data-discount number))) will work. The result is stored in a custom property and displayed in a pseudo-element or a dedicated element.

Why use CSS instead of JavaScript for discount calculations?
Using CSS reduces reliance on JavaScript, leading to a lighter page, lower power consumption on devices, and no additional server requests. It also aligns with the principle of separating presentation from logic—discount information is part of the design, not interactive behavior. For simple, static discounts (like student rates on a subscription list), CSS provides a zero-latency, declarative solution. However, for complex or dynamic pricing (e.g., real-time cart totals), JavaScript remains necessary. CSS-based discounts shine in scenarios where you want to enhance visual feedback without adding script complexity.
What are the browser support concerns for this CSS technique?
This approach relies on several cutting-edge CSS features: attr() with numeric type hints, :has() pseudo-class, and calc() with attr(). As of now, :has() is supported in Chromium-based browsers (Chrome, Edge, Opera) but not in Firefox or Safari. attr() for numbers is still experimental and not widely implemented. Therefore, this technique is best used as a progressive enhancement—gracefully falling back to the static price if the browser doesn't support it. You can test for support with @supports or simply provide the original price as a fallback. As browser support improves, such calculations will become more reliable.
Can this technique be used for multiple products simultaneously?
Yes, you can apply the same CSS structure to multiple list items, each with its own data-price and data-discount. The :has() selector targets each container independently based on its checkbox state. For example, a list of streaming services (Netflix, Disney+, HBO) can each have their own student discount toggle. The CSS rules are generic and apply to all items, so each service's price updates independently when its checkbox is toggled. This makes the technique scalable for product listings, pricing tables, or any set of items with per-item discount controls.
Related Articles
- Optimizing Large-Scale Diff Rendering: A Step-by-Step Performance Guide
- Why I Left Apple Music Behind After Five Years for YouTube Music
- Making the Web Smarter: How the Block Protocol Simplifies Semantic Markup
- 10 Reasons Why YouTube Music Won Me Over After 5 Years with Apple Music
- Decoding Reality: A Step-by-Step Guide to the Boltzmann Brain Paradox
- From Tailwind to Vanilla CSS: Structuring Styles Without a Framework
- The Real Cost of Google's Prompt API: A Developer's Guide to Understanding the Risks
- Speed Up Page Loads with V8's Explicit Compile Hints: A Practical Guide