32 CSS Tab Designs09 / 32

Pure CSSMIT licensed

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

Live Demo
Try it

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

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

Techniques used in this demo

Search CodeFronts

Loading…