10 / 20
All 20 in 20 Tailwind Retro UI
- 01Tailwind CRT Scanlines and Screen Curvature
- 02Tailwind Pixel Art Scaling with Image Rendering
- 03Tailwind Dithering Patterns with Gradients
- 04Tailwind One Bit Bevel Borders with Box Shadow
- 05Tailwind Classic Mac System 7 Window Chrome
- 06Tailwind MS DOS Text Mode Interface Grid
- 07Tailwind Teletext and Ceefax Block Graphics
- 08Tailwind Amber and Green Phosphor Monitor
- 09Tailwind Windows 95 Desktop Interface
- 10Tailwind Retro Futuristic Terminal Interface
- 11Tailwind Eight Bit Pixel Art Buttons and HUD
- 12Tailwind Neo Brutalist Component Board
- 13Tailwind Skeuomorphic Cassette Player
- 14Tailwind Vaporwave Aesthetic Scene
- 15Tailwind Retro Arcade Game Interface
- 16Tailwind ASCII Art Terminal Interface
- 17Tailwind Nineties Web Guestbook Widget
- 18Tailwind Y2K Aqua Gloss Interface Kit
- 19Tailwind Vintage Storefront Layout
- 20Tailwind Retro Desktop Portfolio Template
Tailwind Retro Futuristic Terminal Interface
A command console that boots: staggered POST lines, a typed command and a live status column, all under the CRT treatment from demo 01. The boot reveal is pure CSS with the text in the DOM from the start — and in utilities the stagger becomes something you can read off each line, because every line carries its own [animation-delay:...].
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="twr-10 relative block w-full min-h-svh box-border bg-twr10-bg text-twr10-ink font-twr10-mono" aria-labelledby="twr-10-title">
<h2 class="absolute size-px overflow-hidden [clip-path:inset(50%)] whitespace-nowrap" id="twr-10-title">Retro futuristic terminal console</h2>
<div class="absolute top-4 right-4 z-6">
<input class="peer absolute inset-0 size-11 m-0 opacity-0 cursor-pointer" type="checkbox" id="twr-10-theme">
<label class="grid place-items-center size-11 rounded border border-twr10-line bg-twr10-surface text-twr10-ink2 cursor-pointer peer-focus-visible:outline-2 peer-focus-visible:outline-twr10-acc peer-focus-visible:outline-offset-2" for="twr-10-theme">
<svg class="size-5" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><circle cx="12" cy="12" r="8" fill="none" stroke="currentColor" stroke-width="1.6"/><path d="M12 4a8 8 0 0 0 0 16Z" fill="currentColor"/></svg>
<span class="absolute size-px overflow-hidden [clip-path:inset(50%)] whitespace-nowrap">Toggle light surround</span>
</label>
</div>
<div class="grid grid-cols-[minmax(0,1fr)] gap-4 max-w-[76rem] mx-auto px-[clamp(0.6rem,0.4rem+1.5vw,2rem)] pt-[clamp(3.5rem,3rem+3vw,5rem)] pb-16">
<p class="m-0 justify-self-start min-w-0 rounded-[3px] border border-twr10-line px-2.5 py-1.5 text-[0.68rem] tracking-[0.14em] uppercase text-twr10-ink2">DEC VT-series · 80-column serial console</p>
<div class="min-w-0 rounded-[34px] bg-[#23282b] p-[clamp(0.7rem,0.5rem+1.2vw,1.3rem)] [box-shadow:inset_0_2px_0_rgba(255,255,255,0.18),inset_0_-3px_0_rgba(0,0,0,0.55),0_20px_44px_-22px_rgba(0,0,0,0.8)]">
<div class="relative isolate overflow-hidden rounded-[var(--twr-10-curve)] p-[clamp(0.9rem,0.6rem+1.6vw,1.8rem)] [background:radial-gradient(120%_100%_at_50%_42%,#082018_0%,var(--color-twr10-tube)_76%)] [box-shadow:inset_0_0_0_2px_#04100b,inset_0_0_32px_8px_rgba(0,0,0,0.8)]">
<div class="relative z-2 grid grid-cols-[minmax(0,1fr)] gap-5 md:grid-cols-[minmax(0,1fr)_minmax(0,15rem)]">
<div class="min-w-0 grid content-start gap-3.5">
<h3 class="m-0 min-w-0 font-bold tracking-[0.16em] text-twr10-fg text-[clamp(0.95rem,0.75rem+1.2vw,1.35rem)] [text-shadow:0_0_6px_rgba(125,250,176,0.55)]">ORION-7 SYSTEM CONSOLE</h3>
<ol class="list-none m-0 p-0 grid gap-1 min-w-0 text-twr10-fgdim text-[clamp(0.66rem,0.55rem+0.65vw,0.85rem)]">
<li class="min-w-0 [overflow-wrap:anywhere] opacity-0 animate-twr10-in [animation-delay:calc(0*140ms)] motion-reduce:animate-none motion-reduce:opacity-100" style="--twr-10-i: 0">POST . . . . . . . . . . . . . <b class="font-bold text-twr10-fg">OK</b></li>
<li class="min-w-0 [overflow-wrap:anywhere] opacity-0 animate-twr10-in [animation-delay:calc(1*140ms)] motion-reduce:animate-none motion-reduce:opacity-100" style="--twr-10-i: 1">CPU 0 33 MHz CACHE 8K <b class="font-bold text-twr10-fg">OK</b></li>
<li class="min-w-0 [overflow-wrap:anywhere] opacity-0 animate-twr10-in [animation-delay:calc(2*140ms)] motion-reduce:animate-none motion-reduce:opacity-100" style="--twr-10-i: 2">RAM 0000—FFFF 16384K <b class="font-bold text-twr10-fg">OK</b></li>
<li class="min-w-0 [overflow-wrap:anywhere] opacity-0 animate-twr10-in [animation-delay:calc(3*140ms)] motion-reduce:animate-none motion-reduce:opacity-100" style="--twr-10-i: 3">CONTROLLER SCSI ID 7 . . . . . <b class="font-bold text-twr10-fg">OK</b></li>
<li class="min-w-0 [overflow-wrap:anywhere] opacity-0 animate-twr10-in [animation-delay:calc(4*140ms)] motion-reduce:animate-none motion-reduce:opacity-100" style="--twr-10-i: 4">MOUNT /dev/rd0 READ ONLY <b class="font-bold text-twr10-fg">OK</b></li>
<li class="min-w-0 [overflow-wrap:anywhere] opacity-0 animate-twr10-in [animation-delay:calc(5*140ms)] motion-reduce:animate-none motion-reduce:opacity-100" style="--twr-10-i: 5">LINK ETH0 10BASE-T . . <b class="font-bold text-twr10-fg">UP</b></li>
<li class="min-w-0 [overflow-wrap:anywhere] opacity-0 animate-twr10-in [animation-delay:calc(6*140ms)] motion-reduce:animate-none motion-reduce:opacity-100" style="--twr-10-i: 6">CLOCK SYNC DRIFT 0.004s . <b class="font-bold text-twr10-fg">OK</b></li>
<li class="min-w-0 [overflow-wrap:anywhere] opacity-0 animate-twr10-in [animation-delay:calc(7*140ms)] motion-reduce:animate-none motion-reduce:opacity-100" style="--twr-10-i: 7">SENSOR 4 OUT OF RANGE . . <b class="font-bold text-twr10-warn">CHECK</b></li>
<li class="min-w-0 [overflow-wrap:anywhere] opacity-0 animate-twr10-in [animation-delay:calc(8*140ms)] motion-reduce:animate-none motion-reduce:opacity-100" style="--twr-10-i: 8">KERNEL 2.0.34 LOADED . . . <b class="font-bold text-twr10-fg">OK</b></li>
</ol>
<p class="m-0 flex flex-wrap items-center gap-x-[0.5ch] min-w-0 text-twr10-fg text-[clamp(0.7rem,0.58rem+0.7vw,0.9rem)]">
<span class="text-twr10-acc">orion:~$</span>
<span class="inline-block w-0 overflow-hidden whitespace-nowrap animate-twr10-type motion-reduce:animate-none motion-reduce:w-[22ch]">status --all --verbose</span>
<span class="inline-block w-[0.6em] h-[1.1em] bg-twr10-fg animate-twr10-blink motion-reduce:animate-none" aria-hidden="true"></span>
</p>
</div>
<aside class="min-w-0 grid content-start gap-2.5 rounded border border-[rgba(125,250,176,0.28)] bg-[rgba(125,250,176,0.04)] px-3.5 py-3" aria-label="System status">
<h4 class="m-0 text-[0.66rem] tracking-[0.2em] text-twr10-acc">STATUS</h4>
<dl class="m-0 grid gap-1 text-[0.72rem] text-twr10-fgdim">
<div class="flex justify-between gap-3 min-w-0"><dt class="m-0">LOAD</dt><dd class="m-0 tabular-nums text-twr10-fg">0.42</dd></div>
<div class="flex justify-between gap-3 min-w-0"><dt class="m-0">MEM</dt><dd class="m-0 tabular-nums text-twr10-fg">11208K</dd></div>
<div class="flex justify-between gap-3 min-w-0"><dt class="m-0">SWAP</dt><dd class="m-0 tabular-nums text-twr10-fg">0K</dd></div>
<div class="flex justify-between gap-3 min-w-0"><dt class="m-0">PROC</dt><dd class="m-0 tabular-nums text-twr10-fg">37</dd></div>
<div class="flex justify-between gap-3 min-w-0"><dt class="m-0">UPTIME</dt><dd class="m-0 tabular-nums text-twr10-fg">04:11</dd></div>
<div class="flex justify-between gap-3 min-w-0"><dt class="m-0">TEMP</dt><dd class="m-0 tabular-nums text-twr10-fg">41C</dd></div>
</dl>
<div class="flex items-center gap-2 min-w-0 text-[0.7rem] text-twr10-fgdim" role="img" aria-label="Bus utilisation, 62 percent">
<span>BUS</span>
<span class="flex-1 min-w-0 h-2.5 p-px border border-[rgba(125,250,176,0.4)]"><span class="block h-full w-[62%] [background:repeating-linear-gradient(90deg,var(--color-twr10-fg)_0_4px,rgba(0,0,0,0)_4px_6px)]"></span></span>
<span class="tabular-nums text-twr10-fg">62%</span>
</div>
</aside>
</div>
<div class="twr-10__scan absolute inset-0 z-4 pointer-events-none opacity-50 [background:repeating-linear-gradient(to_bottom,rgba(0,0,0,0)_0,rgba(0,0,0,0)_calc(var(--twr-10-scan)-1px),rgba(0,0,0,0.7)_calc(var(--twr-10-scan)-1px),rgba(0,0,0,0.7)_var(--twr-10-scan))]" aria-hidden="true"></div>
<div class="absolute -inset-px z-5 pointer-events-none [background:radial-gradient(118%_106%_at_50%_50%,rgba(0,0,0,0)_54%,rgba(0,0,0,0.5)_86%,rgba(0,0,0,0.88)_100%)]" aria-hidden="true"></div>
</div>
<div class="flex items-center justify-between gap-3 min-w-0 pt-2.5 px-1.5 pb-0.5 text-[0.62rem] tracking-[0.2em] text-[#8d9498]">
<span>VT-320 / SERIAL A</span>
<span class="flex-none size-[9px] rounded-full bg-[#63f0a0] [box-shadow:0_0_8px_2px_rgba(99,240,160,0.65)]" aria-hidden="true"></span>
</div>
</div>
</div>
</section><section class="twr-10 relative block w-full min-h-svh box-border bg-twr10-bg text-twr10-ink font-twr10-mono" aria-labelledby="twr-10-title">
<h2 class="absolute size-px overflow-hidden [clip-path:inset(50%)] whitespace-nowrap" id="twr-10-title">Retro futuristic terminal console</h2>
<div class="absolute top-4 right-4 z-6">
<input class="peer absolute inset-0 size-11 m-0 opacity-0 cursor-pointer" type="checkbox" id="twr-10-theme">
<label class="grid place-items-center size-11 rounded border border-twr10-line bg-twr10-surface text-twr10-ink2 cursor-pointer peer-focus-visible:outline-2 peer-focus-visible:outline-twr10-acc peer-focus-visible:outline-offset-2" for="twr-10-theme">
<svg class="size-5" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><circle cx="12" cy="12" r="8" fill="none" stroke="currentColor" stroke-width="1.6"/><path d="M12 4a8 8 0 0 0 0 16Z" fill="currentColor"/></svg>
<span class="absolute size-px overflow-hidden [clip-path:inset(50%)] whitespace-nowrap">Toggle light surround</span>
</label>
</div>
<div class="grid grid-cols-[minmax(0,1fr)] gap-4 max-w-[76rem] mx-auto px-[clamp(0.6rem,0.4rem+1.5vw,2rem)] pt-[clamp(3.5rem,3rem+3vw,5rem)] pb-16">
<p class="m-0 justify-self-start min-w-0 rounded-[3px] border border-twr10-line px-2.5 py-1.5 text-[0.68rem] tracking-[0.14em] uppercase text-twr10-ink2">DEC VT-series · 80-column serial console</p>
<div class="min-w-0 rounded-[34px] bg-[#23282b] p-[clamp(0.7rem,0.5rem+1.2vw,1.3rem)] [box-shadow:inset_0_2px_0_rgba(255,255,255,0.18),inset_0_-3px_0_rgba(0,0,0,0.55),0_20px_44px_-22px_rgba(0,0,0,0.8)]">
<div class="relative isolate overflow-hidden rounded-[var(--twr-10-curve)] p-[clamp(0.9rem,0.6rem+1.6vw,1.8rem)] [background:radial-gradient(120%_100%_at_50%_42%,#082018_0%,var(--color-twr10-tube)_76%)] [box-shadow:inset_0_0_0_2px_#04100b,inset_0_0_32px_8px_rgba(0,0,0,0.8)]">
<div class="relative z-2 grid grid-cols-[minmax(0,1fr)] gap-5 md:grid-cols-[minmax(0,1fr)_minmax(0,15rem)]">
<div class="min-w-0 grid content-start gap-3.5">
<h3 class="m-0 min-w-0 font-bold tracking-[0.16em] text-twr10-fg text-[clamp(0.95rem,0.75rem+1.2vw,1.35rem)] [text-shadow:0_0_6px_rgba(125,250,176,0.55)]">ORION-7 SYSTEM CONSOLE</h3>
<ol class="list-none m-0 p-0 grid gap-1 min-w-0 text-twr10-fgdim text-[clamp(0.66rem,0.55rem+0.65vw,0.85rem)]">
<li class="min-w-0 [overflow-wrap:anywhere] opacity-0 animate-twr10-in [animation-delay:calc(0*140ms)] motion-reduce:animate-none motion-reduce:opacity-100" style="--twr-10-i: 0">POST . . . . . . . . . . . . . <b class="font-bold text-twr10-fg">OK</b></li>
<li class="min-w-0 [overflow-wrap:anywhere] opacity-0 animate-twr10-in [animation-delay:calc(1*140ms)] motion-reduce:animate-none motion-reduce:opacity-100" style="--twr-10-i: 1">CPU 0 33 MHz CACHE 8K <b class="font-bold text-twr10-fg">OK</b></li>
<li class="min-w-0 [overflow-wrap:anywhere] opacity-0 animate-twr10-in [animation-delay:calc(2*140ms)] motion-reduce:animate-none motion-reduce:opacity-100" style="--twr-10-i: 2">RAM 0000—FFFF 16384K <b class="font-bold text-twr10-fg">OK</b></li>
<li class="min-w-0 [overflow-wrap:anywhere] opacity-0 animate-twr10-in [animation-delay:calc(3*140ms)] motion-reduce:animate-none motion-reduce:opacity-100" style="--twr-10-i: 3">CONTROLLER SCSI ID 7 . . . . . <b class="font-bold text-twr10-fg">OK</b></li>
<li class="min-w-0 [overflow-wrap:anywhere] opacity-0 animate-twr10-in [animation-delay:calc(4*140ms)] motion-reduce:animate-none motion-reduce:opacity-100" style="--twr-10-i: 4">MOUNT /dev/rd0 READ ONLY <b class="font-bold text-twr10-fg">OK</b></li>
<li class="min-w-0 [overflow-wrap:anywhere] opacity-0 animate-twr10-in [animation-delay:calc(5*140ms)] motion-reduce:animate-none motion-reduce:opacity-100" style="--twr-10-i: 5">LINK ETH0 10BASE-T . . <b class="font-bold text-twr10-fg">UP</b></li>
<li class="min-w-0 [overflow-wrap:anywhere] opacity-0 animate-twr10-in [animation-delay:calc(6*140ms)] motion-reduce:animate-none motion-reduce:opacity-100" style="--twr-10-i: 6">CLOCK SYNC DRIFT 0.004s . <b class="font-bold text-twr10-fg">OK</b></li>
<li class="min-w-0 [overflow-wrap:anywhere] opacity-0 animate-twr10-in [animation-delay:calc(7*140ms)] motion-reduce:animate-none motion-reduce:opacity-100" style="--twr-10-i: 7">SENSOR 4 OUT OF RANGE . . <b class="font-bold text-twr10-warn">CHECK</b></li>
<li class="min-w-0 [overflow-wrap:anywhere] opacity-0 animate-twr10-in [animation-delay:calc(8*140ms)] motion-reduce:animate-none motion-reduce:opacity-100" style="--twr-10-i: 8">KERNEL 2.0.34 LOADED . . . <b class="font-bold text-twr10-fg">OK</b></li>
</ol>
<p class="m-0 flex flex-wrap items-center gap-x-[0.5ch] min-w-0 text-twr10-fg text-[clamp(0.7rem,0.58rem+0.7vw,0.9rem)]">
<span class="text-twr10-acc">orion:~$</span>
<span class="inline-block w-0 overflow-hidden whitespace-nowrap animate-twr10-type motion-reduce:animate-none motion-reduce:w-[22ch]">status --all --verbose</span>
<span class="inline-block w-[0.6em] h-[1.1em] bg-twr10-fg animate-twr10-blink motion-reduce:animate-none" aria-hidden="true"></span>
</p>
</div>
<aside class="min-w-0 grid content-start gap-2.5 rounded border border-[rgba(125,250,176,0.28)] bg-[rgba(125,250,176,0.04)] px-3.5 py-3" aria-label="System status">
<h4 class="m-0 text-[0.66rem] tracking-[0.2em] text-twr10-acc">STATUS</h4>
<dl class="m-0 grid gap-1 text-[0.72rem] text-twr10-fgdim">
<div class="flex justify-between gap-3 min-w-0"><dt class="m-0">LOAD</dt><dd class="m-0 tabular-nums text-twr10-fg">0.42</dd></div>
<div class="flex justify-between gap-3 min-w-0"><dt class="m-0">MEM</dt><dd class="m-0 tabular-nums text-twr10-fg">11208K</dd></div>
<div class="flex justify-between gap-3 min-w-0"><dt class="m-0">SWAP</dt><dd class="m-0 tabular-nums text-twr10-fg">0K</dd></div>
<div class="flex justify-between gap-3 min-w-0"><dt class="m-0">PROC</dt><dd class="m-0 tabular-nums text-twr10-fg">37</dd></div>
<div class="flex justify-between gap-3 min-w-0"><dt class="m-0">UPTIME</dt><dd class="m-0 tabular-nums text-twr10-fg">04:11</dd></div>
<div class="flex justify-between gap-3 min-w-0"><dt class="m-0">TEMP</dt><dd class="m-0 tabular-nums text-twr10-fg">41C</dd></div>
</dl>
<div class="flex items-center gap-2 min-w-0 text-[0.7rem] text-twr10-fgdim" role="img" aria-label="Bus utilisation, 62 percent">
<span>BUS</span>
<span class="flex-1 min-w-0 h-2.5 p-px border border-[rgba(125,250,176,0.4)]"><span class="block h-full w-[62%] [background:repeating-linear-gradient(90deg,var(--color-twr10-fg)_0_4px,rgba(0,0,0,0)_4px_6px)]"></span></span>
<span class="tabular-nums text-twr10-fg">62%</span>
</div>
</aside>
</div>
<div class="twr-10__scan absolute inset-0 z-4 pointer-events-none opacity-50 [background:repeating-linear-gradient(to_bottom,rgba(0,0,0,0)_0,rgba(0,0,0,0)_calc(var(--twr-10-scan)-1px),rgba(0,0,0,0.7)_calc(var(--twr-10-scan)-1px),rgba(0,0,0,0.7)_var(--twr-10-scan))]" aria-hidden="true"></div>
<div class="absolute -inset-px z-5 pointer-events-none [background:radial-gradient(118%_106%_at_50%_50%,rgba(0,0,0,0)_54%,rgba(0,0,0,0.5)_86%,rgba(0,0,0,0.88)_100%)]" aria-hidden="true"></div>
</div>
<div class="flex items-center justify-between gap-3 min-w-0 pt-2.5 px-1.5 pb-0.5 text-[0.62rem] tracking-[0.2em] text-[#8d9498]">
<span>VT-320 / SERIAL A</span>
<span class="flex-none size-[9px] rounded-full bg-[#63f0a0] [box-shadow:0_0_8px_2px_rgba(99,240,160,0.65)]" aria-hidden="true"></span>
</div>
</div>
</div>
</section>@import "tailwindcss";
@theme {
--color-twr10-fg: #7dfab0;
--color-twr10-fg: oklch(0.90 0.14 158);
--color-twr10-fgdim: #3f9f6d;
--color-twr10-fgdim: oklch(0.63 0.11 158);
--color-twr10-warn: #ffd166;
--color-twr10-warn: oklch(0.87 0.13 85);
--color-twr10-tube: #04120c;
--color-twr10-bg: #0c1013;
--color-twr10-bg: oklch(0.16 0.008 210);
--color-twr10-surface: #171c20;
--color-twr10-surface: oklch(0.23 0.009 210);
--color-twr10-ink: #eaeff1;
--color-twr10-ink: oklch(0.95 0.004 210);
--color-twr10-ink2: #9ba5aa;
--color-twr10-ink2: oklch(0.71 0.009 210);
--color-twr10-line: #333c41;
--color-twr10-line: oklch(0.33 0.01 210);
--color-twr10-acc: #6ee7b7;
--color-twr10-acc: oklch(0.85 0.12 165);
--font-twr10-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, 'Cascadia Mono', Menlo, monospace;
--animate-twr10-in: twr-10-in 320ms ease-out forwards;
--animate-twr10-type: twr-10-type 1.6s steps(22, end) 1.4s forwards;
--animate-twr10-blink: twr-10-blink 1.1s steps(1, end) infinite;
}
.twr-10 {
--twr-10-scan: 3px;
--twr-10-curve: 20px;
}
@keyframes twr-10-in {
from {
opacity: 0;
transform: translateY(2px);
}
to {
opacity: 1;
transform: none;
}
}
/* The 22 here must equal the steps() count in --animate-twr10-type and the
motion-reduce:w-[22ch] utility in the markup. */
@keyframes twr-10-type {
to {
width: 22ch;
}
}
@keyframes twr-10-blink {
0%,
49% {
opacity: 1;
}
50%,
100% {
opacity: 0;
}
}
/* No variant form, and unlayered so it outranks the arbitrary background. */
@media (min-resolution: 1.5dppx) {
.twr-10__scan {
background: repeating-linear-gradient(
to bottom,
rgba(0, 0, 0, 0) 0,
rgba(0, 0, 0, 0.6) calc(var(--twr-10-scan) * 0.5),
rgba(0, 0, 0, 0) calc(var(--twr-10-scan) * 1.34)
);
}
}
.twr-10:has(#twr-10-theme:checked) {
--color-twr10-bg: #d8dcde;
--color-twr10-bg: oklch(0.88 0.005 210);
--color-twr10-surface: #eef1f2;
--color-twr10-surface: oklch(0.95 0.003 210);
--color-twr10-ink: #14181a;
--color-twr10-ink: oklch(0.19 0.006 210);
--color-twr10-ink2: #4a5257;
--color-twr10-ink2: oklch(0.43 0.01 210);
--color-twr10-line: #a6aeb2;
--color-twr10-line: oklch(0.74 0.008 210);
--color-twr10-acc: #0c6b4d;
--color-twr10-acc: oklch(0.46 0.11 165);
}@import "tailwindcss";
@theme {
--color-twr10-fg: #7dfab0;
--color-twr10-fg: oklch(0.90 0.14 158);
--color-twr10-fgdim: #3f9f6d;
--color-twr10-fgdim: oklch(0.63 0.11 158);
--color-twr10-warn: #ffd166;
--color-twr10-warn: oklch(0.87 0.13 85);
--color-twr10-tube: #04120c;
--color-twr10-bg: #0c1013;
--color-twr10-bg: oklch(0.16 0.008 210);
--color-twr10-surface: #171c20;
--color-twr10-surface: oklch(0.23 0.009 210);
--color-twr10-ink: #eaeff1;
--color-twr10-ink: oklch(0.95 0.004 210);
--color-twr10-ink2: #9ba5aa;
--color-twr10-ink2: oklch(0.71 0.009 210);
--color-twr10-line: #333c41;
--color-twr10-line: oklch(0.33 0.01 210);
--color-twr10-acc: #6ee7b7;
--color-twr10-acc: oklch(0.85 0.12 165);
--font-twr10-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, 'Cascadia Mono', Menlo, monospace;
--animate-twr10-in: twr-10-in 320ms ease-out forwards;
--animate-twr10-type: twr-10-type 1.6s steps(22, end) 1.4s forwards;
--animate-twr10-blink: twr-10-blink 1.1s steps(1, end) infinite;
}
.twr-10 {
--twr-10-scan: 3px;
--twr-10-curve: 20px;
}
@keyframes twr-10-in {
from {
opacity: 0;
transform: translateY(2px);
}
to {
opacity: 1;
transform: none;
}
}
/* The 22 here must equal the steps() count in --animate-twr10-type and the
motion-reduce:w-[22ch] utility in the markup. */
@keyframes twr-10-type {
to {
width: 22ch;
}
}
@keyframes twr-10-blink {
0%,
49% {
opacity: 1;
}
50%,
100% {
opacity: 0;
}
}
/* No variant form, and unlayered so it outranks the arbitrary background. */
@media (min-resolution: 1.5dppx) {
.twr-10__scan {
background: repeating-linear-gradient(
to bottom,
rgba(0, 0, 0, 0) 0,
rgba(0, 0, 0, 0.6) calc(var(--twr-10-scan) * 0.5),
rgba(0, 0, 0, 0) calc(var(--twr-10-scan) * 1.34)
);
}
}
.twr-10:has(#twr-10-theme:checked) {
--color-twr10-bg: #d8dcde;
--color-twr10-bg: oklch(0.88 0.005 210);
--color-twr10-surface: #eef1f2;
--color-twr10-surface: oklch(0.95 0.003 210);
--color-twr10-ink: #14181a;
--color-twr10-ink: oklch(0.19 0.006 210);
--color-twr10-ink2: #4a5257;
--color-twr10-ink2: oklch(0.43 0.01 210);
--color-twr10-line: #a6aeb2;
--color-twr10-line: oklch(0.74 0.008 210);
--color-twr10-acc: #0c6b4d;
--color-twr10-acc: oklch(0.46 0.11 165);
}Setup — do this once per project
Skip this if your project already runs Tailwind v4.
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<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.
npm install tailwindcss @tailwindcss/vite
npm install tailwindcss @tailwindcss/vite// vite.config.js ("type": "module" in package.json)
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()],
})// vite.config.js ("type": "module" in package.json)
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()],
})Any v4 setup works — Vite, PostCSS or the CLI. Nothing in this demo is bundler-specific.
@import "tailwindcss";
@theme {
--color-twr10-fg: #7dfab0;
--color-twr10-fg: oklch(0.90 0.14 158);
--color-twr10-fgdim: #3f9f6d;
--color-twr10-fgdim: oklch(0.63 0.11 158);
--color-twr10-warn: #ffd166;
--color-twr10-warn: oklch(0.87 0.13 85);
--color-twr10-tube: #04120c;
--color-twr10-bg: #0c1013;
--color-twr10-bg: oklch(0.16 0.008 210);
--color-twr10-surface: #171c20;
--color-twr10-surface: oklch(0.23 0.009 210);
--color-twr10-ink: #eaeff1;
--color-twr10-ink: oklch(0.95 0.004 210);
--color-twr10-ink2: #9ba5aa;
--color-twr10-ink2: oklch(0.71 0.009 210);
--color-twr10-line: #333c41;
--color-twr10-line: oklch(0.33 0.01 210);
--color-twr10-acc: #6ee7b7;
--color-twr10-acc: oklch(0.85 0.12 165);
--font-twr10-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, 'Cascadia Mono', Menlo, monospace;
--animate-twr10-in: twr-10-in 320ms ease-out forwards;
--animate-twr10-type: twr-10-type 1.6s steps(22, end) 1.4s forwards;
--animate-twr10-blink: twr-10-blink 1.1s steps(1, end) infinite;
}
.twr-10 {
--twr-10-scan: 3px;
--twr-10-curve: 20px;
}
@keyframes twr-10-in {
from {
opacity: 0;
transform: translateY(2px);
}
to {
opacity: 1;
transform: none;
}
}
/* The 22 here must equal the steps() count in --animate-twr10-type and the
motion-reduce:w-[22ch] utility in the markup. */
@keyframes twr-10-type {
to {
width: 22ch;
}
}
@keyframes twr-10-blink {
0%,
49% {
opacity: 1;
}
50%,
100% {
opacity: 0;
}
}
/* No variant form, and unlayered so it outranks the arbitrary background. */
@media (min-resolution: 1.5dppx) {
.twr-10__scan {
background: repeating-linear-gradient(
to bottom,
rgba(0, 0, 0, 0) 0,
rgba(0, 0, 0, 0.6) calc(var(--twr-10-scan) * 0.5),
rgba(0, 0, 0, 0) calc(var(--twr-10-scan) * 1.34)
);
}
}
.twr-10:has(#twr-10-theme:checked) {
--color-twr10-bg: #d8dcde;
--color-twr10-bg: oklch(0.88 0.005 210);
--color-twr10-surface: #eef1f2;
--color-twr10-surface: oklch(0.95 0.003 210);
--color-twr10-ink: #14181a;
--color-twr10-ink: oklch(0.19 0.006 210);
--color-twr10-ink2: #4a5257;
--color-twr10-ink2: oklch(0.43 0.01 210);
--color-twr10-line: #a6aeb2;
--color-twr10-line: oklch(0.74 0.008 210);
--color-twr10-acc: #0c6b4d;
--color-twr10-acc: oklch(0.46 0.11 165);
}@import "tailwindcss";
@theme {
--color-twr10-fg: #7dfab0;
--color-twr10-fg: oklch(0.90 0.14 158);
--color-twr10-fgdim: #3f9f6d;
--color-twr10-fgdim: oklch(0.63 0.11 158);
--color-twr10-warn: #ffd166;
--color-twr10-warn: oklch(0.87 0.13 85);
--color-twr10-tube: #04120c;
--color-twr10-bg: #0c1013;
--color-twr10-bg: oklch(0.16 0.008 210);
--color-twr10-surface: #171c20;
--color-twr10-surface: oklch(0.23 0.009 210);
--color-twr10-ink: #eaeff1;
--color-twr10-ink: oklch(0.95 0.004 210);
--color-twr10-ink2: #9ba5aa;
--color-twr10-ink2: oklch(0.71 0.009 210);
--color-twr10-line: #333c41;
--color-twr10-line: oklch(0.33 0.01 210);
--color-twr10-acc: #6ee7b7;
--color-twr10-acc: oklch(0.85 0.12 165);
--font-twr10-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, 'Cascadia Mono', Menlo, monospace;
--animate-twr10-in: twr-10-in 320ms ease-out forwards;
--animate-twr10-type: twr-10-type 1.6s steps(22, end) 1.4s forwards;
--animate-twr10-blink: twr-10-blink 1.1s steps(1, end) infinite;
}
.twr-10 {
--twr-10-scan: 3px;
--twr-10-curve: 20px;
}
@keyframes twr-10-in {
from {
opacity: 0;
transform: translateY(2px);
}
to {
opacity: 1;
transform: none;
}
}
/* The 22 here must equal the steps() count in --animate-twr10-type and the
motion-reduce:w-[22ch] utility in the markup. */
@keyframes twr-10-type {
to {
width: 22ch;
}
}
@keyframes twr-10-blink {
0%,
49% {
opacity: 1;
}
50%,
100% {
opacity: 0;
}
}
/* No variant form, and unlayered so it outranks the arbitrary background. */
@media (min-resolution: 1.5dppx) {
.twr-10__scan {
background: repeating-linear-gradient(
to bottom,
rgba(0, 0, 0, 0) 0,
rgba(0, 0, 0, 0.6) calc(var(--twr-10-scan) * 0.5),
rgba(0, 0, 0, 0) calc(var(--twr-10-scan) * 1.34)
);
}
}
.twr-10:has(#twr-10-theme:checked) {
--color-twr10-bg: #d8dcde;
--color-twr10-bg: oklch(0.88 0.005 210);
--color-twr10-surface: #eef1f2;
--color-twr10-surface: oklch(0.95 0.003 210);
--color-twr10-ink: #14181a;
--color-twr10-ink: oklch(0.19 0.006 210);
--color-twr10-ink2: #4a5257;
--color-twr10-ink2: oklch(0.43 0.01 210);
--color-twr10-line: #a6aeb2;
--color-twr10-line: oklch(0.74 0.008 210);
--color-twr10-acc: #0c6b4d;
--color-twr10-acc: oklch(0.46 0.11 165);
}<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=JetBrains+Mono:wght@400;700&display=swap">
<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=JetBrains+Mono:wght@400;700&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
- The webfonts load as
<link>tags insetup, not as an@importin the stylesheet — Tailwind emits user@importrules after its own layers, where the browser drops them. - Each boot line carries its own
[animation-delay:calc(n*140ms)], so the cascade timing and the reduced-motion fallback are readable on the element. The--twr-10-iindex is kept for template-driven rows. - Every animated element pairs
motion-reduce:animate-nonewith explicit end-state utilities. Without the second half, reduced-motion users get the start state and see nothing. - The typing effect's three coupled numbers (keyframe width,
steps()count,motion-reducewidth) are the one place this port is more fragile than the original — the comment in the stylesheet says so. - Boot text is in the DOM at load and only opacity animates, so no live region and no insertion; the console is complete for assistive technology from the first frame.
- The CRT overlays are the same arbitrary backgrounds as demo 01, including the unlayered
min-resolutionoverride that beats them without!. md:grid-cols-[minmax(0,1fr)_minmax(0,15rem)]replaces the single media query; the status rail drops under the console on narrow screens with no other change.
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: Tailwind Retro Futuristic Terminal Interface
Source: https://codefronts.com/design-styles/tailwind-retro-ui/tailwind-retro-futuristic-terminal-interface/
A command console that boots: staggered POST lines, a typed command and a live status column, all under the CRT treatment from demo 01. The boot reveal is pure CSS with the text in the DOM from the start — and in utilities the stagger becomes something you can read off each line, because every line carries its own [animation-delay:...].
## HTML
```html
<section class="twr-10 relative block w-full min-h-svh box-border bg-twr10-bg text-twr10-ink font-twr10-mono" aria-labelledby="twr-10-title">
<h2 class="absolute size-px overflow-hidden [clip-path:inset(50%)] whitespace-nowrap" id="twr-10-title">Retro futuristic terminal console</h2>
<div class="absolute top-4 right-4 z-6">
<input class="peer absolute inset-0 size-11 m-0 opacity-0 cursor-pointer" type="checkbox" id="twr-10-theme">
<label class="grid place-items-center size-11 rounded border border-twr10-line bg-twr10-surface text-twr10-ink2 cursor-pointer peer-focus-visible:outline-2 peer-focus-visible:outline-twr10-acc peer-focus-visible:outline-offset-2" for="twr-10-theme">
<svg class="size-5" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><circle cx="12" cy="12" r="8" fill="none" stroke="currentColor" stroke-width="1.6"/><path d="M12 4a8 8 0 0 0 0 16Z" fill="currentColor"/></svg>
<span class="absolute size-px overflow-hidden [clip-path:inset(50%)] whitespace-nowrap">Toggle light surround</span>
</label>
</div>
<div class="grid grid-cols-[minmax(0,1fr)] gap-4 max-w-[76rem] mx-auto px-[clamp(0.6rem,0.4rem+1.5vw,2rem)] pt-[clamp(3.5rem,3rem+3vw,5rem)] pb-16">
<p class="m-0 justify-self-start min-w-0 rounded-[3px] border border-twr10-line px-2.5 py-1.5 text-[0.68rem] tracking-[0.14em] uppercase text-twr10-ink2">DEC VT-series · 80-column serial console</p>
<div class="min-w-0 rounded-[34px] bg-[#23282b] p-[clamp(0.7rem,0.5rem+1.2vw,1.3rem)] [box-shadow:inset_0_2px_0_rgba(255,255,255,0.18),inset_0_-3px_0_rgba(0,0,0,0.55),0_20px_44px_-22px_rgba(0,0,0,0.8)]">
<div class="relative isolate overflow-hidden rounded-[var(--twr-10-curve)] p-[clamp(0.9rem,0.6rem+1.6vw,1.8rem)] [background:radial-gradient(120%_100%_at_50%_42%,#082018_0%,var(--color-twr10-tube)_76%)] [box-shadow:inset_0_0_0_2px_#04100b,inset_0_0_32px_8px_rgba(0,0,0,0.8)]">
<div class="relative z-2 grid grid-cols-[minmax(0,1fr)] gap-5 md:grid-cols-[minmax(0,1fr)_minmax(0,15rem)]">
<div class="min-w-0 grid content-start gap-3.5">
<h3 class="m-0 min-w-0 font-bold tracking-[0.16em] text-twr10-fg text-[clamp(0.95rem,0.75rem+1.2vw,1.35rem)] [text-shadow:0_0_6px_rgba(125,250,176,0.55)]">ORION-7 SYSTEM CONSOLE</h3>
<ol class="list-none m-0 p-0 grid gap-1 min-w-0 text-twr10-fgdim text-[clamp(0.66rem,0.55rem+0.65vw,0.85rem)]">
<li class="min-w-0 [overflow-wrap:anywhere] opacity-0 animate-twr10-in [animation-delay:calc(0*140ms)] motion-reduce:animate-none motion-reduce:opacity-100" style="--twr-10-i: 0">POST . . . . . . . . . . . . . <b class="font-bold text-twr10-fg">OK</b></li>
<li class="min-w-0 [overflow-wrap:anywhere] opacity-0 animate-twr10-in [animation-delay:calc(1*140ms)] motion-reduce:animate-none motion-reduce:opacity-100" style="--twr-10-i: 1">CPU 0 33 MHz CACHE 8K <b class="font-bold text-twr10-fg">OK</b></li>
<li class="min-w-0 [overflow-wrap:anywhere] opacity-0 animate-twr10-in [animation-delay:calc(2*140ms)] motion-reduce:animate-none motion-reduce:opacity-100" style="--twr-10-i: 2">RAM 0000—FFFF 16384K <b class="font-bold text-twr10-fg">OK</b></li>
<li class="min-w-0 [overflow-wrap:anywhere] opacity-0 animate-twr10-in [animation-delay:calc(3*140ms)] motion-reduce:animate-none motion-reduce:opacity-100" style="--twr-10-i: 3">CONTROLLER SCSI ID 7 . . . . . <b class="font-bold text-twr10-fg">OK</b></li>
<li class="min-w-0 [overflow-wrap:anywhere] opacity-0 animate-twr10-in [animation-delay:calc(4*140ms)] motion-reduce:animate-none motion-reduce:opacity-100" style="--twr-10-i: 4">MOUNT /dev/rd0 READ ONLY <b class="font-bold text-twr10-fg">OK</b></li>
<li class="min-w-0 [overflow-wrap:anywhere] opacity-0 animate-twr10-in [animation-delay:calc(5*140ms)] motion-reduce:animate-none motion-reduce:opacity-100" style="--twr-10-i: 5">LINK ETH0 10BASE-T . . <b class="font-bold text-twr10-fg">UP</b></li>
<li class="min-w-0 [overflow-wrap:anywhere] opacity-0 animate-twr10-in [animation-delay:calc(6*140ms)] motion-reduce:animate-none motion-reduce:opacity-100" style="--twr-10-i: 6">CLOCK SYNC DRIFT 0.004s . <b class="font-bold text-twr10-fg">OK</b></li>
<li class="min-w-0 [overflow-wrap:anywhere] opacity-0 animate-twr10-in [animation-delay:calc(7*140ms)] motion-reduce:animate-none motion-reduce:opacity-100" style="--twr-10-i: 7">SENSOR 4 OUT OF RANGE . . <b class="font-bold text-twr10-warn">CHECK</b></li>
<li class="min-w-0 [overflow-wrap:anywhere] opacity-0 animate-twr10-in [animation-delay:calc(8*140ms)] motion-reduce:animate-none motion-reduce:opacity-100" style="--twr-10-i: 8">KERNEL 2.0.34 LOADED . . . <b class="font-bold text-twr10-fg">OK</b></li>
</ol>
<p class="m-0 flex flex-wrap items-center gap-x-[0.5ch] min-w-0 text-twr10-fg text-[clamp(0.7rem,0.58rem+0.7vw,0.9rem)]">
<span class="text-twr10-acc">orion:~$</span>
<span class="inline-block w-0 overflow-hidden whitespace-nowrap animate-twr10-type motion-reduce:animate-none motion-reduce:w-[22ch]">status --all --verbose</span>
<span class="inline-block w-[0.6em] h-[1.1em] bg-twr10-fg animate-twr10-blink motion-reduce:animate-none" aria-hidden="true"></span>
</p>
</div>
<aside class="min-w-0 grid content-start gap-2.5 rounded border border-[rgba(125,250,176,0.28)] bg-[rgba(125,250,176,0.04)] px-3.5 py-3" aria-label="System status">
<h4 class="m-0 text-[0.66rem] tracking-[0.2em] text-twr10-acc">STATUS</h4>
<dl class="m-0 grid gap-1 text-[0.72rem] text-twr10-fgdim">
<div class="flex justify-between gap-3 min-w-0"><dt class="m-0">LOAD</dt><dd class="m-0 tabular-nums text-twr10-fg">0.42</dd></div>
<div class="flex justify-between gap-3 min-w-0"><dt class="m-0">MEM</dt><dd class="m-0 tabular-nums text-twr10-fg">11208K</dd></div>
<div class="flex justify-between gap-3 min-w-0"><dt class="m-0">SWAP</dt><dd class="m-0 tabular-nums text-twr10-fg">0K</dd></div>
<div class="flex justify-between gap-3 min-w-0"><dt class="m-0">PROC</dt><dd class="m-0 tabular-nums text-twr10-fg">37</dd></div>
<div class="flex justify-between gap-3 min-w-0"><dt class="m-0">UPTIME</dt><dd class="m-0 tabular-nums text-twr10-fg">04:11</dd></div>
<div class="flex justify-between gap-3 min-w-0"><dt class="m-0">TEMP</dt><dd class="m-0 tabular-nums text-twr10-fg">41C</dd></div>
</dl>
<div class="flex items-center gap-2 min-w-0 text-[0.7rem] text-twr10-fgdim" role="img" aria-label="Bus utilisation, 62 percent">
<span>BUS</span>
<span class="flex-1 min-w-0 h-2.5 p-px border border-[rgba(125,250,176,0.4)]"><span class="block h-full w-[62%] [background:repeating-linear-gradient(90deg,var(--color-twr10-fg)_0_4px,rgba(0,0,0,0)_4px_6px)]"></span></span>
<span class="tabular-nums text-twr10-fg">62%</span>
</div>
</aside>
</div>
<div class="twr-10__scan absolute inset-0 z-4 pointer-events-none opacity-50 [background:repeating-linear-gradient(to_bottom,rgba(0,0,0,0)_0,rgba(0,0,0,0)_calc(var(--twr-10-scan)-1px),rgba(0,0,0,0.7)_calc(var(--twr-10-scan)-1px),rgba(0,0,0,0.7)_var(--twr-10-scan))]" aria-hidden="true"></div>
<div class="absolute -inset-px z-5 pointer-events-none [background:radial-gradient(118%_106%_at_50%_50%,rgba(0,0,0,0)_54%,rgba(0,0,0,0.5)_86%,rgba(0,0,0,0.88)_100%)]" aria-hidden="true"></div>
</div>
<div class="flex items-center justify-between gap-3 min-w-0 pt-2.5 px-1.5 pb-0.5 text-[0.62rem] tracking-[0.2em] text-[#8d9498]">
<span>VT-320 / SERIAL A</span>
<span class="flex-none size-[9px] rounded-full bg-[#63f0a0] [box-shadow:0_0_8px_2px_rgba(99,240,160,0.65)]" aria-hidden="true"></span>
</div>
</div>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-twr10-fg: #7dfab0;
--color-twr10-fg: oklch(0.90 0.14 158);
--color-twr10-fgdim: #3f9f6d;
--color-twr10-fgdim: oklch(0.63 0.11 158);
--color-twr10-warn: #ffd166;
--color-twr10-warn: oklch(0.87 0.13 85);
--color-twr10-tube: #04120c;
--color-twr10-bg: #0c1013;
--color-twr10-bg: oklch(0.16 0.008 210);
--color-twr10-surface: #171c20;
--color-twr10-surface: oklch(0.23 0.009 210);
--color-twr10-ink: #eaeff1;
--color-twr10-ink: oklch(0.95 0.004 210);
--color-twr10-ink2: #9ba5aa;
--color-twr10-ink2: oklch(0.71 0.009 210);
--color-twr10-line: #333c41;
--color-twr10-line: oklch(0.33 0.01 210);
--color-twr10-acc: #6ee7b7;
--color-twr10-acc: oklch(0.85 0.12 165);
--font-twr10-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, 'Cascadia Mono', Menlo, monospace;
--animate-twr10-in: twr-10-in 320ms ease-out forwards;
--animate-twr10-type: twr-10-type 1.6s steps(22, end) 1.4s forwards;
--animate-twr10-blink: twr-10-blink 1.1s steps(1, end) infinite;
}
.twr-10 {
--twr-10-scan: 3px;
--twr-10-curve: 20px;
}
@keyframes twr-10-in {
from {
opacity: 0;
transform: translateY(2px);
}
to {
opacity: 1;
transform: none;
}
}
/* The 22 here must equal the steps() count in --animate-twr10-type and the
motion-reduce:w-[22ch] utility in the markup. */
@keyframes twr-10-type {
to {
width: 22ch;
}
}
@keyframes twr-10-blink {
0%,
49% {
opacity: 1;
}
50%,
100% {
opacity: 0;
}
}
/* No variant form, and unlayered so it outranks the arbitrary background. */
@media (min-resolution: 1.5dppx) {
.twr-10__scan {
background: repeating-linear-gradient(
to bottom,
rgba(0, 0, 0, 0) 0,
rgba(0, 0, 0, 0.6) calc(var(--twr-10-scan) * 0.5),
rgba(0, 0, 0, 0) calc(var(--twr-10-scan) * 1.34)
);
}
}
.twr-10:has(#twr-10-theme:checked) {
--color-twr10-bg: #d8dcde;
--color-twr10-bg: oklch(0.88 0.005 210);
--color-twr10-surface: #eef1f2;
--color-twr10-surface: oklch(0.95 0.003 210);
--color-twr10-ink: #14181a;
--color-twr10-ink: oklch(0.19 0.006 210);
--color-twr10-ink2: #4a5257;
--color-twr10-ink2: oklch(0.43 0.01 210);
--color-twr10-line: #a6aeb2;
--color-twr10-line: oklch(0.74 0.008 210);
--color-twr10-acc: #0c6b4d;
--color-twr10-acc: oklch(0.46 0.11 165);
}
```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: Tailwind Retro Futuristic Terminal Interface
Source: https://codefronts.com/design-styles/tailwind-retro-ui/tailwind-retro-futuristic-terminal-interface/
A command console that boots: staggered POST lines, a typed command and a live status column, all under the CRT treatment from demo 01. The boot reveal is pure CSS with the text in the DOM from the start — and in utilities the stagger becomes something you can read off each line, because every line carries its own [animation-delay:...].
## HTML
```html
<section class="twr-10 relative block w-full min-h-svh box-border bg-twr10-bg text-twr10-ink font-twr10-mono" aria-labelledby="twr-10-title">
<h2 class="absolute size-px overflow-hidden [clip-path:inset(50%)] whitespace-nowrap" id="twr-10-title">Retro futuristic terminal console</h2>
<div class="absolute top-4 right-4 z-6">
<input class="peer absolute inset-0 size-11 m-0 opacity-0 cursor-pointer" type="checkbox" id="twr-10-theme">
<label class="grid place-items-center size-11 rounded border border-twr10-line bg-twr10-surface text-twr10-ink2 cursor-pointer peer-focus-visible:outline-2 peer-focus-visible:outline-twr10-acc peer-focus-visible:outline-offset-2" for="twr-10-theme">
<svg class="size-5" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><circle cx="12" cy="12" r="8" fill="none" stroke="currentColor" stroke-width="1.6"/><path d="M12 4a8 8 0 0 0 0 16Z" fill="currentColor"/></svg>
<span class="absolute size-px overflow-hidden [clip-path:inset(50%)] whitespace-nowrap">Toggle light surround</span>
</label>
</div>
<div class="grid grid-cols-[minmax(0,1fr)] gap-4 max-w-[76rem] mx-auto px-[clamp(0.6rem,0.4rem+1.5vw,2rem)] pt-[clamp(3.5rem,3rem+3vw,5rem)] pb-16">
<p class="m-0 justify-self-start min-w-0 rounded-[3px] border border-twr10-line px-2.5 py-1.5 text-[0.68rem] tracking-[0.14em] uppercase text-twr10-ink2">DEC VT-series · 80-column serial console</p>
<div class="min-w-0 rounded-[34px] bg-[#23282b] p-[clamp(0.7rem,0.5rem+1.2vw,1.3rem)] [box-shadow:inset_0_2px_0_rgba(255,255,255,0.18),inset_0_-3px_0_rgba(0,0,0,0.55),0_20px_44px_-22px_rgba(0,0,0,0.8)]">
<div class="relative isolate overflow-hidden rounded-[var(--twr-10-curve)] p-[clamp(0.9rem,0.6rem+1.6vw,1.8rem)] [background:radial-gradient(120%_100%_at_50%_42%,#082018_0%,var(--color-twr10-tube)_76%)] [box-shadow:inset_0_0_0_2px_#04100b,inset_0_0_32px_8px_rgba(0,0,0,0.8)]">
<div class="relative z-2 grid grid-cols-[minmax(0,1fr)] gap-5 md:grid-cols-[minmax(0,1fr)_minmax(0,15rem)]">
<div class="min-w-0 grid content-start gap-3.5">
<h3 class="m-0 min-w-0 font-bold tracking-[0.16em] text-twr10-fg text-[clamp(0.95rem,0.75rem+1.2vw,1.35rem)] [text-shadow:0_0_6px_rgba(125,250,176,0.55)]">ORION-7 SYSTEM CONSOLE</h3>
<ol class="list-none m-0 p-0 grid gap-1 min-w-0 text-twr10-fgdim text-[clamp(0.66rem,0.55rem+0.65vw,0.85rem)]">
<li class="min-w-0 [overflow-wrap:anywhere] opacity-0 animate-twr10-in [animation-delay:calc(0*140ms)] motion-reduce:animate-none motion-reduce:opacity-100" style="--twr-10-i: 0">POST . . . . . . . . . . . . . <b class="font-bold text-twr10-fg">OK</b></li>
<li class="min-w-0 [overflow-wrap:anywhere] opacity-0 animate-twr10-in [animation-delay:calc(1*140ms)] motion-reduce:animate-none motion-reduce:opacity-100" style="--twr-10-i: 1">CPU 0 33 MHz CACHE 8K <b class="font-bold text-twr10-fg">OK</b></li>
<li class="min-w-0 [overflow-wrap:anywhere] opacity-0 animate-twr10-in [animation-delay:calc(2*140ms)] motion-reduce:animate-none motion-reduce:opacity-100" style="--twr-10-i: 2">RAM 0000—FFFF 16384K <b class="font-bold text-twr10-fg">OK</b></li>
<li class="min-w-0 [overflow-wrap:anywhere] opacity-0 animate-twr10-in [animation-delay:calc(3*140ms)] motion-reduce:animate-none motion-reduce:opacity-100" style="--twr-10-i: 3">CONTROLLER SCSI ID 7 . . . . . <b class="font-bold text-twr10-fg">OK</b></li>
<li class="min-w-0 [overflow-wrap:anywhere] opacity-0 animate-twr10-in [animation-delay:calc(4*140ms)] motion-reduce:animate-none motion-reduce:opacity-100" style="--twr-10-i: 4">MOUNT /dev/rd0 READ ONLY <b class="font-bold text-twr10-fg">OK</b></li>
<li class="min-w-0 [overflow-wrap:anywhere] opacity-0 animate-twr10-in [animation-delay:calc(5*140ms)] motion-reduce:animate-none motion-reduce:opacity-100" style="--twr-10-i: 5">LINK ETH0 10BASE-T . . <b class="font-bold text-twr10-fg">UP</b></li>
<li class="min-w-0 [overflow-wrap:anywhere] opacity-0 animate-twr10-in [animation-delay:calc(6*140ms)] motion-reduce:animate-none motion-reduce:opacity-100" style="--twr-10-i: 6">CLOCK SYNC DRIFT 0.004s . <b class="font-bold text-twr10-fg">OK</b></li>
<li class="min-w-0 [overflow-wrap:anywhere] opacity-0 animate-twr10-in [animation-delay:calc(7*140ms)] motion-reduce:animate-none motion-reduce:opacity-100" style="--twr-10-i: 7">SENSOR 4 OUT OF RANGE . . <b class="font-bold text-twr10-warn">CHECK</b></li>
<li class="min-w-0 [overflow-wrap:anywhere] opacity-0 animate-twr10-in [animation-delay:calc(8*140ms)] motion-reduce:animate-none motion-reduce:opacity-100" style="--twr-10-i: 8">KERNEL 2.0.34 LOADED . . . <b class="font-bold text-twr10-fg">OK</b></li>
</ol>
<p class="m-0 flex flex-wrap items-center gap-x-[0.5ch] min-w-0 text-twr10-fg text-[clamp(0.7rem,0.58rem+0.7vw,0.9rem)]">
<span class="text-twr10-acc">orion:~$</span>
<span class="inline-block w-0 overflow-hidden whitespace-nowrap animate-twr10-type motion-reduce:animate-none motion-reduce:w-[22ch]">status --all --verbose</span>
<span class="inline-block w-[0.6em] h-[1.1em] bg-twr10-fg animate-twr10-blink motion-reduce:animate-none" aria-hidden="true"></span>
</p>
</div>
<aside class="min-w-0 grid content-start gap-2.5 rounded border border-[rgba(125,250,176,0.28)] bg-[rgba(125,250,176,0.04)] px-3.5 py-3" aria-label="System status">
<h4 class="m-0 text-[0.66rem] tracking-[0.2em] text-twr10-acc">STATUS</h4>
<dl class="m-0 grid gap-1 text-[0.72rem] text-twr10-fgdim">
<div class="flex justify-between gap-3 min-w-0"><dt class="m-0">LOAD</dt><dd class="m-0 tabular-nums text-twr10-fg">0.42</dd></div>
<div class="flex justify-between gap-3 min-w-0"><dt class="m-0">MEM</dt><dd class="m-0 tabular-nums text-twr10-fg">11208K</dd></div>
<div class="flex justify-between gap-3 min-w-0"><dt class="m-0">SWAP</dt><dd class="m-0 tabular-nums text-twr10-fg">0K</dd></div>
<div class="flex justify-between gap-3 min-w-0"><dt class="m-0">PROC</dt><dd class="m-0 tabular-nums text-twr10-fg">37</dd></div>
<div class="flex justify-between gap-3 min-w-0"><dt class="m-0">UPTIME</dt><dd class="m-0 tabular-nums text-twr10-fg">04:11</dd></div>
<div class="flex justify-between gap-3 min-w-0"><dt class="m-0">TEMP</dt><dd class="m-0 tabular-nums text-twr10-fg">41C</dd></div>
</dl>
<div class="flex items-center gap-2 min-w-0 text-[0.7rem] text-twr10-fgdim" role="img" aria-label="Bus utilisation, 62 percent">
<span>BUS</span>
<span class="flex-1 min-w-0 h-2.5 p-px border border-[rgba(125,250,176,0.4)]"><span class="block h-full w-[62%] [background:repeating-linear-gradient(90deg,var(--color-twr10-fg)_0_4px,rgba(0,0,0,0)_4px_6px)]"></span></span>
<span class="tabular-nums text-twr10-fg">62%</span>
</div>
</aside>
</div>
<div class="twr-10__scan absolute inset-0 z-4 pointer-events-none opacity-50 [background:repeating-linear-gradient(to_bottom,rgba(0,0,0,0)_0,rgba(0,0,0,0)_calc(var(--twr-10-scan)-1px),rgba(0,0,0,0.7)_calc(var(--twr-10-scan)-1px),rgba(0,0,0,0.7)_var(--twr-10-scan))]" aria-hidden="true"></div>
<div class="absolute -inset-px z-5 pointer-events-none [background:radial-gradient(118%_106%_at_50%_50%,rgba(0,0,0,0)_54%,rgba(0,0,0,0.5)_86%,rgba(0,0,0,0.88)_100%)]" aria-hidden="true"></div>
</div>
<div class="flex items-center justify-between gap-3 min-w-0 pt-2.5 px-1.5 pb-0.5 text-[0.62rem] tracking-[0.2em] text-[#8d9498]">
<span>VT-320 / SERIAL A</span>
<span class="flex-none size-[9px] rounded-full bg-[#63f0a0] [box-shadow:0_0_8px_2px_rgba(99,240,160,0.65)]" aria-hidden="true"></span>
</div>
</div>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-twr10-fg: #7dfab0;
--color-twr10-fg: oklch(0.90 0.14 158);
--color-twr10-fgdim: #3f9f6d;
--color-twr10-fgdim: oklch(0.63 0.11 158);
--color-twr10-warn: #ffd166;
--color-twr10-warn: oklch(0.87 0.13 85);
--color-twr10-tube: #04120c;
--color-twr10-bg: #0c1013;
--color-twr10-bg: oklch(0.16 0.008 210);
--color-twr10-surface: #171c20;
--color-twr10-surface: oklch(0.23 0.009 210);
--color-twr10-ink: #eaeff1;
--color-twr10-ink: oklch(0.95 0.004 210);
--color-twr10-ink2: #9ba5aa;
--color-twr10-ink2: oklch(0.71 0.009 210);
--color-twr10-line: #333c41;
--color-twr10-line: oklch(0.33 0.01 210);
--color-twr10-acc: #6ee7b7;
--color-twr10-acc: oklch(0.85 0.12 165);
--font-twr10-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, 'Cascadia Mono', Menlo, monospace;
--animate-twr10-in: twr-10-in 320ms ease-out forwards;
--animate-twr10-type: twr-10-type 1.6s steps(22, end) 1.4s forwards;
--animate-twr10-blink: twr-10-blink 1.1s steps(1, end) infinite;
}
.twr-10 {
--twr-10-scan: 3px;
--twr-10-curve: 20px;
}
@keyframes twr-10-in {
from {
opacity: 0;
transform: translateY(2px);
}
to {
opacity: 1;
transform: none;
}
}
/* The 22 here must equal the steps() count in --animate-twr10-type and the
motion-reduce:w-[22ch] utility in the markup. */
@keyframes twr-10-type {
to {
width: 22ch;
}
}
@keyframes twr-10-blink {
0%,
49% {
opacity: 1;
}
50%,
100% {
opacity: 0;
}
}
/* No variant form, and unlayered so it outranks the arbitrary background. */
@media (min-resolution: 1.5dppx) {
.twr-10__scan {
background: repeating-linear-gradient(
to bottom,
rgba(0, 0, 0, 0) 0,
rgba(0, 0, 0, 0.6) calc(var(--twr-10-scan) * 0.5),
rgba(0, 0, 0, 0) calc(var(--twr-10-scan) * 1.34)
);
}
}
.twr-10:has(#twr-10-theme:checked) {
--color-twr10-bg: #d8dcde;
--color-twr10-bg: oklch(0.88 0.005 210);
--color-twr10-surface: #eef1f2;
--color-twr10-surface: oklch(0.95 0.003 210);
--color-twr10-ink: #14181a;
--color-twr10-ink: oklch(0.19 0.006 210);
--color-twr10-ink2: #4a5257;
--color-twr10-ink2: oklch(0.43 0.01 210);
--color-twr10-line: #a6aeb2;
--color-twr10-line: oklch(0.74 0.008 210);
--color-twr10-acc: #0c6b4d;
--color-twr10-acc: oklch(0.46 0.11 165);
}
```How this works
A boot sequence is a stagger, not a script. The CSS version reads a per-line index custom property inside animation-delay: calc(var(--rt-10-i) * 140ms). Utilities let each line state its own delay directly:
<li class="opacity-0 animate-twr10-in [animation-delay:calc(3*140ms)]
motion-reduce:animate-none motion-reduce:opacity-100">Both approaches are one edit per line; the difference is that here the timing and the reduced-motion fallback sit together on the element, so nothing about a line's behaviour is off-screen. The index property is kept as well, so a template can still drive it.
Typing must land on character boundaries. Animating width smoothly reveals half a glyph, which no terminal ever did. steps(22, end) over a 22ch width steps exactly one character per tick — and the two 22s must match. Keeping them in one @theme keyframe plus one motion-reduce:w-[22ch] utility at least puts them within a line of each other.
The screen-reader trap, stated plainly. The obvious build appends lines over time inside an aria-live region — and then a screen-reader user is read forty lines of POST output they cannot skip or scroll, before the page's actual content. The fix is structural: every line is in the DOM at load, the animation only changes opacity, and the block is a plain region with a heading. A screen reader gets the finished console immediately; a sighted user gets the cascade.
Reduced motion means finished, not slower. Every animated element carries an explicit motion-reduce: end state — opacity 1, width 22ch, no animation. Slowing a boot sequence is worse than removing it: the user still gets the motion, just for longer.
Make it yours
- Retime the cascade by editing the multiplier in each line's
[animation-delay:calc(n*140ms)], or drive it fromvar(--twr-10-i)if a template generates the rows. - Recolour the phosphor through
--color-twr10-fg; amber and blue-white are both period-correct alternatives to green. - Adjust the scanline period with
--twr-10-scan, or drop the overlay element to strip the CRT layer entirely. - Extend the typed command by editing the keyframe width, the
steps()count and themotion-reduce:w-[...]together — all three must agree. - Add a status row by copying one
<div>; the rail is a plain grid and the tabular figures keep the column aligned. - Swap
rounded-[20px]forrounded-noneon the screen for a flat-panel console that keeps the scanlines but loses the tube geometry.
Gotchas — read before shipping
- Appending boot lines into an
aria-liveregion reads the whole sequence aloud with no way to skip it. Put the text in the DOM and animate opacity. - Animating width without
steps()reveals fractions of a glyph — a smear rather than typing. The step count must equal the character count. motion-reduce:animate-nonealone leaves the element at its start state (opacity 0, width 0). Always pair it with the end-state utilities, or reduced-motion users see nothing at all.- The blinking cursor is exactly what SC 2.2.2 addresses: stop it under reduced motion and keep it under three flashes per second.
- Green-on-black at authentic brightness can fall under 4.5:1 once the scanline overlay multiplies it down. Measure the composited result, not the token.
- The
@media (min-resolution: 1.5dppx)scanline fix has no variant form and must stay in CSS, where its unlayered rule outranks the arbitrary background utility.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
Everything the console draws — repeating gradients, steps() timing, arbitrary animation delays — has been Baseline since 2017. Tailwind v4's generated sheet sets the floor at Chrome 111+, Safari 16.4+, Firefox 128+.