11 / 32
All 32 in 32 Tailwind CSS Tabs
- 01Tailwind CSS Tabs with Sliding Underline Indicator
- 02Neon Glow Tailwind Tabs with Per-Tab Accent Colors
- 03Neo-Brutalist Tailwind Tabs with Press Effect
- 04Color-Coded Tailwind CSS Tabs Component
- 05VS Code Style File Tabs in Tailwind CSS
- 06iOS Segmented Control in Tailwind CSS
- 07Bento Grid Card Tabs in Tailwind CSS
- 08Vertical Tabs with Timeline Progress in Tailwind CSS
- 09Terminal Window Tabs in Tailwind CSS (Retro CRT)
- 10Expanding Icon Tabs in Tailwind CSS
- 11Typewriter Animation Tabs in Tailwind CSS
- 12Tailwind Tabs with Particle Burst Animation
- 13Animated Counter Tabs in Tailwind CSS (Type Specimen)
- 14Oversized Cropped Text Tabs in Tailwind CSS
- 15Tailwind Sidebar Navigation with Sliding Indicator
- 16Tailwind Bottom Navigation Bar with Expanding Labels
- 17Variable Font Weight Tabs in Tailwind CSS
- 18Uppercase Tabs with Thick Sliding Bar in Tailwind CSS
- 19Drop Cap Initial Letter Tabs in Tailwind CSS
- 20Industrial Stencil Tabs in Tailwind CSS
- 21Bracket Selection Tabs in Tailwind CSS
- 22Bauhaus Color Block Tabs in Tailwind CSS
- 23Inverted Rubber Stamp Tabs in Tailwind CSS
- 24Outlined Tabs with Corner Markers in Tailwind CSS
- 25Tailwind Pill Tab Bar with Icons
- 26Risograph Print Effect Tabs in Tailwind CSS
- 27Animated SVG Underline Tabs in Tailwind CSS
- 28Font Specimen Glyph Tabs in Tailwind CSS
- 29Sliding Pill Tabs with Icons in Tailwind CSS
- 30Folded Corner Tabs in Tailwind CSS (clip-path)
- 31Bookmark Ribbon Navigation in Tailwind CSS
- 32Neumorphic Inset Tabs in Tailwind CSS
Typewriter Animation Tabs in Tailwind CSS
Choosing a tab types the panel's line out character by character, then leaves a blinking amber caret. The original's steps(20) was a magic number that only fit one string; the port gives each line its own [--n:16] and animates to calc(var(--n)*1ch) in exactly steps(var(--n)), so any string types one glyph per step.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="ttb-11 group/tabs grid min-h-svh w-full place-items-center bg-ttb11-bg px-5 py-14 font-ttb11-mono text-ttb11-ink" aria-label="Typewriter animation tabs demo">
<div class="grid w-full max-w-lg justify-items-center gap-7">
<div class="w-full rounded-2xl bg-ttb11-card p-2 ring-1 ring-white/8">
<div role="radiogroup" aria-label="Message" class="flex gap-1 rounded-xl bg-black/25 p-1">
<label class="flex min-h-10 flex-1 cursor-pointer items-center justify-center rounded-lg text-[11px] font-semibold tracking-[0.12em] text-ttb11-mut uppercase transition-colors hover:text-ttb11-ink has-checked:bg-ttb11-amber/12 has-checked:text-ttb11-amber has-focus-visible:outline-1 has-focus-visible:-outline-offset-2 has-focus-visible:outline-dashed has-focus-visible:outline-ttb11-amber"><input class="sr-only" type="radio" name="ttb11" id="ttb11-r1" checked>Greet</label>
<label class="flex min-h-10 flex-1 cursor-pointer items-center justify-center rounded-lg text-[11px] font-semibold tracking-[0.12em] text-ttb11-mut uppercase transition-colors hover:text-ttb11-ink has-checked:bg-ttb11-amber/12 has-checked:text-ttb11-amber has-focus-visible:outline-1 has-focus-visible:-outline-offset-2 has-focus-visible:outline-dashed has-focus-visible:outline-ttb11-amber"><input class="sr-only" type="radio" name="ttb11" id="ttb11-r2">Status</label>
<label class="flex min-h-10 flex-1 cursor-pointer items-center justify-center rounded-lg text-[11px] font-semibold tracking-[0.12em] text-ttb11-mut uppercase transition-colors hover:text-ttb11-ink has-checked:bg-ttb11-amber/12 has-checked:text-ttb11-amber has-focus-visible:outline-1 has-focus-visible:-outline-offset-2 has-focus-visible:outline-dashed has-focus-visible:outline-ttb11-amber"><input class="sr-only" type="radio" name="ttb11" id="ttb11-r3">Goodbye</label>
</div>
<div class="flex min-h-24 items-center px-4 text-lg">
<p class="hidden group-has-[#ttb11-r1:checked]/tabs:block"><span aria-hidden="true" class="mr-2 text-ttb11-dim">›</span><span class="inline-block w-[calc(var(--n)*1ch)] animate-ttb11-type overflow-hidden border-r-2 border-ttb11-amber align-bottom whitespace-nowrap text-ttb11-amber [--n:14] motion-reduce:animate-none">Hello, friend.</span></p>
<p class="hidden group-has-[#ttb11-r2:checked]/tabs:block"><span aria-hidden="true" class="mr-2 text-ttb11-dim">›</span><span class="inline-block w-[calc(var(--n)*1ch)] animate-ttb11-type overflow-hidden border-r-2 border-ttb11-amber align-bottom whitespace-nowrap text-ttb11-amber [--n:16] motion-reduce:animate-none">Systems nominal.</span></p>
<p class="hidden group-has-[#ttb11-r3:checked]/tabs:block"><span aria-hidden="true" class="mr-2 text-ttb11-dim">›</span><span class="inline-block w-[calc(var(--n)*1ch)] animate-ttb11-type overflow-hidden border-r-2 border-ttb11-amber align-bottom whitespace-nowrap text-ttb11-amber [--n:17] motion-reduce:animate-none">See you tomorrow.</span></p>
</div>
</div>
<p aria-hidden="true" class="rounded-full border border-white/10 px-3.5 py-2 text-[11.5px] text-ttb11-mut">[--n:16] · w-[calc(var(--n)*1ch)] · steps(var(--n))</p>
</div>
</section><section class="ttb-11 group/tabs grid min-h-svh w-full place-items-center bg-ttb11-bg px-5 py-14 font-ttb11-mono text-ttb11-ink" aria-label="Typewriter animation tabs demo">
<div class="grid w-full max-w-lg justify-items-center gap-7">
<div class="w-full rounded-2xl bg-ttb11-card p-2 ring-1 ring-white/8">
<div role="radiogroup" aria-label="Message" class="flex gap-1 rounded-xl bg-black/25 p-1">
<label class="flex min-h-10 flex-1 cursor-pointer items-center justify-center rounded-lg text-[11px] font-semibold tracking-[0.12em] text-ttb11-mut uppercase transition-colors hover:text-ttb11-ink has-checked:bg-ttb11-amber/12 has-checked:text-ttb11-amber has-focus-visible:outline-1 has-focus-visible:-outline-offset-2 has-focus-visible:outline-dashed has-focus-visible:outline-ttb11-amber"><input class="sr-only" type="radio" name="ttb11" id="ttb11-r1" checked>Greet</label>
<label class="flex min-h-10 flex-1 cursor-pointer items-center justify-center rounded-lg text-[11px] font-semibold tracking-[0.12em] text-ttb11-mut uppercase transition-colors hover:text-ttb11-ink has-checked:bg-ttb11-amber/12 has-checked:text-ttb11-amber has-focus-visible:outline-1 has-focus-visible:-outline-offset-2 has-focus-visible:outline-dashed has-focus-visible:outline-ttb11-amber"><input class="sr-only" type="radio" name="ttb11" id="ttb11-r2">Status</label>
<label class="flex min-h-10 flex-1 cursor-pointer items-center justify-center rounded-lg text-[11px] font-semibold tracking-[0.12em] text-ttb11-mut uppercase transition-colors hover:text-ttb11-ink has-checked:bg-ttb11-amber/12 has-checked:text-ttb11-amber has-focus-visible:outline-1 has-focus-visible:-outline-offset-2 has-focus-visible:outline-dashed has-focus-visible:outline-ttb11-amber"><input class="sr-only" type="radio" name="ttb11" id="ttb11-r3">Goodbye</label>
</div>
<div class="flex min-h-24 items-center px-4 text-lg">
<p class="hidden group-has-[#ttb11-r1:checked]/tabs:block"><span aria-hidden="true" class="mr-2 text-ttb11-dim">›</span><span class="inline-block w-[calc(var(--n)*1ch)] animate-ttb11-type overflow-hidden border-r-2 border-ttb11-amber align-bottom whitespace-nowrap text-ttb11-amber [--n:14] motion-reduce:animate-none">Hello, friend.</span></p>
<p class="hidden group-has-[#ttb11-r2:checked]/tabs:block"><span aria-hidden="true" class="mr-2 text-ttb11-dim">›</span><span class="inline-block w-[calc(var(--n)*1ch)] animate-ttb11-type overflow-hidden border-r-2 border-ttb11-amber align-bottom whitespace-nowrap text-ttb11-amber [--n:16] motion-reduce:animate-none">Systems nominal.</span></p>
<p class="hidden group-has-[#ttb11-r3:checked]/tabs:block"><span aria-hidden="true" class="mr-2 text-ttb11-dim">›</span><span class="inline-block w-[calc(var(--n)*1ch)] animate-ttb11-type overflow-hidden border-r-2 border-ttb11-amber align-bottom whitespace-nowrap text-ttb11-amber [--n:17] motion-reduce:animate-none">See you tomorrow.</span></p>
</div>
</div>
<p aria-hidden="true" class="rounded-full border border-white/10 px-3.5 py-2 text-[11.5px] text-ttb11-mut">[--n:16] · w-[calc(var(--n)*1ch)] · steps(var(--n))</p>
</div>
</section>@import "tailwindcss";
@theme {
--color-ttb11-bg: #17121f;
--color-ttb11-bg: oklch(0.195 0.027 301.6);
--color-ttb11-card: #211a2c;
--color-ttb11-card: oklch(0.235 0.035 301.6);
--color-ttb11-ink: #f3eff8;
--color-ttb11-ink: oklch(0.958 0.013 306.0);
--color-ttb11-mut: #a99fb8;
--color-ttb11-mut: oklch(0.719 0.037 303.6);
--color-ttb11-dim: #8b7f9c;
--color-ttb11-dim: oklch(0.617 0.045 303.9);
--color-ttb11-amber: #fbbf24;
--color-ttb11-amber: oklch(0.837 0.164 84.4);
--font-ttb11-mono: 'JetBrains Mono', ui-monospace, monospace;
--animate-ttb11-type: ttb11-type 1.2s steps(var(--n)) both, ttb11-blink 0.8s step-end 1.2s infinite;
@keyframes ttb11-type {
from {
width: 0;
}
}
@keyframes ttb11-blink {
50% {
border-color: transparent;
}
}
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's utilities, so the
stage is sized before the browser build compiles. */
.ttb-11 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-ttb11-bg: #17121f;
--color-ttb11-bg: oklch(0.195 0.027 301.6);
--color-ttb11-card: #211a2c;
--color-ttb11-card: oklch(0.235 0.035 301.6);
--color-ttb11-ink: #f3eff8;
--color-ttb11-ink: oklch(0.958 0.013 306.0);
--color-ttb11-mut: #a99fb8;
--color-ttb11-mut: oklch(0.719 0.037 303.6);
--color-ttb11-dim: #8b7f9c;
--color-ttb11-dim: oklch(0.617 0.045 303.9);
--color-ttb11-amber: #fbbf24;
--color-ttb11-amber: oklch(0.837 0.164 84.4);
--font-ttb11-mono: 'JetBrains Mono', ui-monospace, monospace;
--animate-ttb11-type: ttb11-type 1.2s steps(var(--n)) both, ttb11-blink 0.8s step-end 1.2s infinite;
@keyframes ttb11-type {
from {
width: 0;
}
}
@keyframes ttb11-blink {
50% {
border-color: transparent;
}
}
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's utilities, so the
stage is sized before the browser build compiles. */
.ttb-11 {
width: 100%;
min-height: 100svh;
}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-ttb11-bg: #17121f;
--color-ttb11-bg: oklch(0.195 0.027 301.6);
--color-ttb11-card: #211a2c;
--color-ttb11-card: oklch(0.235 0.035 301.6);
--color-ttb11-ink: #f3eff8;
--color-ttb11-ink: oklch(0.958 0.013 306.0);
--color-ttb11-mut: #a99fb8;
--color-ttb11-mut: oklch(0.719 0.037 303.6);
--color-ttb11-dim: #8b7f9c;
--color-ttb11-dim: oklch(0.617 0.045 303.9);
--color-ttb11-amber: #fbbf24;
--color-ttb11-amber: oklch(0.837 0.164 84.4);
--font-ttb11-mono: 'JetBrains Mono', ui-monospace, monospace;
--animate-ttb11-type: ttb11-type 1.2s steps(var(--n)) both, ttb11-blink 0.8s step-end 1.2s infinite;
@keyframes ttb11-type {
from {
width: 0;
}
}
@keyframes ttb11-blink {
50% {
border-color: transparent;
}
}
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's utilities, so the
stage is sized before the browser build compiles. */
.ttb-11 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-ttb11-bg: #17121f;
--color-ttb11-bg: oklch(0.195 0.027 301.6);
--color-ttb11-card: #211a2c;
--color-ttb11-card: oklch(0.235 0.035 301.6);
--color-ttb11-ink: #f3eff8;
--color-ttb11-ink: oklch(0.958 0.013 306.0);
--color-ttb11-mut: #a99fb8;
--color-ttb11-mut: oklch(0.719 0.037 303.6);
--color-ttb11-dim: #8b7f9c;
--color-ttb11-dim: oklch(0.617 0.045 303.9);
--color-ttb11-amber: #fbbf24;
--color-ttb11-amber: oklch(0.837 0.164 84.4);
--font-ttb11-mono: 'JetBrains Mono', ui-monospace, monospace;
--animate-ttb11-type: ttb11-type 1.2s steps(var(--n)) both, ttb11-blink 0.8s step-end 1.2s infinite;
@keyframes ttb11-type {
from {
width: 0;
}
}
@keyframes ttb11-blink {
50% {
border-color: transparent;
}
}
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's utilities, so the
stage is sized before the browser build compiles. */
.ttb-11 {
width: 100%;
min-height: 100svh;
}<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;500;600;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;500;600;700&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
steps(20)+width:100%becamesteps(var(--n))+w-[calc(var(--n)*1ch)]: each string declares its own length, so typing is exactly one glyph per step for any copy.- Both keyframes live inside
@themenext to a single--animate-ttb11-typetoken that chains them. - UI upgrade from the CSS original: a prompt glyph, an inset tab well with amber-tinted active state, and an 18px typed line (was 14px).
- UI upgrade: resting labels raised from 45% white (≈3.1:1) to a 6.6:1 token; under reduced motion the full line shows with no caret flicker.
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: Typewriter Animation Tabs in Tailwind CSS
Source: https://codefronts.com/navigation/tailwind-css-tabs/typewriter-animation-tabs-tailwind-css/
Choosing a tab types the panel's line out character by character, then leaves a blinking amber caret. The original's steps(20) was a magic number that only fit one string; the port gives each line its own [--n:16] and animates to calc(var(--n)*1ch) in exactly steps(var(--n)), so any string types one glyph per step.
## HTML
```html
<section class="ttb-11 group/tabs grid min-h-svh w-full place-items-center bg-ttb11-bg px-5 py-14 font-ttb11-mono text-ttb11-ink" aria-label="Typewriter animation tabs demo">
<div class="grid w-full max-w-lg justify-items-center gap-7">
<div class="w-full rounded-2xl bg-ttb11-card p-2 ring-1 ring-white/8">
<div role="radiogroup" aria-label="Message" class="flex gap-1 rounded-xl bg-black/25 p-1">
<label class="flex min-h-10 flex-1 cursor-pointer items-center justify-center rounded-lg text-[11px] font-semibold tracking-[0.12em] text-ttb11-mut uppercase transition-colors hover:text-ttb11-ink has-checked:bg-ttb11-amber/12 has-checked:text-ttb11-amber has-focus-visible:outline-1 has-focus-visible:-outline-offset-2 has-focus-visible:outline-dashed has-focus-visible:outline-ttb11-amber"><input class="sr-only" type="radio" name="ttb11" id="ttb11-r1" checked>Greet</label>
<label class="flex min-h-10 flex-1 cursor-pointer items-center justify-center rounded-lg text-[11px] font-semibold tracking-[0.12em] text-ttb11-mut uppercase transition-colors hover:text-ttb11-ink has-checked:bg-ttb11-amber/12 has-checked:text-ttb11-amber has-focus-visible:outline-1 has-focus-visible:-outline-offset-2 has-focus-visible:outline-dashed has-focus-visible:outline-ttb11-amber"><input class="sr-only" type="radio" name="ttb11" id="ttb11-r2">Status</label>
<label class="flex min-h-10 flex-1 cursor-pointer items-center justify-center rounded-lg text-[11px] font-semibold tracking-[0.12em] text-ttb11-mut uppercase transition-colors hover:text-ttb11-ink has-checked:bg-ttb11-amber/12 has-checked:text-ttb11-amber has-focus-visible:outline-1 has-focus-visible:-outline-offset-2 has-focus-visible:outline-dashed has-focus-visible:outline-ttb11-amber"><input class="sr-only" type="radio" name="ttb11" id="ttb11-r3">Goodbye</label>
</div>
<div class="flex min-h-24 items-center px-4 text-lg">
<p class="hidden group-has-[#ttb11-r1:checked]/tabs:block"><span aria-hidden="true" class="mr-2 text-ttb11-dim">›</span><span class="inline-block w-[calc(var(--n)*1ch)] animate-ttb11-type overflow-hidden border-r-2 border-ttb11-amber align-bottom whitespace-nowrap text-ttb11-amber [--n:14] motion-reduce:animate-none">Hello, friend.</span></p>
<p class="hidden group-has-[#ttb11-r2:checked]/tabs:block"><span aria-hidden="true" class="mr-2 text-ttb11-dim">›</span><span class="inline-block w-[calc(var(--n)*1ch)] animate-ttb11-type overflow-hidden border-r-2 border-ttb11-amber align-bottom whitespace-nowrap text-ttb11-amber [--n:16] motion-reduce:animate-none">Systems nominal.</span></p>
<p class="hidden group-has-[#ttb11-r3:checked]/tabs:block"><span aria-hidden="true" class="mr-2 text-ttb11-dim">›</span><span class="inline-block w-[calc(var(--n)*1ch)] animate-ttb11-type overflow-hidden border-r-2 border-ttb11-amber align-bottom whitespace-nowrap text-ttb11-amber [--n:17] motion-reduce:animate-none">See you tomorrow.</span></p>
</div>
</div>
<p aria-hidden="true" class="rounded-full border border-white/10 px-3.5 py-2 text-[11.5px] text-ttb11-mut">[--n:16] · w-[calc(var(--n)*1ch)] · steps(var(--n))</p>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-ttb11-bg: #17121f;
--color-ttb11-bg: oklch(0.195 0.027 301.6);
--color-ttb11-card: #211a2c;
--color-ttb11-card: oklch(0.235 0.035 301.6);
--color-ttb11-ink: #f3eff8;
--color-ttb11-ink: oklch(0.958 0.013 306.0);
--color-ttb11-mut: #a99fb8;
--color-ttb11-mut: oklch(0.719 0.037 303.6);
--color-ttb11-dim: #8b7f9c;
--color-ttb11-dim: oklch(0.617 0.045 303.9);
--color-ttb11-amber: #fbbf24;
--color-ttb11-amber: oklch(0.837 0.164 84.4);
--font-ttb11-mono: 'JetBrains Mono', ui-monospace, monospace;
--animate-ttb11-type: ttb11-type 1.2s steps(var(--n)) both, ttb11-blink 0.8s step-end 1.2s infinite;
@keyframes ttb11-type {
from {
width: 0;
}
}
@keyframes ttb11-blink {
50% {
border-color: transparent;
}
}
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's utilities, so the
stage is sized before the browser build compiles. */
.ttb-11 {
width: 100%;
min-height: 100svh;
}
```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: Typewriter Animation Tabs in Tailwind CSS
Source: https://codefronts.com/navigation/tailwind-css-tabs/typewriter-animation-tabs-tailwind-css/
Choosing a tab types the panel's line out character by character, then leaves a blinking amber caret. The original's steps(20) was a magic number that only fit one string; the port gives each line its own [--n:16] and animates to calc(var(--n)*1ch) in exactly steps(var(--n)), so any string types one glyph per step.
## HTML
```html
<section class="ttb-11 group/tabs grid min-h-svh w-full place-items-center bg-ttb11-bg px-5 py-14 font-ttb11-mono text-ttb11-ink" aria-label="Typewriter animation tabs demo">
<div class="grid w-full max-w-lg justify-items-center gap-7">
<div class="w-full rounded-2xl bg-ttb11-card p-2 ring-1 ring-white/8">
<div role="radiogroup" aria-label="Message" class="flex gap-1 rounded-xl bg-black/25 p-1">
<label class="flex min-h-10 flex-1 cursor-pointer items-center justify-center rounded-lg text-[11px] font-semibold tracking-[0.12em] text-ttb11-mut uppercase transition-colors hover:text-ttb11-ink has-checked:bg-ttb11-amber/12 has-checked:text-ttb11-amber has-focus-visible:outline-1 has-focus-visible:-outline-offset-2 has-focus-visible:outline-dashed has-focus-visible:outline-ttb11-amber"><input class="sr-only" type="radio" name="ttb11" id="ttb11-r1" checked>Greet</label>
<label class="flex min-h-10 flex-1 cursor-pointer items-center justify-center rounded-lg text-[11px] font-semibold tracking-[0.12em] text-ttb11-mut uppercase transition-colors hover:text-ttb11-ink has-checked:bg-ttb11-amber/12 has-checked:text-ttb11-amber has-focus-visible:outline-1 has-focus-visible:-outline-offset-2 has-focus-visible:outline-dashed has-focus-visible:outline-ttb11-amber"><input class="sr-only" type="radio" name="ttb11" id="ttb11-r2">Status</label>
<label class="flex min-h-10 flex-1 cursor-pointer items-center justify-center rounded-lg text-[11px] font-semibold tracking-[0.12em] text-ttb11-mut uppercase transition-colors hover:text-ttb11-ink has-checked:bg-ttb11-amber/12 has-checked:text-ttb11-amber has-focus-visible:outline-1 has-focus-visible:-outline-offset-2 has-focus-visible:outline-dashed has-focus-visible:outline-ttb11-amber"><input class="sr-only" type="radio" name="ttb11" id="ttb11-r3">Goodbye</label>
</div>
<div class="flex min-h-24 items-center px-4 text-lg">
<p class="hidden group-has-[#ttb11-r1:checked]/tabs:block"><span aria-hidden="true" class="mr-2 text-ttb11-dim">›</span><span class="inline-block w-[calc(var(--n)*1ch)] animate-ttb11-type overflow-hidden border-r-2 border-ttb11-amber align-bottom whitespace-nowrap text-ttb11-amber [--n:14] motion-reduce:animate-none">Hello, friend.</span></p>
<p class="hidden group-has-[#ttb11-r2:checked]/tabs:block"><span aria-hidden="true" class="mr-2 text-ttb11-dim">›</span><span class="inline-block w-[calc(var(--n)*1ch)] animate-ttb11-type overflow-hidden border-r-2 border-ttb11-amber align-bottom whitespace-nowrap text-ttb11-amber [--n:16] motion-reduce:animate-none">Systems nominal.</span></p>
<p class="hidden group-has-[#ttb11-r3:checked]/tabs:block"><span aria-hidden="true" class="mr-2 text-ttb11-dim">›</span><span class="inline-block w-[calc(var(--n)*1ch)] animate-ttb11-type overflow-hidden border-r-2 border-ttb11-amber align-bottom whitespace-nowrap text-ttb11-amber [--n:17] motion-reduce:animate-none">See you tomorrow.</span></p>
</div>
</div>
<p aria-hidden="true" class="rounded-full border border-white/10 px-3.5 py-2 text-[11.5px] text-ttb11-mut">[--n:16] · w-[calc(var(--n)*1ch)] · steps(var(--n))</p>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-ttb11-bg: #17121f;
--color-ttb11-bg: oklch(0.195 0.027 301.6);
--color-ttb11-card: #211a2c;
--color-ttb11-card: oklch(0.235 0.035 301.6);
--color-ttb11-ink: #f3eff8;
--color-ttb11-ink: oklch(0.958 0.013 306.0);
--color-ttb11-mut: #a99fb8;
--color-ttb11-mut: oklch(0.719 0.037 303.6);
--color-ttb11-dim: #8b7f9c;
--color-ttb11-dim: oklch(0.617 0.045 303.9);
--color-ttb11-amber: #fbbf24;
--color-ttb11-amber: oklch(0.837 0.164 84.4);
--font-ttb11-mono: 'JetBrains Mono', ui-monospace, monospace;
--animate-ttb11-type: ttb11-type 1.2s steps(var(--n)) both, ttb11-blink 0.8s step-end 1.2s infinite;
@keyframes ttb11-type {
from {
width: 0;
}
}
@keyframes ttb11-blink {
50% {
border-color: transparent;
}
}
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's utilities, so the
stage is sized before the browser build compiles. */
.ttb-11 {
width: 100%;
min-height: 100svh;
}
```How this works
Steps equal characters. In a monospace font 1ch is one glyph, so a width of n ch holds exactly n characters, and steps(n) reveals one per step:
<span class="[--n:14] w-[calc(var(--n)*1ch)] overflow-hidden whitespace-nowrap
border-r-2 border-ttb11-amber animate-ttb11-type">Hello, friend.</span>The keyframe only needs a start. from { width: 0 } animates to the element's own width, so one keyframe serves every string.
Free replay. A CSS animation restarts whenever its element leaves display:none — switching tabs types the new line again without a script.
Make it yours
- Slower typing: raise
1.2sin the token;steps(var(--n))keeps it one glyph per step. - Green CRT: amber →
#4ade80on near-black. - Multiple lines: give each line its own span and stagger with
[animation-delay:…].
Gotchas — read before shipping
- Set
[--n]to the exact character count, including spaces and punctuation, or the caret stops early or late. chis only one glyph wide in a monospace font; a proportional font breaks the effect.- Long lines overflow narrow phones — keep strings under ~30 characters or allow horizontal scroll.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
var() inside steps() resolves at computed time in all engines; the floor is Tailwind v4's sheet.