32 CSS Tab Designs28 / 32

Pure CSSMIT licensed

Specimen Card

The whole bar reads as a foundry specimen card. Foundry name on the left in tiny mono; tabs on the right are large display glyph specimens. On activation the foundry name pulses a single beat.

Published Updated

Live Demo
Try it

The code

<div class="tt16">
  <nav class="tt16n">
    <div class="tt16meta">
      <div class="tt16name">
        CodeFronts
        <br />Foundry
      </div>
      <div class="tt16id">SPEC · 2026</div>
    </div>
    <div class="tt16tabs">
      <input type="radio" name="tt16" id="tt16-r1" checked />
      <label class="tt16b" for="tt16-r1">Aa</label>
      <input type="radio" name="tt16" id="tt16-r2" />
      <label class="tt16b" for="tt16-r2">Bb</label>
      <input type="radio" name="tt16" id="tt16-r3" />
      <label class="tt16b" for="tt16-r3">Cc</label>
    </div>
  </nav>
  <div class="tt16p">Specimen of the upright Roman cut.</div>
  <div class="tt16p">Specimen of the bold cut.</div>
  <div class="tt16p">Specimen of the small caps cut.</div>
</div>
.tt16 {
  min-height: 100vh;
  background: #1a1a1a;
  padding: 16px 22px;
  font-family: ui-serif, Georgia, serif;
  width: 100%;
}

.tt16n {
  display: flex;
  align-items: stretch;
  gap: 22px;
  padding-bottom: 12px;
  border-bottom: 1px solid rgba(232, 226, 208, 0.18);
}

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

.tt16meta {
  padding-right: 22px;
  border-right: 1px solid rgba(232, 226, 208, 0.18);
  transition: opacity 0.4s;
}

.tt16name {
  font: 800 11px/1.3 ui-monospace, monospace;
  color: #e8e2d0;
  letter-spacing: 0.2em;
  text-transform: uppercase;
}

.tt16id {
  font: 600 9px/1 ui-monospace, monospace;
  color: rgba(232, 226, 208, 0.5);
  margin-top: 6px;
  letter-spacing: 0.2em;
}

.tt16tabs {
  display: flex;
  gap: 18px;
  align-items: baseline;
}

.tt16b {
  border: 0;
  background: transparent;
  padding: 0;
  font: 700 28px/1 ui-serif, Georgia, serif;
  color: rgba(232, 226, 208, 0.4);
  cursor: pointer;
  transition: color 0.3s, transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.tt16b:hover {
  color: rgba(232, 226, 208, 0.8);
}

.tt16n input:checked + .tt16b {
  color: #c43a32;
  transform: scale(1.15);
}

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

.tt16:has(#tt16-r1:checked) .tt16p:nth-of-type(1),
.tt16:has(#tt16-r2:checked) .tt16p:nth-of-type(2),
.tt16:has(#tt16-r3:checked) .tt16p: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: Specimen Card
Source: https://codefronts.com/navigation/css-tabs/specimen-card/

The whole bar reads as a foundry specimen card. Foundry name on the left in tiny mono; tabs on the right are large display glyph specimens. On activation the foundry name pulses a single beat.
## HTML
```html
<div class="tt16">
  <nav class="tt16n">
    <div class="tt16meta">
      <div class="tt16name">
        CodeFronts
        <br />Foundry
      </div>
      <div class="tt16id">SPEC · 2026</div>
    </div>
    <div class="tt16tabs">
      <input type="radio" name="tt16" id="tt16-r1" checked />
      <label class="tt16b" for="tt16-r1">Aa</label>
      <input type="radio" name="tt16" id="tt16-r2" />
      <label class="tt16b" for="tt16-r2">Bb</label>
      <input type="radio" name="tt16" id="tt16-r3" />
      <label class="tt16b" for="tt16-r3">Cc</label>
    </div>
  </nav>
  <div class="tt16p">Specimen of the upright Roman cut.</div>
  <div class="tt16p">Specimen of the bold cut.</div>
  <div class="tt16p">Specimen of the small caps cut.</div>
</div>
```
## CSS
```css
.tt16 {
  min-height: 100vh;
  background: #1a1a1a;
  padding: 16px 22px;
  font-family: ui-serif, Georgia, serif;
  width: 100%;
}

.tt16n {
  display: flex;
  align-items: stretch;
  gap: 22px;
  padding-bottom: 12px;
  border-bottom: 1px solid rgba(232, 226, 208, 0.18);
}

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

.tt16meta {
  padding-right: 22px;
  border-right: 1px solid rgba(232, 226, 208, 0.18);
  transition: opacity 0.4s;
}

.tt16name {
  font: 800 11px/1.3 ui-monospace, monospace;
  color: #e8e2d0;
  letter-spacing: 0.2em;
  text-transform: uppercase;
}

.tt16id {
  font: 600 9px/1 ui-monospace, monospace;
  color: rgba(232, 226, 208, 0.5);
  margin-top: 6px;
  letter-spacing: 0.2em;
}

.tt16tabs {
  display: flex;
  gap: 18px;
  align-items: baseline;
}

.tt16b {
  border: 0;
  background: transparent;
  padding: 0;
  font: 700 28px/1 ui-serif, Georgia, serif;
  color: rgba(232, 226, 208, 0.4);
  cursor: pointer;
  transition: color 0.3s, transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.tt16b:hover {
  color: rgba(232, 226, 208, 0.8);
}

.tt16n input:checked + .tt16b {
  color: #c43a32;
  transform: scale(1.15);
}

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

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

How this works

The .tt16 bar reads as a printed foundry specimen sheet. A left-hand meta block holds the mono foundry name (CodeFronts / Foundry) and a SPEC 2026 catalog id on a warm cream ink at #e8e2d0, separated from the tab column by a 1px hairline at rgba(232, 226, 208, 0.18). The tabs themselves are large 28px serif glyph specimens — Aa, Bb, Cc — sitting on baseline alignment against charcoal #1a1a1a, exactly the way a type foundry pins letterforms in a catalog spread. The meta column and the tab column share a 22px gap, so the whole nav reads as one composed printed artifact rather than two stitched groups.

Selection is hand-coded radio state — three inputs sharing a name, each paired with a .tt16b label. The inputs are visually hidden via position:absolute; opacity:0; pointer-events:none but stay reachable to the keyboard, so tab / space / arrow behavior matches native radio semantics. The checked adjacent-sibling selector .tt16n input:checked + .tt16b repaints the glyph to foundry vermilion #c43a32 and scales it 1.15 via a cubic-bezier(0.34, 1.56, 0.64, 1) overshoot, so the letterform pulses one beat like a hot-metal cast dropping into position. Inactive glyphs sit at 40% cream, hover lifts them to 80%, and the checked state jumps to full vermilion for a clean tri-state hierarchy.

Panels are wired with :has(): .tt16:has(#tt16-r1:checked) .tt16p:nth-of-type(1) flips the matching italic 11px specimen caption from display:none to display:block. No JavaScript, no ARIA scaffolding, no framework — copy-paste the semantic HTML, drop the scoped CSS behind your .tt16 wrapper, and the specimen card renders identically in SSR output and on hydration. Framework-agnostic, INP-safe with zero main-thread work, and honors prefers-reduced-motion the moment you guard the scale transform behind the media query.

Make it yours

  • Swap the vermilion active color #c43a32 for your brand accent — Pantone red, brass, ink blue. Keep contrast ratio above 4.5:1 against the #1a1a1a stock so the checked glyph remains WCAG 2.1 AA legible for tier-1 reader traffic.
  • Replace the Aa/Bb/Cc glyphs with actual foundry ligatures (fi, st, ct) or numerals (0123). The 28px ui-serif stack falls back to Georgia — swap in a licensed display face and the specimen bar reads as your real catalog spread.
  • Retune the overshoot: cubic-bezier(0.34, 1.56, 0.64, 1) gives a single-beat pulse. Reduce the second value to 1.2 for a subtler settle, or lift the scale from 1.15 to 1.25 if your glyphs need more presence on wider stages.
  • The meta column is a hairline-separated flex child — drop a second line under .tt16id (issue number, cut date, mm/dd) using the same 9px mono at rgba(232, 226, 208, 0.5) to deepen the catalog fiction without touching layout.
  • For a light-mode foundry card, invert to cream stock #f4f0e6 with ink glyphs at #1a1a1a and a burgundy active. Keep the hairline at 18% opacity — the tonal ratio between rule and stock is what sells the printed feel.

Gotchas — read before shipping

  • The visually hidden inputs use position:absolute; opacity:0; pointer-events:none — never display:none. Removing them from the accessibility tree breaks arrow-key navigation between radios and forfeits the WCAG 2.1 focus contract users expect from a native tab-strip.
  • The panel reveal relies on :has(), which is Baseline in Chromium, Safari, and Firefox from 2023-12 onward. Older browsers will show every .tt16p stacked because the default is display:block — invert to display:none by default and only unhide via :has(), then the fallback simply hides all panels rather than showing all three.
  • The 28px serif glyphs sit on baseline alignment via align-items:baseline on .tt16tabs. Nesting an inline SVG icon inside a label breaks that baseline — the glyphs jump vertically as the active one scales 1.15. Keep tab labels as text-only, or wrap the SVG in a span with vertical-align:baseline.
  • The scale transform on the checked glyph triggers on every re-render. Users with vestibular sensitivity will feel the 1.56-overshoot as a bounce. Wrap the transform in @media (prefers-reduced-motion:reduce) and drop it to a plain color swap — this is a WCAG 2.1 SC 2.3.3 obligation, not a nice-to-have.
  • The .tt16name line-height at 1.3 with an inline line break is intentional — it stacks the two-line foundry mark at exactly the right catalog cadence. Removing the break and setting white-space:pre looks equivalent in Chromium but wraps unpredictably in Safari's older versions.
  • The vermilion #c43a32 against #1a1a1a hits roughly 5.4:1 — passing AA for large text (28px is well above the 18.66px threshold). If you shrink the tab labels below 18.66px, re-run a contrast checker before shipping to tier-1 audiences where accessibility audits catch this.

Browser support

ChromeSafariFirefoxEdge
105+15.4+121+105+

The :has() parent selector wiring the panel reveal is Baseline as of Firefox 121 (2023-12). The cubic-bezier overshoot and hidden-radio pattern work back to Chrome 60 and Safari 12.

Techniques used in this demo

Search CodeFronts

Loading…