36 CSS Stacked Cards30 / 36
Pure CSS Stacked Card Testimonial Slider
A compact social-proof widget built entirely with hidden radio inputs and the :checked selector — zero JavaScript and no heavy slider libraries. The top review sits crisp while the next two peek behind it; clicking the dots cycles a card to the front. Perfect for landing-page testimonial sections where space is at a premium.
Published Updated
The code
<div class="scd-30">
<div class="scd-30__stage">
<input type="radio" name="scd-30-t" id="scd-30-t1" class="scd-30__radio scd-30__radio--r1" checked>
<input type="radio" name="scd-30-t" id="scd-30-t2" class="scd-30__radio scd-30__radio--r2">
<input type="radio" name="scd-30-t" id="scd-30-t3" class="scd-30__radio scd-30__radio--r3">
<div class="scd-30__slider">
<div class="scd-30__quote scd-30__quote--q1"><p>“This completely changed how our team ships. We cut launch time in half.”</p><div class="scd-30__who"><span class="scd-30__av" style="background:linear-gradient(135deg,#ff9a3c,#ff5e62)"></span><div><h4>Maya Chen</h4><span>Head of Product, Loop</span></div></div></div>
<div class="scd-30__quote scd-30__quote--q2"><p>“Beautiful, fast, and genuinely a joy to use every single day.”</p><div class="scd-30__who"><span class="scd-30__av" style="background:linear-gradient(135deg,#43cea2,#185a9d)"></span><div><h4>Theo Marsh</h4><span>Founder, Northbeam</span></div></div></div>
<div class="scd-30__quote scd-30__quote--q3"><p>“The support alone is worth it. Best decision we made this year.”</p><div class="scd-30__who"><span class="scd-30__av" style="background:linear-gradient(135deg,#c471f5,#7f53ac)"></span><div><h4>Lena Ortiz</h4><span>CTO, Brightside</span></div></div></div>
</div>
<div class="scd-30__dots">
<label for="scd-30-t1"></label><label for="scd-30-t2"></label><label for="scd-30-t3"></label>
</div>
</div>
</div><div class="scd-30">
<div class="scd-30__stage">
<input type="radio" name="scd-30-t" id="scd-30-t1" class="scd-30__radio scd-30__radio--r1" checked>
<input type="radio" name="scd-30-t" id="scd-30-t2" class="scd-30__radio scd-30__radio--r2">
<input type="radio" name="scd-30-t" id="scd-30-t3" class="scd-30__radio scd-30__radio--r3">
<div class="scd-30__slider">
<div class="scd-30__quote scd-30__quote--q1"><p>“This completely changed how our team ships. We cut launch time in half.”</p><div class="scd-30__who"><span class="scd-30__av" style="background:linear-gradient(135deg,#ff9a3c,#ff5e62)"></span><div><h4>Maya Chen</h4><span>Head of Product, Loop</span></div></div></div>
<div class="scd-30__quote scd-30__quote--q2"><p>“Beautiful, fast, and genuinely a joy to use every single day.”</p><div class="scd-30__who"><span class="scd-30__av" style="background:linear-gradient(135deg,#43cea2,#185a9d)"></span><div><h4>Theo Marsh</h4><span>Founder, Northbeam</span></div></div></div>
<div class="scd-30__quote scd-30__quote--q3"><p>“The support alone is worth it. Best decision we made this year.”</p><div class="scd-30__who"><span class="scd-30__av" style="background:linear-gradient(135deg,#c471f5,#7f53ac)"></span><div><h4>Lena Ortiz</h4><span>CTO, Brightside</span></div></div></div>
</div>
<div class="scd-30__dots">
<label for="scd-30-t1"></label><label for="scd-30-t2"></label><label for="scd-30-t3"></label>
</div>
</div>
</div>@import url('https://fonts.googleapis.com/css2?family=Newsreader:ital,opsz,wght@0,6..72,500;1,6..72,400&family=Geist:wght@400;500;600&display=swap');
.scd-30,
.scd-30 *,
.scd-30 *::before,
.scd-30 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.scd-30 {
width: 100%;
min-height: 100vh;
display: grid;
place-items: center;
background: linear-gradient(160deg,#fef6ee,#f4e4d4);
font-family: 'Geist', sans-serif;
}
.scd-30__stage {
position: relative;
}
.scd-30__radio {
position: absolute;
opacity: 0;
pointer-events: none;
width: 0;
height: 0;
}
.scd-30__slider {
position: relative;
width: 330px;
height: 300px;
}
.scd-30__quote {
position: absolute;
inset: 0;
background: #fffdfa;
border-radius: 22px;
padding: 30px;
box-shadow: 0 18px 40px rgba(120,80,50,.18);
border: 1px solid #f0e2d2;
transition: transform .6s cubic-bezier(.3,.9,.3,1), opacity .6s;
display: flex;
flex-direction: column;
justify-content: space-between;
transform: translateY(28px) scale(.88);
opacity: 0;
z-index: 1;
}
.scd-30__quote p {
font-family: 'Newsreader', serif;
font-style: italic;
font-size: 1.25rem;
line-height: 1.45;
color: #3a2e24;
}
.scd-30__who {
display: flex;
align-items: center;
gap: 12px;
}
.scd-30__av {
width: 44px;
height: 44px;
border-radius: 50%;
flex-shrink: 0;
display: block;
}
.scd-30__who h4 {
font-size: .9rem;
font-weight: 600;
color: #2a201a;
}
.scd-30__who span {
font-size: .78rem;
color: #9b7d5f;
}
.scd-30__radio--r1:checked ~ .scd-30__slider .scd-30__quote--q1,
.scd-30__radio--r2:checked ~ .scd-30__slider .scd-30__quote--q2,
.scd-30__radio--r3:checked ~ .scd-30__slider .scd-30__quote--q3 {
transform: translateY(0) scale(1);
opacity: 1;
z-index: 4;
}
.scd-30__radio--r1:checked ~ .scd-30__slider .scd-30__quote--q2,
.scd-30__radio--r2:checked ~ .scd-30__slider .scd-30__quote--q3,
.scd-30__radio--r3:checked ~ .scd-30__slider .scd-30__quote--q1 {
transform: translateY(14px) scale(.94);
opacity: .55;
z-index: 3;
}
.scd-30__radio--r1:checked ~ .scd-30__slider .scd-30__quote--q3,
.scd-30__radio--r2:checked ~ .scd-30__slider .scd-30__quote--q1,
.scd-30__radio--r3:checked ~ .scd-30__slider .scd-30__quote--q2 {
transform: translateY(28px) scale(.88);
opacity: .3;
z-index: 2;
}
.scd-30__dots {
display: flex;
gap: 10px;
justify-content: center;
margin-top: 30px;
}
.scd-30__dots label {
width: 11px;
height: 11px;
border-radius: 50%;
background: #d8c3aa;
cursor: pointer;
transition: background .3s, transform .3s;
}
.scd-30__dots label:hover {
background: #c9a98a;
}
.scd-30__radio--r1:checked ~ .scd-30__dots label[for="scd-30-t1"],
.scd-30__radio--r2:checked ~ .scd-30__dots label[for="scd-30-t2"],
.scd-30__radio--r3:checked ~ .scd-30__dots label[for="scd-30-t3"] {
background: #b8553f;
transform: scale(1.3);
}
@media (prefers-reduced-motion: reduce) {
.scd-30__quote,
.scd-30__dots label {
transition: none !important;
}
}@import url('https://fonts.googleapis.com/css2?family=Newsreader:ital,opsz,wght@0,6..72,500;1,6..72,400&family=Geist:wght@400;500;600&display=swap');
.scd-30,
.scd-30 *,
.scd-30 *::before,
.scd-30 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.scd-30 {
width: 100%;
min-height: 100vh;
display: grid;
place-items: center;
background: linear-gradient(160deg,#fef6ee,#f4e4d4);
font-family: 'Geist', sans-serif;
}
.scd-30__stage {
position: relative;
}
.scd-30__radio {
position: absolute;
opacity: 0;
pointer-events: none;
width: 0;
height: 0;
}
.scd-30__slider {
position: relative;
width: 330px;
height: 300px;
}
.scd-30__quote {
position: absolute;
inset: 0;
background: #fffdfa;
border-radius: 22px;
padding: 30px;
box-shadow: 0 18px 40px rgba(120,80,50,.18);
border: 1px solid #f0e2d2;
transition: transform .6s cubic-bezier(.3,.9,.3,1), opacity .6s;
display: flex;
flex-direction: column;
justify-content: space-between;
transform: translateY(28px) scale(.88);
opacity: 0;
z-index: 1;
}
.scd-30__quote p {
font-family: 'Newsreader', serif;
font-style: italic;
font-size: 1.25rem;
line-height: 1.45;
color: #3a2e24;
}
.scd-30__who {
display: flex;
align-items: center;
gap: 12px;
}
.scd-30__av {
width: 44px;
height: 44px;
border-radius: 50%;
flex-shrink: 0;
display: block;
}
.scd-30__who h4 {
font-size: .9rem;
font-weight: 600;
color: #2a201a;
}
.scd-30__who span {
font-size: .78rem;
color: #9b7d5f;
}
.scd-30__radio--r1:checked ~ .scd-30__slider .scd-30__quote--q1,
.scd-30__radio--r2:checked ~ .scd-30__slider .scd-30__quote--q2,
.scd-30__radio--r3:checked ~ .scd-30__slider .scd-30__quote--q3 {
transform: translateY(0) scale(1);
opacity: 1;
z-index: 4;
}
.scd-30__radio--r1:checked ~ .scd-30__slider .scd-30__quote--q2,
.scd-30__radio--r2:checked ~ .scd-30__slider .scd-30__quote--q3,
.scd-30__radio--r3:checked ~ .scd-30__slider .scd-30__quote--q1 {
transform: translateY(14px) scale(.94);
opacity: .55;
z-index: 3;
}
.scd-30__radio--r1:checked ~ .scd-30__slider .scd-30__quote--q3,
.scd-30__radio--r2:checked ~ .scd-30__slider .scd-30__quote--q1,
.scd-30__radio--r3:checked ~ .scd-30__slider .scd-30__quote--q2 {
transform: translateY(28px) scale(.88);
opacity: .3;
z-index: 2;
}
.scd-30__dots {
display: flex;
gap: 10px;
justify-content: center;
margin-top: 30px;
}
.scd-30__dots label {
width: 11px;
height: 11px;
border-radius: 50%;
background: #d8c3aa;
cursor: pointer;
transition: background .3s, transform .3s;
}
.scd-30__dots label:hover {
background: #c9a98a;
}
.scd-30__radio--r1:checked ~ .scd-30__dots label[for="scd-30-t1"],
.scd-30__radio--r2:checked ~ .scd-30__dots label[for="scd-30-t2"],
.scd-30__radio--r3:checked ~ .scd-30__dots label[for="scd-30-t3"] {
background: #b8553f;
transform: scale(1.3);
}
@media (prefers-reduced-motion: reduce) {
.scd-30__quote,
.scd-30__dots label {
transition: none !important;
}
}Here's a working CSS Stacked Card 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: Pure CSS Stacked Card Testimonial Slider
Source: https://codefronts.com/components/css-stacked-cards/pure-css-stacked-card-testimonial-slider/
A compact social-proof widget built entirely with hidden radio inputs and the :checked selector — zero JavaScript and no heavy slider libraries. The top review sits crisp while the next two peek behind it; clicking the dots cycles a card to the front. Perfect for landing-page testimonial sections where space is at a premium.
## HTML
```html
<div class="scd-30">
<div class="scd-30__stage">
<input type="radio" name="scd-30-t" id="scd-30-t1" class="scd-30__radio scd-30__radio--r1" checked>
<input type="radio" name="scd-30-t" id="scd-30-t2" class="scd-30__radio scd-30__radio--r2">
<input type="radio" name="scd-30-t" id="scd-30-t3" class="scd-30__radio scd-30__radio--r3">
<div class="scd-30__slider">
<div class="scd-30__quote scd-30__quote--q1"><p>“This completely changed how our team ships. We cut launch time in half.”</p><div class="scd-30__who"><span class="scd-30__av" style="background:linear-gradient(135deg,#ff9a3c,#ff5e62)"></span><div><h4>Maya Chen</h4><span>Head of Product, Loop</span></div></div></div>
<div class="scd-30__quote scd-30__quote--q2"><p>“Beautiful, fast, and genuinely a joy to use every single day.”</p><div class="scd-30__who"><span class="scd-30__av" style="background:linear-gradient(135deg,#43cea2,#185a9d)"></span><div><h4>Theo Marsh</h4><span>Founder, Northbeam</span></div></div></div>
<div class="scd-30__quote scd-30__quote--q3"><p>“The support alone is worth it. Best decision we made this year.”</p><div class="scd-30__who"><span class="scd-30__av" style="background:linear-gradient(135deg,#c471f5,#7f53ac)"></span><div><h4>Lena Ortiz</h4><span>CTO, Brightside</span></div></div></div>
</div>
<div class="scd-30__dots">
<label for="scd-30-t1"></label><label for="scd-30-t2"></label><label for="scd-30-t3"></label>
</div>
</div>
</div>
```
## CSS
```css
@import url('https://fonts.googleapis.com/css2?family=Newsreader:ital,opsz,wght@0,6..72,500;1,6..72,400&family=Geist:wght@400;500;600&display=swap');
.scd-30,
.scd-30 *,
.scd-30 *::before,
.scd-30 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.scd-30 {
width: 100%;
min-height: 100vh;
display: grid;
place-items: center;
background: linear-gradient(160deg,#fef6ee,#f4e4d4);
font-family: 'Geist', sans-serif;
}
.scd-30__stage {
position: relative;
}
.scd-30__radio {
position: absolute;
opacity: 0;
pointer-events: none;
width: 0;
height: 0;
}
.scd-30__slider {
position: relative;
width: 330px;
height: 300px;
}
.scd-30__quote {
position: absolute;
inset: 0;
background: #fffdfa;
border-radius: 22px;
padding: 30px;
box-shadow: 0 18px 40px rgba(120,80,50,.18);
border: 1px solid #f0e2d2;
transition: transform .6s cubic-bezier(.3,.9,.3,1), opacity .6s;
display: flex;
flex-direction: column;
justify-content: space-between;
transform: translateY(28px) scale(.88);
opacity: 0;
z-index: 1;
}
.scd-30__quote p {
font-family: 'Newsreader', serif;
font-style: italic;
font-size: 1.25rem;
line-height: 1.45;
color: #3a2e24;
}
.scd-30__who {
display: flex;
align-items: center;
gap: 12px;
}
.scd-30__av {
width: 44px;
height: 44px;
border-radius: 50%;
flex-shrink: 0;
display: block;
}
.scd-30__who h4 {
font-size: .9rem;
font-weight: 600;
color: #2a201a;
}
.scd-30__who span {
font-size: .78rem;
color: #9b7d5f;
}
.scd-30__radio--r1:checked ~ .scd-30__slider .scd-30__quote--q1,
.scd-30__radio--r2:checked ~ .scd-30__slider .scd-30__quote--q2,
.scd-30__radio--r3:checked ~ .scd-30__slider .scd-30__quote--q3 {
transform: translateY(0) scale(1);
opacity: 1;
z-index: 4;
}
.scd-30__radio--r1:checked ~ .scd-30__slider .scd-30__quote--q2,
.scd-30__radio--r2:checked ~ .scd-30__slider .scd-30__quote--q3,
.scd-30__radio--r3:checked ~ .scd-30__slider .scd-30__quote--q1 {
transform: translateY(14px) scale(.94);
opacity: .55;
z-index: 3;
}
.scd-30__radio--r1:checked ~ .scd-30__slider .scd-30__quote--q3,
.scd-30__radio--r2:checked ~ .scd-30__slider .scd-30__quote--q1,
.scd-30__radio--r3:checked ~ .scd-30__slider .scd-30__quote--q2 {
transform: translateY(28px) scale(.88);
opacity: .3;
z-index: 2;
}
.scd-30__dots {
display: flex;
gap: 10px;
justify-content: center;
margin-top: 30px;
}
.scd-30__dots label {
width: 11px;
height: 11px;
border-radius: 50%;
background: #d8c3aa;
cursor: pointer;
transition: background .3s, transform .3s;
}
.scd-30__dots label:hover {
background: #c9a98a;
}
.scd-30__radio--r1:checked ~ .scd-30__dots label[for="scd-30-t1"],
.scd-30__radio--r2:checked ~ .scd-30__dots label[for="scd-30-t2"],
.scd-30__radio--r3:checked ~ .scd-30__dots label[for="scd-30-t3"] {
background: #b8553f;
transform: scale(1.3);
}
@media (prefers-reduced-motion: reduce) {
.scd-30__quote,
.scd-30__dots label {
transition: none !important;
}
}
```Here's a working CSS Stacked Card 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: Pure CSS Stacked Card Testimonial Slider
Source: https://codefronts.com/components/css-stacked-cards/pure-css-stacked-card-testimonial-slider/
A compact social-proof widget built entirely with hidden radio inputs and the :checked selector — zero JavaScript and no heavy slider libraries. The top review sits crisp while the next two peek behind it; clicking the dots cycles a card to the front. Perfect for landing-page testimonial sections where space is at a premium.
## HTML
```html
<div class="scd-30">
<div class="scd-30__stage">
<input type="radio" name="scd-30-t" id="scd-30-t1" class="scd-30__radio scd-30__radio--r1" checked>
<input type="radio" name="scd-30-t" id="scd-30-t2" class="scd-30__radio scd-30__radio--r2">
<input type="radio" name="scd-30-t" id="scd-30-t3" class="scd-30__radio scd-30__radio--r3">
<div class="scd-30__slider">
<div class="scd-30__quote scd-30__quote--q1"><p>“This completely changed how our team ships. We cut launch time in half.”</p><div class="scd-30__who"><span class="scd-30__av" style="background:linear-gradient(135deg,#ff9a3c,#ff5e62)"></span><div><h4>Maya Chen</h4><span>Head of Product, Loop</span></div></div></div>
<div class="scd-30__quote scd-30__quote--q2"><p>“Beautiful, fast, and genuinely a joy to use every single day.”</p><div class="scd-30__who"><span class="scd-30__av" style="background:linear-gradient(135deg,#43cea2,#185a9d)"></span><div><h4>Theo Marsh</h4><span>Founder, Northbeam</span></div></div></div>
<div class="scd-30__quote scd-30__quote--q3"><p>“The support alone is worth it. Best decision we made this year.”</p><div class="scd-30__who"><span class="scd-30__av" style="background:linear-gradient(135deg,#c471f5,#7f53ac)"></span><div><h4>Lena Ortiz</h4><span>CTO, Brightside</span></div></div></div>
</div>
<div class="scd-30__dots">
<label for="scd-30-t1"></label><label for="scd-30-t2"></label><label for="scd-30-t3"></label>
</div>
</div>
</div>
```
## CSS
```css
@import url('https://fonts.googleapis.com/css2?family=Newsreader:ital,opsz,wght@0,6..72,500;1,6..72,400&family=Geist:wght@400;500;600&display=swap');
.scd-30,
.scd-30 *,
.scd-30 *::before,
.scd-30 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.scd-30 {
width: 100%;
min-height: 100vh;
display: grid;
place-items: center;
background: linear-gradient(160deg,#fef6ee,#f4e4d4);
font-family: 'Geist', sans-serif;
}
.scd-30__stage {
position: relative;
}
.scd-30__radio {
position: absolute;
opacity: 0;
pointer-events: none;
width: 0;
height: 0;
}
.scd-30__slider {
position: relative;
width: 330px;
height: 300px;
}
.scd-30__quote {
position: absolute;
inset: 0;
background: #fffdfa;
border-radius: 22px;
padding: 30px;
box-shadow: 0 18px 40px rgba(120,80,50,.18);
border: 1px solid #f0e2d2;
transition: transform .6s cubic-bezier(.3,.9,.3,1), opacity .6s;
display: flex;
flex-direction: column;
justify-content: space-between;
transform: translateY(28px) scale(.88);
opacity: 0;
z-index: 1;
}
.scd-30__quote p {
font-family: 'Newsreader', serif;
font-style: italic;
font-size: 1.25rem;
line-height: 1.45;
color: #3a2e24;
}
.scd-30__who {
display: flex;
align-items: center;
gap: 12px;
}
.scd-30__av {
width: 44px;
height: 44px;
border-radius: 50%;
flex-shrink: 0;
display: block;
}
.scd-30__who h4 {
font-size: .9rem;
font-weight: 600;
color: #2a201a;
}
.scd-30__who span {
font-size: .78rem;
color: #9b7d5f;
}
.scd-30__radio--r1:checked ~ .scd-30__slider .scd-30__quote--q1,
.scd-30__radio--r2:checked ~ .scd-30__slider .scd-30__quote--q2,
.scd-30__radio--r3:checked ~ .scd-30__slider .scd-30__quote--q3 {
transform: translateY(0) scale(1);
opacity: 1;
z-index: 4;
}
.scd-30__radio--r1:checked ~ .scd-30__slider .scd-30__quote--q2,
.scd-30__radio--r2:checked ~ .scd-30__slider .scd-30__quote--q3,
.scd-30__radio--r3:checked ~ .scd-30__slider .scd-30__quote--q1 {
transform: translateY(14px) scale(.94);
opacity: .55;
z-index: 3;
}
.scd-30__radio--r1:checked ~ .scd-30__slider .scd-30__quote--q3,
.scd-30__radio--r2:checked ~ .scd-30__slider .scd-30__quote--q1,
.scd-30__radio--r3:checked ~ .scd-30__slider .scd-30__quote--q2 {
transform: translateY(28px) scale(.88);
opacity: .3;
z-index: 2;
}
.scd-30__dots {
display: flex;
gap: 10px;
justify-content: center;
margin-top: 30px;
}
.scd-30__dots label {
width: 11px;
height: 11px;
border-radius: 50%;
background: #d8c3aa;
cursor: pointer;
transition: background .3s, transform .3s;
}
.scd-30__dots label:hover {
background: #c9a98a;
}
.scd-30__radio--r1:checked ~ .scd-30__dots label[for="scd-30-t1"],
.scd-30__radio--r2:checked ~ .scd-30__dots label[for="scd-30-t2"],
.scd-30__radio--r3:checked ~ .scd-30__dots label[for="scd-30-t3"] {
background: #b8553f;
transform: scale(1.3);
}
@media (prefers-reduced-motion: reduce) {
.scd-30__quote,
.scd-30__dots label {
transition: none !important;
}
}
```How this works
Three testimonial cards stacked with the top card visible and the next two peeking behind. Hidden radio inputs (input type="radio" name="tst") at the top of the DOM drive the state — the :checked radio's index determines which card is on top, via sibling combinators like #tst-1:checked ~ .cards .card:nth-child(1).
Dot buttons at the bottom are actually <label for="tst-N"> elements — clicking a dot checks its radio, which reshuffles the stack via CSS. Zero JavaScript, and the entire state lives in the DOM (radios can even be hydrated from URL query string).
The active card sits at z-index:3; transform:translateY(0); opacity:1. The next card sits behind at z-index:2; transform:translateY(12px) scale(0.96); opacity:0.7. The third card sits further behind. Smooth transition on all three properties creates the peek-behind shuffle.
Make it yours
- Add more testimonials by adding more radios + cards + dots — each new card gets an incremented
:nth-childrule. - Rebrand the card body and dots from CSS custom properties on the wrapper.
- Change the peek offset — smaller
translateYreads as tighter stack; larger reveals more of the cards behind. - Add company logos or star ratings inside each testimonial for richer social proof.
- For SaaS landing pages, wire the radios to auto-cycle every 6-8 seconds via a small setInterval — but keep the manual radios as the fallback.
Gotchas — read before shipping
- Radio inputs must be visually hidden but keyboard-accessible — use
position:absolute; opacity:0; pointer-events:none, notdisplay:none. - The label + radio pattern needs matching
id/forattributes — screen readers use these to announce state changes. - Testimonials must be honest — FTC 16 CFR § 255 (Endorsements Rule, updated 2023) requires substantiation. Fabricated quotes are a compliance risk.
- For SaaS/finance/healthcare testimonials, disclose compensation or affiliations per FTC guidelines.
- Sibling combinator selectors (
~) can get slow with 20+ radios; cap the deck at 5-6 testimonials for performance.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 45+ | 9+ | 16+ | 45+ |
The radio + :checked + sibling combinator pattern is universally supported. No JavaScript required.