36 CSS Stacked Cards08 / 36
Dynamic Tabbed Stack Deck
Cards stacked directly on top of one another, each with an offset colour-coded top tab sticking out. Click any visible tab and that card jumps to the absolute front while the rest translate back — a filing-cabinet metaphor built entirely with radios.
Published
The code
<section class="scd-08" aria-label="Tabbed card deck">
<div class="scd-08__deck">
<input type="radio" name="scd-08" id="scd-08-1" class="scd-08__r" checked>
<input type="radio" name="scd-08" id="scd-08-2" class="scd-08__r">
<input type="radio" name="scd-08" id="scd-08-3" class="scd-08__r">
<input type="radio" name="scd-08" id="scd-08-4" class="scd-08__r">
<label class="scd-08__tab" for="scd-08-1" style="--c:oklch(0.65 0.17 25);--l:0">Overview</label>
<label class="scd-08__tab" for="scd-08-2" style="--c:oklch(0.7 0.16 145);--l:1">Pricing</label>
<label class="scd-08__tab" for="scd-08-3" style="--c:oklch(0.68 0.15 235);--l:2">Docs</label>
<label class="scd-08__tab" for="scd-08-4" style="--c:oklch(0.68 0.16 300);--l:3">Support</label>
<article class="scd-08__card" style="--c:oklch(0.65 0.17 25)"><h3>Overview</h3><p>One deck, four surfaces. Click a tab to bring its card to the very front.</p></article>
<article class="scd-08__card" style="--c:oklch(0.7 0.16 145)"><h3>Pricing</h3><p>Flat monthly tiers, no seats, cancel anytime. Start free, upgrade when it pays off.</p></article>
<article class="scd-08__card" style="--c:oklch(0.68 0.15 235)"><h3>Docs</h3><p>Copy-paste snippets, live playgrounds and a searchable API. Zero setup to try.</p></article>
<article class="scd-08__card" style="--c:oklch(0.68 0.16 300)"><h3>Support</h3><p>Real humans, weekday chat, and a status page that actually stays current.</p></article>
</div>
</section><section class="scd-08" aria-label="Tabbed card deck">
<div class="scd-08__deck">
<input type="radio" name="scd-08" id="scd-08-1" class="scd-08__r" checked>
<input type="radio" name="scd-08" id="scd-08-2" class="scd-08__r">
<input type="radio" name="scd-08" id="scd-08-3" class="scd-08__r">
<input type="radio" name="scd-08" id="scd-08-4" class="scd-08__r">
<label class="scd-08__tab" for="scd-08-1" style="--c:oklch(0.65 0.17 25);--l:0">Overview</label>
<label class="scd-08__tab" for="scd-08-2" style="--c:oklch(0.7 0.16 145);--l:1">Pricing</label>
<label class="scd-08__tab" for="scd-08-3" style="--c:oklch(0.68 0.15 235);--l:2">Docs</label>
<label class="scd-08__tab" for="scd-08-4" style="--c:oklch(0.68 0.16 300);--l:3">Support</label>
<article class="scd-08__card" style="--c:oklch(0.65 0.17 25)"><h3>Overview</h3><p>One deck, four surfaces. Click a tab to bring its card to the very front.</p></article>
<article class="scd-08__card" style="--c:oklch(0.7 0.16 145)"><h3>Pricing</h3><p>Flat monthly tiers, no seats, cancel anytime. Start free, upgrade when it pays off.</p></article>
<article class="scd-08__card" style="--c:oklch(0.68 0.15 235)"><h3>Docs</h3><p>Copy-paste snippets, live playgrounds and a searchable API. Zero setup to try.</p></article>
<article class="scd-08__card" style="--c:oklch(0.68 0.16 300)"><h3>Support</h3><p>Real humans, weekday chat, and a status page that actually stays current.</p></article>
</div>
</section>.scd-08,
.scd-08 *,
.scd-08 *::before,
.scd-08 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.scd-08 {
font-family: 'Segoe UI',system-ui,sans-serif;
width: 100%;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 60px 20px;
background: #20222c;
}
.scd-08__deck {
position: relative;
width: min(400px,100%);
height: 300px;
padding-top: 44px;
}
.scd-08__r {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
overflow: hidden;
clip: rect(0 0 0 0);
}
.scd-08__tab {
position: absolute;
top: 0;
left: calc(var(--l) * 25%);
width: 24%;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
font-size: 13px;
font-weight: 700;
color: #fff;
background: var(--c);
border-radius: 12px 12px 0 0;
cursor: pointer;
z-index: 5;
filter: brightness(.8);
transition: filter .25s,transform .25s;
}
.scd-08__tab:hover {
filter: brightness(.95);
}
.scd-08__card {
position: absolute;
left: 0;
right: 0;
top: 40px;
height: 250px;
padding: 28px;
border-radius: 0 16px 16px 16px;
color: #fff;
background: var(--c);
box-shadow: 0 26px 50px -28px rgba(0,0,0,.7);
transform: translateY(24px) scale(.94);
transition: transform .45s cubic-bezier(.3,.9,.3,1);
z-index: 1;
}
.scd-08__card h3 {
font-size: 24px;
font-weight: 800;
margin-bottom: 12px;
letter-spacing: -.02em;
}
.scd-08__card p {
font-size: 15px;
line-height: 1.6;
max-width: 34ch;
opacity: .95;
}
.scd-08__r:nth-of-type(1):checked ~ .scd-08__tab:nth-of-type(1),
.scd-08__r:nth-of-type(2):checked ~ .scd-08__tab:nth-of-type(2),
.scd-08__r:nth-of-type(3):checked ~ .scd-08__tab:nth-of-type(3),
.scd-08__r:nth-of-type(4):checked ~ .scd-08__tab:nth-of-type(4) {
filter: brightness(1);
transform: translateY(-4px);
}
.scd-08__r:nth-of-type(1):checked ~ .scd-08__card:nth-of-type(1),
.scd-08__r:nth-of-type(2):checked ~ .scd-08__card:nth-of-type(2),
.scd-08__r:nth-of-type(3):checked ~ .scd-08__card:nth-of-type(3),
.scd-08__r:nth-of-type(4):checked ~ .scd-08__card:nth-of-type(4) {
transform: translateY(0) scale(1);
z-index: 4;
}
.scd-08__r:focus-visible ~ .scd-08__tab {
outline: 3px solid #fff;
outline-offset: 2px;
}
.scd-08__r:nth-of-type(1):focus-visible ~ .scd-08__tab:nth-of-type(1),
.scd-08__r:nth-of-type(2):focus-visible ~ .scd-08__tab:nth-of-type(2),
.scd-08__r:nth-of-type(3):focus-visible ~ .scd-08__tab:nth-of-type(3),
.scd-08__r:nth-of-type(4):focus-visible ~ .scd-08__tab:nth-of-type(4) {
outline: 3px solid #fff;
outline-offset: 3px;
}
@media (prefers-reduced-motion: reduce) {
.scd-08__tab,
.scd-08__card {
transition: none;
}
}.scd-08,
.scd-08 *,
.scd-08 *::before,
.scd-08 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.scd-08 {
font-family: 'Segoe UI',system-ui,sans-serif;
width: 100%;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 60px 20px;
background: #20222c;
}
.scd-08__deck {
position: relative;
width: min(400px,100%);
height: 300px;
padding-top: 44px;
}
.scd-08__r {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
overflow: hidden;
clip: rect(0 0 0 0);
}
.scd-08__tab {
position: absolute;
top: 0;
left: calc(var(--l) * 25%);
width: 24%;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
font-size: 13px;
font-weight: 700;
color: #fff;
background: var(--c);
border-radius: 12px 12px 0 0;
cursor: pointer;
z-index: 5;
filter: brightness(.8);
transition: filter .25s,transform .25s;
}
.scd-08__tab:hover {
filter: brightness(.95);
}
.scd-08__card {
position: absolute;
left: 0;
right: 0;
top: 40px;
height: 250px;
padding: 28px;
border-radius: 0 16px 16px 16px;
color: #fff;
background: var(--c);
box-shadow: 0 26px 50px -28px rgba(0,0,0,.7);
transform: translateY(24px) scale(.94);
transition: transform .45s cubic-bezier(.3,.9,.3,1);
z-index: 1;
}
.scd-08__card h3 {
font-size: 24px;
font-weight: 800;
margin-bottom: 12px;
letter-spacing: -.02em;
}
.scd-08__card p {
font-size: 15px;
line-height: 1.6;
max-width: 34ch;
opacity: .95;
}
.scd-08__r:nth-of-type(1):checked ~ .scd-08__tab:nth-of-type(1),
.scd-08__r:nth-of-type(2):checked ~ .scd-08__tab:nth-of-type(2),
.scd-08__r:nth-of-type(3):checked ~ .scd-08__tab:nth-of-type(3),
.scd-08__r:nth-of-type(4):checked ~ .scd-08__tab:nth-of-type(4) {
filter: brightness(1);
transform: translateY(-4px);
}
.scd-08__r:nth-of-type(1):checked ~ .scd-08__card:nth-of-type(1),
.scd-08__r:nth-of-type(2):checked ~ .scd-08__card:nth-of-type(2),
.scd-08__r:nth-of-type(3):checked ~ .scd-08__card:nth-of-type(3),
.scd-08__r:nth-of-type(4):checked ~ .scd-08__card:nth-of-type(4) {
transform: translateY(0) scale(1);
z-index: 4;
}
.scd-08__r:focus-visible ~ .scd-08__tab {
outline: 3px solid #fff;
outline-offset: 2px;
}
.scd-08__r:nth-of-type(1):focus-visible ~ .scd-08__tab:nth-of-type(1),
.scd-08__r:nth-of-type(2):focus-visible ~ .scd-08__tab:nth-of-type(2),
.scd-08__r:nth-of-type(3):focus-visible ~ .scd-08__tab:nth-of-type(3),
.scd-08__r:nth-of-type(4):focus-visible ~ .scd-08__tab:nth-of-type(4) {
outline: 3px solid #fff;
outline-offset: 3px;
}
@media (prefers-reduced-motion: reduce) {
.scd-08__tab,
.scd-08__card {
transition: none;
}
}/* No JavaScript — a radio :checked state machine pulls the chosen card to the front z-index and nudges the rest back to reveal their tabs. */
/* No JavaScript — a radio :checked state machine pulls the chosen card to the front z-index and nudges the rest back to reveal their tabs. */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: Dynamic Tabbed Stack Deck
Source: https://codefronts.com/components/css-stacked-cards/dynamic-tabbed-stack-deck/
Cards stacked directly on top of one another, each with an offset colour-coded top tab sticking out. Click any visible tab and that card jumps to the absolute front while the rest translate back — a filing-cabinet metaphor built entirely with radios.
## HTML
```html
<section class="scd-08" aria-label="Tabbed card deck">
<div class="scd-08__deck">
<input type="radio" name="scd-08" id="scd-08-1" class="scd-08__r" checked>
<input type="radio" name="scd-08" id="scd-08-2" class="scd-08__r">
<input type="radio" name="scd-08" id="scd-08-3" class="scd-08__r">
<input type="radio" name="scd-08" id="scd-08-4" class="scd-08__r">
<label class="scd-08__tab" for="scd-08-1" style="--c:oklch(0.65 0.17 25);--l:0">Overview</label>
<label class="scd-08__tab" for="scd-08-2" style="--c:oklch(0.7 0.16 145);--l:1">Pricing</label>
<label class="scd-08__tab" for="scd-08-3" style="--c:oklch(0.68 0.15 235);--l:2">Docs</label>
<label class="scd-08__tab" for="scd-08-4" style="--c:oklch(0.68 0.16 300);--l:3">Support</label>
<article class="scd-08__card" style="--c:oklch(0.65 0.17 25)"><h3>Overview</h3><p>One deck, four surfaces. Click a tab to bring its card to the very front.</p></article>
<article class="scd-08__card" style="--c:oklch(0.7 0.16 145)"><h3>Pricing</h3><p>Flat monthly tiers, no seats, cancel anytime. Start free, upgrade when it pays off.</p></article>
<article class="scd-08__card" style="--c:oklch(0.68 0.15 235)"><h3>Docs</h3><p>Copy-paste snippets, live playgrounds and a searchable API. Zero setup to try.</p></article>
<article class="scd-08__card" style="--c:oklch(0.68 0.16 300)"><h3>Support</h3><p>Real humans, weekday chat, and a status page that actually stays current.</p></article>
</div>
</section>
```
## CSS
```css
.scd-08,
.scd-08 *,
.scd-08 *::before,
.scd-08 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.scd-08 {
font-family: 'Segoe UI',system-ui,sans-serif;
width: 100%;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 60px 20px;
background: #20222c;
}
.scd-08__deck {
position: relative;
width: min(400px,100%);
height: 300px;
padding-top: 44px;
}
.scd-08__r {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
overflow: hidden;
clip: rect(0 0 0 0);
}
.scd-08__tab {
position: absolute;
top: 0;
left: calc(var(--l) * 25%);
width: 24%;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
font-size: 13px;
font-weight: 700;
color: #fff;
background: var(--c);
border-radius: 12px 12px 0 0;
cursor: pointer;
z-index: 5;
filter: brightness(.8);
transition: filter .25s,transform .25s;
}
.scd-08__tab:hover {
filter: brightness(.95);
}
.scd-08__card {
position: absolute;
left: 0;
right: 0;
top: 40px;
height: 250px;
padding: 28px;
border-radius: 0 16px 16px 16px;
color: #fff;
background: var(--c);
box-shadow: 0 26px 50px -28px rgba(0,0,0,.7);
transform: translateY(24px) scale(.94);
transition: transform .45s cubic-bezier(.3,.9,.3,1);
z-index: 1;
}
.scd-08__card h3 {
font-size: 24px;
font-weight: 800;
margin-bottom: 12px;
letter-spacing: -.02em;
}
.scd-08__card p {
font-size: 15px;
line-height: 1.6;
max-width: 34ch;
opacity: .95;
}
.scd-08__r:nth-of-type(1):checked ~ .scd-08__tab:nth-of-type(1),
.scd-08__r:nth-of-type(2):checked ~ .scd-08__tab:nth-of-type(2),
.scd-08__r:nth-of-type(3):checked ~ .scd-08__tab:nth-of-type(3),
.scd-08__r:nth-of-type(4):checked ~ .scd-08__tab:nth-of-type(4) {
filter: brightness(1);
transform: translateY(-4px);
}
.scd-08__r:nth-of-type(1):checked ~ .scd-08__card:nth-of-type(1),
.scd-08__r:nth-of-type(2):checked ~ .scd-08__card:nth-of-type(2),
.scd-08__r:nth-of-type(3):checked ~ .scd-08__card:nth-of-type(3),
.scd-08__r:nth-of-type(4):checked ~ .scd-08__card:nth-of-type(4) {
transform: translateY(0) scale(1);
z-index: 4;
}
.scd-08__r:focus-visible ~ .scd-08__tab {
outline: 3px solid #fff;
outline-offset: 2px;
}
.scd-08__r:nth-of-type(1):focus-visible ~ .scd-08__tab:nth-of-type(1),
.scd-08__r:nth-of-type(2):focus-visible ~ .scd-08__tab:nth-of-type(2),
.scd-08__r:nth-of-type(3):focus-visible ~ .scd-08__tab:nth-of-type(3),
.scd-08__r:nth-of-type(4):focus-visible ~ .scd-08__tab:nth-of-type(4) {
outline: 3px solid #fff;
outline-offset: 3px;
}
@media (prefers-reduced-motion: reduce) {
.scd-08__tab,
.scd-08__card {
transition: none;
}
}
```
## JavaScript
```js
/* No JavaScript — a radio :checked state machine pulls the chosen card to the front z-index and nudges the rest back to reveal their tabs. */
```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: Dynamic Tabbed Stack Deck
Source: https://codefronts.com/components/css-stacked-cards/dynamic-tabbed-stack-deck/
Cards stacked directly on top of one another, each with an offset colour-coded top tab sticking out. Click any visible tab and that card jumps to the absolute front while the rest translate back — a filing-cabinet metaphor built entirely with radios.
## HTML
```html
<section class="scd-08" aria-label="Tabbed card deck">
<div class="scd-08__deck">
<input type="radio" name="scd-08" id="scd-08-1" class="scd-08__r" checked>
<input type="radio" name="scd-08" id="scd-08-2" class="scd-08__r">
<input type="radio" name="scd-08" id="scd-08-3" class="scd-08__r">
<input type="radio" name="scd-08" id="scd-08-4" class="scd-08__r">
<label class="scd-08__tab" for="scd-08-1" style="--c:oklch(0.65 0.17 25);--l:0">Overview</label>
<label class="scd-08__tab" for="scd-08-2" style="--c:oklch(0.7 0.16 145);--l:1">Pricing</label>
<label class="scd-08__tab" for="scd-08-3" style="--c:oklch(0.68 0.15 235);--l:2">Docs</label>
<label class="scd-08__tab" for="scd-08-4" style="--c:oklch(0.68 0.16 300);--l:3">Support</label>
<article class="scd-08__card" style="--c:oklch(0.65 0.17 25)"><h3>Overview</h3><p>One deck, four surfaces. Click a tab to bring its card to the very front.</p></article>
<article class="scd-08__card" style="--c:oklch(0.7 0.16 145)"><h3>Pricing</h3><p>Flat monthly tiers, no seats, cancel anytime. Start free, upgrade when it pays off.</p></article>
<article class="scd-08__card" style="--c:oklch(0.68 0.15 235)"><h3>Docs</h3><p>Copy-paste snippets, live playgrounds and a searchable API. Zero setup to try.</p></article>
<article class="scd-08__card" style="--c:oklch(0.68 0.16 300)"><h3>Support</h3><p>Real humans, weekday chat, and a status page that actually stays current.</p></article>
</div>
</section>
```
## CSS
```css
.scd-08,
.scd-08 *,
.scd-08 *::before,
.scd-08 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.scd-08 {
font-family: 'Segoe UI',system-ui,sans-serif;
width: 100%;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 60px 20px;
background: #20222c;
}
.scd-08__deck {
position: relative;
width: min(400px,100%);
height: 300px;
padding-top: 44px;
}
.scd-08__r {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
overflow: hidden;
clip: rect(0 0 0 0);
}
.scd-08__tab {
position: absolute;
top: 0;
left: calc(var(--l) * 25%);
width: 24%;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
font-size: 13px;
font-weight: 700;
color: #fff;
background: var(--c);
border-radius: 12px 12px 0 0;
cursor: pointer;
z-index: 5;
filter: brightness(.8);
transition: filter .25s,transform .25s;
}
.scd-08__tab:hover {
filter: brightness(.95);
}
.scd-08__card {
position: absolute;
left: 0;
right: 0;
top: 40px;
height: 250px;
padding: 28px;
border-radius: 0 16px 16px 16px;
color: #fff;
background: var(--c);
box-shadow: 0 26px 50px -28px rgba(0,0,0,.7);
transform: translateY(24px) scale(.94);
transition: transform .45s cubic-bezier(.3,.9,.3,1);
z-index: 1;
}
.scd-08__card h3 {
font-size: 24px;
font-weight: 800;
margin-bottom: 12px;
letter-spacing: -.02em;
}
.scd-08__card p {
font-size: 15px;
line-height: 1.6;
max-width: 34ch;
opacity: .95;
}
.scd-08__r:nth-of-type(1):checked ~ .scd-08__tab:nth-of-type(1),
.scd-08__r:nth-of-type(2):checked ~ .scd-08__tab:nth-of-type(2),
.scd-08__r:nth-of-type(3):checked ~ .scd-08__tab:nth-of-type(3),
.scd-08__r:nth-of-type(4):checked ~ .scd-08__tab:nth-of-type(4) {
filter: brightness(1);
transform: translateY(-4px);
}
.scd-08__r:nth-of-type(1):checked ~ .scd-08__card:nth-of-type(1),
.scd-08__r:nth-of-type(2):checked ~ .scd-08__card:nth-of-type(2),
.scd-08__r:nth-of-type(3):checked ~ .scd-08__card:nth-of-type(3),
.scd-08__r:nth-of-type(4):checked ~ .scd-08__card:nth-of-type(4) {
transform: translateY(0) scale(1);
z-index: 4;
}
.scd-08__r:focus-visible ~ .scd-08__tab {
outline: 3px solid #fff;
outline-offset: 2px;
}
.scd-08__r:nth-of-type(1):focus-visible ~ .scd-08__tab:nth-of-type(1),
.scd-08__r:nth-of-type(2):focus-visible ~ .scd-08__tab:nth-of-type(2),
.scd-08__r:nth-of-type(3):focus-visible ~ .scd-08__tab:nth-of-type(3),
.scd-08__r:nth-of-type(4):focus-visible ~ .scd-08__tab:nth-of-type(4) {
outline: 3px solid #fff;
outline-offset: 3px;
}
@media (prefers-reduced-motion: reduce) {
.scd-08__tab,
.scd-08__card {
transition: none;
}
}
```
## JavaScript
```js
/* No JavaScript — a radio :checked state machine pulls the chosen card to the front z-index and nudges the rest back to reveal their tabs. */
```How this works
A shared radio group backs each card; the card's clickable tab is a <label>. Tabs are spread horizontally so they never overlap. On :checked, the matching card's z-index and transform pull it fully forward while the sibling chain nudges the others down and back, revealing their tabs. It's a pure CSS state machine — the depth hierarchy is data, not script.
Radios keep native arrow-key switching between tabs, :focus-visible rings the active tab, and each card's colour matches its tab so the mental model stays obvious.
Make it yours
- Add a card by adding one radio + tab + panel and one colour.
- Change the resting fan by editing the un-checked
translateY. - Reposition tabs via their
leftoffsets. - Recolour each card/tab pair through its inline
--c.
Gotchas — read before shipping
- Space tabs so all stay clickable even when cards overlap.
- Keep radios focusable (sr-only, not display:none) for keyboard tab-switching.
- Ensure the front card fully covers the others' bodies but never their tabs.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 15.4+ | 113+ | 111+ |
Floor set by oklch() as this demo is written. The core technique itself goes back further — Chrome 49+.
Radio :checked sibling combinators + transforms; universal in modern browsers.