Computing and Displaying Discounted Prices in CSS
Summary
CSS-Tricks walks through calculating and displaying discounted prices entirely in CSS using the upgraded attr() function, CSS math functions like mod() and round(), and custom counters — no JavaScript required.
CSS-Tricks published a tutorial demonstrating how to compute and render discounted prices purely in CSS, eliminating the need for JavaScript on e-commerce-style pricing UIs. The technique leans on several newer CSS capabilities, some of which are not yet Baseline.
What’s actually new
The core trick hinges on the upgraded attr() function, which now accepts a type argument (e.g., attr(data-price number)) so that HTML data-* attribute values can be parsed as numbers rather than strings. This lets you feed them directly into calc() for arithmetic — in this case, Original Price * (1 - Discount). The article notes that this expanded attr() syntax is not yet Baseline, so production use today is premature.
To display the result, the demo uses CSS counters, which only handle integers. The workaround splits the computed price into dollars and cents using round(down, ...) and mod(..., 1) * 100, then stitches them back together inside content. The mod() function itself is noted as newly Baseline, while calc() and round() already have broad support. The :has() pseudo-class drives the toggling logic — when a “student discount” checkbox is checked, the original price gets a strikethrough and the computed sale price appears via ::after.
What it means for your config
This is a CSS authoring pattern, not a tooling or build-config change. That said, there are a few things worth flagging for your setup:
- PostCSS / Lightning CSS users: If your build pipeline processes or downgrades modern CSS, check whether your tooling handles the typed
attr()syntax. Most processors today likely pass it through untouched or may choke on it. Until browser support catches up, you probably want this behind a feature flag or progressive enhancement guard anyway. - Stylelint configs: Rules that lint
attr()usage or counter properties may need updates once teams start adopting this pattern. No immediate action needed, but worth a mental bookmark. - No build-tool migration required. This is purely a CSS-level technique; it doesn’t affect bundler configs, framework settings, or deployment pipelines.
The announcement doesn’t detail interaction with CSS preprocessors like Sass — the typed attr() call is native CSS, so it would need to live in plain .css files or be passed through verbatim by your preprocessor.
Recommended next step
Treat this as a “watch and experiment” item. The upgraded attr() is the linchpin, and until it lands in all major browsers, shipping this in production pricing UIs would be risky. But the underlying ideas — using CSS math to reduce JS surface area for presentational calculations — are worth internalizing now. Read the full tutorial to see the working demo and the exact counter-splitting technique; it’s a clean illustration of where CSS is headed for lightweight computed displays.
Read the full announcement on CSS-Tricks → Computing and Displaying Discounted Prices in CSS
More CSS-Tricks Updates
Get Ready For the Powerful CSS border-shape Property!
CSS-Tricks walks through the new border-shape property, which accepts the same values as clip-path but lets borders, box-shadow, and outline follow the resulting shape instead of being clipped away. Support is currently Chrome-only.
What's !important #14: Gap Decorations, random(), <select> field sizing, and More
CSS-Tricks rounds up recent CSS developments including gap decorations, the random() function, field-sizing for selects reaching Baseline, and modern theming patterns using @property and contrast-color().
translateZ()
CSS-Tricks published an almanac entry on the translateZ() CSS function, covering syntax, the requirement for perspective, and the old GPU-compositing trick.