32 CSS Tab Designs28 / 32
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
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><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;
}.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;
}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;
}
```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
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 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.