Dynamic Price Calculations in CSS: No JavaScript Required
Introduction
CSS has evolved far beyond simple styling. With modern math functions and attribute selectors, you can perform numeric calculations directly in the browser, eliminating the need for JavaScript in many common scenarios. For example, you can calculate and display a discounted product price using only CSS — a task traditionally handled by scripts. This approach reduces page weight, improves performance, and simplifies the frontend stack.

Demo Overview: Streaming Subscription Discounts
Consider an interface showing several streaming services (Netflix, Disney+, HBO, etc.), each with a base price and an optional student discount of 20%. The user can toggle the discount for each service, and the price updates instantly — all without a single line of JavaScript. The demo relies on data-* attributes to store the original price and discount percentage, and CSS calc() with attr() to compute the final price.
Markup Structure
Each service is a list item containing a label for the service name and price, a checkbox to select the service, and another label for the discount toggle. The price element holds the base price and discount as data-price and data-discount attributes. Here’s a simplified example:
<li class="service">
<label>
<span>Netflix</span>
<div class="price" data-price="7.99" data-discount="0.2">$7.99</div>
<input type="checkbox" class="select-service">
</label>
<label>
<span>Apply Student Discount (20%)</span>
<input type="checkbox" class="apply-discount">
</label>
</li>
The discount toggle (.apply-discount) triggers the CSS that recalculates and displays the new price.
Performing the Calculation in CSS
When the discount checkbox is checked, we first strike through the original price using text-decoration: line-through. Then we compute the discounted price with the attr() function. As of now, attr() works only with content property, but future CSS will expand it to other properties. The formula is:
Discounted Price = data-price × (1 - data-discount)
Because attr() returns a string, we use calc() with attr() inside a --custom-property. The actual implementation requires a workaround until browsers fully support attr() in calc(). The demo uses a @property custom property to perform the math:
/* When discount is active */
.service:has(.apply-discount:checked) .price {
text-decoration: line-through;
--discounted-value: calc(attr(data-price number) * (1 - attr(data-discount number)));
content: "$" var(--discounted-value);
}
Note: The number type hint in attr() is part of the newer spec and not yet universally supported, so the demo uses a polyfill or alternative approach with @property and CSS custom properties.

Styling the Discounted Price
Once the discount is applied, we can style the new price separately using ::after or content. The original price remains visible but crossed out. Additional CSS can add a “Save X%” label by subtracting the discounted price from the original and formatting the difference. The demo uses a pseudo-element to insert “You save” messaging.
Considerations for Real-World Use
This technique showcases the power of modern CSS, but it relies on features that are still experimental or have limited browser support. As of 2025, attr() in calc() is not widely supported, and @property may not be present in all browsers. For production, you’d likely still use JavaScript for such calculations, but the CSS-only approach is an excellent benchmark for how the platform is evolving.
- Performance benefits: No script execution, no reflow beyond what CSS already manages.
- Accessibility: Ensure screen readers can interpret dynamic content changes (use
roleandaria-liveregions if needed). - Fallback: Provide a static price with JavaScript enhancement layered on top.
Conclusion
CSS math is no longer limited to layout — it can compute real-world values like discounted prices. While full browser support is still maturing, experimenting with these techniques today prepares developers for a future where style and logic merge seamlessly. The streaming-service demo is just one example; similar approaches can handle progress bars, score counters, or any numeric display driven by data attributes.
To explore the full code and see it in action, check out the CodePen embed in the original article.
Related Articles
- Accelerating JavaScript Startup: A Hands-On Guide to V8's Explicit Compile Hints
- Color Palette Alternatives for Vanilla CSS: A Q&A Guide
- V8 Update Doubles JSON.stringify Performance – What Developers Need to Know
- Modernizing Your React Build Pipeline: From Webpack to Vite
- Building Apple Vision Pro's Scroll Animation with Pure CSS: A Step-by-Step Replication
- Understanding the Web's Missing Structure: A Q&A on the Block Protocol and Semantic Web
- Unlocking Dynamic Design: The Evolution of Native Randomness in CSS
- How to Choose a JavaScript Module System for Your Application Architecture