webdevpuneet.com

Recipe Card with Serving Scaler — Free HTML CSS JS Snippet

Recipe Card with Serving Scaler · Cards · Plain HTML, CSS & JS · Live preview

CategoryCards

Copy & paste

Source Code

<div class="wrap">
  <div class="recipe-card">
    <div class="recipe-photo">
      <span class="recipe-tag">30 min</span>
    </div>
    <div class="recipe-body">
      <h2>Creamy Garlic Parmesan Pasta</h2>
      <p class="recipe-sub">A weeknight pasta with a silky garlic-parmesan sauce.</p>

      <div class="serving-row">
        <span class="serving-label">Servings</span>
        <div class="stepper">
          <button class="step-btn" id="dec" aria-label="Decrease servings">−</button>
          <span class="serving-count" id="count">4</span>
          <button class="step-btn" id="inc" aria-label="Increase servings">+</button>
        </div>
      </div>

      <ul class="ingredients" id="ingredients"></ul>

      <div class="nutrition">
        <div class="nut-item"><span class="nut-val" id="nutCal">560</span><span class="nut-label">kcal</span></div>
        <div class="nut-item"><span class="nut-val" id="nutProtein">22g</span><span class="nut-label">protein</span></div>
        <div class="nut-item"><span class="nut-val" id="nutCarbs">61g</span><span class="nut-label">carbs</span></div>
      </div>
    </div>
  </div>
</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

Plus/minus stepper recalculates every ingredient from one base data array, not by editing text
Quantities scale by servings / BASE_SERVINGS so repeated up/down stepping never drifts
formatQty() trims floating-point noise into a clean, human-readable decimal
Nutrition panel (calories, protein, carbs) scales with the exact same factor as ingredients
Brief pulse animation on every ingredient quantity when servings change
Clamped MIN/MAX serving bounds prevent zero or unrealistically large batches
font-variant-numeric: tabular-nums keeps scaling digits from jittering horizontally
Pure vanilla JS with one render() function as the single source of DOM truth

About this UI Snippet

Recipe Card with Serving Scaler — Proportional Ingredient Math & Live Nutrition Recalculation

Screenshot of the Recipe Card with Serving Scaler snippet rendered live

Printed recipes are locked to whatever serving count the author happened to cook for, which means doubling a recipe for guests or halving it for a solo dinner requires doing the fraction math in your head over the sink. This card removes that friction: a plus/minus stepper next to "Servings" recalculates every single ingredient quantity — and the calorie, protein, and carb totals — the moment the count changes, all derived from one base data array instead of being retyped by hand.

Base quantities, not base text

The trick that makes this work is that INGREDIENTS never stores display strings like "12 oz" — it stores a plain qty number plus a unit and name, all defined for a fixed BASE_SERVINGS of 4. A scale factor of servings / BASE_SERVINGS is computed once per render, and every ingredient's displayed quantity is ing.qty * scale, formatted only at the last step. Because the source numbers are never touched, scaling up to 12 servings and back down to 1 produces exactly the same numbers you started with — no accumulated rounding drift from repeatedly editing displayed text.

Friendly quantity formatting

Multiplying 0.75 cups by 1.5 servings gives 1.125, which is not a number anyone wants to read at a glance. formatQty() rounds to two decimal places and trims a needless trailing zero or dangling decimal point, so the card shows "1.13" rather than "1.125" or "1.1250000000000002" (the kind of floating-point artifact that shows up if you multiply and display without any rounding step at all).

Nutrition scales too, not just ingredients

BASE_NUTRITION holds calorie, protein, and carb totals for the same base serving count, and they're scaled by the identical scale factor used for ingredients — so the nutrition panel at the bottom of the card always describes the batch currently shown above it, not the original 4-serving version. This matters for a card that might get screenshotted or printed at a scaled-up serving count; stale nutrition numbers next to correctly-scaled ingredients would be actively misleading.

A pulse instead of a jump

Every .ing-qty briefly gets a .pulse class (a quick color shift, cleared via setTimeout) whenever the servings change, so a viewer's eye is drawn to the fact that the numbers just updated rather than having them silently swap while attention is on the stepper buttons.

Clamped stepper bounds

MIN_SERVINGS and MAX_SERVINGS (1 and 12 in the demo) prevent the stepper from producing a zero, negative, or absurdly large serving count. In a real recipe app you'd likely tune the upper bound to whatever batch size your kitchen equipment or pot size can realistically handle — a stand mixer bowl or a stockpot has a hard physical ceiling that a UI limit should reflect.

Step by step

How to Use

  1. 1
    Click + or − on the servings stepperEvery ingredient quantity and the calorie/protein/carb totals recalculate instantly and pulse briefly to draw the eye.
  2. 2
    Edit the base ingredientsUpdate the INGREDIENTS array — each item has a qty number (for BASE_SERVINGS), a unit string, and a name string.
  3. 3
    Change the base serving countSet BASE_SERVINGS to whatever count your original recipe quantities were written for; all scaling math is relative to it.
  4. 4
    Adjust the min/max stepper rangeEdit MIN_SERVINGS and MAX_SERVINGS to fit realistic batch sizes for your recipe.
  5. 5
    Update the nutrition base valuesSet BASE_NUTRITION.cal/protein/carbs to the totals for BASE_SERVINGS — they scale with the same factor as ingredients.
  6. 6
    Export in your formatClick "HTML" for a standalone file, "JSX" for a React component, or "Tailwind" for a Tailwind CSS version.

Real-world uses

Common Use Cases

Recipe and cooking apps
The core use case — a recipe detail page where home cooks adjust batch size for however many people they are feeding.
Meal-prep and batch-cooking planners
Scale a base recipe up to a week's worth of meal-prep portions without manual fraction math for every ingredient.
Restaurant and catering back-of-house tools
Scale a prep recipe from a per-plate quantity to a full catering order size, keeping ingredient ratios exact.
Learn proportional-scaling UI patterns
A clean example of deriving every displayed number from one scale factor computed against a fixed base — the same technique applies to unit converters or dosage calculators.
Print-ready recipe cards
Let a user dial in the exact serving count before printing or exporting a shopping list, so quantities on paper match what they actually plan to cook.
Grocery list generators
Feed the scaled INGREDIENTS array straight into a shopping-list builder so the list always matches the currently selected serving count.

Got questions?

Frequently Asked Questions

Storing a plain number lets scale multiplication (qty * scale) work correctly for any serving count. If quantities were stored as pre-formatted strings like "3/4 cup", scaling them would require parsing fractions out of text, which is far more error-prone than multiplying a number.

formatQty() rounds to two decimal places with Math.round(n * 100) / 100, then strips a trailing zero or trailing decimal point with a regex, so 1.125 displays as 1.13 rather than a long floating-point decimal.

Yes — BASE_NUTRITION values are multiplied by the same scale factor (servings / BASE_SERVINGS) used for ingredients, inside the same render() call, so nutrition and ingredients always describe the same batch size.

The current formatting shows decimals rather than fractions (e.g. 0.5 rather than 1/2). To show fractions, add a small decimal-to-fraction lookup (0.25 → "1/4", 0.5 → "1/2", 0.75 → "3/4") in formatQty() before falling back to a decimal for values that do not match a common fraction.

Yes — MAX_SERVINGS already clamps the increment button; the click handler returns early once servings reaches it. Lower or raise the constant to match a realistic limit for your recipe or equipment.

Keep servings in useState(BASE_SERVINGS), compute scale with useMemo, and map INGREDIENTS to <li> elements using the scaled quantity inline — the same formatQty logic ports directly into the render function.