Restaurant Menu Item Customizer — Free Live-Total Order Card

Restaurant Menu Item Customizer · Cards · Plain HTML, CSS & JS · Live preview

Share & Support

What's included

Features

Three-part price calc
Base, size delta, and add-ons in one function.
Radios for exclusive size
Only one size active at a time.
Checkboxes for add-ons
Independent toggles each add their own price.
CSS :has() selected state
No class toggling needed for the highlight.
Clamped quantity stepper
Disables at 1 and 9 guest bounds.
Live total everywhere
One updateTotal() keeps every input in sync.
Add-to-order feedback
Button label briefly confirms the action.
Zero dependencies
Plain HTML, CSS, and JS, no CDN.

About this UI Snippet

Restaurant Menu Item Customizer — Sizes, Add-ons, and a Live Total

Screenshot of the Restaurant Menu Item Customizer snippet rendered live

The menu item customizer is the detail card food ordering apps show when you tap a dish — a base item you can size up, load with add-ons, and multiply by quantity, watching the total price update after every choice. This snippet builds it in plain HTML, CSS, and JavaScript, no dependencies.

Three price contributors, one calculation

The unit price is built from three sources: a fixed basePrice constant in JS, the checked size radio's data-delta (a price difference, which can be 0), and the sum of every checked add-on's data-price. calcUnitPrice() reduces all three into one number, then updateTotal() multiplies by quantity — so every possible combination of size, add-ons, and quantity resolves through the same two small functions.

Radios for size, checkboxes for add-ons — on purpose

Size is mutually exclusive (you can't order Regular and Large at once), so it's a radio group; add-ons are independent toggles, so they're checkboxes. Each price-affecting input carries its own data-delta or data-price, so the calculation logic never hardcodes an option's price — it just reads whatever's on the checked/selected inputs.

A visible selected state without extra JS

Both the radio and checkbox rows use the :has() selector — .mic-radio:has(input:checked) — to recolor the row's border and background when its input is checked, so the "selected" look is pure CSS reacting to native form state, not a class the JavaScript has to add and remove.

Quantity stepper feeding the same total

The quantity stepper works exactly like the reservation and room-picker patterns elsewhere in this library: clamped between 1 and 9, disabling at each bound, and calling the same updateTotal() every size, add-on, or quantity change touches.

A satisfying add-to-order moment

Clicking "Add to order" briefly swaps its label to "Added ✓" before reverting, a lightweight bit of feedback that doesn't require a toast library — just a setTimeout restoring the button's original markup.

Customizing it

Add a "spice level" radio group, a max-add-ons limit, or an out-of-stock disabled state for an add-on. Pair it with a quantity stepper, variant selector, or order summary further down the ordering flow.

Build with AI

Build, Understand, Optimize, and Extend It With AI

Instead of working out the price-calculation logic from scratch, paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain how calcUnitPrice() combines a fixed base price, a size radio's data-delta, and the sum of checked add-on data-price values into one number, and why the selected-row highlight uses the CSS :has() selector instead of JavaScript class toggling. It's also a strong assistant for extending the card — ask it to add a "spice level" radio group, cap the number of selectable add-ons, disable an add-on that's out of stock, or wire the Add to order button into a real cart array with line items. Use the conversation to adapt the calculation and cart logic to your app instead of treating this as a finished checkout system.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a "restaurant menu item customizer" card in plain HTML, CSS, and JavaScript with no external dependencies.

Requirements:
- A dish card with an image/emoji area, a title, and a short description.
- A size section using a radio group (Regular / Large / XL), where each radio input carries a data-delta price attribute (Regular is 0) and only one size can be selected.
- An add-ons section using checkboxes (e.g. extra egg, extra protein, a topping, a sauce), each carrying its own data-price attribute, where any combination can be checked independently of the others.
- A visual "selected" highlight (border color + background tint) on whichever radio or checkbox row is currently checked, implemented using the CSS :has() selector rather than JavaScript adding/removing a class.
- A quantity stepper (decrement/increment buttons around a live count) clamped between 1 and 9, disabling each button at its bound.
- A single calcUnitPrice() function that sums a fixed base price, the checked size's data-delta, and every checked add-on's data-price, and a single updateTotal() function that multiplies that by the quantity and updates a total display on the "Add to order" button — call updateTotal() from the size radios' change event, the add-on checkboxes' change event, and the stepper buttons.
- Clicking "Add to order" should briefly show a confirmation state (e.g. change the label to "Added ✓" for about a second) before reverting to show the button with the current total again.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.

Step by step

How to Use

  1. 1
    Paste HTML, CSS, and JSA dish card with size, add-ons, and stepper renders.
  2. 2
    Pick a sizeThe radio row highlights and price deltas apply.
  3. 3
    Toggle add-onsEach checked add-on adds its price to the total.
  4. 4
    Adjust quantityThe stepper multiplies the unit price live.
  5. 5
    Click Add to orderA brief "Added ✓" confirms the action.
  6. 6
    Wire it to a cartRead calcUnitPrice() and qty on add-to-order.

Real-world uses

Common Use Cases

Food delivery apps
Feed the total into an order summary.
Restaurant ordering kiosks
Customize a dish before adding to cart.
Coffee shop apps
Reuse size/add-ons for drink customization.
Product configurators
A sibling of variant selector.
Meal kit services
Let customers size and add extras to a box.
Catering order forms
Combine with a quantity stepper.

Got questions?

Frequently Asked Questions

calcUnitPrice() adds a fixed basePrice, the currently checked size radio's data-delta, and the sum of every checked add-on checkbox's data-price. updateTotal() then multiplies that unit price by the quantity and writes the formatted result, so every input change routes through the same two functions.

Size options are mutually exclusive — a dish can only be one size at a time — which is exactly what a radio group enforces natively. Add-ons are independent; any combination can be selected, which is what checkboxes are for. Using the right native input type also gets correct keyboard and screen reader behavior for free.

Both .mic-radio and .mic-checkbox rows use a :has() CSS selector — e.g. .mic-radio:has(input:checked) — to change their border and background the moment the input inside them becomes checked. No JavaScript needs to add or remove a "selected" class; the highlight is pure CSS reacting to native form state.

The button's text briefly changes to "Added ✓" and then, after about a second, reverts to "Add to order" followed by the current total — a lightweight confirmation. In a real app you'd also push the configured item (size, add-ons, quantity, unit price) into your cart state at that point.

On the add-to-order click, call calcUnitPrice() and read qty to build a line item object, then push it into your cart state or send it to your backend. The DOM structure already exposes everything needed: the checked size input's value, the checked add-on values, and the quantity.