webdevpuneet.com

Order Summary — Cart Totals HTML CSS JS Snippet

Order Summary · Cards · Plain HTML, CSS & JS · Live preview

CategoryCards

Copy & paste

Source Code

<div class="os-card">
  <div class="os-head">
    <h2 class="os-title">Order summary</h2>
    <span class="os-count" id="osCount">3 items</span>
  </div>

  <div class="os-items" id="osItems">
    <div class="os-item" data-price="89.00">
      <div class="os-thumb" style="background:linear-gradient(135deg,#6366f1,#8b5cf6)">👟</div>
      <div class="os-info">
        <div class="os-name">Aero Runner Sneakers</div>
        <div class="os-meta">Size 10 · Slate</div>
        <div class="os-qty">
          <button class="os-step" onclick="changeQty(this,-1)" aria-label="Decrease">−</button>
          <span class="os-qval">1</span>
          <button class="os-step" onclick="changeQty(this,1)" aria-label="Increase">+</button>
        </div>
      </div>
      <div class="os-side">
        <div class="os-line">$89.00</div>
        <button class="os-remove" onclick="removeItem(this)" aria-label="Remove">Remove</button>
      </div>
    </div>

    <div class="os-item" data-price="14.00">
      <div class="os-thumb" style="background:linear-gradient(135deg,#10b981,#059669)">🧦</div>
      <div class="os-info">
        <div class="os-name">Merino Crew Socks</div>
        <div class="os-meta">3-pack · Charcoal</div>
        <div class="os-qty">
          <button class="os-step" onclick="changeQty(this,-1)" aria-label="Decrease">−</button>
          <span class="os-qval">2</span>
          <button class="os-step" onclick="changeQty(this,1)" aria-label="Increase">+</button>
        </div>
      </div>
      <div class="os-side">
        <div class="os-line">$28.00</div>
        <button class="os-remove" onclick="removeItem(this)" aria-label="Remove">Remove</button>
      </div>
    </div>

    <div class="os-item" data-price="9.50">
      <div class="os-thumb" style="background:linear-gradient(135deg,#f59e0b,#f97316)">🧴</div>
      <div class="os-info">
        <div class="os-name">Sneaker Care Kit</div>
        <div class="os-meta">Cleaner + brush</div>
        <div class="os-qty">
          <button class="os-step" onclick="changeQty(this,-1)" aria-label="Decrease">−</button>
          <span class="os-qval">1</span>
          <button class="os-step" onclick="changeQty(this,1)" aria-label="Increase">+</button>
        </div>
      </div>
      <div class="os-side">
        <div class="os-line">$9.50</div>
        <button class="os-remove" onclick="removeItem(this)" aria-label="Remove">Remove</button>
      </div>
    </div>
  </div>

  <p class="os-empty" id="osEmpty">Your cart is empty.</p>

  <div class="os-promo">
    <input type="text" id="osPromo" placeholder="Promo code (try SAVE10)">
    <button onclick="applyPromo()">Apply</button>
  </div>
  <div class="os-promo-msg" id="osPromoMsg"></div>

  <div class="os-totals">
    <div class="os-row"><span>Subtotal</span><span id="osSubtotal">$0.00</span></div>
    <div class="os-row os-disc-row" id="osDiscRow"><span id="osDiscLabel">Discount</span><span id="osDiscount">−$0.00</span></div>
    <div class="os-row"><span>Tax (8%)</span><span id="osTax">$0.00</span></div>
    <div class="os-row os-total"><span>Total</span><span id="osTotal">$0.00</span></div>
  </div>

  <button class="os-checkout">Checkout</button>
</div>

Extend with AI

Take This Snippet Further With AI

The AI prompt for this one is for Patreon supporters ($3 a month), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.

· Become a supporter

What's included

Features

Single-pass recalculation
One recalc reads the live DOM — every item's data-price and quantity — and rewrites line totals, subtotal, tax, total, and count so numbers can never drift apart.
Clamped quantity steppers
changeQty uses Math.max(1, ...) so a line never hits zero by mistake; the dedicated Remove action handles deletion explicitly.
Animated row removal
A .removing class runs a slide-collapse keyframe; animationend deletes the node, and totals exclude removing rows immediately via :not(.removing).
Promo-code discount map
applyPromo looks codes up in a PROMOS object of percentages, sets discountRate, and shows green success or red error feedback.
Correct tax order of operations
Discount is subtracted from the subtotal first, then tax is computed on the discounted amount — the standard, audit-safe cart calculation.
Conditional discount row
The discount line only renders when a code is active and shows the exact percentage in its label ("Discount (15%)").
Consistent currency formatting
A single fmt helper formats every figure with toFixed(2), so all prices display two decimals uniformly.
Graceful empty state
Removing the last item adds an .empty class that shows an empty-cart message and disables the promo and checkout controls.

About this UI Snippet

Order Summary — Quantity Steppers, Promo-Code Discount & Live Subtotal / Tax / Total

Screenshot of the Order Summary snippet rendered live

The order summary is the panel shoppers stare at before they commit. It has to answer three questions instantly and correctly: what am I buying, can I change it, and what will it cost? Any lag, rounding error, or stale total erodes trust at the exact moment it matters most. This snippet implements a complete, self-recalculating order summary in plain HTML, CSS, and vanilla JavaScript: line items with quantity steppers, remove buttons, a promo-code field with live discount, and subtotal, tax, and total rows that update on every change.

Everything flows through one recalc function. Rather than tracking totals in scattered variables, recalc reads the current DOM — every non-removing line item, its data-price, and its quantity — and rewrites every dependent number in one pass. That single-source-of-truth approach is what guarantees the displayed total can never drift out of sync with the line items.

Quantity steppers with per-line totals

Each item has a − / + stepper around a quantity value. changeQty clamps the quantity to a minimum of 1 (Math.max(1, ...)) so a line can never go to zero by accident, updates the value, and calls recalc. recalc multiplies each item's unit price by its quantity, writes the per-line total, and accumulates the subtotal — so the line price and the cart total always move together.

Remove with exit animation

The Remove button adds a .removing class that runs an os-out keyframe (slide, fade, and collapse height to zero). An animationend listener then removes the row from the DOM and recalculates. Because recalc selects .os-item:not(.removing), the row stops counting toward totals the instant it begins animating out — the numbers update immediately while the visual exit plays.

Promo code with live discount

applyPromo reads the code, upper-cases it, and looks it up in a PROMOS map of percentage discounts. A valid code sets discountRate and shows a green confirmation; an invalid one resets the rate and shows a red message. The discount row only appears (.show) when a discount is active, and its label reflects the exact percentage ("Discount (10%)"). Crucially, the order of operations is correct: discount comes off the subtotal first, then tax is calculated on the discounted amount — the standard, defensible way to compute a cart total.

Correct tax and total order of operations

recalc computes discount = subtotal × rate, then tax = (subtotal − discount) × TAX_RATE, then total = subtotal − discount + tax. All money is formatted through a single fmt helper using toFixed(2) so every figure shows two decimal places consistently.

Empty-cart state

When the last item is removed, recalc adds an .empty class that reveals an "Your cart is empty" message and dims the promo and checkout controls — a graceful end state instead of a broken-looking panel of zeros.

Pair this summary with a checkout payment form on the same page, a quantity stepper on product pages, or a mini cart dropdown in the header.

Step by step

How to Use

  1. 1
    Paste HTML, CSS, and JSAn order summary card appears with three line items, a promo field, and subtotal / tax / total rows already computed from the items.
  2. 2
    Adjust a quantityClick + or − on any item — the per-line price, the item count, the subtotal, tax, and total all update together. Quantities never drop below 1.
  3. 3
    Apply a promo codeType SAVE10 and click Apply — a green message confirms 10% off, a discount row appears, and the total drops with tax recomputed on the discounted amount.
  4. 4
    Try an invalid codeType anything else and Apply — a red "Invalid promo code" message shows and no discount is applied.
  5. 5
    Remove an itemClick Remove — the row slides out and collapses, then disappears, and all totals recalculate instantly.
  6. 6
    Empty the cartRemove every item — the card switches to an "Your cart is empty" state and dims the promo and checkout buttons.

Real-world uses

Common Use Cases

Checkout page summary
The primary use — the right-hand totals panel of a checkout. Place it beside a checkout payment form so shoppers confirm what they pay.
Cart drawer and mini cart
Reuse the line-item and totals logic inside a slide-out cart. Combine with a mini cart header dropdown or a sticky cart drawer.
Subscription and plan checkout
Show plan, add-ons, and proration with a promo field for launch discounts. Pair with a subscription widget for billing controls.
Quote and invoice builders
Adapt line items into billable rows with quantity and rate; the same recalc powers a live invoice preview.
Event ticket ordering
List ticket tiers with quantity steppers and an early-bird promo code; the tax and total logic applies directly to ticketing carts.
Donation and bundle pages
Let users adjust quantities of bundled items and apply a match-code discount, with the running total reassuring them before they commit.

Got questions?

Frequently Asked Questions

Render the .os-item rows from your cart data (server-side or via a template loop), setting each data-price and quantity from the cart, then call recalc() once on load. The function reads whatever rows exist, so it works with one item or fifty without any other change.

Replace the PROMOS lookup in applyPromo with a fetch to your endpoint that returns the discount rate (or rejects the code). Apply the returned rate to discountRate and call recalc. Always re-validate the code server-side at checkout — never trust a client-side discount as the final price.

In most jurisdictions tax is charged on the amount the customer actually pays, so the discount is applied to the subtotal first and tax is computed on the reduced figure. This snippet uses tax = (subtotal − discount) × rate. If your local rules tax the pre-discount amount, move the tax calculation above the discount subtraction.

Add a shipping row to the totals and a PROMOS entry that represents a flat amount or a free-shipping flag rather than a percentage. In recalc, branch on the discount type: subtract a fixed value, or set shipping to zero, before computing the total. Keep all of it inside recalc so the single-source-of-truth model holds.

In React, hold the items array and discountRate in useState and derive subtotal, tax, and total with useMemo so they recompute automatically — no manual recalc. In Vue, make items reactive and use computed totals. In Angular, store items on the component and compute totals in getters. The card layout and animations port unchanged.