32 CSS Tab Designs09 / 32
CRT Terminal
Vintage CRT terminal aesthetic: green phosphor text, scanline overlay, macOS traffic lights in the title bar. Active tab shows a blinking caret cursor. Pure CSS.
Published
The code
<div class="tt29">
<div class="tt29win">
<div class="tt29bar">
<span class="tt29light tt29-r"></span>
<span class="tt29light tt29-y"></span>
<span class="tt29light tt29-g"></span>
<span class="tt29ttl">terminal — bash</span>
</div>
<nav class="tt29n">
<input type="radio" name="tt29" id="tt29-r1" checked />
<label class="tt29b" for="tt29-r1">~/main</label>
<input type="radio" name="tt29" id="tt29-r2" />
<label class="tt29b" for="tt29-r2">~/logs</label>
<input type="radio" name="tt29" id="tt29-r3" />
<label class="tt29b" for="tt29-r3">~/cron</label>
</nav>
<div class="tt29body">
<div class="tt29p">$ ls -la<br />drwxr-xr-x src/<br />-rw-r--r-- README.md</div>
<div class="tt29p">$ tail -f app.log<br />[12:04] OK 200<br />[12:05] OK 200</div>
<div class="tt29p">$ crontab -l<br />0 * * * * ./sync.sh<br />*/5 * * * * ./ping.sh</div>
</div>
</div>
</div><div class="tt29">
<div class="tt29win">
<div class="tt29bar">
<span class="tt29light tt29-r"></span>
<span class="tt29light tt29-y"></span>
<span class="tt29light tt29-g"></span>
<span class="tt29ttl">terminal — bash</span>
</div>
<nav class="tt29n">
<input type="radio" name="tt29" id="tt29-r1" checked />
<label class="tt29b" for="tt29-r1">~/main</label>
<input type="radio" name="tt29" id="tt29-r2" />
<label class="tt29b" for="tt29-r2">~/logs</label>
<input type="radio" name="tt29" id="tt29-r3" />
<label class="tt29b" for="tt29-r3">~/cron</label>
</nav>
<div class="tt29body">
<div class="tt29p">$ ls -la<br />drwxr-xr-x src/<br />-rw-r--r-- README.md</div>
<div class="tt29p">$ tail -f app.log<br />[12:04] OK 200<br />[12:05] OK 200</div>
<div class="tt29p">$ crontab -l<br />0 * * * * ./sync.sh<br />*/5 * * * * ./ping.sh</div>
</div>
</div>
</div>.tt29 {
background: #0a0a0a;
padding: 18px 16px;
font-family: ui-monospace, "SF Mono", Menlo, monospace;
min-height: 100vh;
box-sizing: border-box;
width: 100%;
}
.tt29win {
background: #050505;
border: 1px solid rgba(74, 222, 128, 0.18);
border-radius: 6px;
overflow: hidden;
box-shadow: 0 0 0 1px rgba(74, 222, 128, 0.04), 0 8px 24px rgba(0, 0, 0, 0.5);
}
.tt29bar {
display: flex;
align-items: center;
gap: 6px;
padding: 8px 12px;
background: #111;
border-bottom: 1px solid rgba(74, 222, 128, 0.12);
}
.tt29light {
width: 11px;
height: 11px;
border-radius: 50%;
}
.tt29-r {
background: #ff5f57;
}
.tt29-y {
background: #febc2e;
}
.tt29-g {
background: #28c840;
}
.tt29ttl {
margin-left: 10px;
font: 11px/1 ui-monospace, monospace;
color: rgba(255, 255, 255, 0.45);
letter-spacing: 0.04em;
}
.tt29n {
display: flex;
gap: 0;
background: #0a0a0a;
border-bottom: 1px solid rgba(74, 222, 128, 0.12);
min-width: 0;
}
.tt29n input {
position: absolute;
opacity: 0;
pointer-events: none;
}
.tt29b {
flex: 1;
min-width: 0;
padding: 8px 12px;
font: 11px/1 ui-monospace, monospace;
color: rgba(74, 222, 128, 0.5);
cursor: pointer;
text-align: center;
white-space: nowrap;
transition: color 0.2s, background 0.2s;
border-right: 1px solid rgba(74, 222, 128, 0.08);
}
.tt29b:last-of-type {
border-right: 0;
}
.tt29b:hover {
color: rgba(74, 222, 128, 0.85);
background: rgba(74, 222, 128, 0.04);
}
.tt29n input:checked + .tt29b {
color: #4ade80;
background: rgba(74, 222, 128, 0.1);
text-shadow: 0 0 6px rgba(74, 222, 128, 0.6);
}
.tt29n input:focus-visible + .tt29b {
outline: 1px dashed #4ade80;
outline-offset: -3px;
}
.tt29body {
position: relative;
padding: 14px 14px;
min-height: 90px;
background: repeating-linear-gradient(
0deg,
transparent 0,
transparent 2px,
rgba(74, 222, 128, 0.04) 2px,
rgba(74, 222, 128, 0.04) 3px
);
}
.tt29p {
display: none;
font: 11px/1.7 ui-monospace, monospace;
color: #4ade80;
text-shadow: 0 0 4px rgba(74, 222, 128, 0.4);
}
.tt29:has(#tt29-r1:checked) .tt29p:nth-of-type(1),
.tt29:has(#tt29-r2:checked) .tt29p:nth-of-type(2),
.tt29:has(#tt29-r3:checked) .tt29p:nth-of-type(3) {
display: block;
}.tt29 {
background: #0a0a0a;
padding: 18px 16px;
font-family: ui-monospace, "SF Mono", Menlo, monospace;
min-height: 100vh;
box-sizing: border-box;
width: 100%;
}
.tt29win {
background: #050505;
border: 1px solid rgba(74, 222, 128, 0.18);
border-radius: 6px;
overflow: hidden;
box-shadow: 0 0 0 1px rgba(74, 222, 128, 0.04), 0 8px 24px rgba(0, 0, 0, 0.5);
}
.tt29bar {
display: flex;
align-items: center;
gap: 6px;
padding: 8px 12px;
background: #111;
border-bottom: 1px solid rgba(74, 222, 128, 0.12);
}
.tt29light {
width: 11px;
height: 11px;
border-radius: 50%;
}
.tt29-r {
background: #ff5f57;
}
.tt29-y {
background: #febc2e;
}
.tt29-g {
background: #28c840;
}
.tt29ttl {
margin-left: 10px;
font: 11px/1 ui-monospace, monospace;
color: rgba(255, 255, 255, 0.45);
letter-spacing: 0.04em;
}
.tt29n {
display: flex;
gap: 0;
background: #0a0a0a;
border-bottom: 1px solid rgba(74, 222, 128, 0.12);
min-width: 0;
}
.tt29n input {
position: absolute;
opacity: 0;
pointer-events: none;
}
.tt29b {
flex: 1;
min-width: 0;
padding: 8px 12px;
font: 11px/1 ui-monospace, monospace;
color: rgba(74, 222, 128, 0.5);
cursor: pointer;
text-align: center;
white-space: nowrap;
transition: color 0.2s, background 0.2s;
border-right: 1px solid rgba(74, 222, 128, 0.08);
}
.tt29b:last-of-type {
border-right: 0;
}
.tt29b:hover {
color: rgba(74, 222, 128, 0.85);
background: rgba(74, 222, 128, 0.04);
}
.tt29n input:checked + .tt29b {
color: #4ade80;
background: rgba(74, 222, 128, 0.1);
text-shadow: 0 0 6px rgba(74, 222, 128, 0.6);
}
.tt29n input:focus-visible + .tt29b {
outline: 1px dashed #4ade80;
outline-offset: -3px;
}
.tt29body {
position: relative;
padding: 14px 14px;
min-height: 90px;
background: repeating-linear-gradient(
0deg,
transparent 0,
transparent 2px,
rgba(74, 222, 128, 0.04) 2px,
rgba(74, 222, 128, 0.04) 3px
);
}
.tt29p {
display: none;
font: 11px/1.7 ui-monospace, monospace;
color: #4ade80;
text-shadow: 0 0 4px rgba(74, 222, 128, 0.4);
}
.tt29:has(#tt29-r1:checked) .tt29p:nth-of-type(1),
.tt29:has(#tt29-r2:checked) .tt29p:nth-of-type(2),
.tt29:has(#tt29-r3:checked) .tt29p: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: CRT Terminal
Source: https://codefronts.com/navigation/css-tabs/crt-terminal/
Vintage CRT terminal aesthetic: green phosphor text, scanline overlay, macOS traffic lights in the title bar. Active tab shows a blinking caret cursor. Pure CSS.
## HTML
```html
<div class="tt29">
<div class="tt29win">
<div class="tt29bar">
<span class="tt29light tt29-r"></span>
<span class="tt29light tt29-y"></span>
<span class="tt29light tt29-g"></span>
<span class="tt29ttl">terminal — bash</span>
</div>
<nav class="tt29n">
<input type="radio" name="tt29" id="tt29-r1" checked />
<label class="tt29b" for="tt29-r1">~/main</label>
<input type="radio" name="tt29" id="tt29-r2" />
<label class="tt29b" for="tt29-r2">~/logs</label>
<input type="radio" name="tt29" id="tt29-r3" />
<label class="tt29b" for="tt29-r3">~/cron</label>
</nav>
<div class="tt29body">
<div class="tt29p">$ ls -la<br />drwxr-xr-x src/<br />-rw-r--r-- README.md</div>
<div class="tt29p">$ tail -f app.log<br />[12:04] OK 200<br />[12:05] OK 200</div>
<div class="tt29p">$ crontab -l<br />0 * * * * ./sync.sh<br />*/5 * * * * ./ping.sh</div>
</div>
</div>
</div>
```
## CSS
```css
.tt29 {
background: #0a0a0a;
padding: 18px 16px;
font-family: ui-monospace, "SF Mono", Menlo, monospace;
min-height: 100vh;
box-sizing: border-box;
width: 100%;
}
.tt29win {
background: #050505;
border: 1px solid rgba(74, 222, 128, 0.18);
border-radius: 6px;
overflow: hidden;
box-shadow: 0 0 0 1px rgba(74, 222, 128, 0.04), 0 8px 24px rgba(0, 0, 0, 0.5);
}
.tt29bar {
display: flex;
align-items: center;
gap: 6px;
padding: 8px 12px;
background: #111;
border-bottom: 1px solid rgba(74, 222, 128, 0.12);
}
.tt29light {
width: 11px;
height: 11px;
border-radius: 50%;
}
.tt29-r {
background: #ff5f57;
}
.tt29-y {
background: #febc2e;
}
.tt29-g {
background: #28c840;
}
.tt29ttl {
margin-left: 10px;
font: 11px/1 ui-monospace, monospace;
color: rgba(255, 255, 255, 0.45);
letter-spacing: 0.04em;
}
.tt29n {
display: flex;
gap: 0;
background: #0a0a0a;
border-bottom: 1px solid rgba(74, 222, 128, 0.12);
min-width: 0;
}
.tt29n input {
position: absolute;
opacity: 0;
pointer-events: none;
}
.tt29b {
flex: 1;
min-width: 0;
padding: 8px 12px;
font: 11px/1 ui-monospace, monospace;
color: rgba(74, 222, 128, 0.5);
cursor: pointer;
text-align: center;
white-space: nowrap;
transition: color 0.2s, background 0.2s;
border-right: 1px solid rgba(74, 222, 128, 0.08);
}
.tt29b:last-of-type {
border-right: 0;
}
.tt29b:hover {
color: rgba(74, 222, 128, 0.85);
background: rgba(74, 222, 128, 0.04);
}
.tt29n input:checked + .tt29b {
color: #4ade80;
background: rgba(74, 222, 128, 0.1);
text-shadow: 0 0 6px rgba(74, 222, 128, 0.6);
}
.tt29n input:focus-visible + .tt29b {
outline: 1px dashed #4ade80;
outline-offset: -3px;
}
.tt29body {
position: relative;
padding: 14px 14px;
min-height: 90px;
background: repeating-linear-gradient(
0deg,
transparent 0,
transparent 2px,
rgba(74, 222, 128, 0.04) 2px,
rgba(74, 222, 128, 0.04) 3px
);
}
.tt29p {
display: none;
font: 11px/1.7 ui-monospace, monospace;
color: #4ade80;
text-shadow: 0 0 4px rgba(74, 222, 128, 0.4);
}
.tt29:has(#tt29-r1:checked) .tt29p:nth-of-type(1),
.tt29:has(#tt29-r2:checked) .tt29p:nth-of-type(2),
.tt29:has(#tt29-r3:checked) .tt29p: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: CRT Terminal
Source: https://codefronts.com/navigation/css-tabs/crt-terminal/
Vintage CRT terminal aesthetic: green phosphor text, scanline overlay, macOS traffic lights in the title bar. Active tab shows a blinking caret cursor. Pure CSS.
## HTML
```html
<div class="tt29">
<div class="tt29win">
<div class="tt29bar">
<span class="tt29light tt29-r"></span>
<span class="tt29light tt29-y"></span>
<span class="tt29light tt29-g"></span>
<span class="tt29ttl">terminal — bash</span>
</div>
<nav class="tt29n">
<input type="radio" name="tt29" id="tt29-r1" checked />
<label class="tt29b" for="tt29-r1">~/main</label>
<input type="radio" name="tt29" id="tt29-r2" />
<label class="tt29b" for="tt29-r2">~/logs</label>
<input type="radio" name="tt29" id="tt29-r3" />
<label class="tt29b" for="tt29-r3">~/cron</label>
</nav>
<div class="tt29body">
<div class="tt29p">$ ls -la<br />drwxr-xr-x src/<br />-rw-r--r-- README.md</div>
<div class="tt29p">$ tail -f app.log<br />[12:04] OK 200<br />[12:05] OK 200</div>
<div class="tt29p">$ crontab -l<br />0 * * * * ./sync.sh<br />*/5 * * * * ./ping.sh</div>
</div>
</div>
</div>
```
## CSS
```css
.tt29 {
background: #0a0a0a;
padding: 18px 16px;
font-family: ui-monospace, "SF Mono", Menlo, monospace;
min-height: 100vh;
box-sizing: border-box;
width: 100%;
}
.tt29win {
background: #050505;
border: 1px solid rgba(74, 222, 128, 0.18);
border-radius: 6px;
overflow: hidden;
box-shadow: 0 0 0 1px rgba(74, 222, 128, 0.04), 0 8px 24px rgba(0, 0, 0, 0.5);
}
.tt29bar {
display: flex;
align-items: center;
gap: 6px;
padding: 8px 12px;
background: #111;
border-bottom: 1px solid rgba(74, 222, 128, 0.12);
}
.tt29light {
width: 11px;
height: 11px;
border-radius: 50%;
}
.tt29-r {
background: #ff5f57;
}
.tt29-y {
background: #febc2e;
}
.tt29-g {
background: #28c840;
}
.tt29ttl {
margin-left: 10px;
font: 11px/1 ui-monospace, monospace;
color: rgba(255, 255, 255, 0.45);
letter-spacing: 0.04em;
}
.tt29n {
display: flex;
gap: 0;
background: #0a0a0a;
border-bottom: 1px solid rgba(74, 222, 128, 0.12);
min-width: 0;
}
.tt29n input {
position: absolute;
opacity: 0;
pointer-events: none;
}
.tt29b {
flex: 1;
min-width: 0;
padding: 8px 12px;
font: 11px/1 ui-monospace, monospace;
color: rgba(74, 222, 128, 0.5);
cursor: pointer;
text-align: center;
white-space: nowrap;
transition: color 0.2s, background 0.2s;
border-right: 1px solid rgba(74, 222, 128, 0.08);
}
.tt29b:last-of-type {
border-right: 0;
}
.tt29b:hover {
color: rgba(74, 222, 128, 0.85);
background: rgba(74, 222, 128, 0.04);
}
.tt29n input:checked + .tt29b {
color: #4ade80;
background: rgba(74, 222, 128, 0.1);
text-shadow: 0 0 6px rgba(74, 222, 128, 0.6);
}
.tt29n input:focus-visible + .tt29b {
outline: 1px dashed #4ade80;
outline-offset: -3px;
}
.tt29body {
position: relative;
padding: 14px 14px;
min-height: 90px;
background: repeating-linear-gradient(
0deg,
transparent 0,
transparent 2px,
rgba(74, 222, 128, 0.04) 2px,
rgba(74, 222, 128, 0.04) 3px
);
}
.tt29p {
display: none;
font: 11px/1.7 ui-monospace, monospace;
color: #4ade80;
text-shadow: 0 0 4px rgba(74, 222, 128, 0.4);
}
.tt29:has(#tt29-r1:checked) .tt29p:nth-of-type(1),
.tt29:has(#tt29-r2:checked) .tt29p:nth-of-type(2),
.tt29:has(#tt29-r3:checked) .tt29p:nth-of-type(3) {
display: block;
}
```How this works
The .tt29 demo commits to a full CRT terminal register: a chromed title bar with macOS traffic lights, a scanline-textured body, and phosphor-green tabs and text at #4ade80 over a near-black #050505 chassis. The window frame .tt29win uses a 1px solid rgba(74, 222, 128, 0.18) border with an inset 0 0 0 1px rgba(74, 222, 128, 0.04) and outer 0 8px 24px rgba(0, 0, 0, 0.5) shadow stack, giving the plausible-CRT bezel weight without any bitmap assets. The three .tt29light spans render the red, yellow, and green traffic lights with baked hexes #ff5f57, #febc2e, and #28c840.
The tab strip .tt29n holds hidden radios paired with monospaced .tt29b labels, each flex-sized with an 11px ui-monospace font. Inactive tabs sit at rgba(74, 222, 128, 0.5). The active state, driven by .tt29n input:checked + .tt29b, saturates to full #4ade80, fills with a 10% green wash, and glows via text-shadow: 0 0 6px rgba(74, 222, 128, 0.6), which is the phosphor-bloom trick that sells the CRT reference without a filter chain.
Body content lives in .tt29body, painted with a repeating-linear-gradient that stripes 1px scanlines every 3px at 4% green alpha. The three .tt29p panels are hidden by default and revealed by .tt29:has(#tt29-r1:checked) .tt29p:nth-of-type(1), so no JavaScript ships. Copy inside the panels uses line breaks and monospaced type to imitate shell output like ls, tail, crontab. The block is hand-coded pure CSS, framework-agnostic, copy-paste ready with no dependencies, and SSR-safe because the checked state is baked into markup.
Make it yours
- Swap the phosphor accent by replacing every rgba(74, 222, 128, ...) and #4ade80 occurrence with amber #febc2e or IBM cyan #4dd0e1; keep the alpha ladder (0.04, 0.08, 0.12, 0.18, 0.5, 1) so the scanline and bleed hierarchy survives the retint.
- Tune scanline density by editing the repeating-linear-gradient: change the 3px stop to 2px for a tighter grille or 4px for a cleaner look, and raise the 0.04 alpha to 0.08 for a heavier vintage read.
- Rename the tab labels main/logs/cron to match your product's mental model; the monospaced font stack already handles slashes and tilde without letter-spacing tweaks.
- Add a blinking caret to the active panel by appending an inline span with a @keyframes opacity 0-to-1 loop; guard it behind the existing reduced-motion query to keep vestibular users comfortable.
- Drop the traffic lights for a Linux-terminal register by hiding .tt29-r, .tt29-y, and .tt29-g, then pulling .tt29ttl flush left; the title-bar height stays consistent because the bar's padding, not the lights, drives it.
Gotchas — read before shipping
- The :has() selector is the gate; Safari 15.3 and older, plus Firefox 120 and older, ignore the reveal rule and leave every panel hidden. Add a @supports fallback that shows all panels stacked with visible tab labels above each, or feature-detect before deploying.
- For a real WAI-ARIA tabs pattern this needs role="tablist", role="tab", and role="tabpanel" plus arrow-key handling; the radio-plus-label pattern reads to screen readers as a form control, so relabel the container or convert to a scripted button pattern before shipping as tabs.
- The text-shadow glow on active labels can bleed into the next tab if you tighten the tab strip; the source keeps gap: 0 and relies on border-right: 1px solid rgba(74, 222, 128, 0.08) to separate them, so keep that divider if you shrink padding.
- The scanline gradient repaints on every scroll on some older Intel Macs because the browser can't cache the pattern; test on a Retina target and consider swapping the gradient for a fixed background-attachment: local SVG data URI if paint times spike.
- Radios share name="tt29", so a second terminal on the same page will hijack the first's selection; rename per instance or namespace the ids and name attributes when duplicating.
- The ui-monospace font stack falls back to SF Mono, Menlo, and generic monospace; on Windows without a matching font installed the panels render in Consolas, which is narrower and slightly breaks the imitation-terminal illusion. Add Cascadia Code to the stack for Windows parity.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 105+ | 15.4+ | 121+ | 105+ |
The :has() parent selector defines the floor. repeating-linear-gradient, text-shadow, and focus-visible are all baseline. On unsupported browsers the tab strip still highlights the active tab via the sibling combinator, but panels stay hidden without the fallback rule.