17 / 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 Nineties Web Guestbook Widget
A personal-homepage guestbook: odometer hit counter, webring navigation, signed entries and a “best viewed in” badge, on the grey ground and ridged borders of the era. The point it makes is the one that matters most about this period — reproduce the look, never the markup, because everything that made these pages work is now the reason they fail.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="twr-17 relative block w-full min-h-svh box-border bg-twr17-face text-twr17-ink font-twr17-sans [background-image:repeating-conic-gradient(var(--color-twr17-tileb)_0%_25%,var(--color-twr17-face)_0%_50%)] [background-size:8px_8px]" aria-labelledby="twr-17-title">
<h2 class="absolute size-px overflow-hidden [clip-path:inset(50%)] whitespace-nowrap" id="twr-17-title">Nineties personal homepage guestbook</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-17-theme">
<label class="grid place-items-center size-11 cursor-pointer bg-twr17-face2 text-twr17-ink2 twr17-ridge peer-focus-visible:outline-2 peer-focus-visible:outline-twr17-acc peer-focus-visible:outline-offset-2" for="twr-17-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 ground</span>
</label>
</div>
<div class="grid grid-cols-[minmax(0,1fr)] gap-5 max-w-[64rem] mx-auto px-[clamp(0.75rem,0.5rem+2vw,2rem)] pt-[clamp(3.5rem,3rem+3vw,5rem)] pb-16">
<header class="min-w-0 grid justify-items-center gap-1.5 text-center bg-twr17-face2 px-4 py-5 twr17-ridge">
<h3 class="m-0 min-w-0 font-twr17-serif font-bold text-[clamp(1.5rem,1.1rem+2.4vw,2.6rem)] text-twr17-acc [text-shadow:1px_1px_0_var(--color-twr17-hi)]">Dave’s Homepage</h3>
<p class="m-0 font-twr17-mono text-[0.75rem] text-twr17-hot">~ under construction since 1997 ~</p>
</header>
<div class="grid grid-cols-[minmax(0,1fr)] gap-5 lg:grid-cols-[minmax(0,1.6fr)_minmax(0,1fr)] lg:items-start">
<article class="min-w-0 grid content-start gap-3 bg-twr17-face2 p-[clamp(0.85rem,0.6rem+1vw,1.25rem)] twr17-ridge" aria-labelledby="twr-17-gb">
<h4 class="m-0 font-twr17-serif text-[1rem] font-bold text-twr17-acc" id="twr-17-gb">Sign my guestbook</h4>
<ol class="list-none m-0 p-0 grid gap-3 min-w-0">
<li class="flex gap-3 min-w-0 border-b border-twr17-sh/40 pb-3 last:border-b-0 last:pb-0">
<span class="flex-none grid place-items-center size-10 bg-twr17-acc text-white font-twr17-sans text-[0.75rem] font-bold twr17-ridge" aria-hidden="true">AC</span>
<div class="min-w-0 grid gap-1">
<p class="m-0 min-w-0 font-twr17-sans text-[0.72rem] text-twr17-ink2"><b class="font-bold text-twr17-ink">Alex Chen</b> · 14 Nov 1997 · Manchester</p>
<p class="m-0 min-w-0 font-twr17-serif text-[0.92rem] leading-normal text-pretty">Great page! Love the MIDI. Come see my Star Trek shrine sometime.</p>
</div>
</li>
<li class="flex gap-3 min-w-0 border-b border-twr17-sh/40 pb-3 last:border-b-0 last:pb-0">
<span class="flex-none grid place-items-center size-10 bg-twr17-acc text-white font-twr17-sans text-[0.75rem] font-bold twr17-ridge" aria-hidden="true">PS</span>
<div class="min-w-0 grid gap-1">
<p class="m-0 min-w-0 font-twr17-sans text-[0.72rem] text-twr17-ink2"><b class="font-bold text-twr17-ink">Priya Sharma</b> · 2 Dec 1997 · Leeds</p>
<p class="m-0 min-w-0 font-twr17-serif text-[0.92rem] leading-normal text-pretty">Added you to the ring. The tiled background is a bold choice and I respect it.</p>
</div>
</li>
<li class="flex gap-3 min-w-0 border-b border-twr17-sh/40 pb-3 last:border-b-0 last:pb-0">
<span class="flex-none grid place-items-center size-10 bg-twr17-acc text-white font-twr17-sans text-[0.75rem] font-bold twr17-ridge" aria-hidden="true">JL</span>
<div class="min-w-0 grid gap-1">
<p class="m-0 min-w-0 font-twr17-sans text-[0.72rem] text-twr17-ink2"><b class="font-bold text-twr17-ink">Jordan Lee</b> · 19 Jan 1998 · Bristol</p>
<p class="m-0 min-w-0 font-twr17-serif text-[0.92rem] leading-normal text-pretty">Your links page is the best on the web. Signed, a fellow traveller.</p>
</div>
</li>
</ol>
<p class="m-0 min-w-0 text-[0.75rem] leading-relaxed text-pretty text-twr17-ink2">Entries are a real ordered list. No table, no spacer image, no <code class="font-twr17-mono bg-twr17-face px-1"><font></code> anywhere in this widget.</p>
</article>
<aside class="min-w-0 grid content-start gap-5">
<div class="min-w-0 grid content-start gap-3 bg-twr17-face2 p-[clamp(0.85rem,0.6rem+1vw,1.25rem)] twr17-ridge">
<h4 class="m-0 font-twr17-serif text-[1rem] font-bold text-twr17-acc">Visitors</h4>
<div class="flex gap-1 min-w-0" role="img" aria-label="Visitor count: 0 0 4 2 1 8 7">
<span class="grid place-items-center w-6 h-9 bg-[#111] text-[#7dff9a] font-twr17-mono text-[1.05rem] tabular-nums [box-shadow:inset_0_2px_4px_rgba(0,0,0,0.9)]">0</span><span class="grid place-items-center w-6 h-9 bg-[#111] text-[#7dff9a] font-twr17-mono text-[1.05rem] tabular-nums [box-shadow:inset_0_2px_4px_rgba(0,0,0,0.9)]">0</span><span class="grid place-items-center w-6 h-9 bg-[#111] text-[#7dff9a] font-twr17-mono text-[1.05rem] tabular-nums [box-shadow:inset_0_2px_4px_rgba(0,0,0,0.9)]">4</span><span class="grid place-items-center w-6 h-9 bg-[#111] text-[#7dff9a] font-twr17-mono text-[1.05rem] tabular-nums [box-shadow:inset_0_2px_4px_rgba(0,0,0,0.9)]">2</span><span class="grid place-items-center w-6 h-9 bg-[#111] text-[#7dff9a] font-twr17-mono text-[1.05rem] tabular-nums [box-shadow:inset_0_2px_4px_rgba(0,0,0,0.9)]">1</span><span class="grid place-items-center w-6 h-9 bg-[#111] text-[#7dff9a] font-twr17-mono text-[1.05rem] tabular-nums [box-shadow:inset_0_2px_4px_rgba(0,0,0,0.9)]">8</span><span class="grid place-items-center w-6 h-9 bg-[#111] text-[#7dff9a] font-twr17-mono text-[1.05rem] tabular-nums [box-shadow:inset_0_2px_4px_rgba(0,0,0,0.9)]">7</span>
</div>
<p class="m-0 text-[0.72rem] text-twr17-ink2">since 12 August 1997</p>
</div>
<nav class="min-w-0 grid content-start gap-3 bg-twr17-face2 p-[clamp(0.85rem,0.6rem+1vw,1.25rem)] twr17-ridge" aria-labelledby="twr-17-ring">
<h4 class="m-0 font-twr17-serif text-[1rem] font-bold text-twr17-acc" id="twr-17-ring">The Retro Web Ring</h4>
<ul class="list-none m-0 p-0 grid gap-2 min-w-0 sm:grid-cols-2">
<li><button class="w-full min-h-11 px-3 py-2 border-0 cursor-pointer font-twr17-sans text-[0.8rem] bg-twr17-face text-twr17-ink twr17-ridge active:twr17-ridge-in focus-visible:outline-2 focus-visible:outline-twr17-acc focus-visible:outline-offset-2" type="button">« Prev</button></li>
<li><button class="w-full min-h-11 px-3 py-2 border-0 cursor-pointer font-twr17-sans text-[0.8rem] bg-twr17-face text-twr17-ink twr17-ridge active:twr17-ridge-in focus-visible:outline-2 focus-visible:outline-twr17-acc focus-visible:outline-offset-2" type="button">Random</button></li>
<li><button class="w-full min-h-11 px-3 py-2 border-0 cursor-pointer font-twr17-sans text-[0.8rem] bg-twr17-face text-twr17-ink twr17-ridge active:twr17-ridge-in focus-visible:outline-2 focus-visible:outline-twr17-acc focus-visible:outline-offset-2" type="button">List</button></li>
<li><button class="w-full min-h-11 px-3 py-2 border-0 cursor-pointer font-twr17-sans text-[0.8rem] bg-twr17-face text-twr17-ink twr17-ridge active:twr17-ridge-in focus-visible:outline-2 focus-visible:outline-twr17-acc focus-visible:outline-offset-2" type="button">Next »</button></li>
</ul>
</nav>
<div class="min-w-0 grid content-start gap-3 bg-twr17-face2 p-[clamp(0.85rem,0.6rem+1vw,1.25rem)] twr17-ridge">
<h4 class="m-0 font-twr17-serif text-[1rem] font-bold text-twr17-acc">Best viewed in</h4>
<p class="m-0 justify-self-start px-3 py-2 bg-twr17-hot text-white font-twr17-mono text-[0.72rem] tracking-[0.08em] animate-twr17-pulse motion-reduce:animate-none">800 × 600 · 256 colours</p>
<p class="m-0 min-w-0 text-[0.72rem] leading-relaxed text-pretty text-twr17-ink2">The badge pulses on a two second cycle and stops under reduced motion. The era flashed it; that is the one thing not worth reproducing.</p>
</div>
</aside>
</div>
</div>
</section><section class="twr-17 relative block w-full min-h-svh box-border bg-twr17-face text-twr17-ink font-twr17-sans [background-image:repeating-conic-gradient(var(--color-twr17-tileb)_0%_25%,var(--color-twr17-face)_0%_50%)] [background-size:8px_8px]" aria-labelledby="twr-17-title">
<h2 class="absolute size-px overflow-hidden [clip-path:inset(50%)] whitespace-nowrap" id="twr-17-title">Nineties personal homepage guestbook</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-17-theme">
<label class="grid place-items-center size-11 cursor-pointer bg-twr17-face2 text-twr17-ink2 twr17-ridge peer-focus-visible:outline-2 peer-focus-visible:outline-twr17-acc peer-focus-visible:outline-offset-2" for="twr-17-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 ground</span>
</label>
</div>
<div class="grid grid-cols-[minmax(0,1fr)] gap-5 max-w-[64rem] mx-auto px-[clamp(0.75rem,0.5rem+2vw,2rem)] pt-[clamp(3.5rem,3rem+3vw,5rem)] pb-16">
<header class="min-w-0 grid justify-items-center gap-1.5 text-center bg-twr17-face2 px-4 py-5 twr17-ridge">
<h3 class="m-0 min-w-0 font-twr17-serif font-bold text-[clamp(1.5rem,1.1rem+2.4vw,2.6rem)] text-twr17-acc [text-shadow:1px_1px_0_var(--color-twr17-hi)]">Dave’s Homepage</h3>
<p class="m-0 font-twr17-mono text-[0.75rem] text-twr17-hot">~ under construction since 1997 ~</p>
</header>
<div class="grid grid-cols-[minmax(0,1fr)] gap-5 lg:grid-cols-[minmax(0,1.6fr)_minmax(0,1fr)] lg:items-start">
<article class="min-w-0 grid content-start gap-3 bg-twr17-face2 p-[clamp(0.85rem,0.6rem+1vw,1.25rem)] twr17-ridge" aria-labelledby="twr-17-gb">
<h4 class="m-0 font-twr17-serif text-[1rem] font-bold text-twr17-acc" id="twr-17-gb">Sign my guestbook</h4>
<ol class="list-none m-0 p-0 grid gap-3 min-w-0">
<li class="flex gap-3 min-w-0 border-b border-twr17-sh/40 pb-3 last:border-b-0 last:pb-0">
<span class="flex-none grid place-items-center size-10 bg-twr17-acc text-white font-twr17-sans text-[0.75rem] font-bold twr17-ridge" aria-hidden="true">AC</span>
<div class="min-w-0 grid gap-1">
<p class="m-0 min-w-0 font-twr17-sans text-[0.72rem] text-twr17-ink2"><b class="font-bold text-twr17-ink">Alex Chen</b> · 14 Nov 1997 · Manchester</p>
<p class="m-0 min-w-0 font-twr17-serif text-[0.92rem] leading-normal text-pretty">Great page! Love the MIDI. Come see my Star Trek shrine sometime.</p>
</div>
</li>
<li class="flex gap-3 min-w-0 border-b border-twr17-sh/40 pb-3 last:border-b-0 last:pb-0">
<span class="flex-none grid place-items-center size-10 bg-twr17-acc text-white font-twr17-sans text-[0.75rem] font-bold twr17-ridge" aria-hidden="true">PS</span>
<div class="min-w-0 grid gap-1">
<p class="m-0 min-w-0 font-twr17-sans text-[0.72rem] text-twr17-ink2"><b class="font-bold text-twr17-ink">Priya Sharma</b> · 2 Dec 1997 · Leeds</p>
<p class="m-0 min-w-0 font-twr17-serif text-[0.92rem] leading-normal text-pretty">Added you to the ring. The tiled background is a bold choice and I respect it.</p>
</div>
</li>
<li class="flex gap-3 min-w-0 border-b border-twr17-sh/40 pb-3 last:border-b-0 last:pb-0">
<span class="flex-none grid place-items-center size-10 bg-twr17-acc text-white font-twr17-sans text-[0.75rem] font-bold twr17-ridge" aria-hidden="true">JL</span>
<div class="min-w-0 grid gap-1">
<p class="m-0 min-w-0 font-twr17-sans text-[0.72rem] text-twr17-ink2"><b class="font-bold text-twr17-ink">Jordan Lee</b> · 19 Jan 1998 · Bristol</p>
<p class="m-0 min-w-0 font-twr17-serif text-[0.92rem] leading-normal text-pretty">Your links page is the best on the web. Signed, a fellow traveller.</p>
</div>
</li>
</ol>
<p class="m-0 min-w-0 text-[0.75rem] leading-relaxed text-pretty text-twr17-ink2">Entries are a real ordered list. No table, no spacer image, no <code class="font-twr17-mono bg-twr17-face px-1"><font></code> anywhere in this widget.</p>
</article>
<aside class="min-w-0 grid content-start gap-5">
<div class="min-w-0 grid content-start gap-3 bg-twr17-face2 p-[clamp(0.85rem,0.6rem+1vw,1.25rem)] twr17-ridge">
<h4 class="m-0 font-twr17-serif text-[1rem] font-bold text-twr17-acc">Visitors</h4>
<div class="flex gap-1 min-w-0" role="img" aria-label="Visitor count: 0 0 4 2 1 8 7">
<span class="grid place-items-center w-6 h-9 bg-[#111] text-[#7dff9a] font-twr17-mono text-[1.05rem] tabular-nums [box-shadow:inset_0_2px_4px_rgba(0,0,0,0.9)]">0</span><span class="grid place-items-center w-6 h-9 bg-[#111] text-[#7dff9a] font-twr17-mono text-[1.05rem] tabular-nums [box-shadow:inset_0_2px_4px_rgba(0,0,0,0.9)]">0</span><span class="grid place-items-center w-6 h-9 bg-[#111] text-[#7dff9a] font-twr17-mono text-[1.05rem] tabular-nums [box-shadow:inset_0_2px_4px_rgba(0,0,0,0.9)]">4</span><span class="grid place-items-center w-6 h-9 bg-[#111] text-[#7dff9a] font-twr17-mono text-[1.05rem] tabular-nums [box-shadow:inset_0_2px_4px_rgba(0,0,0,0.9)]">2</span><span class="grid place-items-center w-6 h-9 bg-[#111] text-[#7dff9a] font-twr17-mono text-[1.05rem] tabular-nums [box-shadow:inset_0_2px_4px_rgba(0,0,0,0.9)]">1</span><span class="grid place-items-center w-6 h-9 bg-[#111] text-[#7dff9a] font-twr17-mono text-[1.05rem] tabular-nums [box-shadow:inset_0_2px_4px_rgba(0,0,0,0.9)]">8</span><span class="grid place-items-center w-6 h-9 bg-[#111] text-[#7dff9a] font-twr17-mono text-[1.05rem] tabular-nums [box-shadow:inset_0_2px_4px_rgba(0,0,0,0.9)]">7</span>
</div>
<p class="m-0 text-[0.72rem] text-twr17-ink2">since 12 August 1997</p>
</div>
<nav class="min-w-0 grid content-start gap-3 bg-twr17-face2 p-[clamp(0.85rem,0.6rem+1vw,1.25rem)] twr17-ridge" aria-labelledby="twr-17-ring">
<h4 class="m-0 font-twr17-serif text-[1rem] font-bold text-twr17-acc" id="twr-17-ring">The Retro Web Ring</h4>
<ul class="list-none m-0 p-0 grid gap-2 min-w-0 sm:grid-cols-2">
<li><button class="w-full min-h-11 px-3 py-2 border-0 cursor-pointer font-twr17-sans text-[0.8rem] bg-twr17-face text-twr17-ink twr17-ridge active:twr17-ridge-in focus-visible:outline-2 focus-visible:outline-twr17-acc focus-visible:outline-offset-2" type="button">« Prev</button></li>
<li><button class="w-full min-h-11 px-3 py-2 border-0 cursor-pointer font-twr17-sans text-[0.8rem] bg-twr17-face text-twr17-ink twr17-ridge active:twr17-ridge-in focus-visible:outline-2 focus-visible:outline-twr17-acc focus-visible:outline-offset-2" type="button">Random</button></li>
<li><button class="w-full min-h-11 px-3 py-2 border-0 cursor-pointer font-twr17-sans text-[0.8rem] bg-twr17-face text-twr17-ink twr17-ridge active:twr17-ridge-in focus-visible:outline-2 focus-visible:outline-twr17-acc focus-visible:outline-offset-2" type="button">List</button></li>
<li><button class="w-full min-h-11 px-3 py-2 border-0 cursor-pointer font-twr17-sans text-[0.8rem] bg-twr17-face text-twr17-ink twr17-ridge active:twr17-ridge-in focus-visible:outline-2 focus-visible:outline-twr17-acc focus-visible:outline-offset-2" type="button">Next »</button></li>
</ul>
</nav>
<div class="min-w-0 grid content-start gap-3 bg-twr17-face2 p-[clamp(0.85rem,0.6rem+1vw,1.25rem)] twr17-ridge">
<h4 class="m-0 font-twr17-serif text-[1rem] font-bold text-twr17-acc">Best viewed in</h4>
<p class="m-0 justify-self-start px-3 py-2 bg-twr17-hot text-white font-twr17-mono text-[0.72rem] tracking-[0.08em] animate-twr17-pulse motion-reduce:animate-none">800 × 600 · 256 colours</p>
<p class="m-0 min-w-0 text-[0.72rem] leading-relaxed text-pretty text-twr17-ink2">The badge pulses on a two second cycle and stops under reduced motion. The era flashed it; that is the one thing not worth reproducing.</p>
</div>
</aside>
</div>
</div>
</section>@import "tailwindcss";
@theme {
--color-twr17-face: #c8c8c8;
--color-twr17-face2: #dcdcdc;
--color-twr17-tileb: #bdbdbd;
--color-twr17-hi: #ffffff;
--color-twr17-sh: #7a7a7a;
--color-twr17-ink: #16161a;
--color-twr17-ink: oklch(0.20 0.006 280);
--color-twr17-ink2: #4a4a52;
--color-twr17-ink2: oklch(0.42 0.01 280);
--color-twr17-acc: #0f3fa8;
--color-twr17-acc: oklch(0.42 0.19 262);
--color-twr17-hot: #a01414;
--color-twr17-hot: oklch(0.43 0.18 27);
--font-twr17-serif: Georgia, ui-serif, 'Times New Roman', Times, serif;
--font-twr17-sans: Verdana, ui-sans-serif, Geneva, 'Trebuchet MS', system-ui, sans-serif;
--font-twr17-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
--animate-twr17-pulse: twr-17-pulse 2s ease-in-out infinite;
}
/* border-style: ridge derives its tones from border-color by an unspecified
rule, so it differs per engine and occupies layout space. Four insets do not. */
@utility twr17-ridge {
box-shadow: inset 2px 2px 0 0 var(--color-twr17-hi), inset -2px -2px 0 0 var(--color-twr17-sh), inset 4px 4px 0 0 var(--color-twr17-sh), inset -4px -4px 0 0 var(--color-twr17-hi);
}
@utility twr17-ridge-in {
box-shadow: inset 2px 2px 0 0 var(--color-twr17-sh), inset -2px -2px 0 0 var(--color-twr17-hi), inset 4px 4px 0 0 var(--color-twr17-hi), inset -4px -4px 0 0 var(--color-twr17-sh);
}
/* A pulse, not a blink: 2s period, and it stops entirely under motion-reduce. */
@keyframes twr-17-pulse {
0%,
100% {
transform: scale(1);
box-shadow: 0 0 0 rgba(160, 20, 20, 0);
}
50% {
transform: scale(1.03);
box-shadow: 0 0 12px rgba(160, 20, 20, 0.55);
}
}
.twr-17:has(#twr-17-theme:checked) {
--color-twr17-face: #2a2a2e;
--color-twr17-face2: #35353a;
--color-twr17-tileb: #303034;
--color-twr17-hi: #55555c;
--color-twr17-sh: #17171a;
--color-twr17-ink: #eeeef2;
--color-twr17-ink: oklch(0.95 0.004 280);
--color-twr17-ink2: #b0b0b8;
--color-twr17-ink2: oklch(0.76 0.008 280);
--color-twr17-acc: #8fb4ff;
--color-twr17-acc: oklch(0.79 0.12 262);
--color-twr17-hot: #ff8f8f;
--color-twr17-hot: oklch(0.78 0.14 27);
}@import "tailwindcss";
@theme {
--color-twr17-face: #c8c8c8;
--color-twr17-face2: #dcdcdc;
--color-twr17-tileb: #bdbdbd;
--color-twr17-hi: #ffffff;
--color-twr17-sh: #7a7a7a;
--color-twr17-ink: #16161a;
--color-twr17-ink: oklch(0.20 0.006 280);
--color-twr17-ink2: #4a4a52;
--color-twr17-ink2: oklch(0.42 0.01 280);
--color-twr17-acc: #0f3fa8;
--color-twr17-acc: oklch(0.42 0.19 262);
--color-twr17-hot: #a01414;
--color-twr17-hot: oklch(0.43 0.18 27);
--font-twr17-serif: Georgia, ui-serif, 'Times New Roman', Times, serif;
--font-twr17-sans: Verdana, ui-sans-serif, Geneva, 'Trebuchet MS', system-ui, sans-serif;
--font-twr17-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
--animate-twr17-pulse: twr-17-pulse 2s ease-in-out infinite;
}
/* border-style: ridge derives its tones from border-color by an unspecified
rule, so it differs per engine and occupies layout space. Four insets do not. */
@utility twr17-ridge {
box-shadow: inset 2px 2px 0 0 var(--color-twr17-hi), inset -2px -2px 0 0 var(--color-twr17-sh), inset 4px 4px 0 0 var(--color-twr17-sh), inset -4px -4px 0 0 var(--color-twr17-hi);
}
@utility twr17-ridge-in {
box-shadow: inset 2px 2px 0 0 var(--color-twr17-sh), inset -2px -2px 0 0 var(--color-twr17-hi), inset 4px 4px 0 0 var(--color-twr17-hi), inset -4px -4px 0 0 var(--color-twr17-sh);
}
/* A pulse, not a blink: 2s period, and it stops entirely under motion-reduce. */
@keyframes twr-17-pulse {
0%,
100% {
transform: scale(1);
box-shadow: 0 0 0 rgba(160, 20, 20, 0);
}
50% {
transform: scale(1.03);
box-shadow: 0 0 12px rgba(160, 20, 20, 0.55);
}
}
.twr-17:has(#twr-17-theme:checked) {
--color-twr17-face: #2a2a2e;
--color-twr17-face2: #35353a;
--color-twr17-tileb: #303034;
--color-twr17-hi: #55555c;
--color-twr17-sh: #17171a;
--color-twr17-ink: #eeeef2;
--color-twr17-ink: oklch(0.95 0.004 280);
--color-twr17-ink2: #b0b0b8;
--color-twr17-ink2: oklch(0.76 0.008 280);
--color-twr17-acc: #8fb4ff;
--color-twr17-acc: oklch(0.79 0.12 262);
--color-twr17-hot: #ff8f8f;
--color-twr17-hot: oklch(0.78 0.14 27);
}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-twr17-face: #c8c8c8;
--color-twr17-face2: #dcdcdc;
--color-twr17-tileb: #bdbdbd;
--color-twr17-hi: #ffffff;
--color-twr17-sh: #7a7a7a;
--color-twr17-ink: #16161a;
--color-twr17-ink: oklch(0.20 0.006 280);
--color-twr17-ink2: #4a4a52;
--color-twr17-ink2: oklch(0.42 0.01 280);
--color-twr17-acc: #0f3fa8;
--color-twr17-acc: oklch(0.42 0.19 262);
--color-twr17-hot: #a01414;
--color-twr17-hot: oklch(0.43 0.18 27);
--font-twr17-serif: Georgia, ui-serif, 'Times New Roman', Times, serif;
--font-twr17-sans: Verdana, ui-sans-serif, Geneva, 'Trebuchet MS', system-ui, sans-serif;
--font-twr17-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
--animate-twr17-pulse: twr-17-pulse 2s ease-in-out infinite;
}
/* border-style: ridge derives its tones from border-color by an unspecified
rule, so it differs per engine and occupies layout space. Four insets do not. */
@utility twr17-ridge {
box-shadow: inset 2px 2px 0 0 var(--color-twr17-hi), inset -2px -2px 0 0 var(--color-twr17-sh), inset 4px 4px 0 0 var(--color-twr17-sh), inset -4px -4px 0 0 var(--color-twr17-hi);
}
@utility twr17-ridge-in {
box-shadow: inset 2px 2px 0 0 var(--color-twr17-sh), inset -2px -2px 0 0 var(--color-twr17-hi), inset 4px 4px 0 0 var(--color-twr17-hi), inset -4px -4px 0 0 var(--color-twr17-sh);
}
/* A pulse, not a blink: 2s period, and it stops entirely under motion-reduce. */
@keyframes twr-17-pulse {
0%,
100% {
transform: scale(1);
box-shadow: 0 0 0 rgba(160, 20, 20, 0);
}
50% {
transform: scale(1.03);
box-shadow: 0 0 12px rgba(160, 20, 20, 0.55);
}
}
.twr-17:has(#twr-17-theme:checked) {
--color-twr17-face: #2a2a2e;
--color-twr17-face2: #35353a;
--color-twr17-tileb: #303034;
--color-twr17-hi: #55555c;
--color-twr17-sh: #17171a;
--color-twr17-ink: #eeeef2;
--color-twr17-ink: oklch(0.95 0.004 280);
--color-twr17-ink2: #b0b0b8;
--color-twr17-ink2: oklch(0.76 0.008 280);
--color-twr17-acc: #8fb4ff;
--color-twr17-acc: oklch(0.79 0.12 262);
--color-twr17-hot: #ff8f8f;
--color-twr17-hot: oklch(0.78 0.14 27);
}@import "tailwindcss";
@theme {
--color-twr17-face: #c8c8c8;
--color-twr17-face2: #dcdcdc;
--color-twr17-tileb: #bdbdbd;
--color-twr17-hi: #ffffff;
--color-twr17-sh: #7a7a7a;
--color-twr17-ink: #16161a;
--color-twr17-ink: oklch(0.20 0.006 280);
--color-twr17-ink2: #4a4a52;
--color-twr17-ink2: oklch(0.42 0.01 280);
--color-twr17-acc: #0f3fa8;
--color-twr17-acc: oklch(0.42 0.19 262);
--color-twr17-hot: #a01414;
--color-twr17-hot: oklch(0.43 0.18 27);
--font-twr17-serif: Georgia, ui-serif, 'Times New Roman', Times, serif;
--font-twr17-sans: Verdana, ui-sans-serif, Geneva, 'Trebuchet MS', system-ui, sans-serif;
--font-twr17-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
--animate-twr17-pulse: twr-17-pulse 2s ease-in-out infinite;
}
/* border-style: ridge derives its tones from border-color by an unspecified
rule, so it differs per engine and occupies layout space. Four insets do not. */
@utility twr17-ridge {
box-shadow: inset 2px 2px 0 0 var(--color-twr17-hi), inset -2px -2px 0 0 var(--color-twr17-sh), inset 4px 4px 0 0 var(--color-twr17-sh), inset -4px -4px 0 0 var(--color-twr17-hi);
}
@utility twr17-ridge-in {
box-shadow: inset 2px 2px 0 0 var(--color-twr17-sh), inset -2px -2px 0 0 var(--color-twr17-hi), inset 4px 4px 0 0 var(--color-twr17-hi), inset -4px -4px 0 0 var(--color-twr17-sh);
}
/* A pulse, not a blink: 2s period, and it stops entirely under motion-reduce. */
@keyframes twr-17-pulse {
0%,
100% {
transform: scale(1);
box-shadow: 0 0 0 rgba(160, 20, 20, 0);
}
50% {
transform: scale(1.03);
box-shadow: 0 0 12px rgba(160, 20, 20, 0.55);
}
}
.twr-17:has(#twr-17-theme:checked) {
--color-twr17-face: #2a2a2e;
--color-twr17-face2: #35353a;
--color-twr17-tileb: #303034;
--color-twr17-hi: #55555c;
--color-twr17-sh: #17171a;
--color-twr17-ink: #eeeef2;
--color-twr17-ink: oklch(0.95 0.004 280);
--color-twr17-ink2: #b0b0b8;
--color-twr17-ink2: oklch(0.76 0.008 280);
--color-twr17-acc: #8fb4ff;
--color-twr17-acc: oklch(0.79 0.12 262);
--color-twr17-hot: #ff8f8f;
--color-twr17-hot: oklch(0.78 0.14 27);
}<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=Verdana&family=Georgia&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=Verdana&family=Georgia&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. - There is an irony this demo names rather than dodges: putting appearance in the markup is what
<font>was reaching for. The difference is that utilities are compiled from@themetokens with variants and a scale, not ad-hoc presentational HTML. - The ridge is an
@utilitybecause it appears on eight panels and needs anactive:partner — the same test applied in demo 04, and the opposite conclusion from demo 13. last:border-b-0 last:pb-0replaces the CSS version's:last-childrule, so the entry separator logic sits on the entry.- The badge pulse is an
--animate-*token with amotion-reduce:animate-nonetwin; retiming it is a token edit, not a markup edit. - The tiled ground is one arbitrary
repeating-conic-gradientplus a[background-size:8px_8px]on the section — the tiled GIF with no file. - Colours stay
@themetokens even where each is used once, because the dark-ground flip re-points all of them in a single rule.
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 Nineties Web Guestbook Widget
Source: https://codefronts.com/design-styles/tailwind-retro-ui/tailwind-nineties-web-guestbook-widget/
A personal-homepage guestbook: odometer hit counter, webring navigation, signed entries and a “best viewed in” badge, on the grey ground and ridged borders of the era. The point it makes is the one that matters most about this period — reproduce the look, never the markup, because everything that made these pages work is now the reason they fail.
## HTML
```html
<section class="twr-17 relative block w-full min-h-svh box-border bg-twr17-face text-twr17-ink font-twr17-sans [background-image:repeating-conic-gradient(var(--color-twr17-tileb)_0%_25%,var(--color-twr17-face)_0%_50%)] [background-size:8px_8px]" aria-labelledby="twr-17-title">
<h2 class="absolute size-px overflow-hidden [clip-path:inset(50%)] whitespace-nowrap" id="twr-17-title">Nineties personal homepage guestbook</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-17-theme">
<label class="grid place-items-center size-11 cursor-pointer bg-twr17-face2 text-twr17-ink2 twr17-ridge peer-focus-visible:outline-2 peer-focus-visible:outline-twr17-acc peer-focus-visible:outline-offset-2" for="twr-17-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 ground</span>
</label>
</div>
<div class="grid grid-cols-[minmax(0,1fr)] gap-5 max-w-[64rem] mx-auto px-[clamp(0.75rem,0.5rem+2vw,2rem)] pt-[clamp(3.5rem,3rem+3vw,5rem)] pb-16">
<header class="min-w-0 grid justify-items-center gap-1.5 text-center bg-twr17-face2 px-4 py-5 twr17-ridge">
<h3 class="m-0 min-w-0 font-twr17-serif font-bold text-[clamp(1.5rem,1.1rem+2.4vw,2.6rem)] text-twr17-acc [text-shadow:1px_1px_0_var(--color-twr17-hi)]">Dave’s Homepage</h3>
<p class="m-0 font-twr17-mono text-[0.75rem] text-twr17-hot">~ under construction since 1997 ~</p>
</header>
<div class="grid grid-cols-[minmax(0,1fr)] gap-5 lg:grid-cols-[minmax(0,1.6fr)_minmax(0,1fr)] lg:items-start">
<article class="min-w-0 grid content-start gap-3 bg-twr17-face2 p-[clamp(0.85rem,0.6rem+1vw,1.25rem)] twr17-ridge" aria-labelledby="twr-17-gb">
<h4 class="m-0 font-twr17-serif text-[1rem] font-bold text-twr17-acc" id="twr-17-gb">Sign my guestbook</h4>
<ol class="list-none m-0 p-0 grid gap-3 min-w-0">
<li class="flex gap-3 min-w-0 border-b border-twr17-sh/40 pb-3 last:border-b-0 last:pb-0">
<span class="flex-none grid place-items-center size-10 bg-twr17-acc text-white font-twr17-sans text-[0.75rem] font-bold twr17-ridge" aria-hidden="true">AC</span>
<div class="min-w-0 grid gap-1">
<p class="m-0 min-w-0 font-twr17-sans text-[0.72rem] text-twr17-ink2"><b class="font-bold text-twr17-ink">Alex Chen</b> · 14 Nov 1997 · Manchester</p>
<p class="m-0 min-w-0 font-twr17-serif text-[0.92rem] leading-normal text-pretty">Great page! Love the MIDI. Come see my Star Trek shrine sometime.</p>
</div>
</li>
<li class="flex gap-3 min-w-0 border-b border-twr17-sh/40 pb-3 last:border-b-0 last:pb-0">
<span class="flex-none grid place-items-center size-10 bg-twr17-acc text-white font-twr17-sans text-[0.75rem] font-bold twr17-ridge" aria-hidden="true">PS</span>
<div class="min-w-0 grid gap-1">
<p class="m-0 min-w-0 font-twr17-sans text-[0.72rem] text-twr17-ink2"><b class="font-bold text-twr17-ink">Priya Sharma</b> · 2 Dec 1997 · Leeds</p>
<p class="m-0 min-w-0 font-twr17-serif text-[0.92rem] leading-normal text-pretty">Added you to the ring. The tiled background is a bold choice and I respect it.</p>
</div>
</li>
<li class="flex gap-3 min-w-0 border-b border-twr17-sh/40 pb-3 last:border-b-0 last:pb-0">
<span class="flex-none grid place-items-center size-10 bg-twr17-acc text-white font-twr17-sans text-[0.75rem] font-bold twr17-ridge" aria-hidden="true">JL</span>
<div class="min-w-0 grid gap-1">
<p class="m-0 min-w-0 font-twr17-sans text-[0.72rem] text-twr17-ink2"><b class="font-bold text-twr17-ink">Jordan Lee</b> · 19 Jan 1998 · Bristol</p>
<p class="m-0 min-w-0 font-twr17-serif text-[0.92rem] leading-normal text-pretty">Your links page is the best on the web. Signed, a fellow traveller.</p>
</div>
</li>
</ol>
<p class="m-0 min-w-0 text-[0.75rem] leading-relaxed text-pretty text-twr17-ink2">Entries are a real ordered list. No table, no spacer image, no <code class="font-twr17-mono bg-twr17-face px-1"><font></code> anywhere in this widget.</p>
</article>
<aside class="min-w-0 grid content-start gap-5">
<div class="min-w-0 grid content-start gap-3 bg-twr17-face2 p-[clamp(0.85rem,0.6rem+1vw,1.25rem)] twr17-ridge">
<h4 class="m-0 font-twr17-serif text-[1rem] font-bold text-twr17-acc">Visitors</h4>
<div class="flex gap-1 min-w-0" role="img" aria-label="Visitor count: 0 0 4 2 1 8 7">
<span class="grid place-items-center w-6 h-9 bg-[#111] text-[#7dff9a] font-twr17-mono text-[1.05rem] tabular-nums [box-shadow:inset_0_2px_4px_rgba(0,0,0,0.9)]">0</span><span class="grid place-items-center w-6 h-9 bg-[#111] text-[#7dff9a] font-twr17-mono text-[1.05rem] tabular-nums [box-shadow:inset_0_2px_4px_rgba(0,0,0,0.9)]">0</span><span class="grid place-items-center w-6 h-9 bg-[#111] text-[#7dff9a] font-twr17-mono text-[1.05rem] tabular-nums [box-shadow:inset_0_2px_4px_rgba(0,0,0,0.9)]">4</span><span class="grid place-items-center w-6 h-9 bg-[#111] text-[#7dff9a] font-twr17-mono text-[1.05rem] tabular-nums [box-shadow:inset_0_2px_4px_rgba(0,0,0,0.9)]">2</span><span class="grid place-items-center w-6 h-9 bg-[#111] text-[#7dff9a] font-twr17-mono text-[1.05rem] tabular-nums [box-shadow:inset_0_2px_4px_rgba(0,0,0,0.9)]">1</span><span class="grid place-items-center w-6 h-9 bg-[#111] text-[#7dff9a] font-twr17-mono text-[1.05rem] tabular-nums [box-shadow:inset_0_2px_4px_rgba(0,0,0,0.9)]">8</span><span class="grid place-items-center w-6 h-9 bg-[#111] text-[#7dff9a] font-twr17-mono text-[1.05rem] tabular-nums [box-shadow:inset_0_2px_4px_rgba(0,0,0,0.9)]">7</span>
</div>
<p class="m-0 text-[0.72rem] text-twr17-ink2">since 12 August 1997</p>
</div>
<nav class="min-w-0 grid content-start gap-3 bg-twr17-face2 p-[clamp(0.85rem,0.6rem+1vw,1.25rem)] twr17-ridge" aria-labelledby="twr-17-ring">
<h4 class="m-0 font-twr17-serif text-[1rem] font-bold text-twr17-acc" id="twr-17-ring">The Retro Web Ring</h4>
<ul class="list-none m-0 p-0 grid gap-2 min-w-0 sm:grid-cols-2">
<li><button class="w-full min-h-11 px-3 py-2 border-0 cursor-pointer font-twr17-sans text-[0.8rem] bg-twr17-face text-twr17-ink twr17-ridge active:twr17-ridge-in focus-visible:outline-2 focus-visible:outline-twr17-acc focus-visible:outline-offset-2" type="button">« Prev</button></li>
<li><button class="w-full min-h-11 px-3 py-2 border-0 cursor-pointer font-twr17-sans text-[0.8rem] bg-twr17-face text-twr17-ink twr17-ridge active:twr17-ridge-in focus-visible:outline-2 focus-visible:outline-twr17-acc focus-visible:outline-offset-2" type="button">Random</button></li>
<li><button class="w-full min-h-11 px-3 py-2 border-0 cursor-pointer font-twr17-sans text-[0.8rem] bg-twr17-face text-twr17-ink twr17-ridge active:twr17-ridge-in focus-visible:outline-2 focus-visible:outline-twr17-acc focus-visible:outline-offset-2" type="button">List</button></li>
<li><button class="w-full min-h-11 px-3 py-2 border-0 cursor-pointer font-twr17-sans text-[0.8rem] bg-twr17-face text-twr17-ink twr17-ridge active:twr17-ridge-in focus-visible:outline-2 focus-visible:outline-twr17-acc focus-visible:outline-offset-2" type="button">Next »</button></li>
</ul>
</nav>
<div class="min-w-0 grid content-start gap-3 bg-twr17-face2 p-[clamp(0.85rem,0.6rem+1vw,1.25rem)] twr17-ridge">
<h4 class="m-0 font-twr17-serif text-[1rem] font-bold text-twr17-acc">Best viewed in</h4>
<p class="m-0 justify-self-start px-3 py-2 bg-twr17-hot text-white font-twr17-mono text-[0.72rem] tracking-[0.08em] animate-twr17-pulse motion-reduce:animate-none">800 × 600 · 256 colours</p>
<p class="m-0 min-w-0 text-[0.72rem] leading-relaxed text-pretty text-twr17-ink2">The badge pulses on a two second cycle and stops under reduced motion. The era flashed it; that is the one thing not worth reproducing.</p>
</div>
</aside>
</div>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-twr17-face: #c8c8c8;
--color-twr17-face2: #dcdcdc;
--color-twr17-tileb: #bdbdbd;
--color-twr17-hi: #ffffff;
--color-twr17-sh: #7a7a7a;
--color-twr17-ink: #16161a;
--color-twr17-ink: oklch(0.20 0.006 280);
--color-twr17-ink2: #4a4a52;
--color-twr17-ink2: oklch(0.42 0.01 280);
--color-twr17-acc: #0f3fa8;
--color-twr17-acc: oklch(0.42 0.19 262);
--color-twr17-hot: #a01414;
--color-twr17-hot: oklch(0.43 0.18 27);
--font-twr17-serif: Georgia, ui-serif, 'Times New Roman', Times, serif;
--font-twr17-sans: Verdana, ui-sans-serif, Geneva, 'Trebuchet MS', system-ui, sans-serif;
--font-twr17-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
--animate-twr17-pulse: twr-17-pulse 2s ease-in-out infinite;
}
/* border-style: ridge derives its tones from border-color by an unspecified
rule, so it differs per engine and occupies layout space. Four insets do not. */
@utility twr17-ridge {
box-shadow: inset 2px 2px 0 0 var(--color-twr17-hi), inset -2px -2px 0 0 var(--color-twr17-sh), inset 4px 4px 0 0 var(--color-twr17-sh), inset -4px -4px 0 0 var(--color-twr17-hi);
}
@utility twr17-ridge-in {
box-shadow: inset 2px 2px 0 0 var(--color-twr17-sh), inset -2px -2px 0 0 var(--color-twr17-hi), inset 4px 4px 0 0 var(--color-twr17-hi), inset -4px -4px 0 0 var(--color-twr17-sh);
}
/* A pulse, not a blink: 2s period, and it stops entirely under motion-reduce. */
@keyframes twr-17-pulse {
0%,
100% {
transform: scale(1);
box-shadow: 0 0 0 rgba(160, 20, 20, 0);
}
50% {
transform: scale(1.03);
box-shadow: 0 0 12px rgba(160, 20, 20, 0.55);
}
}
.twr-17:has(#twr-17-theme:checked) {
--color-twr17-face: #2a2a2e;
--color-twr17-face2: #35353a;
--color-twr17-tileb: #303034;
--color-twr17-hi: #55555c;
--color-twr17-sh: #17171a;
--color-twr17-ink: #eeeef2;
--color-twr17-ink: oklch(0.95 0.004 280);
--color-twr17-ink2: #b0b0b8;
--color-twr17-ink2: oklch(0.76 0.008 280);
--color-twr17-acc: #8fb4ff;
--color-twr17-acc: oklch(0.79 0.12 262);
--color-twr17-hot: #ff8f8f;
--color-twr17-hot: oklch(0.78 0.14 27);
}
```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 Nineties Web Guestbook Widget
Source: https://codefronts.com/design-styles/tailwind-retro-ui/tailwind-nineties-web-guestbook-widget/
A personal-homepage guestbook: odometer hit counter, webring navigation, signed entries and a “best viewed in” badge, on the grey ground and ridged borders of the era. The point it makes is the one that matters most about this period — reproduce the look, never the markup, because everything that made these pages work is now the reason they fail.
## HTML
```html
<section class="twr-17 relative block w-full min-h-svh box-border bg-twr17-face text-twr17-ink font-twr17-sans [background-image:repeating-conic-gradient(var(--color-twr17-tileb)_0%_25%,var(--color-twr17-face)_0%_50%)] [background-size:8px_8px]" aria-labelledby="twr-17-title">
<h2 class="absolute size-px overflow-hidden [clip-path:inset(50%)] whitespace-nowrap" id="twr-17-title">Nineties personal homepage guestbook</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-17-theme">
<label class="grid place-items-center size-11 cursor-pointer bg-twr17-face2 text-twr17-ink2 twr17-ridge peer-focus-visible:outline-2 peer-focus-visible:outline-twr17-acc peer-focus-visible:outline-offset-2" for="twr-17-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 ground</span>
</label>
</div>
<div class="grid grid-cols-[minmax(0,1fr)] gap-5 max-w-[64rem] mx-auto px-[clamp(0.75rem,0.5rem+2vw,2rem)] pt-[clamp(3.5rem,3rem+3vw,5rem)] pb-16">
<header class="min-w-0 grid justify-items-center gap-1.5 text-center bg-twr17-face2 px-4 py-5 twr17-ridge">
<h3 class="m-0 min-w-0 font-twr17-serif font-bold text-[clamp(1.5rem,1.1rem+2.4vw,2.6rem)] text-twr17-acc [text-shadow:1px_1px_0_var(--color-twr17-hi)]">Dave’s Homepage</h3>
<p class="m-0 font-twr17-mono text-[0.75rem] text-twr17-hot">~ under construction since 1997 ~</p>
</header>
<div class="grid grid-cols-[minmax(0,1fr)] gap-5 lg:grid-cols-[minmax(0,1.6fr)_minmax(0,1fr)] lg:items-start">
<article class="min-w-0 grid content-start gap-3 bg-twr17-face2 p-[clamp(0.85rem,0.6rem+1vw,1.25rem)] twr17-ridge" aria-labelledby="twr-17-gb">
<h4 class="m-0 font-twr17-serif text-[1rem] font-bold text-twr17-acc" id="twr-17-gb">Sign my guestbook</h4>
<ol class="list-none m-0 p-0 grid gap-3 min-w-0">
<li class="flex gap-3 min-w-0 border-b border-twr17-sh/40 pb-3 last:border-b-0 last:pb-0">
<span class="flex-none grid place-items-center size-10 bg-twr17-acc text-white font-twr17-sans text-[0.75rem] font-bold twr17-ridge" aria-hidden="true">AC</span>
<div class="min-w-0 grid gap-1">
<p class="m-0 min-w-0 font-twr17-sans text-[0.72rem] text-twr17-ink2"><b class="font-bold text-twr17-ink">Alex Chen</b> · 14 Nov 1997 · Manchester</p>
<p class="m-0 min-w-0 font-twr17-serif text-[0.92rem] leading-normal text-pretty">Great page! Love the MIDI. Come see my Star Trek shrine sometime.</p>
</div>
</li>
<li class="flex gap-3 min-w-0 border-b border-twr17-sh/40 pb-3 last:border-b-0 last:pb-0">
<span class="flex-none grid place-items-center size-10 bg-twr17-acc text-white font-twr17-sans text-[0.75rem] font-bold twr17-ridge" aria-hidden="true">PS</span>
<div class="min-w-0 grid gap-1">
<p class="m-0 min-w-0 font-twr17-sans text-[0.72rem] text-twr17-ink2"><b class="font-bold text-twr17-ink">Priya Sharma</b> · 2 Dec 1997 · Leeds</p>
<p class="m-0 min-w-0 font-twr17-serif text-[0.92rem] leading-normal text-pretty">Added you to the ring. The tiled background is a bold choice and I respect it.</p>
</div>
</li>
<li class="flex gap-3 min-w-0 border-b border-twr17-sh/40 pb-3 last:border-b-0 last:pb-0">
<span class="flex-none grid place-items-center size-10 bg-twr17-acc text-white font-twr17-sans text-[0.75rem] font-bold twr17-ridge" aria-hidden="true">JL</span>
<div class="min-w-0 grid gap-1">
<p class="m-0 min-w-0 font-twr17-sans text-[0.72rem] text-twr17-ink2"><b class="font-bold text-twr17-ink">Jordan Lee</b> · 19 Jan 1998 · Bristol</p>
<p class="m-0 min-w-0 font-twr17-serif text-[0.92rem] leading-normal text-pretty">Your links page is the best on the web. Signed, a fellow traveller.</p>
</div>
</li>
</ol>
<p class="m-0 min-w-0 text-[0.75rem] leading-relaxed text-pretty text-twr17-ink2">Entries are a real ordered list. No table, no spacer image, no <code class="font-twr17-mono bg-twr17-face px-1"><font></code> anywhere in this widget.</p>
</article>
<aside class="min-w-0 grid content-start gap-5">
<div class="min-w-0 grid content-start gap-3 bg-twr17-face2 p-[clamp(0.85rem,0.6rem+1vw,1.25rem)] twr17-ridge">
<h4 class="m-0 font-twr17-serif text-[1rem] font-bold text-twr17-acc">Visitors</h4>
<div class="flex gap-1 min-w-0" role="img" aria-label="Visitor count: 0 0 4 2 1 8 7">
<span class="grid place-items-center w-6 h-9 bg-[#111] text-[#7dff9a] font-twr17-mono text-[1.05rem] tabular-nums [box-shadow:inset_0_2px_4px_rgba(0,0,0,0.9)]">0</span><span class="grid place-items-center w-6 h-9 bg-[#111] text-[#7dff9a] font-twr17-mono text-[1.05rem] tabular-nums [box-shadow:inset_0_2px_4px_rgba(0,0,0,0.9)]">0</span><span class="grid place-items-center w-6 h-9 bg-[#111] text-[#7dff9a] font-twr17-mono text-[1.05rem] tabular-nums [box-shadow:inset_0_2px_4px_rgba(0,0,0,0.9)]">4</span><span class="grid place-items-center w-6 h-9 bg-[#111] text-[#7dff9a] font-twr17-mono text-[1.05rem] tabular-nums [box-shadow:inset_0_2px_4px_rgba(0,0,0,0.9)]">2</span><span class="grid place-items-center w-6 h-9 bg-[#111] text-[#7dff9a] font-twr17-mono text-[1.05rem] tabular-nums [box-shadow:inset_0_2px_4px_rgba(0,0,0,0.9)]">1</span><span class="grid place-items-center w-6 h-9 bg-[#111] text-[#7dff9a] font-twr17-mono text-[1.05rem] tabular-nums [box-shadow:inset_0_2px_4px_rgba(0,0,0,0.9)]">8</span><span class="grid place-items-center w-6 h-9 bg-[#111] text-[#7dff9a] font-twr17-mono text-[1.05rem] tabular-nums [box-shadow:inset_0_2px_4px_rgba(0,0,0,0.9)]">7</span>
</div>
<p class="m-0 text-[0.72rem] text-twr17-ink2">since 12 August 1997</p>
</div>
<nav class="min-w-0 grid content-start gap-3 bg-twr17-face2 p-[clamp(0.85rem,0.6rem+1vw,1.25rem)] twr17-ridge" aria-labelledby="twr-17-ring">
<h4 class="m-0 font-twr17-serif text-[1rem] font-bold text-twr17-acc" id="twr-17-ring">The Retro Web Ring</h4>
<ul class="list-none m-0 p-0 grid gap-2 min-w-0 sm:grid-cols-2">
<li><button class="w-full min-h-11 px-3 py-2 border-0 cursor-pointer font-twr17-sans text-[0.8rem] bg-twr17-face text-twr17-ink twr17-ridge active:twr17-ridge-in focus-visible:outline-2 focus-visible:outline-twr17-acc focus-visible:outline-offset-2" type="button">« Prev</button></li>
<li><button class="w-full min-h-11 px-3 py-2 border-0 cursor-pointer font-twr17-sans text-[0.8rem] bg-twr17-face text-twr17-ink twr17-ridge active:twr17-ridge-in focus-visible:outline-2 focus-visible:outline-twr17-acc focus-visible:outline-offset-2" type="button">Random</button></li>
<li><button class="w-full min-h-11 px-3 py-2 border-0 cursor-pointer font-twr17-sans text-[0.8rem] bg-twr17-face text-twr17-ink twr17-ridge active:twr17-ridge-in focus-visible:outline-2 focus-visible:outline-twr17-acc focus-visible:outline-offset-2" type="button">List</button></li>
<li><button class="w-full min-h-11 px-3 py-2 border-0 cursor-pointer font-twr17-sans text-[0.8rem] bg-twr17-face text-twr17-ink twr17-ridge active:twr17-ridge-in focus-visible:outline-2 focus-visible:outline-twr17-acc focus-visible:outline-offset-2" type="button">Next »</button></li>
</ul>
</nav>
<div class="min-w-0 grid content-start gap-3 bg-twr17-face2 p-[clamp(0.85rem,0.6rem+1vw,1.25rem)] twr17-ridge">
<h4 class="m-0 font-twr17-serif text-[1rem] font-bold text-twr17-acc">Best viewed in</h4>
<p class="m-0 justify-self-start px-3 py-2 bg-twr17-hot text-white font-twr17-mono text-[0.72rem] tracking-[0.08em] animate-twr17-pulse motion-reduce:animate-none">800 × 600 · 256 colours</p>
<p class="m-0 min-w-0 text-[0.72rem] leading-relaxed text-pretty text-twr17-ink2">The badge pulses on a two second cycle and stops under reduced motion. The era flashed it; that is the one thing not worth reproducing.</p>
</div>
</aside>
</div>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-twr17-face: #c8c8c8;
--color-twr17-face2: #dcdcdc;
--color-twr17-tileb: #bdbdbd;
--color-twr17-hi: #ffffff;
--color-twr17-sh: #7a7a7a;
--color-twr17-ink: #16161a;
--color-twr17-ink: oklch(0.20 0.006 280);
--color-twr17-ink2: #4a4a52;
--color-twr17-ink2: oklch(0.42 0.01 280);
--color-twr17-acc: #0f3fa8;
--color-twr17-acc: oklch(0.42 0.19 262);
--color-twr17-hot: #a01414;
--color-twr17-hot: oklch(0.43 0.18 27);
--font-twr17-serif: Georgia, ui-serif, 'Times New Roman', Times, serif;
--font-twr17-sans: Verdana, ui-sans-serif, Geneva, 'Trebuchet MS', system-ui, sans-serif;
--font-twr17-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
--animate-twr17-pulse: twr-17-pulse 2s ease-in-out infinite;
}
/* border-style: ridge derives its tones from border-color by an unspecified
rule, so it differs per engine and occupies layout space. Four insets do not. */
@utility twr17-ridge {
box-shadow: inset 2px 2px 0 0 var(--color-twr17-hi), inset -2px -2px 0 0 var(--color-twr17-sh), inset 4px 4px 0 0 var(--color-twr17-sh), inset -4px -4px 0 0 var(--color-twr17-hi);
}
@utility twr17-ridge-in {
box-shadow: inset 2px 2px 0 0 var(--color-twr17-sh), inset -2px -2px 0 0 var(--color-twr17-hi), inset 4px 4px 0 0 var(--color-twr17-hi), inset -4px -4px 0 0 var(--color-twr17-sh);
}
/* A pulse, not a blink: 2s period, and it stops entirely under motion-reduce. */
@keyframes twr-17-pulse {
0%,
100% {
transform: scale(1);
box-shadow: 0 0 0 rgba(160, 20, 20, 0);
}
50% {
transform: scale(1.03);
box-shadow: 0 0 12px rgba(160, 20, 20, 0.55);
}
}
.twr-17:has(#twr-17-theme:checked) {
--color-twr17-face: #2a2a2e;
--color-twr17-face2: #35353a;
--color-twr17-tileb: #303034;
--color-twr17-hi: #55555c;
--color-twr17-sh: #17171a;
--color-twr17-ink: #eeeef2;
--color-twr17-ink: oklch(0.95 0.004 280);
--color-twr17-ink2: #b0b0b8;
--color-twr17-ink2: oklch(0.76 0.008 280);
--color-twr17-acc: #8fb4ff;
--color-twr17-acc: oklch(0.79 0.12 262);
--color-twr17-hot: #ff8f8f;
--color-twr17-hot: oklch(0.78 0.14 27);
}
```How this works
Reproduce the look, never the markup. Everything the era used is now a defect: tables for layout destroy reading order, <blink> is unimplemented for good accessibility reasons, spacer GIFs are meaningless images in the accessibility tree, and <font> puts presentation beyond any user stylesheet. Utilities produce the identical visual with none of it — and there is a small irony worth naming: a class-per-property markup style is what 1997 was reaching for with <font> tags, except utilities are a compiled design system with tokens, variants and a scale rather than ad-hoc presentational HTML.
The ridged border is two insets, not a keyword. border-style: ridge derives its tones from border-color by an unspecified rule, so it differs per engine and shifts when the background changes. Four inset shadows give the same groove everywhere and cost no layout space, so hover and active states never reflow the widget. Because it appears on eight elements and needs an active: partner, it is an @utility rather than an inline value — the same judgement as demo 04.
The counter is the motif, and it should count something real. An odometer was a CGI script writing a number into a GIF. As styled digit wells the look survives and the number becomes selectable text, which is strictly better. If the count is decorative, label it as such; if it is real, it should be a live figure rather than a picture of one.
The badge is a pulse, not a blink. The era's badges flashed hard and fast, which is what the seizure threshold exists to prevent. Here it is a slow scale-and-glow on a two second period, stopped entirely under motion-reduce. It reads period-correct at a glance and puts nobody at risk — the trade this whole collection is built on.
Make it yours
- Change the tiled ground by editing the section's arbitrary
[background-size:...]— 8px reads as a coarse 90s tile, 4px as fine noise. - Swap
--color-twr17-acc; the ridged borders are neutral and work against any hue. - Add a counter digit by copying one well — the odometer is a flex row with no fixed width.
- Extend the webring to five links; the nav is a plain list and wraps on its own.
- Retime or remove the badge pulse in the
--animate-twr17-pulseentry without touching its styling. - Replace entry initials with drawn glyphs; they are grid cells with no image dependency.
Gotchas — read before shipping
- Tables for layout,
<blink>,<font>and spacer GIFs are the era's real techniques and every one is an accessibility failure today. Reproduce the appearance, not the source. border-style: ridgeandgrooverender differently in every engine and shift layout when toggled. Use the inset utility.- Grey-on-grey was everywhere in 1997 and most of those pairings sit near 3:1. Keep the ground and raise the ink.
- A fast flashing badge is a genuine seizure risk. Slow it to a pulse and stop it under
motion-reduce— nothing about the aesthetic requires the speed. - Do not confuse utilities with presentational HTML. Both put appearance in the markup, but one is a compiled token system and the other was a dead end; keeping colours as
@themetokens rather than raw hexes is what makes the difference real. last:border-b-0only works on the last element child. A trailing comment node is fine, but a trailing text node inside the list can break the assumption in some structures.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
repeating-conic-gradient for the tiled ground lands at Chrome 69 / Safari 12.1 / Firefox 83 and degrades to flat grey without it, which is if anything more period-accurate. Tailwind v4's generated sheet and @utility set the floor at Chrome 111+, Safari 16.4+, Firefox 128+.