32 Tailwind CSS Tabs
09 / 32
Pure TailwindMIT licensed

Terminal Window Tabs in Tailwind CSS (Retro CRT)

A phosphor-green terminal: traffic-light title bar, tabs for three shells, scanlines, a vignette and a blinking block cursor after the prompt. The port sets the output in VT323, a face drawn from DEC terminal glyphs, and makes the glow a token-driven [text-shadow:…].

Live Demo
Plain CSSTailwind v4
Try it

Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time

Untitled UI React · Affiliate link

Get Untitled UI Pro

The code

<section class="ttb-09 group/tabs grid min-h-svh w-full place-items-center bg-ttb09-bg px-5 py-14 font-ttb09-mono text-ttb09-ph" aria-label="Retro CRT terminal tabs demo">
  <div class="grid w-full justify-items-center gap-7">
    <div class="w-full max-w-xl overflow-hidden rounded-xl bg-ttb09-screen ring-1 ring-ttb09-ph/20 shadow-[0_0_0_1px_rgb(0_0_0),0_28px_60px_-20px_rgb(0_0_0/0.8),0_0_40px_-10px_color-mix(in_oklab,var(--color-ttb09-ph)_25%,transparent)]">
      <div class="flex items-center gap-2 border-b border-ttb09-ph/12 bg-ttb09-bar px-3.5 py-2.5">
        <span aria-hidden="true" class="size-3 rounded-full bg-ttb09-red"></span><span aria-hidden="true" class="size-3 rounded-full bg-ttb09-yellow"></span><span aria-hidden="true" class="size-3 rounded-full bg-ttb09-green"></span>
        <span class="flex-1 text-center text-[11px] tracking-[0.04em] text-ttb09-mut">terminal — bash — 80×24</span>
      </div>
      <div role="radiogroup" aria-label="Shell" class="flex border-b border-ttb09-ph/12">
        <label class="flex min-h-10 flex-1 cursor-pointer items-center justify-center border-r border-ttb09-ph/10 text-xs text-ttb09-tab transition-colors last:border-r-0 hover:bg-ttb09-ph/5 hover:text-ttb09-ph has-checked:bg-ttb09-ph/10 has-checked:text-ttb09-ph has-checked:[text-shadow:0_0_6px_color-mix(in_oklab,var(--color-ttb09-ph)_70%,transparent)] has-focus-visible:outline-1 has-focus-visible:-outline-offset-4 has-focus-visible:outline-dashed has-focus-visible:outline-ttb09-ph"><input class="sr-only" type="radio" name="ttb09" id="ttb09-r1" checked>~/main</label>
        <label class="flex min-h-10 flex-1 cursor-pointer items-center justify-center border-r border-ttb09-ph/10 text-xs text-ttb09-tab transition-colors last:border-r-0 hover:bg-ttb09-ph/5 hover:text-ttb09-ph has-checked:bg-ttb09-ph/10 has-checked:text-ttb09-ph has-checked:[text-shadow:0_0_6px_color-mix(in_oklab,var(--color-ttb09-ph)_70%,transparent)] has-focus-visible:outline-1 has-focus-visible:-outline-offset-4 has-focus-visible:outline-dashed has-focus-visible:outline-ttb09-ph"><input class="sr-only" type="radio" name="ttb09" id="ttb09-r2">~/logs</label>
        <label class="flex min-h-10 flex-1 cursor-pointer items-center justify-center text-xs text-ttb09-tab transition-colors hover:bg-ttb09-ph/5 hover:text-ttb09-ph has-checked:bg-ttb09-ph/10 has-checked:text-ttb09-ph has-checked:[text-shadow:0_0_6px_color-mix(in_oklab,var(--color-ttb09-ph)_70%,transparent)] has-focus-visible:outline-1 has-focus-visible:-outline-offset-4 has-focus-visible:outline-dashed has-focus-visible:outline-ttb09-ph"><input class="sr-only" type="radio" name="ttb09" id="ttb09-r3">~/cron</label>
      </div>
      <div class="min-h-44 bg-[repeating-linear-gradient(0deg,transparent_0_2px,var(--color-ttb09-scan)_2px_3px)] px-5 py-4 font-ttb09-crt text-[20px] leading-[1.25] shadow-[inset_0_0_60px_rgb(0_0_0/0.6)] [text-shadow:0_0_4px_color-mix(in_oklab,var(--color-ttb09-ph)_45%,transparent)]">
        <div class="hidden group-has-[#ttb09-r1:checked]/tabs:block"><p><span class="text-ttb09-dim">$</span> ls -la</p><p>drwxr-xr-x  src/</p><p>-rw-r--r--  README.md</p><p><span class="text-ttb09-dim">$</span> <span aria-hidden="true" class="inline-block h-[0.9em] w-[0.55em] translate-y-[0.1em] animate-ttb09-blink bg-ttb09-ph motion-reduce:animate-none"></span></p></div>
        <div class="hidden group-has-[#ttb09-r2:checked]/tabs:block"><p><span class="text-ttb09-dim">$</span> tail -f app.log</p><p>[12:04] GET /api 200 · 18ms</p><p>[12:05] GET /api 200 · 21ms</p><p><span aria-hidden="true" class="inline-block h-[0.9em] w-[0.55em] translate-y-[0.1em] animate-ttb09-blink bg-ttb09-ph motion-reduce:animate-none"></span></p></div>
        <div class="hidden group-has-[#ttb09-r3:checked]/tabs:block"><p><span class="text-ttb09-dim">$</span> crontab -l</p><p>0 * * * *    ./sync.sh</p><p>*/5 * * * *  ./ping.sh</p><p><span class="text-ttb09-dim">$</span> <span aria-hidden="true" class="inline-block h-[0.9em] w-[0.55em] translate-y-[0.1em] animate-ttb09-blink bg-ttb09-ph motion-reduce:animate-none"></span></p></div>
      </div>
    </div>
    <p aria-hidden="true" class="rounded-full border border-ttb09-ph/15 px-3.5 py-2 text-[11.5px] text-ttb09-mut">scanline gradient · inset vignette · [text-shadow] bloom · steps(1) cursor</p>
  </div>
</section>
@import "tailwindcss";

@theme {
  --color-ttb09-bg: #080908;
  --color-ttb09-bg: oklch(0.138 0.003 145.4);
  --color-ttb09-screen: #050605;
  --color-ttb09-screen: oklch(0.120 0.004 145.3);
  --color-ttb09-bar: #121412;
  --color-ttb09-bar: oklch(0.188 0.005 145.4);
  --color-ttb09-ph: #4ade80;
  --color-ttb09-ph: oklch(0.800 0.182 151.7);
  --color-ttb09-tab: #74c08f;
  --color-ttb09-tab: oklch(0.745 0.104 154.7);
  --color-ttb09-dim: #2f9a58;
  --color-ttb09-dim: oklch(0.610 0.138 152.2);
  --color-ttb09-scan: #4ade800d;
  --color-ttb09-scan: oklch(0.800 0.182 151.7 / 0.05);
  --color-ttb09-mut: #9aa39c;
  --color-ttb09-mut: oklch(0.706 0.014 152.5);
  --color-ttb09-red: #ff5f57;
  --color-ttb09-red: oklch(0.694 0.196 26.4);
  --color-ttb09-yellow: #febc2e;
  --color-ttb09-yellow: oklch(0.835 0.161 80.9);
  --color-ttb09-green: #28c840;
  --color-ttb09-green: oklch(0.728 0.217 144.7);
  --font-ttb09-mono: 'JetBrains Mono', ui-monospace, monospace;
  --font-ttb09-crt: 'VT323', ui-monospace, monospace;
  --animate-ttb09-blink: ttb09-blink 1.05s steps(1, end) infinite;

  @keyframes ttb09-blink {
    0%,
        49% {
      opacity: 1;
    }

    50%,
        100% {
      opacity: 0;
    }
  }
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's utilities, so the
   stage is sized before the browser build compiles. */

.ttb-09 {
  width: 100%;
  min-height: 100svh;
}

Setup — do this once per project

Skip this if your project already runs Tailwind v4.

1Load Tailwind
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>

Compiles in the browser — perfect for a CodePen or a quick test, not for production.

2Add the stylesheetRequiredthe @theme block is what makes the custom utilities exist
@import "tailwindcss";

@theme {
  --color-ttb09-bg: #080908;
  --color-ttb09-bg: oklch(0.138 0.003 145.4);
  --color-ttb09-screen: #050605;
  --color-ttb09-screen: oklch(0.120 0.004 145.3);
  --color-ttb09-bar: #121412;
  --color-ttb09-bar: oklch(0.188 0.005 145.4);
  --color-ttb09-ph: #4ade80;
  --color-ttb09-ph: oklch(0.800 0.182 151.7);
  --color-ttb09-tab: #74c08f;
  --color-ttb09-tab: oklch(0.745 0.104 154.7);
  --color-ttb09-dim: #2f9a58;
  --color-ttb09-dim: oklch(0.610 0.138 152.2);
  --color-ttb09-scan: #4ade800d;
  --color-ttb09-scan: oklch(0.800 0.182 151.7 / 0.05);
  --color-ttb09-mut: #9aa39c;
  --color-ttb09-mut: oklch(0.706 0.014 152.5);
  --color-ttb09-red: #ff5f57;
  --color-ttb09-red: oklch(0.694 0.196 26.4);
  --color-ttb09-yellow: #febc2e;
  --color-ttb09-yellow: oklch(0.835 0.161 80.9);
  --color-ttb09-green: #28c840;
  --color-ttb09-green: oklch(0.728 0.217 144.7);
  --font-ttb09-mono: 'JetBrains Mono', ui-monospace, monospace;
  --font-ttb09-crt: 'VT323', ui-monospace, monospace;
  --animate-ttb09-blink: ttb09-blink 1.05s steps(1, end) infinite;

  @keyframes ttb09-blink {
    0%,
        49% {
      opacity: 1;
    }

    50%,
        100% {
      opacity: 0;
    }
  }
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's utilities, so the
   stage is sized before the browser build compiles. */

.ttb-09 {
  width: 100%;
  min-height: 100svh;
}
3Fonts used by this demooptional — skip if you have your own type stack
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=VT323&family=JetBrains+Mono:wght@400;500;600&display=swap">

What changed in the port

Which parts became utilities, which stayed CSS, and why.

  • The scanline repeating-linear-gradient ported verbatim as an arbitrary bg-[…]; underscores carry the spaces.
  • input:checked + .tt29b with its text-shadow became has-checked:[text-shadow:…] — the glow colour is derived from the phosphor token with color-mix().
  • UI upgrade from the CSS original: output is set in VT323 at 20px with a CRT vignette and a blinking block cursor after the prompt; the window gains a soft phosphor halo.
  • UI upgrade: resting tab labels raised from 50% green (≈3.9:1) to a solid 6.2:1 token; tabs are 40px tall with 44px rows on touch screens via full-width hit areas.
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 demo 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: Terminal Window Tabs in Tailwind CSS (Retro CRT)
Source: https://codefronts.com/navigation/tailwind-css-tabs/terminal-window-tabs-tailwind-css-retro-crt/

A phosphor-green terminal: traffic-light title bar, tabs for three shells, scanlines, a vignette and a blinking block cursor after the prompt. The port sets the output in VT323, a face drawn from DEC terminal glyphs, and makes the glow a token-driven [text-shadow:…].
## HTML
```html
<section class="ttb-09 group/tabs grid min-h-svh w-full place-items-center bg-ttb09-bg px-5 py-14 font-ttb09-mono text-ttb09-ph" aria-label="Retro CRT terminal tabs demo">
  <div class="grid w-full justify-items-center gap-7">
    <div class="w-full max-w-xl overflow-hidden rounded-xl bg-ttb09-screen ring-1 ring-ttb09-ph/20 shadow-[0_0_0_1px_rgb(0_0_0),0_28px_60px_-20px_rgb(0_0_0/0.8),0_0_40px_-10px_color-mix(in_oklab,var(--color-ttb09-ph)_25%,transparent)]">
      <div class="flex items-center gap-2 border-b border-ttb09-ph/12 bg-ttb09-bar px-3.5 py-2.5">
        <span aria-hidden="true" class="size-3 rounded-full bg-ttb09-red"></span><span aria-hidden="true" class="size-3 rounded-full bg-ttb09-yellow"></span><span aria-hidden="true" class="size-3 rounded-full bg-ttb09-green"></span>
        <span class="flex-1 text-center text-[11px] tracking-[0.04em] text-ttb09-mut">terminal — bash — 80×24</span>
      </div>
      <div role="radiogroup" aria-label="Shell" class="flex border-b border-ttb09-ph/12">
        <label class="flex min-h-10 flex-1 cursor-pointer items-center justify-center border-r border-ttb09-ph/10 text-xs text-ttb09-tab transition-colors last:border-r-0 hover:bg-ttb09-ph/5 hover:text-ttb09-ph has-checked:bg-ttb09-ph/10 has-checked:text-ttb09-ph has-checked:[text-shadow:0_0_6px_color-mix(in_oklab,var(--color-ttb09-ph)_70%,transparent)] has-focus-visible:outline-1 has-focus-visible:-outline-offset-4 has-focus-visible:outline-dashed has-focus-visible:outline-ttb09-ph"><input class="sr-only" type="radio" name="ttb09" id="ttb09-r1" checked>~/main</label>
        <label class="flex min-h-10 flex-1 cursor-pointer items-center justify-center border-r border-ttb09-ph/10 text-xs text-ttb09-tab transition-colors last:border-r-0 hover:bg-ttb09-ph/5 hover:text-ttb09-ph has-checked:bg-ttb09-ph/10 has-checked:text-ttb09-ph has-checked:[text-shadow:0_0_6px_color-mix(in_oklab,var(--color-ttb09-ph)_70%,transparent)] has-focus-visible:outline-1 has-focus-visible:-outline-offset-4 has-focus-visible:outline-dashed has-focus-visible:outline-ttb09-ph"><input class="sr-only" type="radio" name="ttb09" id="ttb09-r2">~/logs</label>
        <label class="flex min-h-10 flex-1 cursor-pointer items-center justify-center text-xs text-ttb09-tab transition-colors hover:bg-ttb09-ph/5 hover:text-ttb09-ph has-checked:bg-ttb09-ph/10 has-checked:text-ttb09-ph has-checked:[text-shadow:0_0_6px_color-mix(in_oklab,var(--color-ttb09-ph)_70%,transparent)] has-focus-visible:outline-1 has-focus-visible:-outline-offset-4 has-focus-visible:outline-dashed has-focus-visible:outline-ttb09-ph"><input class="sr-only" type="radio" name="ttb09" id="ttb09-r3">~/cron</label>
      </div>
      <div class="min-h-44 bg-[repeating-linear-gradient(0deg,transparent_0_2px,var(--color-ttb09-scan)_2px_3px)] px-5 py-4 font-ttb09-crt text-[20px] leading-[1.25] shadow-[inset_0_0_60px_rgb(0_0_0/0.6)] [text-shadow:0_0_4px_color-mix(in_oklab,var(--color-ttb09-ph)_45%,transparent)]">
        <div class="hidden group-has-[#ttb09-r1:checked]/tabs:block"><p><span class="text-ttb09-dim">$</span> ls -la</p><p>drwxr-xr-x  src/</p><p>-rw-r--r--  README.md</p><p><span class="text-ttb09-dim">$</span> <span aria-hidden="true" class="inline-block h-[0.9em] w-[0.55em] translate-y-[0.1em] animate-ttb09-blink bg-ttb09-ph motion-reduce:animate-none"></span></p></div>
        <div class="hidden group-has-[#ttb09-r2:checked]/tabs:block"><p><span class="text-ttb09-dim">$</span> tail -f app.log</p><p>[12:04] GET /api 200 · 18ms</p><p>[12:05] GET /api 200 · 21ms</p><p><span aria-hidden="true" class="inline-block h-[0.9em] w-[0.55em] translate-y-[0.1em] animate-ttb09-blink bg-ttb09-ph motion-reduce:animate-none"></span></p></div>
        <div class="hidden group-has-[#ttb09-r3:checked]/tabs:block"><p><span class="text-ttb09-dim">$</span> crontab -l</p><p>0 * * * *    ./sync.sh</p><p>*/5 * * * *  ./ping.sh</p><p><span class="text-ttb09-dim">$</span> <span aria-hidden="true" class="inline-block h-[0.9em] w-[0.55em] translate-y-[0.1em] animate-ttb09-blink bg-ttb09-ph motion-reduce:animate-none"></span></p></div>
      </div>
    </div>
    <p aria-hidden="true" class="rounded-full border border-ttb09-ph/15 px-3.5 py-2 text-[11.5px] text-ttb09-mut">scanline gradient · inset vignette · [text-shadow] bloom · steps(1) cursor</p>
  </div>
</section>
```
## CSS
```css
@import "tailwindcss";

@theme {
  --color-ttb09-bg: #080908;
  --color-ttb09-bg: oklch(0.138 0.003 145.4);
  --color-ttb09-screen: #050605;
  --color-ttb09-screen: oklch(0.120 0.004 145.3);
  --color-ttb09-bar: #121412;
  --color-ttb09-bar: oklch(0.188 0.005 145.4);
  --color-ttb09-ph: #4ade80;
  --color-ttb09-ph: oklch(0.800 0.182 151.7);
  --color-ttb09-tab: #74c08f;
  --color-ttb09-tab: oklch(0.745 0.104 154.7);
  --color-ttb09-dim: #2f9a58;
  --color-ttb09-dim: oklch(0.610 0.138 152.2);
  --color-ttb09-scan: #4ade800d;
  --color-ttb09-scan: oklch(0.800 0.182 151.7 / 0.05);
  --color-ttb09-mut: #9aa39c;
  --color-ttb09-mut: oklch(0.706 0.014 152.5);
  --color-ttb09-red: #ff5f57;
  --color-ttb09-red: oklch(0.694 0.196 26.4);
  --color-ttb09-yellow: #febc2e;
  --color-ttb09-yellow: oklch(0.835 0.161 80.9);
  --color-ttb09-green: #28c840;
  --color-ttb09-green: oklch(0.728 0.217 144.7);
  --font-ttb09-mono: 'JetBrains Mono', ui-monospace, monospace;
  --font-ttb09-crt: 'VT323', ui-monospace, monospace;
  --animate-ttb09-blink: ttb09-blink 1.05s steps(1, end) infinite;

  @keyframes ttb09-blink {
    0%,
        49% {
      opacity: 1;
    }

    50%,
        100% {
      opacity: 0;
    }
  }
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's utilities, so the
   stage is sized before the browser build compiles. */

.ttb-09 {
  width: 100%;
  min-height: 100svh;
}
```

How this works

Three layers make a CRT. Scanlines are a repeating gradient; the vignette is an inset shadow; the bloom is a text-shadow in the phosphor colour:

<div class="bg-[repeating-linear-gradient(0deg,transparent_0_2px,var(--color-ttb09-scan)_2px_3px)]
  shadow-[inset_0_0_60px_rgb(0_0_0/0.6)]
  [text-shadow:0_0_4px_color-mix(in_oklab,var(--color-ttb09-ph)_45%,transparent)]">

The cursor only lives in the visible panel. Each panel ends with a block cursor; hidden panels are display:none, so only one blinks, and it stops under motion-reduce:.

Make it yours

  • Amber terminal: phosphor #ffb000; keep the scanline token at the same low alpha.
  • Denser grille: transparent_0_1px,…_1px_2px.
  • Linux look: remove the traffic lights and left-align the title.

Gotchas — read before shipping

  • Scanlines over a large area can repaint on scroll in older engines; keep the body modest in size.
  • VT323 is a display face — keep it at 18px or larger for legibility.
  • Text-shadow glow lowers apparent sharpness; resting tab labels use a solid token for contrast, not glow.

Browser support

ChromeSafariFirefoxEdge
111+16.4+128+111+

Gradients, text-shadow and keyframes are universal; the floor is Tailwind v4's sheet.

Techniques used in this demo

Utilitiesfirst-class utility or variant

Arbitraryneeds bracket syntax

Plain CSSno Tailwind surface

Search CodeFronts

Loading…