06 / 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 MS DOS Text Mode Interface Grid
A character-cell interface reproduced as one: an 80×25 field where every cell is exactly one monospace advance, framed with code page 437 box-drawing characters and restricted to the 16-colour CGA palette as theme tokens. The constraint is the subject — nothing can sit between two cells — and Tailwind's container-query utilities turn out to be the cleanest way yet to make 80 columns fit their frame exactly.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="twr-06 relative block w-full min-h-svh box-border bg-twr06-bg text-twr06-ink font-twr06-mono" aria-labelledby="twr-06-title">
<h2 class="absolute size-px overflow-hidden [clip-path:inset(50%)] whitespace-nowrap" id="twr-06-title">MS-DOS text mode character grid</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-06-theme">
<label class="grid place-items-center size-11 border border-twr06-line bg-twr06-surface text-twr06-ink2 cursor-pointer peer-focus-visible:outline-2 peer-focus-visible:outline-twr06-acc peer-focus-visible:outline-offset-2" for="twr-06-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 dark surround</span>
</label>
</div>
<div class="grid grid-cols-[minmax(0,1fr)] gap-4.5 max-w-[72rem] mx-auto px-[clamp(0.6rem,0.4rem+1.5vw,2rem)] pt-[clamp(3.5rem,3rem+3vw,5rem)] pb-16 lg:grid-cols-2">
<p class="m-0 justify-self-start min-w-0 lg:col-span-2 border border-twr06-line bg-twr06-surface px-2.5 py-1.5 text-[0.68rem] tracking-[0.12em] uppercase text-twr06-ink2">IBM CGA · 16 colours · 80 × 25 text mode · code page 437</p>
<div class="min-w-0 lg:col-span-2 rounded-md bg-[#6d6f70] p-[clamp(0.5rem,0.3rem+1vw,1rem)] [box-shadow:inset_0_1px_0_rgba(255,255,255,0.35),inset_0_-2px_0_rgba(0,0,0,0.4),0_14px_30px_-16px_rgba(0,0,0,0.6)]">
<div class="@container min-w-0 overflow-hidden grid grid-cols-[minmax(0,1fr)] content-start border-2 border-[#1b1d1f] bg-twr06-blue text-twr06-ltgrey">
<div class="flex items-center min-h-[1.6em] px-[0.5ch] min-w-0 overflow-hidden whitespace-nowrap bg-twr06-ltgrey text-twr06-k text-[clamp(0.66rem,0.55rem+0.6vw,0.85rem)]" role="presentation">
<span class="font-bold text-twr06-red">F</span><span>ile</span>
<span class="flex-none w-[2ch]"></span>
<span class="font-bold text-twr06-red">E</span><span>dit</span>
<span class="flex-none w-[2ch]"></span>
<span class="font-bold text-twr06-red">S</span><span>earch</span>
<span class="flex-none w-[2ch]"></span>
<span class="font-bold text-twr06-red">O</span><span>ptions</span>
<span class="ms-auto ps-[1ch] text-[#303030]">MEM 640K</span>
</div>
<pre class="twr-06__art hidden md:block m-0 py-2 min-w-0 overflow-hidden font-inherit whitespace-pre text-center leading-[1.16] text-twr06-ltcyan text-[min(0.95rem,2.02cqi)]" aria-hidden="true">╔══════════════════════════════════════════════════════════════════════════════╗
║ C:\ ─ Directory of drive C ─ 6 file(s) ─ 90,671 bytes ║
╠══════════════════════════════════════════════════════════════════════════════╣
║ Name Size Date Attr Type ║
║ ──────────────────────────────────────────────────────────────────── ║
║ AUTOEXEC.BAT 1,024 04-12-1995 A Batch file ║
║ COMMAND.COM 54,619 05-31-1994 RA Command interpreter ║
║ CONFIG.SYS 412 04-12-1995 A System configuration ║
║ HIMEM.SYS 29,136 05-31-1994 RA Extended memory manager ║
║ README.TXT 7,880 11-03-1995 A Text ║
║ ║
║ Every glyph above occupies exactly one cell. Nothing sits between two cells.║
║ ║
║ ║
║ ║
║ ║
║ ║
║ ║
║ ║
║ ║
╚══════════════════════════════════════════════════════════════════════════════╝</pre>
<pre class="twr-06__art block md:hidden m-0 py-2 min-w-0 overflow-hidden font-inherit whitespace-pre text-center leading-[1.16] text-twr06-ltcyan text-[min(0.95rem,4.1cqi)]" aria-hidden="true">╔══════════════════════════════════════╗
║ C:\ ─ 6 file(s), 90,671 bytes ║
╠══════════════════════════════════════╣
║ Name Size Attr ║
║ ─────────────────────────────────────║
║ AUTOEXEC.BAT 1,024 A ║
║ COMMAND.COM 54,619 RA ║
║ CONFIG.SYS 412 A ║
║ HIMEM.SYS 29,136 RA ║
║ README.TXT 7,880 A ║
║ ║
║ One glyph = one cell. No between. ║
║ ║
║ ║
╚══════════════════════════════════════╝</pre>
<p class="absolute size-px overflow-hidden [clip-path:inset(50%)] whitespace-nowrap">A DOS-style file manager panel listing AUTOEXEC.BAT, CONFIG.SYS, COMMAND.COM and HIMEM.SYS with sizes and dates, framed in double-line box characters.</p>
<div class="flex flex-wrap items-center gap-x-[2ch] min-h-[1.6em] px-[0.5ch] min-w-0 bg-twr06-ltgrey text-twr06-k text-[clamp(0.66rem,0.55rem+0.6vw,0.85rem)]">
<span><span class="font-bold text-twr06-red">F1</span> Help</span>
<span><span class="font-bold text-twr06-red">F3</span> Open</span>
<span><span class="font-bold text-twr06-red">F10</span> Menu</span>
<span class="ms-auto text-[#303030]">80 × 25</span>
</div>
</div>
</div>
<aside class="min-w-0 grid content-start gap-2.5 border border-twr06-line bg-twr06-surface p-[clamp(0.8rem,0.6rem+0.8vw,1.1rem)]">
<h3 class="m-0 text-[0.72rem] tracking-[0.1em] uppercase text-twr06-ink2">Cell inspector — the grid the text lands on</h3>
<div class="grid grid-cols-12 gap-px min-w-0 border border-twr06-line bg-twr06-line text-[clamp(0.8rem,0.6rem+1.4vw,1.35rem)]" aria-hidden="true">
<span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">╔</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">═</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">═</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">╦</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">═</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">═</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">╗</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan text-twr06-yellow">█</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan text-twr06-yellow">▓</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan text-twr06-yellow">▒</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan text-twr06-yellow">░</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan"> </span>
<span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">║</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">A</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">B</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">║</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">C</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">D</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">║</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">─</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">┼</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">─</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">┤</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan"> </span>
<span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">╚</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">═</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">═</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">╩</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">═</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">═</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">╝</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">└</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">┴</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">┘</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan"> </span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan"> </span>
</div>
<p class="m-0 min-w-0 text-[0.8rem] leading-relaxed text-pretty text-twr06-ink2">Each box is <code class="bg-twr06-bg px-1">1ch</code> wide and one line-height tall. The frame is characters, not borders — a border would have to live between two cells, and there is no between.</p>
</aside>
<aside class="min-w-0 grid content-start gap-2.5 border border-twr06-line bg-twr06-surface p-[clamp(0.8rem,0.6rem+0.8vw,1.1rem)]">
<h3 class="m-0 text-[0.72rem] tracking-[0.1em] uppercase text-twr06-ink2">CGA palette — 16 defined values</h3>
<ul class="list-none m-0 p-0 min-w-0 grid gap-0.5 grid-cols-[repeat(auto-fit,minmax(min(100%,7.5rem),1fr))]">
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#000000]">0 black</li>
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#0000aa]">1 blue</li>
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#00aa00]">2 green</li>
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#00aaaa]">3 cyan</li>
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#aa0000]">4 red</li>
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#aa00aa]">5 magenta</li>
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#aa5500]">6 brown</li>
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#aaaaaa]">7 lt grey</li>
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#555555]">8 dk grey</li>
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#5555ff]">9 lt blue</li>
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#55ff55]">10 lt green</li>
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#55ffff]">11 lt cyan</li>
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#ff5555]">12 lt red</li>
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#ff55ff]">13 lt magenta</li>
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#ffff55]">14 yellow</li>
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#ffffff]">15 white</li>
</ul>
</aside>
</div>
</section><section class="twr-06 relative block w-full min-h-svh box-border bg-twr06-bg text-twr06-ink font-twr06-mono" aria-labelledby="twr-06-title">
<h2 class="absolute size-px overflow-hidden [clip-path:inset(50%)] whitespace-nowrap" id="twr-06-title">MS-DOS text mode character grid</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-06-theme">
<label class="grid place-items-center size-11 border border-twr06-line bg-twr06-surface text-twr06-ink2 cursor-pointer peer-focus-visible:outline-2 peer-focus-visible:outline-twr06-acc peer-focus-visible:outline-offset-2" for="twr-06-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 dark surround</span>
</label>
</div>
<div class="grid grid-cols-[minmax(0,1fr)] gap-4.5 max-w-[72rem] mx-auto px-[clamp(0.6rem,0.4rem+1.5vw,2rem)] pt-[clamp(3.5rem,3rem+3vw,5rem)] pb-16 lg:grid-cols-2">
<p class="m-0 justify-self-start min-w-0 lg:col-span-2 border border-twr06-line bg-twr06-surface px-2.5 py-1.5 text-[0.68rem] tracking-[0.12em] uppercase text-twr06-ink2">IBM CGA · 16 colours · 80 × 25 text mode · code page 437</p>
<div class="min-w-0 lg:col-span-2 rounded-md bg-[#6d6f70] p-[clamp(0.5rem,0.3rem+1vw,1rem)] [box-shadow:inset_0_1px_0_rgba(255,255,255,0.35),inset_0_-2px_0_rgba(0,0,0,0.4),0_14px_30px_-16px_rgba(0,0,0,0.6)]">
<div class="@container min-w-0 overflow-hidden grid grid-cols-[minmax(0,1fr)] content-start border-2 border-[#1b1d1f] bg-twr06-blue text-twr06-ltgrey">
<div class="flex items-center min-h-[1.6em] px-[0.5ch] min-w-0 overflow-hidden whitespace-nowrap bg-twr06-ltgrey text-twr06-k text-[clamp(0.66rem,0.55rem+0.6vw,0.85rem)]" role="presentation">
<span class="font-bold text-twr06-red">F</span><span>ile</span>
<span class="flex-none w-[2ch]"></span>
<span class="font-bold text-twr06-red">E</span><span>dit</span>
<span class="flex-none w-[2ch]"></span>
<span class="font-bold text-twr06-red">S</span><span>earch</span>
<span class="flex-none w-[2ch]"></span>
<span class="font-bold text-twr06-red">O</span><span>ptions</span>
<span class="ms-auto ps-[1ch] text-[#303030]">MEM 640K</span>
</div>
<pre class="twr-06__art hidden md:block m-0 py-2 min-w-0 overflow-hidden font-inherit whitespace-pre text-center leading-[1.16] text-twr06-ltcyan text-[min(0.95rem,2.02cqi)]" aria-hidden="true">╔══════════════════════════════════════════════════════════════════════════════╗
║ C:\ ─ Directory of drive C ─ 6 file(s) ─ 90,671 bytes ║
╠══════════════════════════════════════════════════════════════════════════════╣
║ Name Size Date Attr Type ║
║ ──────────────────────────────────────────────────────────────────── ║
║ AUTOEXEC.BAT 1,024 04-12-1995 A Batch file ║
║ COMMAND.COM 54,619 05-31-1994 RA Command interpreter ║
║ CONFIG.SYS 412 04-12-1995 A System configuration ║
║ HIMEM.SYS 29,136 05-31-1994 RA Extended memory manager ║
║ README.TXT 7,880 11-03-1995 A Text ║
║ ║
║ Every glyph above occupies exactly one cell. Nothing sits between two cells.║
║ ║
║ ║
║ ║
║ ║
║ ║
║ ║
║ ║
║ ║
╚══════════════════════════════════════════════════════════════════════════════╝</pre>
<pre class="twr-06__art block md:hidden m-0 py-2 min-w-0 overflow-hidden font-inherit whitespace-pre text-center leading-[1.16] text-twr06-ltcyan text-[min(0.95rem,4.1cqi)]" aria-hidden="true">╔══════════════════════════════════════╗
║ C:\ ─ 6 file(s), 90,671 bytes ║
╠══════════════════════════════════════╣
║ Name Size Attr ║
║ ─────────────────────────────────────║
║ AUTOEXEC.BAT 1,024 A ║
║ COMMAND.COM 54,619 RA ║
║ CONFIG.SYS 412 A ║
║ HIMEM.SYS 29,136 RA ║
║ README.TXT 7,880 A ║
║ ║
║ One glyph = one cell. No between. ║
║ ║
║ ║
╚══════════════════════════════════════╝</pre>
<p class="absolute size-px overflow-hidden [clip-path:inset(50%)] whitespace-nowrap">A DOS-style file manager panel listing AUTOEXEC.BAT, CONFIG.SYS, COMMAND.COM and HIMEM.SYS with sizes and dates, framed in double-line box characters.</p>
<div class="flex flex-wrap items-center gap-x-[2ch] min-h-[1.6em] px-[0.5ch] min-w-0 bg-twr06-ltgrey text-twr06-k text-[clamp(0.66rem,0.55rem+0.6vw,0.85rem)]">
<span><span class="font-bold text-twr06-red">F1</span> Help</span>
<span><span class="font-bold text-twr06-red">F3</span> Open</span>
<span><span class="font-bold text-twr06-red">F10</span> Menu</span>
<span class="ms-auto text-[#303030]">80 × 25</span>
</div>
</div>
</div>
<aside class="min-w-0 grid content-start gap-2.5 border border-twr06-line bg-twr06-surface p-[clamp(0.8rem,0.6rem+0.8vw,1.1rem)]">
<h3 class="m-0 text-[0.72rem] tracking-[0.1em] uppercase text-twr06-ink2">Cell inspector — the grid the text lands on</h3>
<div class="grid grid-cols-12 gap-px min-w-0 border border-twr06-line bg-twr06-line text-[clamp(0.8rem,0.6rem+1.4vw,1.35rem)]" aria-hidden="true">
<span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">╔</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">═</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">═</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">╦</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">═</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">═</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">╗</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan text-twr06-yellow">█</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan text-twr06-yellow">▓</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan text-twr06-yellow">▒</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan text-twr06-yellow">░</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan"> </span>
<span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">║</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">A</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">B</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">║</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">C</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">D</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">║</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">─</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">┼</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">─</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">┤</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan"> </span>
<span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">╚</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">═</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">═</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">╩</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">═</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">═</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">╝</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">└</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">┴</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">┘</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan"> </span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan"> </span>
</div>
<p class="m-0 min-w-0 text-[0.8rem] leading-relaxed text-pretty text-twr06-ink2">Each box is <code class="bg-twr06-bg px-1">1ch</code> wide and one line-height tall. The frame is characters, not borders — a border would have to live between two cells, and there is no between.</p>
</aside>
<aside class="min-w-0 grid content-start gap-2.5 border border-twr06-line bg-twr06-surface p-[clamp(0.8rem,0.6rem+0.8vw,1.1rem)]">
<h3 class="m-0 text-[0.72rem] tracking-[0.1em] uppercase text-twr06-ink2">CGA palette — 16 defined values</h3>
<ul class="list-none m-0 p-0 min-w-0 grid gap-0.5 grid-cols-[repeat(auto-fit,minmax(min(100%,7.5rem),1fr))]">
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#000000]">0 black</li>
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#0000aa]">1 blue</li>
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#00aa00]">2 green</li>
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#00aaaa]">3 cyan</li>
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#aa0000]">4 red</li>
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#aa00aa]">5 magenta</li>
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#aa5500]">6 brown</li>
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#aaaaaa]">7 lt grey</li>
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#555555]">8 dk grey</li>
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#5555ff]">9 lt blue</li>
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#55ff55]">10 lt green</li>
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#55ffff]">11 lt cyan</li>
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#ff5555]">12 lt red</li>
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#ff55ff]">13 lt magenta</li>
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#ffff55]">14 yellow</li>
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#ffffff]">15 white</li>
</ul>
</aside>
</div>
</section>@import "tailwindcss";
@theme {
--color-twr06-k: #000000;
--color-twr06-blue: #0000aa;
--color-twr06-cyan: #00aaaa;
--color-twr06-ltgrey: #aaaaaa;
--color-twr06-dkgrey: #555555;
--color-twr06-ltcyan: #55ffff;
--color-twr06-yellow: #ffff55;
--color-twr06-white: #ffffff;
--color-twr06-red: #aa0000;
--color-twr06-bg: #c9ccd2;
--color-twr06-bg: oklch(0.84 0.008 260);
--color-twr06-surface: #eef0f3;
--color-twr06-surface: oklch(0.95 0.004 260);
--color-twr06-ink: #14171c;
--color-twr06-ink: oklch(0.19 0.008 260);
--color-twr06-ink2: #464e58;
--color-twr06-ink2: oklch(0.42 0.014 260);
--color-twr06-line: #9aa1ab;
--color-twr06-line: oklch(0.71 0.012 260);
--color-twr06-acc: #1247c9;
--color-twr06-acc: oklch(0.45 0.2 264);
--font-twr06-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, 'Cascadia Mono', Consolas, monospace;
}
/* A feature query that substitutes the very property it tests cannot be a
variant — the utility and the fallback would compete in the same layer. */
@supports not (font-size: 1cqi) {
.twr-06__art {
font-size: clamp(0.35rem, 1.6vw, 0.95rem);
}
}
.twr-06:has(#twr-06-theme:checked) {
--color-twr06-bg: #101318;
--color-twr06-bg: oklch(0.18 0.008 260);
--color-twr06-surface: #1a1e25;
--color-twr06-surface: oklch(0.24 0.01 260);
--color-twr06-ink: #eef1f5;
--color-twr06-ink: oklch(0.96 0.004 260);
--color-twr06-ink2: #a3abb6;
--color-twr06-ink2: oklch(0.74 0.012 260);
--color-twr06-line: #39414c;
--color-twr06-line: oklch(0.35 0.014 260);
--color-twr06-acc: #7fa8ff;
--color-twr06-acc: oklch(0.76 0.13 264);
}@import "tailwindcss";
@theme {
--color-twr06-k: #000000;
--color-twr06-blue: #0000aa;
--color-twr06-cyan: #00aaaa;
--color-twr06-ltgrey: #aaaaaa;
--color-twr06-dkgrey: #555555;
--color-twr06-ltcyan: #55ffff;
--color-twr06-yellow: #ffff55;
--color-twr06-white: #ffffff;
--color-twr06-red: #aa0000;
--color-twr06-bg: #c9ccd2;
--color-twr06-bg: oklch(0.84 0.008 260);
--color-twr06-surface: #eef0f3;
--color-twr06-surface: oklch(0.95 0.004 260);
--color-twr06-ink: #14171c;
--color-twr06-ink: oklch(0.19 0.008 260);
--color-twr06-ink2: #464e58;
--color-twr06-ink2: oklch(0.42 0.014 260);
--color-twr06-line: #9aa1ab;
--color-twr06-line: oklch(0.71 0.012 260);
--color-twr06-acc: #1247c9;
--color-twr06-acc: oklch(0.45 0.2 264);
--font-twr06-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, 'Cascadia Mono', Consolas, monospace;
}
/* A feature query that substitutes the very property it tests cannot be a
variant — the utility and the fallback would compete in the same layer. */
@supports not (font-size: 1cqi) {
.twr-06__art {
font-size: clamp(0.35rem, 1.6vw, 0.95rem);
}
}
.twr-06:has(#twr-06-theme:checked) {
--color-twr06-bg: #101318;
--color-twr06-bg: oklch(0.18 0.008 260);
--color-twr06-surface: #1a1e25;
--color-twr06-surface: oklch(0.24 0.01 260);
--color-twr06-ink: #eef1f5;
--color-twr06-ink: oklch(0.96 0.004 260);
--color-twr06-ink2: #a3abb6;
--color-twr06-ink2: oklch(0.74 0.012 260);
--color-twr06-line: #39414c;
--color-twr06-line: oklch(0.35 0.014 260);
--color-twr06-acc: #7fa8ff;
--color-twr06-acc: oklch(0.76 0.13 264);
}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-twr06-k: #000000;
--color-twr06-blue: #0000aa;
--color-twr06-cyan: #00aaaa;
--color-twr06-ltgrey: #aaaaaa;
--color-twr06-dkgrey: #555555;
--color-twr06-ltcyan: #55ffff;
--color-twr06-yellow: #ffff55;
--color-twr06-white: #ffffff;
--color-twr06-red: #aa0000;
--color-twr06-bg: #c9ccd2;
--color-twr06-bg: oklch(0.84 0.008 260);
--color-twr06-surface: #eef0f3;
--color-twr06-surface: oklch(0.95 0.004 260);
--color-twr06-ink: #14171c;
--color-twr06-ink: oklch(0.19 0.008 260);
--color-twr06-ink2: #464e58;
--color-twr06-ink2: oklch(0.42 0.014 260);
--color-twr06-line: #9aa1ab;
--color-twr06-line: oklch(0.71 0.012 260);
--color-twr06-acc: #1247c9;
--color-twr06-acc: oklch(0.45 0.2 264);
--font-twr06-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, 'Cascadia Mono', Consolas, monospace;
}
/* A feature query that substitutes the very property it tests cannot be a
variant — the utility and the fallback would compete in the same layer. */
@supports not (font-size: 1cqi) {
.twr-06__art {
font-size: clamp(0.35rem, 1.6vw, 0.95rem);
}
}
.twr-06:has(#twr-06-theme:checked) {
--color-twr06-bg: #101318;
--color-twr06-bg: oklch(0.18 0.008 260);
--color-twr06-surface: #1a1e25;
--color-twr06-surface: oklch(0.24 0.01 260);
--color-twr06-ink: #eef1f5;
--color-twr06-ink: oklch(0.96 0.004 260);
--color-twr06-ink2: #a3abb6;
--color-twr06-ink2: oklch(0.74 0.012 260);
--color-twr06-line: #39414c;
--color-twr06-line: oklch(0.35 0.014 260);
--color-twr06-acc: #7fa8ff;
--color-twr06-acc: oklch(0.76 0.13 264);
}@import "tailwindcss";
@theme {
--color-twr06-k: #000000;
--color-twr06-blue: #0000aa;
--color-twr06-cyan: #00aaaa;
--color-twr06-ltgrey: #aaaaaa;
--color-twr06-dkgrey: #555555;
--color-twr06-ltcyan: #55ffff;
--color-twr06-yellow: #ffff55;
--color-twr06-white: #ffffff;
--color-twr06-red: #aa0000;
--color-twr06-bg: #c9ccd2;
--color-twr06-bg: oklch(0.84 0.008 260);
--color-twr06-surface: #eef0f3;
--color-twr06-surface: oklch(0.95 0.004 260);
--color-twr06-ink: #14171c;
--color-twr06-ink: oklch(0.19 0.008 260);
--color-twr06-ink2: #464e58;
--color-twr06-ink2: oklch(0.42 0.014 260);
--color-twr06-line: #9aa1ab;
--color-twr06-line: oklch(0.71 0.012 260);
--color-twr06-acc: #1247c9;
--color-twr06-acc: oklch(0.45 0.2 264);
--font-twr06-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, 'Cascadia Mono', Consolas, monospace;
}
/* A feature query that substitutes the very property it tests cannot be a
variant — the utility and the fallback would compete in the same layer. */
@supports not (font-size: 1cqi) {
.twr-06__art {
font-size: clamp(0.35rem, 1.6vw, 0.95rem);
}
}
.twr-06:has(#twr-06-theme:checked) {
--color-twr06-bg: #101318;
--color-twr06-bg: oklch(0.18 0.008 260);
--color-twr06-surface: #1a1e25;
--color-twr06-surface: oklch(0.24 0.01 260);
--color-twr06-ink: #eef1f5;
--color-twr06-ink: oklch(0.96 0.004 260);
--color-twr06-ink2: #a3abb6;
--color-twr06-ink2: oklch(0.74 0.012 260);
--color-twr06-line: #39414c;
--color-twr06-line: oklch(0.35 0.014 260);
--color-twr06-acc: #7fa8ff;
--color-twr06-acc: oklch(0.76 0.13 264);
}<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. @containerplustext-[min(0.95rem,2.02cqi)]is the port's best moment: the container and the query unit that depends on it are two classes on two elements, visible together.- Character-relative units stay arbitrary values (
px-[0.5ch],gap-x-[2ch],min-h-[1.6em]). The design scale here is the character cell, which is not a spacing ramp and should not be themed as one. - The 40-column fallback is a plain
hidden md:block/md:hiddenpair, and each<pre>carries its owncqicoefficient — two fields, two clocks, no media query. - Palette swatches use per-item
before:bg-[#hex]rather than sixteen tokens, because each value appears once. Tokens are for reuse, not for inventory. - The nine palette entries the chrome actually uses are tokens, so the screen can be reskinned by swapping which token each element names.
- The only CSS left is the
@supportsfallback and the surround flip — the character grid itself is entirely utilities.
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 MS DOS Text Mode Interface Grid
Source: https://codefronts.com/design-styles/tailwind-retro-ui/tailwind-ms-dos-text-mode-interface-grid/
A character-cell interface reproduced as one: an 80×25 field where every cell is exactly one monospace advance, framed with code page 437 box-drawing characters and restricted to the 16-colour CGA palette as theme tokens. The constraint is the subject — nothing can sit between two cells — and Tailwind's container-query utilities turn out to be the cleanest way yet to make 80 columns fit their frame exactly.
## HTML
```html
<section class="twr-06 relative block w-full min-h-svh box-border bg-twr06-bg text-twr06-ink font-twr06-mono" aria-labelledby="twr-06-title">
<h2 class="absolute size-px overflow-hidden [clip-path:inset(50%)] whitespace-nowrap" id="twr-06-title">MS-DOS text mode character grid</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-06-theme">
<label class="grid place-items-center size-11 border border-twr06-line bg-twr06-surface text-twr06-ink2 cursor-pointer peer-focus-visible:outline-2 peer-focus-visible:outline-twr06-acc peer-focus-visible:outline-offset-2" for="twr-06-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 dark surround</span>
</label>
</div>
<div class="grid grid-cols-[minmax(0,1fr)] gap-4.5 max-w-[72rem] mx-auto px-[clamp(0.6rem,0.4rem+1.5vw,2rem)] pt-[clamp(3.5rem,3rem+3vw,5rem)] pb-16 lg:grid-cols-2">
<p class="m-0 justify-self-start min-w-0 lg:col-span-2 border border-twr06-line bg-twr06-surface px-2.5 py-1.5 text-[0.68rem] tracking-[0.12em] uppercase text-twr06-ink2">IBM CGA · 16 colours · 80 × 25 text mode · code page 437</p>
<div class="min-w-0 lg:col-span-2 rounded-md bg-[#6d6f70] p-[clamp(0.5rem,0.3rem+1vw,1rem)] [box-shadow:inset_0_1px_0_rgba(255,255,255,0.35),inset_0_-2px_0_rgba(0,0,0,0.4),0_14px_30px_-16px_rgba(0,0,0,0.6)]">
<div class="@container min-w-0 overflow-hidden grid grid-cols-[minmax(0,1fr)] content-start border-2 border-[#1b1d1f] bg-twr06-blue text-twr06-ltgrey">
<div class="flex items-center min-h-[1.6em] px-[0.5ch] min-w-0 overflow-hidden whitespace-nowrap bg-twr06-ltgrey text-twr06-k text-[clamp(0.66rem,0.55rem+0.6vw,0.85rem)]" role="presentation">
<span class="font-bold text-twr06-red">F</span><span>ile</span>
<span class="flex-none w-[2ch]"></span>
<span class="font-bold text-twr06-red">E</span><span>dit</span>
<span class="flex-none w-[2ch]"></span>
<span class="font-bold text-twr06-red">S</span><span>earch</span>
<span class="flex-none w-[2ch]"></span>
<span class="font-bold text-twr06-red">O</span><span>ptions</span>
<span class="ms-auto ps-[1ch] text-[#303030]">MEM 640K</span>
</div>
<pre class="twr-06__art hidden md:block m-0 py-2 min-w-0 overflow-hidden font-inherit whitespace-pre text-center leading-[1.16] text-twr06-ltcyan text-[min(0.95rem,2.02cqi)]" aria-hidden="true">╔══════════════════════════════════════════════════════════════════════════════╗
║ C:\ ─ Directory of drive C ─ 6 file(s) ─ 90,671 bytes ║
╠══════════════════════════════════════════════════════════════════════════════╣
║ Name Size Date Attr Type ║
║ ──────────────────────────────────────────────────────────────────── ║
║ AUTOEXEC.BAT 1,024 04-12-1995 A Batch file ║
║ COMMAND.COM 54,619 05-31-1994 RA Command interpreter ║
║ CONFIG.SYS 412 04-12-1995 A System configuration ║
║ HIMEM.SYS 29,136 05-31-1994 RA Extended memory manager ║
║ README.TXT 7,880 11-03-1995 A Text ║
║ ║
║ Every glyph above occupies exactly one cell. Nothing sits between two cells.║
║ ║
║ ║
║ ║
║ ║
║ ║
║ ║
║ ║
║ ║
╚══════════════════════════════════════════════════════════════════════════════╝</pre>
<pre class="twr-06__art block md:hidden m-0 py-2 min-w-0 overflow-hidden font-inherit whitespace-pre text-center leading-[1.16] text-twr06-ltcyan text-[min(0.95rem,4.1cqi)]" aria-hidden="true">╔══════════════════════════════════════╗
║ C:\ ─ 6 file(s), 90,671 bytes ║
╠══════════════════════════════════════╣
║ Name Size Attr ║
║ ─────────────────────────────────────║
║ AUTOEXEC.BAT 1,024 A ║
║ COMMAND.COM 54,619 RA ║
║ CONFIG.SYS 412 A ║
║ HIMEM.SYS 29,136 RA ║
║ README.TXT 7,880 A ║
║ ║
║ One glyph = one cell. No between. ║
║ ║
║ ║
╚══════════════════════════════════════╝</pre>
<p class="absolute size-px overflow-hidden [clip-path:inset(50%)] whitespace-nowrap">A DOS-style file manager panel listing AUTOEXEC.BAT, CONFIG.SYS, COMMAND.COM and HIMEM.SYS with sizes and dates, framed in double-line box characters.</p>
<div class="flex flex-wrap items-center gap-x-[2ch] min-h-[1.6em] px-[0.5ch] min-w-0 bg-twr06-ltgrey text-twr06-k text-[clamp(0.66rem,0.55rem+0.6vw,0.85rem)]">
<span><span class="font-bold text-twr06-red">F1</span> Help</span>
<span><span class="font-bold text-twr06-red">F3</span> Open</span>
<span><span class="font-bold text-twr06-red">F10</span> Menu</span>
<span class="ms-auto text-[#303030]">80 × 25</span>
</div>
</div>
</div>
<aside class="min-w-0 grid content-start gap-2.5 border border-twr06-line bg-twr06-surface p-[clamp(0.8rem,0.6rem+0.8vw,1.1rem)]">
<h3 class="m-0 text-[0.72rem] tracking-[0.1em] uppercase text-twr06-ink2">Cell inspector — the grid the text lands on</h3>
<div class="grid grid-cols-12 gap-px min-w-0 border border-twr06-line bg-twr06-line text-[clamp(0.8rem,0.6rem+1.4vw,1.35rem)]" aria-hidden="true">
<span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">╔</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">═</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">═</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">╦</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">═</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">═</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">╗</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan text-twr06-yellow">█</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan text-twr06-yellow">▓</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan text-twr06-yellow">▒</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan text-twr06-yellow">░</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan"> </span>
<span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">║</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">A</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">B</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">║</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">C</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">D</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">║</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">─</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">┼</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">─</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">┤</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan"> </span>
<span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">╚</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">═</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">═</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">╩</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">═</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">═</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">╝</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">└</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">┴</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">┘</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan"> </span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan"> </span>
</div>
<p class="m-0 min-w-0 text-[0.8rem] leading-relaxed text-pretty text-twr06-ink2">Each box is <code class="bg-twr06-bg px-1">1ch</code> wide and one line-height tall. The frame is characters, not borders — a border would have to live between two cells, and there is no between.</p>
</aside>
<aside class="min-w-0 grid content-start gap-2.5 border border-twr06-line bg-twr06-surface p-[clamp(0.8rem,0.6rem+0.8vw,1.1rem)]">
<h3 class="m-0 text-[0.72rem] tracking-[0.1em] uppercase text-twr06-ink2">CGA palette — 16 defined values</h3>
<ul class="list-none m-0 p-0 min-w-0 grid gap-0.5 grid-cols-[repeat(auto-fit,minmax(min(100%,7.5rem),1fr))]">
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#000000]">0 black</li>
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#0000aa]">1 blue</li>
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#00aa00]">2 green</li>
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#00aaaa]">3 cyan</li>
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#aa0000]">4 red</li>
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#aa00aa]">5 magenta</li>
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#aa5500]">6 brown</li>
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#aaaaaa]">7 lt grey</li>
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#555555]">8 dk grey</li>
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#5555ff]">9 lt blue</li>
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#55ff55]">10 lt green</li>
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#55ffff]">11 lt cyan</li>
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#ff5555]">12 lt red</li>
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#ff55ff]">13 lt magenta</li>
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#ffff55]">14 yellow</li>
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#ffffff]">15 white</li>
</ul>
</aside>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-twr06-k: #000000;
--color-twr06-blue: #0000aa;
--color-twr06-cyan: #00aaaa;
--color-twr06-ltgrey: #aaaaaa;
--color-twr06-dkgrey: #555555;
--color-twr06-ltcyan: #55ffff;
--color-twr06-yellow: #ffff55;
--color-twr06-white: #ffffff;
--color-twr06-red: #aa0000;
--color-twr06-bg: #c9ccd2;
--color-twr06-bg: oklch(0.84 0.008 260);
--color-twr06-surface: #eef0f3;
--color-twr06-surface: oklch(0.95 0.004 260);
--color-twr06-ink: #14171c;
--color-twr06-ink: oklch(0.19 0.008 260);
--color-twr06-ink2: #464e58;
--color-twr06-ink2: oklch(0.42 0.014 260);
--color-twr06-line: #9aa1ab;
--color-twr06-line: oklch(0.71 0.012 260);
--color-twr06-acc: #1247c9;
--color-twr06-acc: oklch(0.45 0.2 264);
--font-twr06-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, 'Cascadia Mono', Consolas, monospace;
}
/* A feature query that substitutes the very property it tests cannot be a
variant — the utility and the fallback would compete in the same layer. */
@supports not (font-size: 1cqi) {
.twr-06__art {
font-size: clamp(0.35rem, 1.6vw, 0.95rem);
}
}
.twr-06:has(#twr-06-theme:checked) {
--color-twr06-bg: #101318;
--color-twr06-bg: oklch(0.18 0.008 260);
--color-twr06-surface: #1a1e25;
--color-twr06-surface: oklch(0.24 0.01 260);
--color-twr06-ink: #eef1f5;
--color-twr06-ink: oklch(0.96 0.004 260);
--color-twr06-ink2: #a3abb6;
--color-twr06-ink2: oklch(0.74 0.012 260);
--color-twr06-line: #39414c;
--color-twr06-line: oklch(0.35 0.014 260);
--color-twr06-acc: #7fa8ff;
--color-twr06-acc: oklch(0.76 0.13 264);
}
```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 MS DOS Text Mode Interface Grid
Source: https://codefronts.com/design-styles/tailwind-retro-ui/tailwind-ms-dos-text-mode-interface-grid/
A character-cell interface reproduced as one: an 80×25 field where every cell is exactly one monospace advance, framed with code page 437 box-drawing characters and restricted to the 16-colour CGA palette as theme tokens. The constraint is the subject — nothing can sit between two cells — and Tailwind's container-query utilities turn out to be the cleanest way yet to make 80 columns fit their frame exactly.
## HTML
```html
<section class="twr-06 relative block w-full min-h-svh box-border bg-twr06-bg text-twr06-ink font-twr06-mono" aria-labelledby="twr-06-title">
<h2 class="absolute size-px overflow-hidden [clip-path:inset(50%)] whitespace-nowrap" id="twr-06-title">MS-DOS text mode character grid</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-06-theme">
<label class="grid place-items-center size-11 border border-twr06-line bg-twr06-surface text-twr06-ink2 cursor-pointer peer-focus-visible:outline-2 peer-focus-visible:outline-twr06-acc peer-focus-visible:outline-offset-2" for="twr-06-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 dark surround</span>
</label>
</div>
<div class="grid grid-cols-[minmax(0,1fr)] gap-4.5 max-w-[72rem] mx-auto px-[clamp(0.6rem,0.4rem+1.5vw,2rem)] pt-[clamp(3.5rem,3rem+3vw,5rem)] pb-16 lg:grid-cols-2">
<p class="m-0 justify-self-start min-w-0 lg:col-span-2 border border-twr06-line bg-twr06-surface px-2.5 py-1.5 text-[0.68rem] tracking-[0.12em] uppercase text-twr06-ink2">IBM CGA · 16 colours · 80 × 25 text mode · code page 437</p>
<div class="min-w-0 lg:col-span-2 rounded-md bg-[#6d6f70] p-[clamp(0.5rem,0.3rem+1vw,1rem)] [box-shadow:inset_0_1px_0_rgba(255,255,255,0.35),inset_0_-2px_0_rgba(0,0,0,0.4),0_14px_30px_-16px_rgba(0,0,0,0.6)]">
<div class="@container min-w-0 overflow-hidden grid grid-cols-[minmax(0,1fr)] content-start border-2 border-[#1b1d1f] bg-twr06-blue text-twr06-ltgrey">
<div class="flex items-center min-h-[1.6em] px-[0.5ch] min-w-0 overflow-hidden whitespace-nowrap bg-twr06-ltgrey text-twr06-k text-[clamp(0.66rem,0.55rem+0.6vw,0.85rem)]" role="presentation">
<span class="font-bold text-twr06-red">F</span><span>ile</span>
<span class="flex-none w-[2ch]"></span>
<span class="font-bold text-twr06-red">E</span><span>dit</span>
<span class="flex-none w-[2ch]"></span>
<span class="font-bold text-twr06-red">S</span><span>earch</span>
<span class="flex-none w-[2ch]"></span>
<span class="font-bold text-twr06-red">O</span><span>ptions</span>
<span class="ms-auto ps-[1ch] text-[#303030]">MEM 640K</span>
</div>
<pre class="twr-06__art hidden md:block m-0 py-2 min-w-0 overflow-hidden font-inherit whitespace-pre text-center leading-[1.16] text-twr06-ltcyan text-[min(0.95rem,2.02cqi)]" aria-hidden="true">╔══════════════════════════════════════════════════════════════════════════════╗
║ C:\ ─ Directory of drive C ─ 6 file(s) ─ 90,671 bytes ║
╠══════════════════════════════════════════════════════════════════════════════╣
║ Name Size Date Attr Type ║
║ ──────────────────────────────────────────────────────────────────── ║
║ AUTOEXEC.BAT 1,024 04-12-1995 A Batch file ║
║ COMMAND.COM 54,619 05-31-1994 RA Command interpreter ║
║ CONFIG.SYS 412 04-12-1995 A System configuration ║
║ HIMEM.SYS 29,136 05-31-1994 RA Extended memory manager ║
║ README.TXT 7,880 11-03-1995 A Text ║
║ ║
║ Every glyph above occupies exactly one cell. Nothing sits between two cells.║
║ ║
║ ║
║ ║
║ ║
║ ║
║ ║
║ ║
║ ║
╚══════════════════════════════════════════════════════════════════════════════╝</pre>
<pre class="twr-06__art block md:hidden m-0 py-2 min-w-0 overflow-hidden font-inherit whitespace-pre text-center leading-[1.16] text-twr06-ltcyan text-[min(0.95rem,4.1cqi)]" aria-hidden="true">╔══════════════════════════════════════╗
║ C:\ ─ 6 file(s), 90,671 bytes ║
╠══════════════════════════════════════╣
║ Name Size Attr ║
║ ─────────────────────────────────────║
║ AUTOEXEC.BAT 1,024 A ║
║ COMMAND.COM 54,619 RA ║
║ CONFIG.SYS 412 A ║
║ HIMEM.SYS 29,136 RA ║
║ README.TXT 7,880 A ║
║ ║
║ One glyph = one cell. No between. ║
║ ║
║ ║
╚══════════════════════════════════════╝</pre>
<p class="absolute size-px overflow-hidden [clip-path:inset(50%)] whitespace-nowrap">A DOS-style file manager panel listing AUTOEXEC.BAT, CONFIG.SYS, COMMAND.COM and HIMEM.SYS with sizes and dates, framed in double-line box characters.</p>
<div class="flex flex-wrap items-center gap-x-[2ch] min-h-[1.6em] px-[0.5ch] min-w-0 bg-twr06-ltgrey text-twr06-k text-[clamp(0.66rem,0.55rem+0.6vw,0.85rem)]">
<span><span class="font-bold text-twr06-red">F1</span> Help</span>
<span><span class="font-bold text-twr06-red">F3</span> Open</span>
<span><span class="font-bold text-twr06-red">F10</span> Menu</span>
<span class="ms-auto text-[#303030]">80 × 25</span>
</div>
</div>
</div>
<aside class="min-w-0 grid content-start gap-2.5 border border-twr06-line bg-twr06-surface p-[clamp(0.8rem,0.6rem+0.8vw,1.1rem)]">
<h3 class="m-0 text-[0.72rem] tracking-[0.1em] uppercase text-twr06-ink2">Cell inspector — the grid the text lands on</h3>
<div class="grid grid-cols-12 gap-px min-w-0 border border-twr06-line bg-twr06-line text-[clamp(0.8rem,0.6rem+1.4vw,1.35rem)]" aria-hidden="true">
<span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">╔</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">═</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">═</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">╦</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">═</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">═</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">╗</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan text-twr06-yellow">█</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan text-twr06-yellow">▓</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan text-twr06-yellow">▒</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan text-twr06-yellow">░</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan"> </span>
<span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">║</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">A</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">B</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">║</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">C</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">D</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">║</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">─</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">┼</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">─</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">┤</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan"> </span>
<span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">╚</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">═</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">═</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">╩</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">═</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">═</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">╝</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">└</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">┴</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan">┘</span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan"> </span><span class="grid place-items-center aspect-square min-w-0 leading-none bg-twr06-blue text-twr06-ltcyan"> </span>
</div>
<p class="m-0 min-w-0 text-[0.8rem] leading-relaxed text-pretty text-twr06-ink2">Each box is <code class="bg-twr06-bg px-1">1ch</code> wide and one line-height tall. The frame is characters, not borders — a border would have to live between two cells, and there is no between.</p>
</aside>
<aside class="min-w-0 grid content-start gap-2.5 border border-twr06-line bg-twr06-surface p-[clamp(0.8rem,0.6rem+0.8vw,1.1rem)]">
<h3 class="m-0 text-[0.72rem] tracking-[0.1em] uppercase text-twr06-ink2">CGA palette — 16 defined values</h3>
<ul class="list-none m-0 p-0 min-w-0 grid gap-0.5 grid-cols-[repeat(auto-fit,minmax(min(100%,7.5rem),1fr))]">
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#000000]">0 black</li>
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#0000aa]">1 blue</li>
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#00aa00]">2 green</li>
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#00aaaa]">3 cyan</li>
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#aa0000]">4 red</li>
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#aa00aa]">5 magenta</li>
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#aa5500]">6 brown</li>
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#aaaaaa]">7 lt grey</li>
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#555555]">8 dk grey</li>
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#5555ff]">9 lt blue</li>
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#55ff55]">10 lt green</li>
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#55ffff]">11 lt cyan</li>
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#ff5555]">12 lt red</li>
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#ff55ff]">13 lt magenta</li>
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#ffff55]">14 yellow</li>
<li class="min-w-0 flex items-center gap-2 px-2 py-1.5 text-[0.68rem] text-twr06-ink2 border border-twr06-line before:content-[''] before:flex-none before:size-4 before:border before:border-twr06-line before:bg-[#ffffff]">15 white</li>
</ul>
</aside>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-twr06-k: #000000;
--color-twr06-blue: #0000aa;
--color-twr06-cyan: #00aaaa;
--color-twr06-ltgrey: #aaaaaa;
--color-twr06-dkgrey: #555555;
--color-twr06-ltcyan: #55ffff;
--color-twr06-yellow: #ffff55;
--color-twr06-white: #ffffff;
--color-twr06-red: #aa0000;
--color-twr06-bg: #c9ccd2;
--color-twr06-bg: oklch(0.84 0.008 260);
--color-twr06-surface: #eef0f3;
--color-twr06-surface: oklch(0.95 0.004 260);
--color-twr06-ink: #14171c;
--color-twr06-ink: oklch(0.19 0.008 260);
--color-twr06-ink2: #464e58;
--color-twr06-ink2: oklch(0.42 0.014 260);
--color-twr06-line: #9aa1ab;
--color-twr06-line: oklch(0.71 0.012 260);
--color-twr06-acc: #1247c9;
--color-twr06-acc: oklch(0.45 0.2 264);
--font-twr06-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, 'Cascadia Mono', Consolas, monospace;
}
/* A feature query that substitutes the very property it tests cannot be a
variant — the utility and the fallback would compete in the same layer. */
@supports not (font-size: 1cqi) {
.twr-06__art {
font-size: clamp(0.35rem, 1.6vw, 0.95rem);
}
}
.twr-06:has(#twr-06-theme:checked) {
--color-twr06-bg: #101318;
--color-twr06-bg: oklch(0.18 0.008 260);
--color-twr06-surface: #1a1e25;
--color-twr06-surface: oklch(0.24 0.01 260);
--color-twr06-ink: #eef1f5;
--color-twr06-ink: oklch(0.96 0.004 260);
--color-twr06-ink2: #a3abb6;
--color-twr06-ink2: oklch(0.74 0.012 260);
--color-twr06-line: #39414c;
--color-twr06-line: oklch(0.35 0.014 260);
--color-twr06-acc: #7fa8ff;
--color-twr06-acc: oklch(0.76 0.13 264);
}
```How this works
Text mode is not a font choice, it is an addressing model. A CGA or VGA text screen is a fixed array of cells — 80 across, 25 down — each holding one character code and one attribute byte. There is no position between cell 12 and cell 13. That is why DOS dialogs are built from box-drawing characters rather than lines: a line would have to live between cells, and there is no between.
ch is the unit that preserves the model, and it survives the port as an arbitrary value. One ch is the advance width of the zero glyph, which in a monospace face is every glyph's advance. Tailwind has no ch scale and should not: the design scale here is the character, not a spacing ramp. So the chrome measures itself in characters directly:
class="flex items-center min-h-[1.6em] px-[0.5ch] gap-x-[2ch]"Fitting 80 columns is a font-size problem, and container queries solve it exactly. The field is 80 advances wide, so the only free variable is the advance. The screen gets @container and the <pre> gets text-[min(0.95rem,2.02cqi)] — 80 columns fill the container's inline size and stop. This is a case where the utility form is genuinely nicer than the CSS: the container and the query unit that depends on it are two classes on two elements you can see at once.
The responsive answer is honest, not clever. Below the md breakpoint the computed size drops under legibility, so a 40-column field takes over: hidden md:block on the wide art, md:hidden on the narrow. Scaling an 80-column screen onto a phone is period-accurate and unreadable, and the demo says so on screen.
The CGA palette is sixteen defined values, not a mood. Eight low-intensity and eight high-intensity colours as exact sRGB. Several authentic pairings fail modern contrast outright — dark grey #555555 on blue #0000aa is under 2:1 — so those stay decorative chrome while body text uses the light-cyan-on-blue pair that passes. The palette swatches use per-item arbitrary colours rather than tokens, because each hex appears exactly once and a token would add a name with no reuse.
What stays in CSS. The @supports not (font-size: 1cqi) fallback, because a feature query has no variant form and the fallback replaces the very property being tested; and the :has() surround flip. Everything else — the cell grid, the character-unit chrome, the palette, the responsive field swap — is in the markup.
Make it yours
- Set the field width by editing the
2.02cqicoefficient: it is 160/80 minus frame padding, so a 40-column field wants roughly double. - Reskin the whole screen by swapping which palette tokens the screen and text use —
bg-twr06-blue text-twr06-ltcyantobg-twr06-k text-twr06-ltgreyis the DOS Shell look. - Swap the double-line frame characters for single-line ones by editing the
<pre>content; the geometry is unchanged because both occupy one cell. - Raise the
min()ceiling past0.95remfor a large-display kiosk look; the field stays 80 columns and simply gets bigger. - Turn the inspector's grid lines off by removing the
gap-pxand its background — the text layer does not depend on them. - Add a second screen with a different palette pair by duplicating the frame; the tokens are shared but nothing is scoped to a single instance.
Gotchas — read before shipping
- An 80-column field cannot fit a 320px viewport at a legible size. Drop to 40 columns at a breakpoint or accept a sub-8px cell — do not pretend scaling solves it.
- Sizing chrome in
pxinstead ofchoremputs the cell boundaries out of phase with the glyph advances, and the error accumulates into visible shear across a wide row. cqineeds an ancestor withcontainer-type: inline-size. Tailwind's@containerclass provides it, but if the query unit resolves against nothing the text silently falls back to its initial size.- The
@supportsfallback cannot be a variant here, because it substitutes the same property it tests. Keep it in CSS. - Not every monospace stack carries code page 437 box-drawing glyphs at the same advance. Test the frame in your fallback fonts or the frame shears where a glyph substitutes.
- Authentic CGA pairings such as dark grey on blue fail SC 1.4.3 badly. Keep them for chrome, raise body text, and never carry meaning in the low-intensity pair alone.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
Container query units (cqi) land at Chrome 105 / Safari 16 / Firefox 110, with a viewport-based font size as the @supports fallback. The ch unit has been Baseline since 2011 and the box-drawing characters are plain UTF-8, so the character grid itself works far further back. Tailwind v4's generated sheet is the binding floor at Chrome 111+, Safari 16.4+, Firefox 128+.