32 CSS Tab Designs20 / 32

Pure CSSMIT licensed

Stencil Cut

Active tab is a solid safety-yellow plate stamped on ink. Pure two-color print — black ink on yellow for the active label, muted yellow on ink for inactive. The 2px upward translate gives the yellow plate a small physical lift over the surrounding stage.

Published Updated

Live Demo
Try it

The code

<div class="tt08">
  <nav class="tt08n">
    <input type="radio" name="tt08" id="tt08-r1" />
    <label class="tt08b" for="tt08-r1">
      <span>READ</span>
    </label>
    <input type="radio" name="tt08" id="tt08-r2" checked />
    <label class="tt08b" for="tt08-r2">
      <span>WRITE</span>
    </label>
    <input type="radio" name="tt08" id="tt08-r3" />
    <label class="tt08b" for="tt08-r3">
      <span>SHIP</span>
    </label>
  </nav>
  <div class="tt08p">Drafts, archives, and reference material.</div>
  <div class="tt08p">The current notebook in active edit mode.</div>
  <div class="tt08p">Approvals, releases, and publication state.</div>
</div>
.tt08 {
  min-height: 100vh;
  background: #1a1a1a;
  padding: 22px 22px 18px;
  font-family: ui-monospace, monospace;
  width: 100%;
}

.tt08n {
  display: flex;
  gap: 8px;
}

.tt08n input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

.tt08b {
  flex: 1;
  padding: 14px 12px;
  border: 0;
  background: transparent;
  font: 900 14px/1 ui-monospace, monospace;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: rgba(245, 203, 26, 0.45);
  cursor: pointer;
  position: relative;
  overflow: hidden;
  transition: color 0.25s, background 0.25s, transform 0.25s;
}

.tt08b:hover {
  color: rgba(245, 203, 26, 0.8);
}

.tt08n input:checked + .tt08b {
  color: #1a1a1a;
  background: #f5cb1a;
  transform: translateY(-2px);
  box-shadow: 0 4px 0 -2px rgba(0, 0, 0, 0.4);
}

.tt08p {
  display: none;
  padding-top: 14px;
  font: 11px/1.6 ui-serif, Georgia, serif;
  color: rgba(232, 226, 208, 0.7);
  font-style: italic;
}

.tt08:has(#tt08-r1:checked) .tt08p:nth-of-type(1),
.tt08:has(#tt08-r2:checked) .tt08p:nth-of-type(2),
.tt08:has(#tt08-r3:checked) .tt08p:nth-of-type(3) {
  display: block;
}
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: Stencil Cut
Source: https://codefronts.com/navigation/css-tabs/stencil-cut/

Active tab is a solid safety-yellow plate stamped on ink. Pure two-color print — black ink on yellow for the active label, muted yellow on ink for inactive. The 2px upward translate gives the yellow plate a small physical lift over the surrounding stage.
## HTML
```html
<div class="tt08">
  <nav class="tt08n">
    <input type="radio" name="tt08" id="tt08-r1" />
    <label class="tt08b" for="tt08-r1">
      <span>READ</span>
    </label>
    <input type="radio" name="tt08" id="tt08-r2" checked />
    <label class="tt08b" for="tt08-r2">
      <span>WRITE</span>
    </label>
    <input type="radio" name="tt08" id="tt08-r3" />
    <label class="tt08b" for="tt08-r3">
      <span>SHIP</span>
    </label>
  </nav>
  <div class="tt08p">Drafts, archives, and reference material.</div>
  <div class="tt08p">The current notebook in active edit mode.</div>
  <div class="tt08p">Approvals, releases, and publication state.</div>
</div>
```
## CSS
```css
.tt08 {
  min-height: 100vh;
  background: #1a1a1a;
  padding: 22px 22px 18px;
  font-family: ui-monospace, monospace;
  width: 100%;
}

.tt08n {
  display: flex;
  gap: 8px;
}

.tt08n input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

.tt08b {
  flex: 1;
  padding: 14px 12px;
  border: 0;
  background: transparent;
  font: 900 14px/1 ui-monospace, monospace;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: rgba(245, 203, 26, 0.45);
  cursor: pointer;
  position: relative;
  overflow: hidden;
  transition: color 0.25s, background 0.25s, transform 0.25s;
}

.tt08b:hover {
  color: rgba(245, 203, 26, 0.8);
}

.tt08n input:checked + .tt08b {
  color: #1a1a1a;
  background: #f5cb1a;
  transform: translateY(-2px);
  box-shadow: 0 4px 0 -2px rgba(0, 0, 0, 0.4);
}

.tt08p {
  display: none;
  padding-top: 14px;
  font: 11px/1.6 ui-serif, Georgia, serif;
  color: rgba(232, 226, 208, 0.7);
  font-style: italic;
}

.tt08:has(#tt08-r1:checked) .tt08p:nth-of-type(1),
.tt08:has(#tt08-r2:checked) .tt08p:nth-of-type(2),
.tt08:has(#tt08-r3:checked) .tt08p:nth-of-type(3) {
  display: block;
}
```

How this works

Stencil Cut is a two-color print in code — safety yellow #f5cb1a plates stamped on matte ink #1a1a1a. The nav (.tt08n) is a flex row of three radio-driven labels, each flex: 1 so the plates share equal width. Labels are set in ui-monospace at 900 14px/1 with a wide letter-spacing: 0.24em and uppercase transform, giving them the register of a Bauhaus workshop sign or an aircraft-carrier hangar mark. Inactive labels sit at rgba(245, 203, 26, 0.45) — the same yellow at half strength, keeping the two-color palette pure.

Activation is a single rule: .tt08n input:checked + .tt08b flips color to ink #1a1a1a, background to solid safety yellow #f5cb1a, and applies transform: translateY(-2px). That 2px upward lift, paired with box-shadow: 0 4px 0 -2px rgba(0, 0, 0, 0.4), gives the plate a small physical rise above the ink surround — as though a stencil letterpress plate has been pushed up out of the frame. All three properties (color, background, transform) transition over 0.25s so the plate stamps down rather than fades in.

Panel switching uses :has(): .tt08:has(#tt08-r1:checked) .tt08p:nth-of-type(1) flips the matching italic caption to display: block. Panels use ui-serif italic at 11px/1.6 in bone rgba(232, 226, 208, 0.7) — a deliberate register break from the monospace plates so the caption reads as a footnote rather than a continuation. The markup is semantic radio + label, keyboard-navigable through native arrow keys, copy-paste, zero-dependency, framework-agnostic, scoped under .tt08, and SSR-safe. The whole demo runs on two colors and three CSS rules.

Make it yours

  • Swap the palette pair for a Bauhaus red-on-white — background: #e63946 for the active plate, color: #f1faee for the label, and light the whole .tt08 stage. The two-color logic survives any color swap as long as contrast holds.
  • Retune the lift. translateY(-2px) reads as subtle plate rise; push to -4px with a matching box-shadow: 0 6px 0 -3px for a poster-mount register. Above -6px the plate detaches visually from the row and looks like a floating tile.
  • Widen the letter-spacing from 0.24em to 0.36em and drop the font-size to 12px for a formal industrial-standard sign register. Wider tracking + smaller size reads more authoritative than large tracked type.
  • Replace the italic serif panel with the same monospace face at 11px/1.5 for a single-face demo that reads more machine-shop and less magazine. The two-face contrast is a deliberate editorial move — collapse it if your brand is single-family.
  • Add a text-shadow: 0 0 0 currentColor or a font-synthesis boost if your monospace face lacks a real 900 weight. Some ui-mono fallbacks cap at 700, which reads too thin against the yellow plate.

Gotchas — read before shipping

  • The active plate uses translateY(-2px), so the yellow rectangle sits 2px higher than the surrounding row. If your nav is immediately below another element with no margin, the lift will visually collide — add at least 4px top clearance to .tt08n.
  • The overflow: hidden on .tt08b clips any hover halo you might add. The source only uses color and background transitions, so it is safe — but if you add a ripple or spread pseudo-element, remove the overflow or the effect will silently disappear.
  • The :has() panel switcher requires Chrome 105+, Safari 15.4+, and Firefox 121+. Older Firefox will render all three panels as display: none. Ship a @supports not selector(:has(*)) block that shows the first panel unconditionally as a fallback.
  • Monospace fallback stacks vary wildly. ui-monospace hits SF Mono on macOS, Cascadia Mono on Windows, and generic mono on Linux — the tracking and weight balance changes across all three. Test on all three OSes or specify a webfont like JetBrains Mono for consistency.
  • There is no prefers-reduced-motion guard. The 0.25s transform + background transition is short enough that most users will not notice, but strict-accessibility ships should add a media block that zeros the transition and lets the plate stamp instantly.
  • The radios are absolutely positioned with opacity: 0. Add a visible focus ring on .tt08n input:focus-visible + .tt08b — outline or box-shadow — or keyboard users navigate blind and WCAG 2.1 focus-visible fails.

Browser support

ChromeSafariFirefoxEdge
105+15.4+121+105+

The :has() panel switcher sets the floor. Everything else (transforms, box-shadow, sibling combinator, uppercase transform) has been stable since 2017.

Techniques used in this demo

Search CodeFronts

Loading…