32 CSS Tab Designs23 / 32

Pure CSSMIT licensed

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

Live Demo
Try it

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>
.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;
  }
}
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 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

ChromeSafariFirefoxEdge
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.

Techniques used in this demo

Search CodeFronts

Loading…