Pricing cards combine a lot: grid, borders, badges, lists with custom markers and emphasis. This project builds three plans with a featured one in the middle.
The finished cards
<style>
*, *::before, *::after { box-sizing: border-box; }
body { margin: 0; padding: 20px 16px; font-family: system-ui, sans-serif; background: #f9f5f1; }
.plans { display: grid; gap: 16px; grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr)); align-items: start; max-width: 800px; margin: 0 auto; }
.plan { position: relative; background: #fff; border: 2px solid #e6ddd7; border-radius: 14px; padding: 24px 20px; }
.plan--featured { border-color: #d94a00; box-shadow: 0 10px 24px rgba(217,74,0,.18); }
.plan__badge { position: absolute; top: -12px; left: 50%; transform: translateX(-50%); background: #d94a00; color: #fff; font-size: .75rem; font-weight: 700; padding: 3px 12px; border-radius: 999px; white-space: nowrap; }
.plan__name { margin: 0; font-size: 1.125rem; }
.plan__price { margin: 8px 0 16px; font-size: 2.25rem; font-weight: 800; line-height: 1; }
.plan__price span { font-size: .9rem; font-weight: 400; color: #5c5750; }
.plan__list { list-style: none; margin: 0 0 20px; padding: 0; }
.plan__list li { position: relative; padding: 5px 0 5px 24px; font-size: .9375rem; }
.plan__list li::before { content: "✓"; position: absolute; left: 0; color: #27ae60; font-weight: 700; }
.plan__cta { display: block; text-align: center; padding: 11px; border-radius: 8px; border: 2px solid #d94a00; color: #d94a00; text-decoration: none; font-weight: 700; }
.plan__cta:hover, .plan__cta:focus-visible { background: #d94a00; color: #fff; }
.plan--featured .plan__cta { background: #d94a00; color: #fff; }
.plan--featured .plan__cta:hover { background: #b84624; border-color: #b84624; }
</style>
<div class="plans">
<section class="plan">
<h2 class="plan__name">Starter</h2>
<p class="plan__price">$9<span>/month</span></p>
<ul class="plan__list"><li>1 shop</li><li>100 products</li><li>Email support</li></ul>
<a class="plan__cta" href="#">Choose Starter</a>
</section>
<section class="plan plan--featured">
<span class="plan__badge">Most popular</span>
<h2 class="plan__name">Standard</h2>
<p class="plan__price">$19<span>/month</span></p>
<ul class="plan__list"><li>3 shops</li><li>Unlimited products</li><li>Priority support</li><li>Sales reports</li></ul>
<a class="plan__cta" href="#">Choose Standard</a>
</section>
<section class="plan">
<h2 class="plan__name">Studio</h2>
<p class="plan__price">$39<span>/month</span></p>
<ul class="plan__list"><li>10 shops</li><li>Unlimited products</li><li>Phone support</li></ul>
<a class="plan__cta" href="#">Choose Studio</a>
</section>
</div>
The plan names and prices are made-up sample content for the exercise.
Notes
- Grid with
auto-fitlets the cards sit in a row or stack by themselves. See auto-fit and auto-fill. align-items: startstops shorter cards stretching to the height of the tallest.- The badge uses
position: absoluteagainst aposition: relativecard, andtranslateX(-50%)to center itself. - Checkmarks come from
::before, so the HTML stays a plain list. - The featured card is emphasised with a color, a border and a shadow, not by size alone, so it also works for people who cannot tell colors apart.
Make it yours
- Make the featured card slightly taller on wide screens with
transform: scale(1.03). - Add a “yearly / monthly” switch (needs JavaScript) that changes the price using a variable.
Exercise: a disabled plan
Add a .plan--soon modifier that dims a card to 60% opacity and makes its button look disabled and non-clickable.
Show a possible solution
.plan--soon { opacity: .6; }
.plan--soon .plan__cta { border-color: #999; color: #666; pointer-events: none; }
For real accessibility also add aria-disabled="true" to the link.