Calculate and Display Discounted Prices Using Only CSS

By

Introduction

CSS isn't just for styling—it can also perform calculations and display dynamic data without JavaScript. With modern CSS features like attr(), calc(), and the :has() selector, you can compute discounted prices directly in the browser. This eliminates the need for extra scripts, reduces latency, and saves browser resources. In this guide, you'll learn how to build a pricing interface similar to e-commerce sites, where a student discount is applied to subscription fees. We'll use only HTML and CSS, leveraging custom properties and logical selectors to show original prices, discounts, and final sale prices.

Calculate and Display Discounted Prices Using Only CSS
Source: css-tricks.com

What You Need

Step 1: Set Up the HTML Structure

Create a container for your product list. Each item will have a label with the service name, the base price stored in data-price, and the discount rate stored in data-discount. Use checkboxes to let users toggle selections and apply discounts.

<ul class="service-list">
  <li>
    <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>
  <!-- Repeat for Disney+, HBO, etc. -->
</ul>

Step 2: Style the Base Price and Discount Toggle

First, ensure the base price is displayed normally. Then, when the user checks the discount toggle, we'll cross out the original price and show the new discounted value. Use the :has() pseudo-class to detect the toggle state.

/* Initially show price normally */
.price { font-size: 1.2em; color: #333; }

/* When discount checkbox is checked inside the list item */
li:has(.apply-discount:checked) .price {
  text-decoration: line-through;
  color: #999;
}

Step 3: Retrieve Numeric Values from data-* Attributes

CSS can read data-* attributes using the attr() function. However, attr() currently returns a string. To use it in calculations, we must convert it to a number within a custom property. Combine attr() with calc() by assigning the attribute to a CSS variable.

li {
  --base-price: attr(data-price number);
  --discount: attr(data-discount number);
}

Note: The number type hint is still experimental. For practical use, you may need a @property registration or a fallback approach. We'll use a workaround in the next step.

Step 4: Calculate the Discounted Price

Once we have numeric values, we can compute the sale price: Original Price × (1 − Discount). Use calc() inside a custom property and then display that value using a pseudo-element.

li:has(.apply-discount:checked) .price::after {
  content: "$" calc(var(--base-price) * (1 - var(--discount)));
}

But attr() may not be perfectly supported. A robust alternative is to hardcode the discount in CSS variables or use a @property declaration:

@property --price-value {
  syntax: '<number>';
  initial-value: 0;
  inherits: false;
}

li {
  --price-value: attr(data-price number);
  --discount-value: attr(data-discount number);
}

Step 5: Display the Discounted Price

To show the sale price, we'll add a new element or a pseudo-element. Since we already have the .price div, we can append the discounted amount using ::after or create a separate span. For clarity, use a new span inside the list item that becomes visible only when the discount is applied.

Calculate and Display Discounted Prices Using Only CSS
Source: css-tricks.com
<div class="price-wrapper">
  <span class="original-price" data-price="7.99" data-discount="0.2">$7.99</span>
  <span class="sale-price"></span>
</div>

Then in CSS:

li:has(.apply-discount:checked) .sale-price::before {
  content: "Now $" calc(attr(data-price number) * (1 - attr(data-discount number)));
  display: block;
  color: green;
  font-weight: bold;
}

Step 6: Add Visual Feedback and Interactivity

Enhance the user experience with transitions, hover effects, and clear labels. When the discount is enabled, the original price turns gray and gets a strikethrough, while the new price appears in a prominent color. Use the :has() selector to style multiple elements simultaneously.

li:has(.apply-discount:checked) {
  background: #f0fff0;
  border-left: 4px solid #28a745;
}

li:has(.apply-discount:checked) .original-price {
  text-decoration: line-through;
  opacity: 0.6;
}

li:has(.apply-discount:checked) .sale-price {
  display: inline;
}

Step 7: Test for Browser Compatibility

Because :has() and numeric attr() are cutting-edge, test your implementation in Chrome Canary or with experimental flags. For production, consider a progressive enhancement approach—let JavaScript handle the calculation if CSS fails. You can also use @supports to conditionally load the CSS feature.

@supports selector(:has(*)) and (attr(data-price number)) {
  /* Your discount CSS here */
}

Tips for Best Results

Tags:

Related Articles

Recommended

Discover More

Ransomware in 2026: Quantum-Proof Strains Emerge as Defense Evasion Tactics Escalate10 Key Insights: How AI Diffusion Models Are Revolutionizing Drug DesignAerobic Exercise Triumphs as Top Remedy for Knee Arthritis Pain, Landmark Study FindsHow to Navigate the FDA's New Stance on Compounded Obesity and Diabetes Drugs: A Step-by-Step Guide10 Critical Insights into JavaScript's Date-Time Maelstrom — and How Temporal Will Fix It