webdevpuneet.com

Hotel Room Type Picker — Free Radio-Card Selector With Total

Hotel Room Type Picker · Cards · Plain HTML, CSS & JS · Live preview

CategoryCards

Copy & paste

Source Code

<div class="hrp-wrap">
  <h2 class="hrp-heading">Choose your room</h2>
  <div class="hrp-cards" id="hrpCards" role="radiogroup" aria-label="Room type">
    <label class="hrp-card">
      <input type="radio" name="hrpRoom" value="standard" data-price="129" />
      <div class="hrp-card-inner">
        <span class="hrp-radio-dot" aria-hidden="true"></span>
        <h3 class="hrp-name">Standard Queen</h3>
        <p class="hrp-desc">1 queen bed · 28 m²</p>
        <ul class="hrp-amenities">
          <li>Free Wi-Fi</li>
          <li>City view</li>
        </ul>
        <span class="hrp-price">$129<small>/night</small></span>
      </div>
    </label>
    <label class="hrp-card">
      <input type="radio" name="hrpRoom" value="deluxe" data-price="189" checked />
      <div class="hrp-card-inner">
        <span class="hrp-radio-dot" aria-hidden="true"></span>
        <span class="hrp-badge">Most popular</span>
        <h3 class="hrp-name">Deluxe King</h3>
        <p class="hrp-desc">1 king bed · 36 m²</p>
        <ul class="hrp-amenities">
          <li>Free Wi-Fi</li>
          <li>Ocean view</li>
          <li>Minibar</li>
        </ul>
        <span class="hrp-price">$189<small>/night</small></span>
      </div>
    </label>
    <label class="hrp-card">
      <input type="radio" name="hrpRoom" value="suite" data-price="309" />
      <div class="hrp-card-inner">
        <span class="hrp-radio-dot" aria-hidden="true"></span>
        <h3 class="hrp-name">Executive Suite</h3>
        <p class="hrp-desc">1 king bed + lounge · 58 m²</p>
        <ul class="hrp-amenities">
          <li>Free Wi-Fi</li>
          <li>Ocean view</li>
          <li>Minibar</li>
          <li>Butler service</li>
        </ul>
        <span class="hrp-price">$309<small>/night</small></span>
      </div>
    </label>
  </div>

  <div class="hrp-summary">
    <div class="hrp-nights">
      <span class="hrp-nights-label">Nights</span>
      <div class="hrp-stepper">
        <button type="button" class="hrp-step-btn" id="hrpMinus" aria-label="Decrease nights">−</button>
        <span class="hrp-nights-value" id="hrpNights">3</span>
        <button type="button" class="hrp-step-btn" id="hrpPlus" aria-label="Increase nights">+</button>
      </div>
    </div>
    <div class="hrp-total">
      <span class="hrp-total-label">Total</span>
      <span class="hrp-total-value" id="hrpTotal">$567</span>
    </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

Radio inputs as cards
Sibling selector drives the checked look.
True radiogroup semantics
Keyboard and screen-reader accessible.
Per-card data-price
Total math reads price straight off the input.
Nights stepper
Clamped +/- buttons recompute the total.
Single source of truth
One updateTotal() keeps everything in sync.
Popular-choice badge
Inline markup highlights a recommended tier.
Amenity checklists
Per-room bullet list with check icons.
Zero dependencies
Plain HTML, CSS, and JS, no CDN.

About this UI Snippet

Hotel Room Type Picker — Radio Cards With a Live Nightly Total

Screenshot of the Hotel Room Type Picker snippet rendered live

The hotel room type picker is the card-based room selector booking sites use in place of a plain dropdown — Standard, Deluxe, Suite, each showing its bed configuration, size, amenities, and price per night, with the whole card clickable and a nights stepper that recalculates a running total live. This snippet builds it in plain HTML, CSS, and JavaScript.

Radio inputs disguised as cards

Each card is a <label> wrapping a visually hidden <input type="radio"> and a styled .hrp-card-inner. Because the input and its sibling div share a parent label, clicking anywhere on the card toggles the radio, and the input:checked + .hrp-card-inner sibling selector drives the entire selected look — border color, background tint, lift, and a filled radio dot — with zero JavaScript for the visual state.

Real radiogroup semantics

The cards keep native <input type="radio" name="hrpRoom"> elements grouped by name, wrapped in a role="radiogroup" container. That means keyboard users get free arrow-key navigation between options and screen readers announce them as a proper radio group — the styling is a skin over standard form semantics, not a replacement for them.

Price per room, on the input itself

Each radio input carries a data-price attribute holding that room's nightly rate. The total calculation reads whichever input is currently :checked and multiplies its data-price by the nights count — so adding a fourth room tier is just adding another labeled card with its own data-price, no JS changes required.

A nights stepper wired to the same total

Two buttons increment and decrement a nights counter, clamped between 1 and 14, disabling at each bound. Both the radio change event and the stepper buttons call the same updateTotal() function, so switching rooms or nights always keeps the total in sync from a single source of truth.

A popular-choice badge

The Deluxe card carries a small "Most popular" badge — a common booking-site nudge — built as plain inline markup rather than a script-driven recommendation, easy to move to whichever card should be highlighted.

Customizing it

Add a taxes-and-fees line, a "sold out" disabled state for a room type, or swap the stepper for a date-range picker that derives nights automatically. Pair it with a property listing card, checkout form, or radio card group for other selection patterns.

Step by step

How to Use

  1. 1
    Paste HTML, CSS, and JSThree room cards and a nights/total summary render.
  2. 2
    Click a cardThe whole card is clickable and shows the checked state.
  3. 3
    Use the nights stepper+/- buttons clamp between 1 and 14 nights.
  4. 4
    Watch the total updatePrice × nights recalculates on every change.
  5. 5
    Add a room tierCopy a card, adjust amenities and data-price.
  6. 6
    Wire real booking dataRead the checked radio's value on submit.

Real-world uses

Common Use Cases

Hotel booking pages
Let guests choose between Standard, Deluxe and Suite cards that show bed type, size and amenities, beside a property listing card for the hotel itself.
Checkout hand-off
Feed the selected room and number of nights straight into a checkout form, with the total read from each radio's data-price attribute.
Vacation rental units
Offer unit tiers with live pricing, where the whole card is a real radio input so keyboard and screen reader users get proper radiogroup behaviour.
Ticket tier selection
Reuse the pattern for event ticket tiers, with a sibling selector driving the checked appearance and a clamped stepper recomputing the total.
Plan comparison siblings
Compare with a radio card group for subscription plans, and use it in corporate travel tools that compare room options against a running total.
Related: Staking Rewards Card
See the Staking Rewards Card for a related cards pattern worth pairing with this one.

Got questions?

Frequently Asked Questions

Each card is a label element wrapping a visually hidden radio input and the styled card body. Because a label toggles its wrapped input on click anywhere inside it, the whole card area is effectively the click target, and the input:checked + .hrp-card-inner sibling selector applies the selected styling automatically.

Yes. The underlying inputs are real type="radio" elements sharing a name, grouped in a container with role="radiogroup", so arrow-key navigation and screen reader announcements work exactly as they would for a native radio group — the card look is a CSS skin, not a custom-built widget.

Each radio input carries a data-price attribute. updateTotal() finds the currently :checked input in the group, reads its data-price, multiplies by the nights counter, and writes the result to the total display. Both the stepper buttons and the radios' change event call this same function.

Copy one .hrp-card label block, change its input's value and data-price, and update the name, description, and amenities list. No JavaScript changes are needed since the total logic reads whichever input is checked generically.

Yes — compute nights as the difference in days between a check-in and check-out date from a date picker, then call updateTotal() (or inline the same multiplication) whenever the date range changes instead of the +/- buttons.