24 CSS Radio Buttons17 / 24

Pure CSSMIT licensed

Subscription Pricing Plan Cards

A complete pricing-table selector: three plan cards with price, feature list and a highlighted "Recommended" tier — each card a radio button, so the whole table is one submittable form control. Checked card lifts with an accent crown bar; a live summary line below confirms the choice, pure CSS.

Published Updated

Live Demo
Try it

The code

<div class="crb-17-group">
<section class="crb-17a" aria-label="Subscription pricing plan cards">
  <fieldset class="crb-17a__group">
    <legend class="crb-17a__legend">Pick your plan</legend>
    <div class="crb-17a__cards">
      <label class="crb-17a__card">
        <input class="crb-17a__input" type="radio" name="crb-17a" value="starter">
        <span class="crb-17a__name">Starter</span>
        <span class="crb-17a__price">$0<small>/mo</small></span>
        <ul class="crb-17a__feats"><li>3 projects</li><li>Community support</li><li>1 GB storage</li></ul>
        <span class="crb-17a__pill"><span class="crb-17a__pill-idle">Select</span><span class="crb-17a__pill-on">Selected ✓</span></span>
      </label>
      <label class="crb-17a__card crb-17a__card--reco">
        <input class="crb-17a__input" type="radio" name="crb-17a" value="pro" checked>
        <span class="crb-17a__crown">Recommended</span>
        <span class="crb-17a__name">Pro</span>
        <span class="crb-17a__price">$19<small>/mo</small></span>
        <ul class="crb-17a__feats"><li>Unlimited projects</li><li>Priority support</li><li>100 GB storage</li></ul>
        <span class="crb-17a__pill"><span class="crb-17a__pill-idle">Select</span><span class="crb-17a__pill-on">Selected ✓</span></span>
      </label>
      <label class="crb-17a__card">
        <input class="crb-17a__input" type="radio" name="crb-17a" value="team">
        <span class="crb-17a__name">Team</span>
        <span class="crb-17a__price">$49<small>/mo</small></span>
        <ul class="crb-17a__feats"><li>Everything in Pro</li><li>SSO &amp; audit log</li><li>Unlimited storage</li></ul>
        <span class="crb-17a__pill"><span class="crb-17a__pill-idle">Select</span><span class="crb-17a__pill-on">Selected ✓</span></span>
      </label>
    </div>
    <p class="crb-17a__sum"><span data-sum="starter">Starter — free forever, upgrade anytime.</span><span data-sum="pro">Pro — 14-day free trial, cancel anytime.</span><span data-sum="team">Team — includes onboarding for your whole org.</span></p>
  </fieldset>
</section>
</div>
.crb-17-group {
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
  gap: 0;
}

.crb-17-group > section {
  flex: 1 1 340px;
  min-width: 300px;
}

.crb-17a,
.crb-17a *,
.crb-17a *::before,
.crb-17a *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.crb-17a {
  --accent: oklch(0.55 0.2 262);
  --ink: #1c2130;
  font-family: 'Segoe UI',system-ui,sans-serif;
  width: 100%;
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 48px 24px;
  background: #f4f5f9;
}

.crb-17a__group {
  border: none;
  display: grid;
  justify-items: center;
}

.crb-17a__legend {
  font-size: 26px;
  font-weight: 750;
  color: var(--ink);
  letter-spacing: -.02em;
  text-align: center;
}

.crb-17a__cards {
  display: flex;
  flex-wrap: wrap;
  gap: 18px;
  justify-content: center;
  margin-top: 30px;
}

.crb-17a__card {
  position: relative;
  display: grid;
  align-content: start;
  gap: 6px;
  width: 230px;
  padding: 28px 22px 22px;
  background: #fff;
  border: 2px solid #e5e7f0;
  border-radius: 18px;
  cursor: pointer;
  box-shadow: 0 0 0 transparent;
  transition: border-color .25s,transform .25s,box-shadow .25s;
}

.crb-17a__card--reco {
  border-color: color-mix(in oklab,var(--accent) 35%,#e5e7f0);
}

.crb-17a__crown {
  position: absolute;
  top: -11px;
  left: 50%;
  transform: translateX(-50%);
  font-size: 11px;
  font-weight: 750;
  letter-spacing: .04em;
  padding: 3px 12px;
  border-radius: 9999px;
  background: var(--accent);
  color: #fff;
  white-space: nowrap;
}

.crb-17a__input {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
}

.crb-17a__name {
  font-size: 15px;
  font-weight: 750;
  color: #6b7189;
  text-transform: uppercase;
  letter-spacing: .08em;
}

.crb-17a__price {
  font-size: 34px;
  font-weight: 800;
  color: var(--ink);
  letter-spacing: -.03em;
}

.crb-17a__price small {
  font-size: 14px;
  font-weight: 600;
  color: #8a8fa3;
  letter-spacing: 0;
}

.crb-17a__feats {
  list-style: none;
  display: grid;
  gap: 7px;
  margin-top: 10px;
}

.crb-17a__feats li {
  font-size: 13.5px;
  color: #5d6378;
  padding-left: 20px;
  position: relative;
}

.crb-17a__feats li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 5px;
  width: 10px;
  height: 6px;
  border-left: 2px solid var(--accent);
  border-bottom: 2px solid var(--accent);
  transform: rotate(-45deg);
}

.crb-17a__pill {
  position: relative;
  display: grid;
  place-items: center;
  margin-top: 18px;
  min-height: 42px;
  border-radius: 10px;
  border: 1.5px solid #d7dae6;
  font-size: 14px;
  font-weight: 700;
  color: #555b70;
  overflow: hidden;
  transition: border-color .25s,background .25s,color .25s;
}

.crb-17a__pill-idle,
.crb-17a__pill-on {
  grid-area: 1/1;
  transition: opacity .25s,transform .25s;
}

.crb-17a__pill-on {
  opacity: 0;
  transform: translateY(6px);
  color: #fff;
}

.crb-17a__card:hover {
  border-color: color-mix(in oklab,var(--accent) 45%,#e5e7f0);
  transform: translateY(-2px);
}

.crb-17a__card:has(.crb-17a__input:checked) {
  border-color: var(--accent);
  transform: translateY(-4px);
  box-shadow: 0 22px 44px -20px color-mix(in oklab,var(--accent) 55%,transparent);
}

.crb-17a__card:has(.crb-17a__input:checked) .crb-17a__pill {
  background: var(--accent);
  border-color: var(--accent);
}

.crb-17a__card:has(.crb-17a__input:checked) .crb-17a__pill-idle {
  opacity: 0;
  transform: translateY(-6px);
}

.crb-17a__card:has(.crb-17a__input:checked) .crb-17a__pill-on {
  opacity: 1;
  transform: translateY(0);
}

.crb-17a__card:has(.crb-17a__input:focus-visible) {
  outline: 3px solid var(--accent);
  outline-offset: 3px;
}

.crb-17a__sum {
  margin-top: 22px;
  font-size: 14px;
  color: #6b7189;
  min-height: 20px;
  text-align: center;
}

.crb-17a__sum span {
  display: none;
}

.crb-17a__group:has(.crb-17a__input[value="starter"]:checked) .crb-17a__sum [data-sum="starter"] {
  display: inline;
}

.crb-17a__group:has(.crb-17a__input[value="pro"]:checked) .crb-17a__sum [data-sum="pro"] {
  display: inline;
}

.crb-17a__group:has(.crb-17a__input[value="team"]:checked) .crb-17a__sum [data-sum="team"] {
  display: inline;
}

@media (prefers-reduced-motion: reduce) {
  .crb-17a__card,
    .crb-17a__pill,
    .crb-17a__pill-idle,
    .crb-17a__pill-on {
    transition: none;
  }

  .crb-17a__card:hover,
    .crb-17a__card:has(.crb-17a__input:checked) {
    transform: none;
  }
}
/* No JavaScript — each pricing card is a radio label; :has() lifts the checked card, cross-fades the Select/Selected pill and switches the live summary sentence below the table. */
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
Here's a working CSS Radio Button from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Subscription Pricing Plan Cards
Source: https://codefronts.com/components/css-radio-buttons/pricing-tier/

A complete pricing-table selector: three plan cards with price, feature list and a highlighted "Recommended" tier — each card a radio button, so the whole table is one submittable form control. Checked card lifts with an accent crown bar; a live summary line below confirms the choice, pure CSS.
## HTML
```html
<div class="crb-17-group">
<section class="crb-17a" aria-label="Subscription pricing plan cards">
  <fieldset class="crb-17a__group">
    <legend class="crb-17a__legend">Pick your plan</legend>
    <div class="crb-17a__cards">
      <label class="crb-17a__card">
        <input class="crb-17a__input" type="radio" name="crb-17a" value="starter">
        <span class="crb-17a__name">Starter</span>
        <span class="crb-17a__price">$0<small>/mo</small></span>
        <ul class="crb-17a__feats"><li>3 projects</li><li>Community support</li><li>1 GB storage</li></ul>
        <span class="crb-17a__pill"><span class="crb-17a__pill-idle">Select</span><span class="crb-17a__pill-on">Selected ✓</span></span>
      </label>
      <label class="crb-17a__card crb-17a__card--reco">
        <input class="crb-17a__input" type="radio" name="crb-17a" value="pro" checked>
        <span class="crb-17a__crown">Recommended</span>
        <span class="crb-17a__name">Pro</span>
        <span class="crb-17a__price">$19<small>/mo</small></span>
        <ul class="crb-17a__feats"><li>Unlimited projects</li><li>Priority support</li><li>100 GB storage</li></ul>
        <span class="crb-17a__pill"><span class="crb-17a__pill-idle">Select</span><span class="crb-17a__pill-on">Selected ✓</span></span>
      </label>
      <label class="crb-17a__card">
        <input class="crb-17a__input" type="radio" name="crb-17a" value="team">
        <span class="crb-17a__name">Team</span>
        <span class="crb-17a__price">$49<small>/mo</small></span>
        <ul class="crb-17a__feats"><li>Everything in Pro</li><li>SSO &amp; audit log</li><li>Unlimited storage</li></ul>
        <span class="crb-17a__pill"><span class="crb-17a__pill-idle">Select</span><span class="crb-17a__pill-on">Selected ✓</span></span>
      </label>
    </div>
    <p class="crb-17a__sum"><span data-sum="starter">Starter — free forever, upgrade anytime.</span><span data-sum="pro">Pro — 14-day free trial, cancel anytime.</span><span data-sum="team">Team — includes onboarding for your whole org.</span></p>
  </fieldset>
</section>
</div>
```
## CSS
```css
.crb-17-group {
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
  gap: 0;
}

.crb-17-group > section {
  flex: 1 1 340px;
  min-width: 300px;
}

.crb-17a,
.crb-17a *,
.crb-17a *::before,
.crb-17a *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.crb-17a {
  --accent: oklch(0.55 0.2 262);
  --ink: #1c2130;
  font-family: 'Segoe UI',system-ui,sans-serif;
  width: 100%;
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 48px 24px;
  background: #f4f5f9;
}

.crb-17a__group {
  border: none;
  display: grid;
  justify-items: center;
}

.crb-17a__legend {
  font-size: 26px;
  font-weight: 750;
  color: var(--ink);
  letter-spacing: -.02em;
  text-align: center;
}

.crb-17a__cards {
  display: flex;
  flex-wrap: wrap;
  gap: 18px;
  justify-content: center;
  margin-top: 30px;
}

.crb-17a__card {
  position: relative;
  display: grid;
  align-content: start;
  gap: 6px;
  width: 230px;
  padding: 28px 22px 22px;
  background: #fff;
  border: 2px solid #e5e7f0;
  border-radius: 18px;
  cursor: pointer;
  box-shadow: 0 0 0 transparent;
  transition: border-color .25s,transform .25s,box-shadow .25s;
}

.crb-17a__card--reco {
  border-color: color-mix(in oklab,var(--accent) 35%,#e5e7f0);
}

.crb-17a__crown {
  position: absolute;
  top: -11px;
  left: 50%;
  transform: translateX(-50%);
  font-size: 11px;
  font-weight: 750;
  letter-spacing: .04em;
  padding: 3px 12px;
  border-radius: 9999px;
  background: var(--accent);
  color: #fff;
  white-space: nowrap;
}

.crb-17a__input {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
}

.crb-17a__name {
  font-size: 15px;
  font-weight: 750;
  color: #6b7189;
  text-transform: uppercase;
  letter-spacing: .08em;
}

.crb-17a__price {
  font-size: 34px;
  font-weight: 800;
  color: var(--ink);
  letter-spacing: -.03em;
}

.crb-17a__price small {
  font-size: 14px;
  font-weight: 600;
  color: #8a8fa3;
  letter-spacing: 0;
}

.crb-17a__feats {
  list-style: none;
  display: grid;
  gap: 7px;
  margin-top: 10px;
}

.crb-17a__feats li {
  font-size: 13.5px;
  color: #5d6378;
  padding-left: 20px;
  position: relative;
}

.crb-17a__feats li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 5px;
  width: 10px;
  height: 6px;
  border-left: 2px solid var(--accent);
  border-bottom: 2px solid var(--accent);
  transform: rotate(-45deg);
}

.crb-17a__pill {
  position: relative;
  display: grid;
  place-items: center;
  margin-top: 18px;
  min-height: 42px;
  border-radius: 10px;
  border: 1.5px solid #d7dae6;
  font-size: 14px;
  font-weight: 700;
  color: #555b70;
  overflow: hidden;
  transition: border-color .25s,background .25s,color .25s;
}

.crb-17a__pill-idle,
.crb-17a__pill-on {
  grid-area: 1/1;
  transition: opacity .25s,transform .25s;
}

.crb-17a__pill-on {
  opacity: 0;
  transform: translateY(6px);
  color: #fff;
}

.crb-17a__card:hover {
  border-color: color-mix(in oklab,var(--accent) 45%,#e5e7f0);
  transform: translateY(-2px);
}

.crb-17a__card:has(.crb-17a__input:checked) {
  border-color: var(--accent);
  transform: translateY(-4px);
  box-shadow: 0 22px 44px -20px color-mix(in oklab,var(--accent) 55%,transparent);
}

.crb-17a__card:has(.crb-17a__input:checked) .crb-17a__pill {
  background: var(--accent);
  border-color: var(--accent);
}

.crb-17a__card:has(.crb-17a__input:checked) .crb-17a__pill-idle {
  opacity: 0;
  transform: translateY(-6px);
}

.crb-17a__card:has(.crb-17a__input:checked) .crb-17a__pill-on {
  opacity: 1;
  transform: translateY(0);
}

.crb-17a__card:has(.crb-17a__input:focus-visible) {
  outline: 3px solid var(--accent);
  outline-offset: 3px;
}

.crb-17a__sum {
  margin-top: 22px;
  font-size: 14px;
  color: #6b7189;
  min-height: 20px;
  text-align: center;
}

.crb-17a__sum span {
  display: none;
}

.crb-17a__group:has(.crb-17a__input[value="starter"]:checked) .crb-17a__sum [data-sum="starter"] {
  display: inline;
}

.crb-17a__group:has(.crb-17a__input[value="pro"]:checked) .crb-17a__sum [data-sum="pro"] {
  display: inline;
}

.crb-17a__group:has(.crb-17a__input[value="team"]:checked) .crb-17a__sum [data-sum="team"] {
  display: inline;
}

@media (prefers-reduced-motion: reduce) {
  .crb-17a__card,
    .crb-17a__pill,
    .crb-17a__pill-idle,
    .crb-17a__pill-on {
    transition: none;
  }

  .crb-17a__card:hover,
    .crb-17a__card:has(.crb-17a__input:checked) {
    transform: none;
  }
}
```

## JavaScript
```js
/* No JavaScript — each pricing card is a radio label; :has() lifts the checked card, cross-fades the Select/Selected pill and switches the live summary sentence below the table. */
```

How this works

Each card is a label around a hidden radio, with a grid layout: name, price, features, and a fake 'Select' pill that flips to 'Selected ✓' via two stacked spans cross-faded by :has(input:checked). The recommended tier is a modifier class adding the crown bar and a raised resting elevation — selection styling stacks on top of it cleanly.

The summary line under the table uses the same document-level :has(input[value=…]:checked) trick as the swatch demo, showing the matching sentence. Feature lists are real <ul>s inside the label; for screen readers the option announces name + price first because those come first in DOM order.

Make it yours

  • Change which tier is recommended by moving one modifier class.
  • Accent colour, wash and shadow all derive from --accent.
  • Cards flex-wrap: mobile gets a clean vertical stack automatically.
  • The 'Select' pill copy pair is plain text — localize freely.

Gotchas — read before shipping

  • Long feature lists inside labels get read in full — keep them to 3–4 items or move detail outside with aria-describedby.
  • The pill is decorative (a span, not a button) — the label is the control; don't nest a real button inside.
  • Reserve elevation space with a transparent shadow at rest so the lift doesn't clip against siblings.

Browser support

ChromeSafariFirefoxEdge
111+16.2+121+111+

Floor set by :has(), oklch(), color-mix() as this demo is written. The core technique itself goes back further — Chrome 105+.

:has() drives card state, pill swap and summary line. Without it cards still select natively.

Search CodeFronts

Loading…