32 CSS Tab Designs23 / 32
Reverse Stamp
The whole bar is cream-on-crimson. The active tab inverts — crimson ink stamped on cream. A single-frame impact shadow flashes on activation, like a rubber stamp pressing in.
Published Updated
The code
<div class="tt11">
<nav class="tt11n">
<input type="radio" name="tt11" id="tt11-r1" checked />
<label class="tt11b" for="tt11-r1">Recto</label>
<input type="radio" name="tt11" id="tt11-r2" />
<label class="tt11b" for="tt11-r2">Verso</label>
<input type="radio" name="tt11" id="tt11-r3" />
<label class="tt11b" for="tt11-r3">Folio</label>
</nav>
<div class="tt11p">The right-hand page, traditionally odd-numbered.</div>
<div class="tt11p">The left-hand page, traditionally even.</div>
<div class="tt11p">The full leaf — both sides counted as one.</div>
</div><div class="tt11">
<nav class="tt11n">
<input type="radio" name="tt11" id="tt11-r1" checked />
<label class="tt11b" for="tt11-r1">Recto</label>
<input type="radio" name="tt11" id="tt11-r2" />
<label class="tt11b" for="tt11-r2">Verso</label>
<input type="radio" name="tt11" id="tt11-r3" />
<label class="tt11b" for="tt11-r3">Folio</label>
</nav>
<div class="tt11p">The right-hand page, traditionally odd-numbered.</div>
<div class="tt11p">The left-hand page, traditionally even.</div>
<div class="tt11p">The full leaf — both sides counted as one.</div>
</div>.tt11 {
background: #dc1e3a;
padding: 0;
font-family: ui-monospace, monospace;
min-height: 100vh;
display: flex;
flex-direction: column;
width: 100%;
}
.tt11n {
display: flex;
gap: 0;
flex-shrink: 0;
}
.tt11n input {
position: absolute;
opacity: 0;
pointer-events: none;
}
.tt11b {
flex: 1;
padding: 18px 12px;
border: 0;
background: #dc1e3a;
color: #f5ead0;
font: 800 12px/1 ui-monospace, monospace;
letter-spacing: 0.22em;
text-transform: uppercase;
cursor: pointer;
border-right: 1px solid rgba(245, 234, 208, 0.25);
transition: background 0.05s steps(1, end), color 0.05s steps(1, end), box-shadow 0.4s;
}
.tt11b:last-child {
border-right: 0;
}
.tt11b:hover {
background: rgba(245, 234, 208, 0.1);
}
.tt11n input:checked + .tt11b {
background: #f5ead0;
color: #dc1e3a;
box-shadow: inset 0 0 0 0 rgba(0, 0, 0, 0.4);
animation: tt11-impact 0.35s cubic-bezier(0.65, 0, 0.35, 1);
}
@keyframes tt11-impact {
0% {
box-shadow: inset 0 0 0 0 rgba(0, 0, 0, 0.4);
}
20% {
box-shadow: inset 0 4px 14px rgba(0, 0, 0, 0.5);
}
100% {
box-shadow: inset 0 0 0 0 rgba(0, 0, 0, 0);
}
}
.tt11p {
display: none;
padding: 14px 18px;
background: #f5ead0;
font: italic 11px/1.6 ui-serif, Georgia, serif;
color: #3a3a42;
flex: 1;
}
.tt11:has(#tt11-r1:checked) .tt11p:nth-of-type(1),
.tt11:has(#tt11-r2:checked) .tt11p:nth-of-type(2),
.tt11:has(#tt11-r3:checked) .tt11p:nth-of-type(3),
.tt11:has(#tt11-r4:checked) .tt11p:nth-of-type(4),
.tt11:has(#tt11-r5:checked) .tt11p:nth-of-type(5),
.tt11:has(#tt11-r6:checked) .tt11p:nth-of-type(6) {
display: flex;
align-items: center;
}
@media (prefers-reduced-motion: reduce) {
.tt11n input:checked + .tt11b {
animation: none;
}
}.tt11 {
background: #dc1e3a;
padding: 0;
font-family: ui-monospace, monospace;
min-height: 100vh;
display: flex;
flex-direction: column;
width: 100%;
}
.tt11n {
display: flex;
gap: 0;
flex-shrink: 0;
}
.tt11n input {
position: absolute;
opacity: 0;
pointer-events: none;
}
.tt11b {
flex: 1;
padding: 18px 12px;
border: 0;
background: #dc1e3a;
color: #f5ead0;
font: 800 12px/1 ui-monospace, monospace;
letter-spacing: 0.22em;
text-transform: uppercase;
cursor: pointer;
border-right: 1px solid rgba(245, 234, 208, 0.25);
transition: background 0.05s steps(1, end), color 0.05s steps(1, end), box-shadow 0.4s;
}
.tt11b:last-child {
border-right: 0;
}
.tt11b:hover {
background: rgba(245, 234, 208, 0.1);
}
.tt11n input:checked + .tt11b {
background: #f5ead0;
color: #dc1e3a;
box-shadow: inset 0 0 0 0 rgba(0, 0, 0, 0.4);
animation: tt11-impact 0.35s cubic-bezier(0.65, 0, 0.35, 1);
}
@keyframes tt11-impact {
0% {
box-shadow: inset 0 0 0 0 rgba(0, 0, 0, 0.4);
}
20% {
box-shadow: inset 0 4px 14px rgba(0, 0, 0, 0.5);
}
100% {
box-shadow: inset 0 0 0 0 rgba(0, 0, 0, 0);
}
}
.tt11p {
display: none;
padding: 14px 18px;
background: #f5ead0;
font: italic 11px/1.6 ui-serif, Georgia, serif;
color: #3a3a42;
flex: 1;
}
.tt11:has(#tt11-r1:checked) .tt11p:nth-of-type(1),
.tt11:has(#tt11-r2:checked) .tt11p:nth-of-type(2),
.tt11:has(#tt11-r3:checked) .tt11p:nth-of-type(3),
.tt11:has(#tt11-r4:checked) .tt11p:nth-of-type(4),
.tt11:has(#tt11-r5:checked) .tt11p:nth-of-type(5),
.tt11:has(#tt11-r6:checked) .tt11p:nth-of-type(6) {
display: flex;
align-items: center;
}
@media (prefers-reduced-motion: reduce) {
.tt11n input:checked + .tt11b {
animation: none;
}
}Here's a working CSS Tab Design 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: Reverse Stamp
Source: https://codefronts.com/navigation/css-tabs/reverse-stamp/
The whole bar is cream-on-crimson. The active tab inverts — crimson ink stamped on cream. A single-frame impact shadow flashes on activation, like a rubber stamp pressing in.
## HTML
```html
<div class="tt11">
<nav class="tt11n">
<input type="radio" name="tt11" id="tt11-r1" checked />
<label class="tt11b" for="tt11-r1">Recto</label>
<input type="radio" name="tt11" id="tt11-r2" />
<label class="tt11b" for="tt11-r2">Verso</label>
<input type="radio" name="tt11" id="tt11-r3" />
<label class="tt11b" for="tt11-r3">Folio</label>
</nav>
<div class="tt11p">The right-hand page, traditionally odd-numbered.</div>
<div class="tt11p">The left-hand page, traditionally even.</div>
<div class="tt11p">The full leaf — both sides counted as one.</div>
</div>
```
## CSS
```css
.tt11 {
background: #dc1e3a;
padding: 0;
font-family: ui-monospace, monospace;
min-height: 100vh;
display: flex;
flex-direction: column;
width: 100%;
}
.tt11n {
display: flex;
gap: 0;
flex-shrink: 0;
}
.tt11n input {
position: absolute;
opacity: 0;
pointer-events: none;
}
.tt11b {
flex: 1;
padding: 18px 12px;
border: 0;
background: #dc1e3a;
color: #f5ead0;
font: 800 12px/1 ui-monospace, monospace;
letter-spacing: 0.22em;
text-transform: uppercase;
cursor: pointer;
border-right: 1px solid rgba(245, 234, 208, 0.25);
transition: background 0.05s steps(1, end), color 0.05s steps(1, end), box-shadow 0.4s;
}
.tt11b:last-child {
border-right: 0;
}
.tt11b:hover {
background: rgba(245, 234, 208, 0.1);
}
.tt11n input:checked + .tt11b {
background: #f5ead0;
color: #dc1e3a;
box-shadow: inset 0 0 0 0 rgba(0, 0, 0, 0.4);
animation: tt11-impact 0.35s cubic-bezier(0.65, 0, 0.35, 1);
}
@keyframes tt11-impact {
0% {
box-shadow: inset 0 0 0 0 rgba(0, 0, 0, 0.4);
}
20% {
box-shadow: inset 0 4px 14px rgba(0, 0, 0, 0.5);
}
100% {
box-shadow: inset 0 0 0 0 rgba(0, 0, 0, 0);
}
}
.tt11p {
display: none;
padding: 14px 18px;
background: #f5ead0;
font: italic 11px/1.6 ui-serif, Georgia, serif;
color: #3a3a42;
flex: 1;
}
.tt11:has(#tt11-r1:checked) .tt11p:nth-of-type(1),
.tt11:has(#tt11-r2:checked) .tt11p:nth-of-type(2),
.tt11:has(#tt11-r3:checked) .tt11p:nth-of-type(3),
.tt11:has(#tt11-r4:checked) .tt11p:nth-of-type(4),
.tt11:has(#tt11-r5:checked) .tt11p:nth-of-type(5),
.tt11:has(#tt11-r6:checked) .tt11p:nth-of-type(6) {
display: flex;
align-items: center;
}
@media (prefers-reduced-motion: reduce) {
.tt11n input:checked + .tt11b {
animation: none;
}
}
```Here's a working CSS Tab Design 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: Reverse Stamp
Source: https://codefronts.com/navigation/css-tabs/reverse-stamp/
The whole bar is cream-on-crimson. The active tab inverts — crimson ink stamped on cream. A single-frame impact shadow flashes on activation, like a rubber stamp pressing in.
## HTML
```html
<div class="tt11">
<nav class="tt11n">
<input type="radio" name="tt11" id="tt11-r1" checked />
<label class="tt11b" for="tt11-r1">Recto</label>
<input type="radio" name="tt11" id="tt11-r2" />
<label class="tt11b" for="tt11-r2">Verso</label>
<input type="radio" name="tt11" id="tt11-r3" />
<label class="tt11b" for="tt11-r3">Folio</label>
</nav>
<div class="tt11p">The right-hand page, traditionally odd-numbered.</div>
<div class="tt11p">The left-hand page, traditionally even.</div>
<div class="tt11p">The full leaf — both sides counted as one.</div>
</div>
```
## CSS
```css
.tt11 {
background: #dc1e3a;
padding: 0;
font-family: ui-monospace, monospace;
min-height: 100vh;
display: flex;
flex-direction: column;
width: 100%;
}
.tt11n {
display: flex;
gap: 0;
flex-shrink: 0;
}
.tt11n input {
position: absolute;
opacity: 0;
pointer-events: none;
}
.tt11b {
flex: 1;
padding: 18px 12px;
border: 0;
background: #dc1e3a;
color: #f5ead0;
font: 800 12px/1 ui-monospace, monospace;
letter-spacing: 0.22em;
text-transform: uppercase;
cursor: pointer;
border-right: 1px solid rgba(245, 234, 208, 0.25);
transition: background 0.05s steps(1, end), color 0.05s steps(1, end), box-shadow 0.4s;
}
.tt11b:last-child {
border-right: 0;
}
.tt11b:hover {
background: rgba(245, 234, 208, 0.1);
}
.tt11n input:checked + .tt11b {
background: #f5ead0;
color: #dc1e3a;
box-shadow: inset 0 0 0 0 rgba(0, 0, 0, 0.4);
animation: tt11-impact 0.35s cubic-bezier(0.65, 0, 0.35, 1);
}
@keyframes tt11-impact {
0% {
box-shadow: inset 0 0 0 0 rgba(0, 0, 0, 0.4);
}
20% {
box-shadow: inset 0 4px 14px rgba(0, 0, 0, 0.5);
}
100% {
box-shadow: inset 0 0 0 0 rgba(0, 0, 0, 0);
}
}
.tt11p {
display: none;
padding: 14px 18px;
background: #f5ead0;
font: italic 11px/1.6 ui-serif, Georgia, serif;
color: #3a3a42;
flex: 1;
}
.tt11:has(#tt11-r1:checked) .tt11p:nth-of-type(1),
.tt11:has(#tt11-r2:checked) .tt11p:nth-of-type(2),
.tt11:has(#tt11-r3:checked) .tt11p:nth-of-type(3),
.tt11:has(#tt11-r4:checked) .tt11p:nth-of-type(4),
.tt11:has(#tt11-r5:checked) .tt11p:nth-of-type(5),
.tt11:has(#tt11-r6:checked) .tt11p:nth-of-type(6) {
display: flex;
align-items: center;
}
@media (prefers-reduced-motion: reduce) {
.tt11n input:checked + .tt11b {
animation: none;
}
}
```How this works
The .tt11 demo treats the whole nav bar as a crimson ink block at #dc1e3a with cream #f5ead0 lettering. Selecting a tab inverts the pair: the label ground becomes cream, the ink becomes crimson, and a single-frame inset shadow flashes as if a rubber stamp has just been pressed into the paper. It is the register you see on library due-date cards and broadsheet mastheads — colour reversal doing the work of a border or underline.
The mechanism is a step-function colour swap plus a keyframe. The base transition is background 0.05s steps(1, end) and color 0.05s steps(1, end), which snaps the crimson-on-cream inversion in a single frame with no interpolation. On top of that, @keyframes tt11-impact pushes an inset 0 4px 14px rgba(0,0,0,0.5) shadow to 20% and releases it back to zero by 100%, giving the impression of the stamp bedding in and then lifting. Total duration is 0.35s on cubic-bezier(0.65, 0, 0.35, 1).
Panels are routed with .tt11:has(#tt11-rN:checked) .tt11p:nth-of-type(N), and the cream .tt11p panel uses italic 11px/1.6 ui-serif against the same cream stock — a deliberate typographic continuity between the flashed tab and the settled caption. A @media (prefers-reduced-motion: reduce) block disables the impact animation entirely for users who ask for it. The whole component is copy-paste, framework-agnostic and SSR-safe with semantic radios.
Make it yours
- Change the ink colour by editing #dc1e3a in .tt11 and .tt11b, and the paper colour by editing #f5ead0 in .tt11b color and .tt11p background. Both pairs must flip together or the reverse-stamp register breaks.
- Tune the impact by adjusting the 20% keyframe in tt11-impact. Deeper shadows like inset 0 6px 20px rgba(0,0,0,0.6) read as a wetter stamp; lighter ones like inset 0 2px 8px rgba(0,0,0,0.3) feel like a dry impression on rough stock.
- The label typography is 800 12px/1 ui-monospace with letter-spacing:0.22em and uppercase. Swap for a heavy grotesque only if it holds its counters at 12px — the monospaced default is what makes the tab read as a stamped legend.
- The border-right: 1px solid rgba(245,234,208,0.25) separators are set on every label except the last, mimicking a perforated card. Remove them for a continuous ink block, or thicken to 2px for a stronger ledger register.
- The step-function transitions background 0.05s steps(1, end) are what make the inversion snap. Replace with a normal ease if you want a wash effect, but you will lose the rubber-stamp punch that gives the demo its name.
Gotchas — read before shipping
- The :has() selector list references #tt11-r4, #tt11-r5, and #tt11-r6 even though the shipped demo only wires three tabs. That is intentional — copy the extra selectors when scaling up so the fourth, fifth, and sixth tabs route correctly. Trim only if you keep three.
- The impact keyframe uses inset shadow, not a filter. If you wrap .tt11b in an element with overflow:hidden and a smaller inset, the shadow gets clipped inside the wrapper and the stamp effect vanishes on the leading edge.
- The prefers-reduced-motion block disables only the animation, not the colour swap. That is correct — the inversion is meaning, not motion — but confirm your accessibility audit does not flag the instant snap as a flash. It stays under WCAG 2.1 three-flashes-per-second.
- The nav uses flex-shrink:0 and the panel uses flex:1. If you drop the flex-column layout on .tt11, the cream panel collapses to intrinsic height and the crimson ground bleeds through underneath the label bar, breaking the ledger read.
- Both the label and the caption use cream #f5ead0 against crimson at some point. Contrast against crimson #dc1e3a lands at roughly 4.7:1 for the label text — passing WCAG 2.1 AA for large text but tight for body copy, so keep the label size at 12px or above.
- The step transitions are cheap, but the keyframe animation runs on box-shadow. Do not add a second animated box-shadow in a wrapper — the two will multiply on the composite layer and INP will suffer on lower-end devices.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 105+ | 15.4+ | 121+ | 105+ |
The :has() selector gates panel switching. steps() timing and inset box-shadow have been baseline for a decade, so the visual register still holds in older browsers even if panel routing needs a fallback.