12 / 20
All 20 in 20 Tailwind CSS Grain Texture
- 01Tailwind SVG feTurbulence Noise Parameter Breakdown
- 02Tailwind baseFrequency and numOctaves Grain Size Control
- 03Tailwind Data URI SVG Noise Texture Without an Image Request
- 04Tailwind Grain Opacity and mix-blend-mode Composite Matrix
- 05Tailwind Tinted SVG Noise with feColorMatrix
- 06Tailwind Grain Paint Cost and Byte Size Measured Live
- 07Tailwind Anti-Banding Grainy CSS Gradient Hero
- 08Tailwind Vignette Photo Overlay with Film Grain
- 09Tailwind Tactile Card Surface with Subtle Noise
- 10Tailwind Full-Bleed Hero Section Noise Background
- 11Tailwind Noise Layer Order Over backdrop-filter Glass
- 12Tailwind Textured Button and Input Surfaces
- 13Tailwind Textured Section Divider with SVG Grain
- 14Tailwind Static Halftone Dot Print Effect
- 15Tailwind Dithered Bayer Matrix Shading Effect
- 16Tailwind Paper Stock and Letterpress Press Effect
- 17Tailwind Risograph Print Misregistration Texture
- 18Tailwind Woven Fabric and Canvas Texture Surface
- 19Tailwind Dark Mode Noise Without Muddy Shadows
- 20Tailwind Reduced Motion Static Grain Fallback
Tailwind Textured Button and Input Surfaces
A control kit where every surface is a material: buttons are raised, inputs are recessed, and both carry the same grain token. Press a button and three things change at once — grain weight, highlight direction and shadow depth. The port's wins are active:after: for the pressed texture and peer-user-invalid: for validation copy that cannot drift from the field it describes.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="gtw-12 relative block w-full min-h-svh bg-gtw12-bg text-gtw12-ink" aria-labelledby="gtw-12-title">
<div class="absolute top-[clamp(1rem,3vw,1.75rem)] right-[clamp(0.85rem,3vw,1.75rem)] z-40">
<input type="checkbox" id="gtw-12-theme" class="gtw-12-theme peer sr-only">
<label for="gtw-12-theme" class="grid place-items-center w-11 h-11 rounded-full border border-gtw12-line bg-gtw12-surf text-gtw12-ink cursor-pointer shadow-[inset_0_1px_0_var(--gtw12-hl)] peer-focus-visible:outline-2 peer-focus-visible:outline-gtw12-acc peer-focus-visible:outline-offset-2">
<svg viewBox="0 0 24 24" class="w-5 h-5" aria-hidden="true" focusable="false"><circle cx="12" cy="12" r="8.5" fill="none" stroke="currentColor" stroke-width="1.6"/><path d="M12 3.5a8.5 8.5 0 0 0 0 17Z" fill="currentColor"/></svg>
<span class="sr-only">Toggle dark theme</span>
</label>
</div>
<main class="w-[min(76rem,calc(100%-clamp(1.5rem,5vw,4rem)))] mx-auto pt-[clamp(3rem,8vw,5.5rem)] pb-[clamp(2.5rem,6vw,4rem)] grid gap-[clamp(2rem,5vw,3rem)]">
<header class="max-w-[44rem] grid gap-[0.6rem]">
<p class="m-0 font-gtw12-mono text-[0.72rem] font-medium leading-none tracking-[0.18em] uppercase text-gtw12-acc">Control surfaces · 12</p>
<h1 id="gtw-12-title" class="m-0 font-gtw12-display text-[clamp(2rem,5.4vw,3.3rem)] font-semibold leading-[1.03] tracking-[-0.035em] text-balance">Every control is a material</h1>
<p class="m-0 max-w-[38rem] text-gtw12-mut text-[clamp(0.95rem,1.9vw,1.08rem)] text-pretty">Buttons are raised, inputs are recessed, and both carry the same grain. Press a button and three things change at once — grain weight, highlight direction, shadow depth.</p>
</header>
<div class="grid gap-[clamp(1rem,2.4vw,1.5rem)] grid-cols-[repeat(auto-fit,minmax(min(100%,18rem),1fr))] items-start">
<section class="relative isolate overflow-hidden grid gap-4 content-start min-w-0 p-[clamp(1.2rem,3vw,1.7rem)] rounded-[18px] border border-gtw12-line bg-gtw12-surf shadow-[inset_0_1px_0_var(--gtw12-hl),0_12px_30px_-20px_oklch(0.2_0.03_275/0.3)] after:content-[''] after:absolute after:inset-0 after:-z-[1] after:pointer-events-none after:bg-[image:var(--gtw12-noise)] after:opacity-[0.04] after:[mix-blend-mode:var(--gtw12-blend)]" aria-labelledby="gtw-12-h-btn">
<h2 id="gtw-12-h-btn" class="m-0 pb-[0.6rem] border-b border-gtw12-line font-gtw12-display text-[1.05rem] font-semibold leading-tight tracking-[-0.02em]">Buttons</h2>
<div class="flex flex-wrap gap-[0.6rem]">
<button type="button" class="relative isolate overflow-hidden inline-flex items-center justify-center min-h-11 px-[1.2rem] rounded-[12px] border-0 bg-gtw12-acc text-gtw12-onacc font-gtw12-display text-[0.88rem] font-semibold leading-none cursor-pointer shadow-[inset_0_1px_0_rgba(255,255,255,0.24),0_2px_5px_oklch(0.2_0.03_275/0.22)] transition-[box-shadow] duration-200 active:shadow-[inset_0_-1px_0_rgba(255,255,255,0.2),inset_0_2px_6px_oklch(0_0_0/0.3)] focus-visible:outline-2 focus-visible:outline-gtw12-acc focus-visible:outline-offset-2 after:content-[''] after:absolute after:inset-0 after:pointer-events-none after:bg-[image:var(--gtw12-noise)] after:mix-blend-overlay after:opacity-[var(--gtw12-idle)] after:transition-opacity after:duration-150 hover:after:opacity-[var(--gtw12-hover)] active:after:opacity-[var(--gtw12-active)]">Save changes</button>
<button type="button" class="relative isolate overflow-hidden inline-flex items-center justify-center min-h-11 px-[1.2rem] rounded-[12px] border-0 bg-gtw12-sunk text-gtw12-ink font-gtw12-display text-[0.88rem] font-semibold leading-none cursor-pointer shadow-[inset_0_1px_0_var(--gtw12-hl),0_1px_3px_oklch(0.2_0.03_275/0.14)] transition-[box-shadow] duration-200 active:shadow-[inset_0_2px_6px_oklch(0_0_0/0.22)] focus-visible:outline-2 focus-visible:outline-gtw12-acc focus-visible:outline-offset-2 after:content-[''] after:absolute after:inset-0 after:pointer-events-none after:bg-[image:var(--gtw12-noise)] after:[mix-blend-mode:var(--gtw12-blend)] after:opacity-[var(--gtw12-idle)] after:transition-opacity after:duration-150 hover:after:opacity-[var(--gtw12-hover)] active:after:opacity-[var(--gtw12-active)]">Preview</button>
<button type="button" class="inline-flex items-center justify-center min-h-11 px-[1.2rem] rounded-[12px] bg-transparent border border-gtw12-line text-gtw12-mut font-gtw12-display text-[0.88rem] font-semibold leading-none cursor-pointer transition-colors duration-200 hover:text-gtw12-ink hover:border-gtw12-acc focus-visible:outline-2 focus-visible:outline-gtw12-acc focus-visible:outline-offset-2">Cancel</button>
<button type="button" disabled class="inline-flex items-center justify-center min-h-11 px-[1.2rem] rounded-[12px] border-0 bg-gtw12-acc text-gtw12-onacc font-gtw12-display text-[0.88rem] font-semibold leading-none opacity-45 cursor-not-allowed shadow-none">Disabled</button>
</div>
<div role="group" aria-label="Density" class="flex flex-wrap p-1 rounded-[16px] border border-gtw12-line bg-gtw12-sunk shadow-[inset_0_2px_5px_oklch(0.2_0.03_275/0.12)]">
<input type="radio" name="gtw-12-density" id="gtw-12-d1" checked class="peer/d1 sr-only">
<label for="gtw-12-d1" class="relative isolate overflow-hidden flex-auto inline-flex items-center justify-center min-h-10 px-[0.8rem] rounded-[12px] text-gtw12-mut font-gtw12-display text-[0.83rem] font-semibold leading-none cursor-pointer transition-colors duration-200 peer-checked/d1:bg-gtw12-surf peer-checked/d1:text-gtw12-ink peer-checked/d1:shadow-[inset_0_1px_0_var(--gtw12-hl),0_1px_3px_oklch(0.2_0.03_275/0.18)] peer-focus-visible/d1:outline-2 peer-focus-visible/d1:outline-gtw12-acc peer-focus-visible/d1:outline-offset-2 peer-checked/d1:after:content-[''] after:absolute after:inset-0 after:pointer-events-none after:bg-[image:var(--gtw12-noise)] after:[mix-blend-mode:var(--gtw12-blend)] after:opacity-[var(--gtw12-idle)]">Compact</label>
<input type="radio" name="gtw-12-density" id="gtw-12-d2" class="peer/d2 sr-only">
<label for="gtw-12-d2" class="relative isolate overflow-hidden flex-auto inline-flex items-center justify-center min-h-10 px-[0.8rem] rounded-[12px] text-gtw12-mut font-gtw12-display text-[0.83rem] font-semibold leading-none cursor-pointer transition-colors duration-200 peer-checked/d2:bg-gtw12-surf peer-checked/d2:text-gtw12-ink peer-checked/d2:shadow-[inset_0_1px_0_var(--gtw12-hl),0_1px_3px_oklch(0.2_0.03_275/0.18)] peer-focus-visible/d2:outline-2 peer-focus-visible/d2:outline-gtw12-acc peer-focus-visible/d2:outline-offset-2 peer-checked/d2:after:content-[''] after:absolute after:inset-0 after:pointer-events-none after:bg-[image:var(--gtw12-noise)] after:[mix-blend-mode:var(--gtw12-blend)] after:opacity-[var(--gtw12-idle)]">Regular</label>
<input type="radio" name="gtw-12-density" id="gtw-12-d3" class="peer/d3 sr-only">
<label for="gtw-12-d3" class="relative isolate overflow-hidden flex-auto inline-flex items-center justify-center min-h-10 px-[0.8rem] rounded-[12px] text-gtw12-mut font-gtw12-display text-[0.83rem] font-semibold leading-none cursor-pointer transition-colors duration-200 peer-checked/d3:bg-gtw12-surf peer-checked/d3:text-gtw12-ink peer-checked/d3:shadow-[inset_0_1px_0_var(--gtw12-hl),0_1px_3px_oklch(0.2_0.03_275/0.18)] peer-focus-visible/d3:outline-2 peer-focus-visible/d3:outline-gtw12-acc peer-focus-visible/d3:outline-offset-2 peer-checked/d3:after:content-[''] after:absolute after:inset-0 after:pointer-events-none after:bg-[image:var(--gtw12-noise)] after:[mix-blend-mode:var(--gtw12-blend)] after:opacity-[var(--gtw12-idle)]">Roomy</label>
</div>
<p class="m-0 text-gtw12-mut text-[0.8rem] text-pretty">Idle 0.05 · hover 0.06 · active 0.08 with the highlight inverted.</p>
</section>
<section class="relative isolate overflow-hidden grid gap-4 content-start min-w-0 p-[clamp(1.2rem,3vw,1.7rem)] rounded-[18px] border border-gtw12-line bg-gtw12-surf shadow-[inset_0_1px_0_var(--gtw12-hl),0_12px_30px_-20px_oklch(0.2_0.03_275/0.3)] after:content-[''] after:absolute after:inset-0 after:-z-[1] after:pointer-events-none after:bg-[image:var(--gtw12-noise)] after:opacity-[0.04] after:[mix-blend-mode:var(--gtw12-blend)]" aria-labelledby="gtw-12-h-fields">
<h2 id="gtw-12-h-fields" class="m-0 pb-[0.6rem] border-b border-gtw12-line font-gtw12-display text-[1.05rem] font-semibold leading-tight tracking-[-0.02em]">Fields</h2>
<div class="grid gap-[0.35rem]">
<label for="gtw-12-name" class="font-gtw12-display text-[0.82rem] font-medium leading-[1.3]">Full name</label>
<input type="text" id="gtw-12-name" name="gtw-12-name" placeholder="Ada Lovelace" autocomplete="name" class="min-h-[46px] px-[0.85rem] rounded-[12px] border border-gtw12-line text-gtw12-ink text-[0.95rem] leading-none [background-image:var(--gtw12-noise),linear-gradient(var(--color-gtw12-sunk),var(--color-gtw12-sunk))] [background-blend-mode:var(--gtw12-blend),normal] shadow-[inset_0_2px_5px_oklch(0.2_0.03_275/0.13),inset_0_-1px_0_var(--gtw12-hl)] placeholder:text-[color-mix(in_oklab,var(--color-gtw12-mut)_70%,transparent)] transition-colors duration-200 hover:border-[color-mix(in_oklab,var(--color-gtw12-acc)_45%,var(--color-gtw12-line))] focus-visible:outline-2 focus-visible:outline-gtw12-acc focus-visible:outline-offset-2">
</div>
<div class="grid gap-[0.35rem]">
<label for="gtw-12-mail" class="font-gtw12-display text-[0.82rem] font-medium leading-[1.3]">Work email</label>
<input type="email" id="gtw-12-mail" name="gtw-12-mail" placeholder="ada@example.com" autocomplete="email" required aria-describedby="gtw-12-mail-hint" class="peer/mail min-h-[46px] px-[0.85rem] rounded-[12px] border border-gtw12-line text-gtw12-ink text-[0.95rem] leading-none [background-image:var(--gtw12-noise),linear-gradient(var(--color-gtw12-sunk),var(--color-gtw12-sunk))] [background-blend-mode:var(--gtw12-blend),normal] shadow-[inset_0_2px_5px_oklch(0.2_0.03_275/0.13),inset_0_-1px_0_var(--gtw12-hl)] placeholder:text-[color-mix(in_oklab,var(--color-gtw12-mut)_70%,transparent)] transition-colors duration-200 hover:border-[color-mix(in_oklab,var(--color-gtw12-acc)_45%,var(--color-gtw12-line))] user-invalid:border-gtw12-err focus-visible:outline-2 focus-visible:outline-gtw12-acc focus-visible:outline-offset-2">
<p id="gtw-12-mail-hint" class="m-0 text-[0.8rem] text-gtw12-mut peer-user-invalid/mail:text-gtw12-err">We only email about deploys. Real address required.</p>
</div>
<div class="grid gap-[0.35rem]">
<label for="gtw-12-region" class="font-gtw12-display text-[0.82rem] font-medium leading-[1.3]">Region</label>
<select id="gtw-12-region" name="gtw-12-region" class="appearance-none min-h-[46px] pl-[0.85rem] pr-[2.2rem] rounded-[12px] border border-gtw12-line text-gtw12-ink text-[0.95rem] cursor-pointer [background-image:var(--gtw12-caret),var(--gtw12-noise),linear-gradient(var(--color-gtw12-sunk),var(--color-gtw12-sunk))] [background-position:right_0.9rem_center,0_0,0_0] [background-repeat:no-repeat,repeat,no-repeat] [background-blend-mode:normal,var(--gtw12-blend),normal] shadow-[inset_0_2px_5px_oklch(0.2_0.03_275/0.13),inset_0_-1px_0_var(--gtw12-hl)] transition-colors duration-200 hover:border-[color-mix(in_oklab,var(--color-gtw12-acc)_45%,var(--color-gtw12-line))] focus-visible:outline-2 focus-visible:outline-gtw12-acc focus-visible:outline-offset-2">
<option>us-east-1 · N. Virginia</option>
<option>us-west-2 · Oregon</option>
<option>eu-west-2 · London</option>
<option>ca-central-1 · Montréal</option>
<option>ap-southeast-2 · Sydney</option>
</select>
</div>
</section>
<section class="relative isolate overflow-hidden grid gap-4 content-start min-w-0 p-[clamp(1.2rem,3vw,1.7rem)] rounded-[18px] border border-gtw12-line bg-gtw12-surf shadow-[inset_0_1px_0_var(--gtw12-hl),0_12px_30px_-20px_oklch(0.2_0.03_275/0.3)] after:content-[''] after:absolute after:inset-0 after:-z-[1] after:pointer-events-none after:bg-[image:var(--gtw12-noise)] after:opacity-[0.04] after:[mix-blend-mode:var(--gtw12-blend)]" aria-labelledby="gtw-12-h-misc">
<h2 id="gtw-12-h-misc" class="m-0 pb-[0.6rem] border-b border-gtw12-line font-gtw12-display text-[1.05rem] font-semibold leading-tight tracking-[-0.02em]">Toggles and ranges</h2>
<ul class="list-none m-0 p-0 grid gap-[0.1rem]">
<li class="flex items-center gap-[0.6rem] min-h-11"><input type="checkbox" id="gtw-12-c1" checked class="w-5 h-5 m-0 cursor-pointer"><label for="gtw-12-c1" class="text-[0.92rem] cursor-pointer">Textured surfaces</label></li>
<li class="flex items-center gap-[0.6rem] min-h-11"><input type="checkbox" id="gtw-12-c2" class="w-5 h-5 m-0 cursor-pointer"><label for="gtw-12-c2" class="text-[0.92rem] cursor-pointer">Reduce transparency</label></li>
<li class="flex items-center gap-[0.6rem] min-h-11"><input type="radio" name="gtw-12-r" id="gtw-12-r1" checked class="w-5 h-5 m-0 cursor-pointer"><label for="gtw-12-r1" class="text-[0.92rem] cursor-pointer">Match system theme</label></li>
<li class="flex items-center gap-[0.6rem] min-h-11"><input type="radio" name="gtw-12-r" id="gtw-12-r2" class="w-5 h-5 m-0 cursor-pointer"><label for="gtw-12-r2" class="text-[0.92rem] cursor-pointer">Always dark</label></li>
</ul>
<div class="grid gap-[0.35rem]">
<label for="gtw-12-grain" class="font-gtw12-display text-[0.82rem] font-medium leading-[1.3]">Grain weight</label>
<input type="range" id="gtw-12-grain" name="gtw-12-grain" min="0" max="20" value="6" step="1" class="w-full min-h-11 m-0 bg-transparent cursor-pointer">
<p class="m-0 text-gtw12-mut text-[0.8rem] text-pretty">Native thumb, textured track, <code class="font-gtw12-mono text-[0.9em]">accent-color</code> for the fill.</p>
</div>
</section>
</div>
</main>
</section><section class="gtw-12 relative block w-full min-h-svh bg-gtw12-bg text-gtw12-ink" aria-labelledby="gtw-12-title">
<div class="absolute top-[clamp(1rem,3vw,1.75rem)] right-[clamp(0.85rem,3vw,1.75rem)] z-40">
<input type="checkbox" id="gtw-12-theme" class="gtw-12-theme peer sr-only">
<label for="gtw-12-theme" class="grid place-items-center w-11 h-11 rounded-full border border-gtw12-line bg-gtw12-surf text-gtw12-ink cursor-pointer shadow-[inset_0_1px_0_var(--gtw12-hl)] peer-focus-visible:outline-2 peer-focus-visible:outline-gtw12-acc peer-focus-visible:outline-offset-2">
<svg viewBox="0 0 24 24" class="w-5 h-5" aria-hidden="true" focusable="false"><circle cx="12" cy="12" r="8.5" fill="none" stroke="currentColor" stroke-width="1.6"/><path d="M12 3.5a8.5 8.5 0 0 0 0 17Z" fill="currentColor"/></svg>
<span class="sr-only">Toggle dark theme</span>
</label>
</div>
<main class="w-[min(76rem,calc(100%-clamp(1.5rem,5vw,4rem)))] mx-auto pt-[clamp(3rem,8vw,5.5rem)] pb-[clamp(2.5rem,6vw,4rem)] grid gap-[clamp(2rem,5vw,3rem)]">
<header class="max-w-[44rem] grid gap-[0.6rem]">
<p class="m-0 font-gtw12-mono text-[0.72rem] font-medium leading-none tracking-[0.18em] uppercase text-gtw12-acc">Control surfaces · 12</p>
<h1 id="gtw-12-title" class="m-0 font-gtw12-display text-[clamp(2rem,5.4vw,3.3rem)] font-semibold leading-[1.03] tracking-[-0.035em] text-balance">Every control is a material</h1>
<p class="m-0 max-w-[38rem] text-gtw12-mut text-[clamp(0.95rem,1.9vw,1.08rem)] text-pretty">Buttons are raised, inputs are recessed, and both carry the same grain. Press a button and three things change at once — grain weight, highlight direction, shadow depth.</p>
</header>
<div class="grid gap-[clamp(1rem,2.4vw,1.5rem)] grid-cols-[repeat(auto-fit,minmax(min(100%,18rem),1fr))] items-start">
<section class="relative isolate overflow-hidden grid gap-4 content-start min-w-0 p-[clamp(1.2rem,3vw,1.7rem)] rounded-[18px] border border-gtw12-line bg-gtw12-surf shadow-[inset_0_1px_0_var(--gtw12-hl),0_12px_30px_-20px_oklch(0.2_0.03_275/0.3)] after:content-[''] after:absolute after:inset-0 after:-z-[1] after:pointer-events-none after:bg-[image:var(--gtw12-noise)] after:opacity-[0.04] after:[mix-blend-mode:var(--gtw12-blend)]" aria-labelledby="gtw-12-h-btn">
<h2 id="gtw-12-h-btn" class="m-0 pb-[0.6rem] border-b border-gtw12-line font-gtw12-display text-[1.05rem] font-semibold leading-tight tracking-[-0.02em]">Buttons</h2>
<div class="flex flex-wrap gap-[0.6rem]">
<button type="button" class="relative isolate overflow-hidden inline-flex items-center justify-center min-h-11 px-[1.2rem] rounded-[12px] border-0 bg-gtw12-acc text-gtw12-onacc font-gtw12-display text-[0.88rem] font-semibold leading-none cursor-pointer shadow-[inset_0_1px_0_rgba(255,255,255,0.24),0_2px_5px_oklch(0.2_0.03_275/0.22)] transition-[box-shadow] duration-200 active:shadow-[inset_0_-1px_0_rgba(255,255,255,0.2),inset_0_2px_6px_oklch(0_0_0/0.3)] focus-visible:outline-2 focus-visible:outline-gtw12-acc focus-visible:outline-offset-2 after:content-[''] after:absolute after:inset-0 after:pointer-events-none after:bg-[image:var(--gtw12-noise)] after:mix-blend-overlay after:opacity-[var(--gtw12-idle)] after:transition-opacity after:duration-150 hover:after:opacity-[var(--gtw12-hover)] active:after:opacity-[var(--gtw12-active)]">Save changes</button>
<button type="button" class="relative isolate overflow-hidden inline-flex items-center justify-center min-h-11 px-[1.2rem] rounded-[12px] border-0 bg-gtw12-sunk text-gtw12-ink font-gtw12-display text-[0.88rem] font-semibold leading-none cursor-pointer shadow-[inset_0_1px_0_var(--gtw12-hl),0_1px_3px_oklch(0.2_0.03_275/0.14)] transition-[box-shadow] duration-200 active:shadow-[inset_0_2px_6px_oklch(0_0_0/0.22)] focus-visible:outline-2 focus-visible:outline-gtw12-acc focus-visible:outline-offset-2 after:content-[''] after:absolute after:inset-0 after:pointer-events-none after:bg-[image:var(--gtw12-noise)] after:[mix-blend-mode:var(--gtw12-blend)] after:opacity-[var(--gtw12-idle)] after:transition-opacity after:duration-150 hover:after:opacity-[var(--gtw12-hover)] active:after:opacity-[var(--gtw12-active)]">Preview</button>
<button type="button" class="inline-flex items-center justify-center min-h-11 px-[1.2rem] rounded-[12px] bg-transparent border border-gtw12-line text-gtw12-mut font-gtw12-display text-[0.88rem] font-semibold leading-none cursor-pointer transition-colors duration-200 hover:text-gtw12-ink hover:border-gtw12-acc focus-visible:outline-2 focus-visible:outline-gtw12-acc focus-visible:outline-offset-2">Cancel</button>
<button type="button" disabled class="inline-flex items-center justify-center min-h-11 px-[1.2rem] rounded-[12px] border-0 bg-gtw12-acc text-gtw12-onacc font-gtw12-display text-[0.88rem] font-semibold leading-none opacity-45 cursor-not-allowed shadow-none">Disabled</button>
</div>
<div role="group" aria-label="Density" class="flex flex-wrap p-1 rounded-[16px] border border-gtw12-line bg-gtw12-sunk shadow-[inset_0_2px_5px_oklch(0.2_0.03_275/0.12)]">
<input type="radio" name="gtw-12-density" id="gtw-12-d1" checked class="peer/d1 sr-only">
<label for="gtw-12-d1" class="relative isolate overflow-hidden flex-auto inline-flex items-center justify-center min-h-10 px-[0.8rem] rounded-[12px] text-gtw12-mut font-gtw12-display text-[0.83rem] font-semibold leading-none cursor-pointer transition-colors duration-200 peer-checked/d1:bg-gtw12-surf peer-checked/d1:text-gtw12-ink peer-checked/d1:shadow-[inset_0_1px_0_var(--gtw12-hl),0_1px_3px_oklch(0.2_0.03_275/0.18)] peer-focus-visible/d1:outline-2 peer-focus-visible/d1:outline-gtw12-acc peer-focus-visible/d1:outline-offset-2 peer-checked/d1:after:content-[''] after:absolute after:inset-0 after:pointer-events-none after:bg-[image:var(--gtw12-noise)] after:[mix-blend-mode:var(--gtw12-blend)] after:opacity-[var(--gtw12-idle)]">Compact</label>
<input type="radio" name="gtw-12-density" id="gtw-12-d2" class="peer/d2 sr-only">
<label for="gtw-12-d2" class="relative isolate overflow-hidden flex-auto inline-flex items-center justify-center min-h-10 px-[0.8rem] rounded-[12px] text-gtw12-mut font-gtw12-display text-[0.83rem] font-semibold leading-none cursor-pointer transition-colors duration-200 peer-checked/d2:bg-gtw12-surf peer-checked/d2:text-gtw12-ink peer-checked/d2:shadow-[inset_0_1px_0_var(--gtw12-hl),0_1px_3px_oklch(0.2_0.03_275/0.18)] peer-focus-visible/d2:outline-2 peer-focus-visible/d2:outline-gtw12-acc peer-focus-visible/d2:outline-offset-2 peer-checked/d2:after:content-[''] after:absolute after:inset-0 after:pointer-events-none after:bg-[image:var(--gtw12-noise)] after:[mix-blend-mode:var(--gtw12-blend)] after:opacity-[var(--gtw12-idle)]">Regular</label>
<input type="radio" name="gtw-12-density" id="gtw-12-d3" class="peer/d3 sr-only">
<label for="gtw-12-d3" class="relative isolate overflow-hidden flex-auto inline-flex items-center justify-center min-h-10 px-[0.8rem] rounded-[12px] text-gtw12-mut font-gtw12-display text-[0.83rem] font-semibold leading-none cursor-pointer transition-colors duration-200 peer-checked/d3:bg-gtw12-surf peer-checked/d3:text-gtw12-ink peer-checked/d3:shadow-[inset_0_1px_0_var(--gtw12-hl),0_1px_3px_oklch(0.2_0.03_275/0.18)] peer-focus-visible/d3:outline-2 peer-focus-visible/d3:outline-gtw12-acc peer-focus-visible/d3:outline-offset-2 peer-checked/d3:after:content-[''] after:absolute after:inset-0 after:pointer-events-none after:bg-[image:var(--gtw12-noise)] after:[mix-blend-mode:var(--gtw12-blend)] after:opacity-[var(--gtw12-idle)]">Roomy</label>
</div>
<p class="m-0 text-gtw12-mut text-[0.8rem] text-pretty">Idle 0.05 · hover 0.06 · active 0.08 with the highlight inverted.</p>
</section>
<section class="relative isolate overflow-hidden grid gap-4 content-start min-w-0 p-[clamp(1.2rem,3vw,1.7rem)] rounded-[18px] border border-gtw12-line bg-gtw12-surf shadow-[inset_0_1px_0_var(--gtw12-hl),0_12px_30px_-20px_oklch(0.2_0.03_275/0.3)] after:content-[''] after:absolute after:inset-0 after:-z-[1] after:pointer-events-none after:bg-[image:var(--gtw12-noise)] after:opacity-[0.04] after:[mix-blend-mode:var(--gtw12-blend)]" aria-labelledby="gtw-12-h-fields">
<h2 id="gtw-12-h-fields" class="m-0 pb-[0.6rem] border-b border-gtw12-line font-gtw12-display text-[1.05rem] font-semibold leading-tight tracking-[-0.02em]">Fields</h2>
<div class="grid gap-[0.35rem]">
<label for="gtw-12-name" class="font-gtw12-display text-[0.82rem] font-medium leading-[1.3]">Full name</label>
<input type="text" id="gtw-12-name" name="gtw-12-name" placeholder="Ada Lovelace" autocomplete="name" class="min-h-[46px] px-[0.85rem] rounded-[12px] border border-gtw12-line text-gtw12-ink text-[0.95rem] leading-none [background-image:var(--gtw12-noise),linear-gradient(var(--color-gtw12-sunk),var(--color-gtw12-sunk))] [background-blend-mode:var(--gtw12-blend),normal] shadow-[inset_0_2px_5px_oklch(0.2_0.03_275/0.13),inset_0_-1px_0_var(--gtw12-hl)] placeholder:text-[color-mix(in_oklab,var(--color-gtw12-mut)_70%,transparent)] transition-colors duration-200 hover:border-[color-mix(in_oklab,var(--color-gtw12-acc)_45%,var(--color-gtw12-line))] focus-visible:outline-2 focus-visible:outline-gtw12-acc focus-visible:outline-offset-2">
</div>
<div class="grid gap-[0.35rem]">
<label for="gtw-12-mail" class="font-gtw12-display text-[0.82rem] font-medium leading-[1.3]">Work email</label>
<input type="email" id="gtw-12-mail" name="gtw-12-mail" placeholder="ada@example.com" autocomplete="email" required aria-describedby="gtw-12-mail-hint" class="peer/mail min-h-[46px] px-[0.85rem] rounded-[12px] border border-gtw12-line text-gtw12-ink text-[0.95rem] leading-none [background-image:var(--gtw12-noise),linear-gradient(var(--color-gtw12-sunk),var(--color-gtw12-sunk))] [background-blend-mode:var(--gtw12-blend),normal] shadow-[inset_0_2px_5px_oklch(0.2_0.03_275/0.13),inset_0_-1px_0_var(--gtw12-hl)] placeholder:text-[color-mix(in_oklab,var(--color-gtw12-mut)_70%,transparent)] transition-colors duration-200 hover:border-[color-mix(in_oklab,var(--color-gtw12-acc)_45%,var(--color-gtw12-line))] user-invalid:border-gtw12-err focus-visible:outline-2 focus-visible:outline-gtw12-acc focus-visible:outline-offset-2">
<p id="gtw-12-mail-hint" class="m-0 text-[0.8rem] text-gtw12-mut peer-user-invalid/mail:text-gtw12-err">We only email about deploys. Real address required.</p>
</div>
<div class="grid gap-[0.35rem]">
<label for="gtw-12-region" class="font-gtw12-display text-[0.82rem] font-medium leading-[1.3]">Region</label>
<select id="gtw-12-region" name="gtw-12-region" class="appearance-none min-h-[46px] pl-[0.85rem] pr-[2.2rem] rounded-[12px] border border-gtw12-line text-gtw12-ink text-[0.95rem] cursor-pointer [background-image:var(--gtw12-caret),var(--gtw12-noise),linear-gradient(var(--color-gtw12-sunk),var(--color-gtw12-sunk))] [background-position:right_0.9rem_center,0_0,0_0] [background-repeat:no-repeat,repeat,no-repeat] [background-blend-mode:normal,var(--gtw12-blend),normal] shadow-[inset_0_2px_5px_oklch(0.2_0.03_275/0.13),inset_0_-1px_0_var(--gtw12-hl)] transition-colors duration-200 hover:border-[color-mix(in_oklab,var(--color-gtw12-acc)_45%,var(--color-gtw12-line))] focus-visible:outline-2 focus-visible:outline-gtw12-acc focus-visible:outline-offset-2">
<option>us-east-1 · N. Virginia</option>
<option>us-west-2 · Oregon</option>
<option>eu-west-2 · London</option>
<option>ca-central-1 · Montréal</option>
<option>ap-southeast-2 · Sydney</option>
</select>
</div>
</section>
<section class="relative isolate overflow-hidden grid gap-4 content-start min-w-0 p-[clamp(1.2rem,3vw,1.7rem)] rounded-[18px] border border-gtw12-line bg-gtw12-surf shadow-[inset_0_1px_0_var(--gtw12-hl),0_12px_30px_-20px_oklch(0.2_0.03_275/0.3)] after:content-[''] after:absolute after:inset-0 after:-z-[1] after:pointer-events-none after:bg-[image:var(--gtw12-noise)] after:opacity-[0.04] after:[mix-blend-mode:var(--gtw12-blend)]" aria-labelledby="gtw-12-h-misc">
<h2 id="gtw-12-h-misc" class="m-0 pb-[0.6rem] border-b border-gtw12-line font-gtw12-display text-[1.05rem] font-semibold leading-tight tracking-[-0.02em]">Toggles and ranges</h2>
<ul class="list-none m-0 p-0 grid gap-[0.1rem]">
<li class="flex items-center gap-[0.6rem] min-h-11"><input type="checkbox" id="gtw-12-c1" checked class="w-5 h-5 m-0 cursor-pointer"><label for="gtw-12-c1" class="text-[0.92rem] cursor-pointer">Textured surfaces</label></li>
<li class="flex items-center gap-[0.6rem] min-h-11"><input type="checkbox" id="gtw-12-c2" class="w-5 h-5 m-0 cursor-pointer"><label for="gtw-12-c2" class="text-[0.92rem] cursor-pointer">Reduce transparency</label></li>
<li class="flex items-center gap-[0.6rem] min-h-11"><input type="radio" name="gtw-12-r" id="gtw-12-r1" checked class="w-5 h-5 m-0 cursor-pointer"><label for="gtw-12-r1" class="text-[0.92rem] cursor-pointer">Match system theme</label></li>
<li class="flex items-center gap-[0.6rem] min-h-11"><input type="radio" name="gtw-12-r" id="gtw-12-r2" class="w-5 h-5 m-0 cursor-pointer"><label for="gtw-12-r2" class="text-[0.92rem] cursor-pointer">Always dark</label></li>
</ul>
<div class="grid gap-[0.35rem]">
<label for="gtw-12-grain" class="font-gtw12-display text-[0.82rem] font-medium leading-[1.3]">Grain weight</label>
<input type="range" id="gtw-12-grain" name="gtw-12-grain" min="0" max="20" value="6" step="1" class="w-full min-h-11 m-0 bg-transparent cursor-pointer">
<p class="m-0 text-gtw12-mut text-[0.8rem] text-pretty">Native thumb, textured track, <code class="font-gtw12-mono text-[0.9em]">accent-color</code> for the fill.</p>
</div>
</section>
</div>
</main>
</section>@import "tailwindcss";
@theme {
--color-gtw12-bg: #eef0f7;
--color-gtw12-bg: oklch(0.95 0.01 275);
--color-gtw12-surf: #ffffff;
--color-gtw12-surf: oklch(1 0 0);
--color-gtw12-sunk: #e7eaf4;
--color-gtw12-sunk: oklch(0.93 0.012 275);
--color-gtw12-line: #d3d8e8;
--color-gtw12-line: oklch(0.87 0.018 275);
--color-gtw12-ink: #14162a;
--color-gtw12-ink: oklch(0.22 0.04 275);
--color-gtw12-mut: #5c6280;
--color-gtw12-mut: oklch(0.47 0.04 275);
--color-gtw12-acc: #4f46e5;
--color-gtw12-acc: oklch(0.52 0.22 275);
--color-gtw12-onacc: #f2f1fe;
--color-gtw12-onacc: oklch(0.97 0.015 280);
--color-gtw12-err: #b42318;
--color-gtw12-err: oklch(0.5 0.18 27);
--font-gtw12-display: 'Space Grotesk', 'Inter Tight', ui-sans-serif, system-ui, sans-serif;
--font-gtw12-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
}
.gtw-12 {
width: 100%;
min-height: 100svh;
--gtw12-idle: 0.05;
--gtw12-hover: 0.06;
--gtw12-active: 0.08;
--gtw12-sunkw: 0.035;
--gtw12-blend: multiply;
--gtw12-hl: rgba(255, 255, 255, 0.85);
--gtw12-noise: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='150' height='150'%3E%3Cfilter id='gtw12n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='150' height='150' filter='url(%23gtw12n)'/%3E%3C/svg%3E");
--gtw12-caret: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='9' viewBox='0 0 14 9'%3E%3Cpath d='M1 1l6 6 6-6' fill='none' stroke='%235c6280' stroke-width='1.8' stroke-linecap='round'/%3E%3C/svg%3E");
font-family: system-ui, -apple-system, 'Segoe UI', sans-serif;
line-height: 1.55;
accent-color: var(--color-gtw12-acc);
}
.gtw-12:has(.gtw-12-theme:checked) {
--color-gtw12-bg: #0c0d16;
--color-gtw12-bg: oklch(0.16 0.02 275);
--color-gtw12-surf: #15172a;
--color-gtw12-surf: oklch(0.22 0.03 275);
--color-gtw12-sunk: #10121f;
--color-gtw12-sunk: oklch(0.18 0.025 275);
--color-gtw12-line: #262a45;
--color-gtw12-line: oklch(0.3 0.04 275);
--color-gtw12-ink: #ecebfb;
--color-gtw12-ink: oklch(0.94 0.015 280);
--color-gtw12-mut: #9a9ac4;
--color-gtw12-mut: oklch(0.7 0.05 280);
--color-gtw12-acc: #a5b4fc;
--color-gtw12-acc: oklch(0.8 0.11 278);
--color-gtw12-onacc: #10122a;
--color-gtw12-onacc: oklch(0.19 0.04 278);
--color-gtw12-err: #fca5a5;
--color-gtw12-err: oklch(0.78 0.13 22);
--gtw12-blend: soft-light;
--gtw12-hl: rgba(255, 255, 255, 0.12);
--gtw12-idle: 0.07;
--gtw12-hover: 0.085;
--gtw12-active: 0.11;
--gtw12-sunkw: 0.05;
--gtw12-caret: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='9' viewBox='0 0 14 9'%3E%3Cpath d='M1 1l6 6 6-6' fill='none' stroke='%239a9ac4' stroke-width='1.8' stroke-linecap='round'/%3E%3C/svg%3E");
}
@media (prefers-color-scheme: dark) {
.gtw-12 {
--color-gtw12-bg: #0c0d16;
--color-gtw12-bg: oklch(0.16 0.02 275);
--color-gtw12-surf: #15172a;
--color-gtw12-surf: oklch(0.22 0.03 275);
--color-gtw12-sunk: #10121f;
--color-gtw12-sunk: oklch(0.18 0.025 275);
--color-gtw12-line: #262a45;
--color-gtw12-line: oklch(0.3 0.04 275);
--color-gtw12-ink: #ecebfb;
--color-gtw12-ink: oklch(0.94 0.015 280);
--color-gtw12-mut: #9a9ac4;
--color-gtw12-mut: oklch(0.7 0.05 280);
--color-gtw12-acc: #a5b4fc;
--color-gtw12-acc: oklch(0.8 0.11 278);
--color-gtw12-onacc: #10122a;
--color-gtw12-onacc: oklch(0.19 0.04 278);
--color-gtw12-err: #fca5a5;
--color-gtw12-err: oklch(0.78 0.13 22);
--gtw12-blend: soft-light;
--gtw12-hl: rgba(255, 255, 255, 0.12);
--gtw12-idle: 0.07;
--gtw12-hover: 0.085;
--gtw12-active: 0.11;
--gtw12-sunkw: 0.05;
--gtw12-caret: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='9' viewBox='0 0 14 9'%3E%3Cpath d='M1 1l6 6 6-6' fill='none' stroke='%239a9ac4' stroke-width='1.8' stroke-linecap='round'/%3E%3C/svg%3E");
}
.gtw-12:has(.gtw-12-theme:checked) {
--color-gtw12-bg: #eef0f7;
--color-gtw12-bg: oklch(0.95 0.01 275);
--color-gtw12-surf: #ffffff;
--color-gtw12-surf: oklch(1 0 0);
--color-gtw12-sunk: #e7eaf4;
--color-gtw12-sunk: oklch(0.93 0.012 275);
--color-gtw12-line: #d3d8e8;
--color-gtw12-line: oklch(0.87 0.018 275);
--color-gtw12-ink: #14162a;
--color-gtw12-ink: oklch(0.22 0.04 275);
--color-gtw12-mut: #5c6280;
--color-gtw12-mut: oklch(0.47 0.04 275);
--color-gtw12-acc: #4f46e5;
--color-gtw12-acc: oklch(0.52 0.22 275);
--color-gtw12-onacc: #f2f1fe;
--color-gtw12-onacc: oklch(0.97 0.015 280);
--color-gtw12-err: #b42318;
--color-gtw12-err: oklch(0.5 0.18 27);
--gtw12-blend: multiply;
--gtw12-hl: rgba(255, 255, 255, 0.85);
--gtw12-idle: 0.05;
--gtw12-hover: 0.06;
--gtw12-active: 0.08;
--gtw12-sunkw: 0.035;
--gtw12-caret: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='9' viewBox='0 0 14 9'%3E%3Cpath d='M1 1l6 6 6-6' fill='none' stroke='%235c6280' stroke-width='1.8' stroke-linecap='round'/%3E%3C/svg%3E");
}
}
@media (prefers-color-scheme: light) {
.gtw-12:has(.gtw-12-theme:checked) {
--color-gtw12-bg: #0c0d16;
--color-gtw12-bg: oklch(0.16 0.02 275);
--color-gtw12-surf: #15172a;
--color-gtw12-surf: oklch(0.22 0.03 275);
--color-gtw12-sunk: #10121f;
--color-gtw12-sunk: oklch(0.18 0.025 275);
--color-gtw12-line: #262a45;
--color-gtw12-line: oklch(0.3 0.04 275);
--color-gtw12-ink: #ecebfb;
--color-gtw12-ink: oklch(0.94 0.015 280);
--color-gtw12-mut: #9a9ac4;
--color-gtw12-mut: oklch(0.7 0.05 280);
--color-gtw12-acc: #a5b4fc;
--color-gtw12-acc: oklch(0.8 0.11 278);
--color-gtw12-onacc: #10122a;
--color-gtw12-onacc: oklch(0.19 0.04 278);
--color-gtw12-err: #fca5a5;
--color-gtw12-err: oklch(0.78 0.13 22);
--gtw12-blend: soft-light;
--gtw12-hl: rgba(255, 255, 255, 0.12);
--gtw12-idle: 0.07;
--gtw12-hover: 0.085;
--gtw12-active: 0.11;
--gtw12-sunkw: 0.05;
--gtw12-caret: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='9' viewBox='0 0 14 9'%3E%3Cpath d='M1 1l6 6 6-6' fill='none' stroke='%239a9ac4' stroke-width='1.8' stroke-linecap='round'/%3E%3C/svg%3E");
}
}@import "tailwindcss";
@theme {
--color-gtw12-bg: #eef0f7;
--color-gtw12-bg: oklch(0.95 0.01 275);
--color-gtw12-surf: #ffffff;
--color-gtw12-surf: oklch(1 0 0);
--color-gtw12-sunk: #e7eaf4;
--color-gtw12-sunk: oklch(0.93 0.012 275);
--color-gtw12-line: #d3d8e8;
--color-gtw12-line: oklch(0.87 0.018 275);
--color-gtw12-ink: #14162a;
--color-gtw12-ink: oklch(0.22 0.04 275);
--color-gtw12-mut: #5c6280;
--color-gtw12-mut: oklch(0.47 0.04 275);
--color-gtw12-acc: #4f46e5;
--color-gtw12-acc: oklch(0.52 0.22 275);
--color-gtw12-onacc: #f2f1fe;
--color-gtw12-onacc: oklch(0.97 0.015 280);
--color-gtw12-err: #b42318;
--color-gtw12-err: oklch(0.5 0.18 27);
--font-gtw12-display: 'Space Grotesk', 'Inter Tight', ui-sans-serif, system-ui, sans-serif;
--font-gtw12-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
}
.gtw-12 {
width: 100%;
min-height: 100svh;
--gtw12-idle: 0.05;
--gtw12-hover: 0.06;
--gtw12-active: 0.08;
--gtw12-sunkw: 0.035;
--gtw12-blend: multiply;
--gtw12-hl: rgba(255, 255, 255, 0.85);
--gtw12-noise: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='150' height='150'%3E%3Cfilter id='gtw12n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='150' height='150' filter='url(%23gtw12n)'/%3E%3C/svg%3E");
--gtw12-caret: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='9' viewBox='0 0 14 9'%3E%3Cpath d='M1 1l6 6 6-6' fill='none' stroke='%235c6280' stroke-width='1.8' stroke-linecap='round'/%3E%3C/svg%3E");
font-family: system-ui, -apple-system, 'Segoe UI', sans-serif;
line-height: 1.55;
accent-color: var(--color-gtw12-acc);
}
.gtw-12:has(.gtw-12-theme:checked) {
--color-gtw12-bg: #0c0d16;
--color-gtw12-bg: oklch(0.16 0.02 275);
--color-gtw12-surf: #15172a;
--color-gtw12-surf: oklch(0.22 0.03 275);
--color-gtw12-sunk: #10121f;
--color-gtw12-sunk: oklch(0.18 0.025 275);
--color-gtw12-line: #262a45;
--color-gtw12-line: oklch(0.3 0.04 275);
--color-gtw12-ink: #ecebfb;
--color-gtw12-ink: oklch(0.94 0.015 280);
--color-gtw12-mut: #9a9ac4;
--color-gtw12-mut: oklch(0.7 0.05 280);
--color-gtw12-acc: #a5b4fc;
--color-gtw12-acc: oklch(0.8 0.11 278);
--color-gtw12-onacc: #10122a;
--color-gtw12-onacc: oklch(0.19 0.04 278);
--color-gtw12-err: #fca5a5;
--color-gtw12-err: oklch(0.78 0.13 22);
--gtw12-blend: soft-light;
--gtw12-hl: rgba(255, 255, 255, 0.12);
--gtw12-idle: 0.07;
--gtw12-hover: 0.085;
--gtw12-active: 0.11;
--gtw12-sunkw: 0.05;
--gtw12-caret: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='9' viewBox='0 0 14 9'%3E%3Cpath d='M1 1l6 6 6-6' fill='none' stroke='%239a9ac4' stroke-width='1.8' stroke-linecap='round'/%3E%3C/svg%3E");
}
@media (prefers-color-scheme: dark) {
.gtw-12 {
--color-gtw12-bg: #0c0d16;
--color-gtw12-bg: oklch(0.16 0.02 275);
--color-gtw12-surf: #15172a;
--color-gtw12-surf: oklch(0.22 0.03 275);
--color-gtw12-sunk: #10121f;
--color-gtw12-sunk: oklch(0.18 0.025 275);
--color-gtw12-line: #262a45;
--color-gtw12-line: oklch(0.3 0.04 275);
--color-gtw12-ink: #ecebfb;
--color-gtw12-ink: oklch(0.94 0.015 280);
--color-gtw12-mut: #9a9ac4;
--color-gtw12-mut: oklch(0.7 0.05 280);
--color-gtw12-acc: #a5b4fc;
--color-gtw12-acc: oklch(0.8 0.11 278);
--color-gtw12-onacc: #10122a;
--color-gtw12-onacc: oklch(0.19 0.04 278);
--color-gtw12-err: #fca5a5;
--color-gtw12-err: oklch(0.78 0.13 22);
--gtw12-blend: soft-light;
--gtw12-hl: rgba(255, 255, 255, 0.12);
--gtw12-idle: 0.07;
--gtw12-hover: 0.085;
--gtw12-active: 0.11;
--gtw12-sunkw: 0.05;
--gtw12-caret: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='9' viewBox='0 0 14 9'%3E%3Cpath d='M1 1l6 6 6-6' fill='none' stroke='%239a9ac4' stroke-width='1.8' stroke-linecap='round'/%3E%3C/svg%3E");
}
.gtw-12:has(.gtw-12-theme:checked) {
--color-gtw12-bg: #eef0f7;
--color-gtw12-bg: oklch(0.95 0.01 275);
--color-gtw12-surf: #ffffff;
--color-gtw12-surf: oklch(1 0 0);
--color-gtw12-sunk: #e7eaf4;
--color-gtw12-sunk: oklch(0.93 0.012 275);
--color-gtw12-line: #d3d8e8;
--color-gtw12-line: oklch(0.87 0.018 275);
--color-gtw12-ink: #14162a;
--color-gtw12-ink: oklch(0.22 0.04 275);
--color-gtw12-mut: #5c6280;
--color-gtw12-mut: oklch(0.47 0.04 275);
--color-gtw12-acc: #4f46e5;
--color-gtw12-acc: oklch(0.52 0.22 275);
--color-gtw12-onacc: #f2f1fe;
--color-gtw12-onacc: oklch(0.97 0.015 280);
--color-gtw12-err: #b42318;
--color-gtw12-err: oklch(0.5 0.18 27);
--gtw12-blend: multiply;
--gtw12-hl: rgba(255, 255, 255, 0.85);
--gtw12-idle: 0.05;
--gtw12-hover: 0.06;
--gtw12-active: 0.08;
--gtw12-sunkw: 0.035;
--gtw12-caret: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='9' viewBox='0 0 14 9'%3E%3Cpath d='M1 1l6 6 6-6' fill='none' stroke='%235c6280' stroke-width='1.8' stroke-linecap='round'/%3E%3C/svg%3E");
}
}
@media (prefers-color-scheme: light) {
.gtw-12:has(.gtw-12-theme:checked) {
--color-gtw12-bg: #0c0d16;
--color-gtw12-bg: oklch(0.16 0.02 275);
--color-gtw12-surf: #15172a;
--color-gtw12-surf: oklch(0.22 0.03 275);
--color-gtw12-sunk: #10121f;
--color-gtw12-sunk: oklch(0.18 0.025 275);
--color-gtw12-line: #262a45;
--color-gtw12-line: oklch(0.3 0.04 275);
--color-gtw12-ink: #ecebfb;
--color-gtw12-ink: oklch(0.94 0.015 280);
--color-gtw12-mut: #9a9ac4;
--color-gtw12-mut: oklch(0.7 0.05 280);
--color-gtw12-acc: #a5b4fc;
--color-gtw12-acc: oklch(0.8 0.11 278);
--color-gtw12-onacc: #10122a;
--color-gtw12-onacc: oklch(0.19 0.04 278);
--color-gtw12-err: #fca5a5;
--color-gtw12-err: oklch(0.78 0.13 22);
--gtw12-blend: soft-light;
--gtw12-hl: rgba(255, 255, 255, 0.12);
--gtw12-idle: 0.07;
--gtw12-hover: 0.085;
--gtw12-active: 0.11;
--gtw12-sunkw: 0.05;
--gtw12-caret: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='9' viewBox='0 0 14 9'%3E%3Cpath d='M1 1l6 6 6-6' fill='none' stroke='%239a9ac4' stroke-width='1.8' stroke-linecap='round'/%3E%3C/svg%3E");
}
}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-gtw12-bg: #eef0f7;
--color-gtw12-bg: oklch(0.95 0.01 275);
--color-gtw12-surf: #ffffff;
--color-gtw12-surf: oklch(1 0 0);
--color-gtw12-sunk: #e7eaf4;
--color-gtw12-sunk: oklch(0.93 0.012 275);
--color-gtw12-line: #d3d8e8;
--color-gtw12-line: oklch(0.87 0.018 275);
--color-gtw12-ink: #14162a;
--color-gtw12-ink: oklch(0.22 0.04 275);
--color-gtw12-mut: #5c6280;
--color-gtw12-mut: oklch(0.47 0.04 275);
--color-gtw12-acc: #4f46e5;
--color-gtw12-acc: oklch(0.52 0.22 275);
--color-gtw12-onacc: #f2f1fe;
--color-gtw12-onacc: oklch(0.97 0.015 280);
--color-gtw12-err: #b42318;
--color-gtw12-err: oklch(0.5 0.18 27);
--font-gtw12-display: 'Space Grotesk', 'Inter Tight', ui-sans-serif, system-ui, sans-serif;
--font-gtw12-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
}
.gtw-12 {
width: 100%;
min-height: 100svh;
--gtw12-idle: 0.05;
--gtw12-hover: 0.06;
--gtw12-active: 0.08;
--gtw12-sunkw: 0.035;
--gtw12-blend: multiply;
--gtw12-hl: rgba(255, 255, 255, 0.85);
--gtw12-noise: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='150' height='150'%3E%3Cfilter id='gtw12n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='150' height='150' filter='url(%23gtw12n)'/%3E%3C/svg%3E");
--gtw12-caret: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='9' viewBox='0 0 14 9'%3E%3Cpath d='M1 1l6 6 6-6' fill='none' stroke='%235c6280' stroke-width='1.8' stroke-linecap='round'/%3E%3C/svg%3E");
font-family: system-ui, -apple-system, 'Segoe UI', sans-serif;
line-height: 1.55;
accent-color: var(--color-gtw12-acc);
}
.gtw-12:has(.gtw-12-theme:checked) {
--color-gtw12-bg: #0c0d16;
--color-gtw12-bg: oklch(0.16 0.02 275);
--color-gtw12-surf: #15172a;
--color-gtw12-surf: oklch(0.22 0.03 275);
--color-gtw12-sunk: #10121f;
--color-gtw12-sunk: oklch(0.18 0.025 275);
--color-gtw12-line: #262a45;
--color-gtw12-line: oklch(0.3 0.04 275);
--color-gtw12-ink: #ecebfb;
--color-gtw12-ink: oklch(0.94 0.015 280);
--color-gtw12-mut: #9a9ac4;
--color-gtw12-mut: oklch(0.7 0.05 280);
--color-gtw12-acc: #a5b4fc;
--color-gtw12-acc: oklch(0.8 0.11 278);
--color-gtw12-onacc: #10122a;
--color-gtw12-onacc: oklch(0.19 0.04 278);
--color-gtw12-err: #fca5a5;
--color-gtw12-err: oklch(0.78 0.13 22);
--gtw12-blend: soft-light;
--gtw12-hl: rgba(255, 255, 255, 0.12);
--gtw12-idle: 0.07;
--gtw12-hover: 0.085;
--gtw12-active: 0.11;
--gtw12-sunkw: 0.05;
--gtw12-caret: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='9' viewBox='0 0 14 9'%3E%3Cpath d='M1 1l6 6 6-6' fill='none' stroke='%239a9ac4' stroke-width='1.8' stroke-linecap='round'/%3E%3C/svg%3E");
}
@media (prefers-color-scheme: dark) {
.gtw-12 {
--color-gtw12-bg: #0c0d16;
--color-gtw12-bg: oklch(0.16 0.02 275);
--color-gtw12-surf: #15172a;
--color-gtw12-surf: oklch(0.22 0.03 275);
--color-gtw12-sunk: #10121f;
--color-gtw12-sunk: oklch(0.18 0.025 275);
--color-gtw12-line: #262a45;
--color-gtw12-line: oklch(0.3 0.04 275);
--color-gtw12-ink: #ecebfb;
--color-gtw12-ink: oklch(0.94 0.015 280);
--color-gtw12-mut: #9a9ac4;
--color-gtw12-mut: oklch(0.7 0.05 280);
--color-gtw12-acc: #a5b4fc;
--color-gtw12-acc: oklch(0.8 0.11 278);
--color-gtw12-onacc: #10122a;
--color-gtw12-onacc: oklch(0.19 0.04 278);
--color-gtw12-err: #fca5a5;
--color-gtw12-err: oklch(0.78 0.13 22);
--gtw12-blend: soft-light;
--gtw12-hl: rgba(255, 255, 255, 0.12);
--gtw12-idle: 0.07;
--gtw12-hover: 0.085;
--gtw12-active: 0.11;
--gtw12-sunkw: 0.05;
--gtw12-caret: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='9' viewBox='0 0 14 9'%3E%3Cpath d='M1 1l6 6 6-6' fill='none' stroke='%239a9ac4' stroke-width='1.8' stroke-linecap='round'/%3E%3C/svg%3E");
}
.gtw-12:has(.gtw-12-theme:checked) {
--color-gtw12-bg: #eef0f7;
--color-gtw12-bg: oklch(0.95 0.01 275);
--color-gtw12-surf: #ffffff;
--color-gtw12-surf: oklch(1 0 0);
--color-gtw12-sunk: #e7eaf4;
--color-gtw12-sunk: oklch(0.93 0.012 275);
--color-gtw12-line: #d3d8e8;
--color-gtw12-line: oklch(0.87 0.018 275);
--color-gtw12-ink: #14162a;
--color-gtw12-ink: oklch(0.22 0.04 275);
--color-gtw12-mut: #5c6280;
--color-gtw12-mut: oklch(0.47 0.04 275);
--color-gtw12-acc: #4f46e5;
--color-gtw12-acc: oklch(0.52 0.22 275);
--color-gtw12-onacc: #f2f1fe;
--color-gtw12-onacc: oklch(0.97 0.015 280);
--color-gtw12-err: #b42318;
--color-gtw12-err: oklch(0.5 0.18 27);
--gtw12-blend: multiply;
--gtw12-hl: rgba(255, 255, 255, 0.85);
--gtw12-idle: 0.05;
--gtw12-hover: 0.06;
--gtw12-active: 0.08;
--gtw12-sunkw: 0.035;
--gtw12-caret: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='9' viewBox='0 0 14 9'%3E%3Cpath d='M1 1l6 6 6-6' fill='none' stroke='%235c6280' stroke-width='1.8' stroke-linecap='round'/%3E%3C/svg%3E");
}
}
@media (prefers-color-scheme: light) {
.gtw-12:has(.gtw-12-theme:checked) {
--color-gtw12-bg: #0c0d16;
--color-gtw12-bg: oklch(0.16 0.02 275);
--color-gtw12-surf: #15172a;
--color-gtw12-surf: oklch(0.22 0.03 275);
--color-gtw12-sunk: #10121f;
--color-gtw12-sunk: oklch(0.18 0.025 275);
--color-gtw12-line: #262a45;
--color-gtw12-line: oklch(0.3 0.04 275);
--color-gtw12-ink: #ecebfb;
--color-gtw12-ink: oklch(0.94 0.015 280);
--color-gtw12-mut: #9a9ac4;
--color-gtw12-mut: oklch(0.7 0.05 280);
--color-gtw12-acc: #a5b4fc;
--color-gtw12-acc: oklch(0.8 0.11 278);
--color-gtw12-onacc: #10122a;
--color-gtw12-onacc: oklch(0.19 0.04 278);
--color-gtw12-err: #fca5a5;
--color-gtw12-err: oklch(0.78 0.13 22);
--gtw12-blend: soft-light;
--gtw12-hl: rgba(255, 255, 255, 0.12);
--gtw12-idle: 0.07;
--gtw12-hover: 0.085;
--gtw12-active: 0.11;
--gtw12-sunkw: 0.05;
--gtw12-caret: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='9' viewBox='0 0 14 9'%3E%3Cpath d='M1 1l6 6 6-6' fill='none' stroke='%239a9ac4' stroke-width='1.8' stroke-linecap='round'/%3E%3C/svg%3E");
}
}@import "tailwindcss";
@theme {
--color-gtw12-bg: #eef0f7;
--color-gtw12-bg: oklch(0.95 0.01 275);
--color-gtw12-surf: #ffffff;
--color-gtw12-surf: oklch(1 0 0);
--color-gtw12-sunk: #e7eaf4;
--color-gtw12-sunk: oklch(0.93 0.012 275);
--color-gtw12-line: #d3d8e8;
--color-gtw12-line: oklch(0.87 0.018 275);
--color-gtw12-ink: #14162a;
--color-gtw12-ink: oklch(0.22 0.04 275);
--color-gtw12-mut: #5c6280;
--color-gtw12-mut: oklch(0.47 0.04 275);
--color-gtw12-acc: #4f46e5;
--color-gtw12-acc: oklch(0.52 0.22 275);
--color-gtw12-onacc: #f2f1fe;
--color-gtw12-onacc: oklch(0.97 0.015 280);
--color-gtw12-err: #b42318;
--color-gtw12-err: oklch(0.5 0.18 27);
--font-gtw12-display: 'Space Grotesk', 'Inter Tight', ui-sans-serif, system-ui, sans-serif;
--font-gtw12-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
}
.gtw-12 {
width: 100%;
min-height: 100svh;
--gtw12-idle: 0.05;
--gtw12-hover: 0.06;
--gtw12-active: 0.08;
--gtw12-sunkw: 0.035;
--gtw12-blend: multiply;
--gtw12-hl: rgba(255, 255, 255, 0.85);
--gtw12-noise: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='150' height='150'%3E%3Cfilter id='gtw12n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='150' height='150' filter='url(%23gtw12n)'/%3E%3C/svg%3E");
--gtw12-caret: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='9' viewBox='0 0 14 9'%3E%3Cpath d='M1 1l6 6 6-6' fill='none' stroke='%235c6280' stroke-width='1.8' stroke-linecap='round'/%3E%3C/svg%3E");
font-family: system-ui, -apple-system, 'Segoe UI', sans-serif;
line-height: 1.55;
accent-color: var(--color-gtw12-acc);
}
.gtw-12:has(.gtw-12-theme:checked) {
--color-gtw12-bg: #0c0d16;
--color-gtw12-bg: oklch(0.16 0.02 275);
--color-gtw12-surf: #15172a;
--color-gtw12-surf: oklch(0.22 0.03 275);
--color-gtw12-sunk: #10121f;
--color-gtw12-sunk: oklch(0.18 0.025 275);
--color-gtw12-line: #262a45;
--color-gtw12-line: oklch(0.3 0.04 275);
--color-gtw12-ink: #ecebfb;
--color-gtw12-ink: oklch(0.94 0.015 280);
--color-gtw12-mut: #9a9ac4;
--color-gtw12-mut: oklch(0.7 0.05 280);
--color-gtw12-acc: #a5b4fc;
--color-gtw12-acc: oklch(0.8 0.11 278);
--color-gtw12-onacc: #10122a;
--color-gtw12-onacc: oklch(0.19 0.04 278);
--color-gtw12-err: #fca5a5;
--color-gtw12-err: oklch(0.78 0.13 22);
--gtw12-blend: soft-light;
--gtw12-hl: rgba(255, 255, 255, 0.12);
--gtw12-idle: 0.07;
--gtw12-hover: 0.085;
--gtw12-active: 0.11;
--gtw12-sunkw: 0.05;
--gtw12-caret: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='9' viewBox='0 0 14 9'%3E%3Cpath d='M1 1l6 6 6-6' fill='none' stroke='%239a9ac4' stroke-width='1.8' stroke-linecap='round'/%3E%3C/svg%3E");
}
@media (prefers-color-scheme: dark) {
.gtw-12 {
--color-gtw12-bg: #0c0d16;
--color-gtw12-bg: oklch(0.16 0.02 275);
--color-gtw12-surf: #15172a;
--color-gtw12-surf: oklch(0.22 0.03 275);
--color-gtw12-sunk: #10121f;
--color-gtw12-sunk: oklch(0.18 0.025 275);
--color-gtw12-line: #262a45;
--color-gtw12-line: oklch(0.3 0.04 275);
--color-gtw12-ink: #ecebfb;
--color-gtw12-ink: oklch(0.94 0.015 280);
--color-gtw12-mut: #9a9ac4;
--color-gtw12-mut: oklch(0.7 0.05 280);
--color-gtw12-acc: #a5b4fc;
--color-gtw12-acc: oklch(0.8 0.11 278);
--color-gtw12-onacc: #10122a;
--color-gtw12-onacc: oklch(0.19 0.04 278);
--color-gtw12-err: #fca5a5;
--color-gtw12-err: oklch(0.78 0.13 22);
--gtw12-blend: soft-light;
--gtw12-hl: rgba(255, 255, 255, 0.12);
--gtw12-idle: 0.07;
--gtw12-hover: 0.085;
--gtw12-active: 0.11;
--gtw12-sunkw: 0.05;
--gtw12-caret: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='9' viewBox='0 0 14 9'%3E%3Cpath d='M1 1l6 6 6-6' fill='none' stroke='%239a9ac4' stroke-width='1.8' stroke-linecap='round'/%3E%3C/svg%3E");
}
.gtw-12:has(.gtw-12-theme:checked) {
--color-gtw12-bg: #eef0f7;
--color-gtw12-bg: oklch(0.95 0.01 275);
--color-gtw12-surf: #ffffff;
--color-gtw12-surf: oklch(1 0 0);
--color-gtw12-sunk: #e7eaf4;
--color-gtw12-sunk: oklch(0.93 0.012 275);
--color-gtw12-line: #d3d8e8;
--color-gtw12-line: oklch(0.87 0.018 275);
--color-gtw12-ink: #14162a;
--color-gtw12-ink: oklch(0.22 0.04 275);
--color-gtw12-mut: #5c6280;
--color-gtw12-mut: oklch(0.47 0.04 275);
--color-gtw12-acc: #4f46e5;
--color-gtw12-acc: oklch(0.52 0.22 275);
--color-gtw12-onacc: #f2f1fe;
--color-gtw12-onacc: oklch(0.97 0.015 280);
--color-gtw12-err: #b42318;
--color-gtw12-err: oklch(0.5 0.18 27);
--gtw12-blend: multiply;
--gtw12-hl: rgba(255, 255, 255, 0.85);
--gtw12-idle: 0.05;
--gtw12-hover: 0.06;
--gtw12-active: 0.08;
--gtw12-sunkw: 0.035;
--gtw12-caret: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='9' viewBox='0 0 14 9'%3E%3Cpath d='M1 1l6 6 6-6' fill='none' stroke='%235c6280' stroke-width='1.8' stroke-linecap='round'/%3E%3C/svg%3E");
}
}
@media (prefers-color-scheme: light) {
.gtw-12:has(.gtw-12-theme:checked) {
--color-gtw12-bg: #0c0d16;
--color-gtw12-bg: oklch(0.16 0.02 275);
--color-gtw12-surf: #15172a;
--color-gtw12-surf: oklch(0.22 0.03 275);
--color-gtw12-sunk: #10121f;
--color-gtw12-sunk: oklch(0.18 0.025 275);
--color-gtw12-line: #262a45;
--color-gtw12-line: oklch(0.3 0.04 275);
--color-gtw12-ink: #ecebfb;
--color-gtw12-ink: oklch(0.94 0.015 280);
--color-gtw12-mut: #9a9ac4;
--color-gtw12-mut: oklch(0.7 0.05 280);
--color-gtw12-acc: #a5b4fc;
--color-gtw12-acc: oklch(0.8 0.11 278);
--color-gtw12-onacc: #10122a;
--color-gtw12-onacc: oklch(0.19 0.04 278);
--color-gtw12-err: #fca5a5;
--color-gtw12-err: oklch(0.78 0.13 22);
--gtw12-blend: soft-light;
--gtw12-hl: rgba(255, 255, 255, 0.12);
--gtw12-idle: 0.07;
--gtw12-hover: 0.085;
--gtw12-active: 0.11;
--gtw12-sunkw: 0.05;
--gtw12-caret: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='9' viewBox='0 0 14 9'%3E%3Cpath d='M1 1l6 6 6-6' fill='none' stroke='%239a9ac4' stroke-width='1.8' stroke-linecap='round'/%3E%3C/svg%3E");
}
}<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=Space+Grotesk:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&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=Space+Grotesk:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
active:after:opacity-[…]is the port's best single line: a pressed-state texture change that used to need its own:active::afterrule now sits in the button's class list next to the shadow it moves with.- The segmented control uses named peers (
peer/d1,peer-checked/d1:) because three inputs and three labels share one wrapper — unnamed peers would each bind to the nearest preceding input and the second segment would style off the first. user-invalid:andpeer-user-invalid/mail:replace:user-invalidplus a sibling-combinator rule. The message's colour is now provably tied to the field's own validation state.- The select's arrow became a second data-URI token (
--gtw12-caret) with a three-layerbackground-position/repeat/blend-modestack, becauseappearance-noneremoves the native one and the caret must recolour per theme. - UI change from the CSS original: region options now lead with US and CA targets alongside London and Sydney, the ghost button gains a hover border, and the theme toggle forwards focus from its input — the original left that one control without a visible ring.
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 Textured Button and Input Surfaces
Source: https://codefronts.com/design-styles/tailwind-css-grain-texture/tailwind-textured-button-input-surfaces/
A control kit where every surface is a material: buttons are raised, inputs are recessed, and both carry the same grain token. Press a button and three things change at once — grain weight, highlight direction and shadow depth. The port's wins are active:after: for the pressed texture and peer-user-invalid: for validation copy that cannot drift from the field it describes.
## HTML
```html
<section class="gtw-12 relative block w-full min-h-svh bg-gtw12-bg text-gtw12-ink" aria-labelledby="gtw-12-title">
<div class="absolute top-[clamp(1rem,3vw,1.75rem)] right-[clamp(0.85rem,3vw,1.75rem)] z-40">
<input type="checkbox" id="gtw-12-theme" class="gtw-12-theme peer sr-only">
<label for="gtw-12-theme" class="grid place-items-center w-11 h-11 rounded-full border border-gtw12-line bg-gtw12-surf text-gtw12-ink cursor-pointer shadow-[inset_0_1px_0_var(--gtw12-hl)] peer-focus-visible:outline-2 peer-focus-visible:outline-gtw12-acc peer-focus-visible:outline-offset-2">
<svg viewBox="0 0 24 24" class="w-5 h-5" aria-hidden="true" focusable="false"><circle cx="12" cy="12" r="8.5" fill="none" stroke="currentColor" stroke-width="1.6"/><path d="M12 3.5a8.5 8.5 0 0 0 0 17Z" fill="currentColor"/></svg>
<span class="sr-only">Toggle dark theme</span>
</label>
</div>
<main class="w-[min(76rem,calc(100%-clamp(1.5rem,5vw,4rem)))] mx-auto pt-[clamp(3rem,8vw,5.5rem)] pb-[clamp(2.5rem,6vw,4rem)] grid gap-[clamp(2rem,5vw,3rem)]">
<header class="max-w-[44rem] grid gap-[0.6rem]">
<p class="m-0 font-gtw12-mono text-[0.72rem] font-medium leading-none tracking-[0.18em] uppercase text-gtw12-acc">Control surfaces · 12</p>
<h1 id="gtw-12-title" class="m-0 font-gtw12-display text-[clamp(2rem,5.4vw,3.3rem)] font-semibold leading-[1.03] tracking-[-0.035em] text-balance">Every control is a material</h1>
<p class="m-0 max-w-[38rem] text-gtw12-mut text-[clamp(0.95rem,1.9vw,1.08rem)] text-pretty">Buttons are raised, inputs are recessed, and both carry the same grain. Press a button and three things change at once — grain weight, highlight direction, shadow depth.</p>
</header>
<div class="grid gap-[clamp(1rem,2.4vw,1.5rem)] grid-cols-[repeat(auto-fit,minmax(min(100%,18rem),1fr))] items-start">
<section class="relative isolate overflow-hidden grid gap-4 content-start min-w-0 p-[clamp(1.2rem,3vw,1.7rem)] rounded-[18px] border border-gtw12-line bg-gtw12-surf shadow-[inset_0_1px_0_var(--gtw12-hl),0_12px_30px_-20px_oklch(0.2_0.03_275/0.3)] after:content-[''] after:absolute after:inset-0 after:-z-[1] after:pointer-events-none after:bg-[image:var(--gtw12-noise)] after:opacity-[0.04] after:[mix-blend-mode:var(--gtw12-blend)]" aria-labelledby="gtw-12-h-btn">
<h2 id="gtw-12-h-btn" class="m-0 pb-[0.6rem] border-b border-gtw12-line font-gtw12-display text-[1.05rem] font-semibold leading-tight tracking-[-0.02em]">Buttons</h2>
<div class="flex flex-wrap gap-[0.6rem]">
<button type="button" class="relative isolate overflow-hidden inline-flex items-center justify-center min-h-11 px-[1.2rem] rounded-[12px] border-0 bg-gtw12-acc text-gtw12-onacc font-gtw12-display text-[0.88rem] font-semibold leading-none cursor-pointer shadow-[inset_0_1px_0_rgba(255,255,255,0.24),0_2px_5px_oklch(0.2_0.03_275/0.22)] transition-[box-shadow] duration-200 active:shadow-[inset_0_-1px_0_rgba(255,255,255,0.2),inset_0_2px_6px_oklch(0_0_0/0.3)] focus-visible:outline-2 focus-visible:outline-gtw12-acc focus-visible:outline-offset-2 after:content-[''] after:absolute after:inset-0 after:pointer-events-none after:bg-[image:var(--gtw12-noise)] after:mix-blend-overlay after:opacity-[var(--gtw12-idle)] after:transition-opacity after:duration-150 hover:after:opacity-[var(--gtw12-hover)] active:after:opacity-[var(--gtw12-active)]">Save changes</button>
<button type="button" class="relative isolate overflow-hidden inline-flex items-center justify-center min-h-11 px-[1.2rem] rounded-[12px] border-0 bg-gtw12-sunk text-gtw12-ink font-gtw12-display text-[0.88rem] font-semibold leading-none cursor-pointer shadow-[inset_0_1px_0_var(--gtw12-hl),0_1px_3px_oklch(0.2_0.03_275/0.14)] transition-[box-shadow] duration-200 active:shadow-[inset_0_2px_6px_oklch(0_0_0/0.22)] focus-visible:outline-2 focus-visible:outline-gtw12-acc focus-visible:outline-offset-2 after:content-[''] after:absolute after:inset-0 after:pointer-events-none after:bg-[image:var(--gtw12-noise)] after:[mix-blend-mode:var(--gtw12-blend)] after:opacity-[var(--gtw12-idle)] after:transition-opacity after:duration-150 hover:after:opacity-[var(--gtw12-hover)] active:after:opacity-[var(--gtw12-active)]">Preview</button>
<button type="button" class="inline-flex items-center justify-center min-h-11 px-[1.2rem] rounded-[12px] bg-transparent border border-gtw12-line text-gtw12-mut font-gtw12-display text-[0.88rem] font-semibold leading-none cursor-pointer transition-colors duration-200 hover:text-gtw12-ink hover:border-gtw12-acc focus-visible:outline-2 focus-visible:outline-gtw12-acc focus-visible:outline-offset-2">Cancel</button>
<button type="button" disabled class="inline-flex items-center justify-center min-h-11 px-[1.2rem] rounded-[12px] border-0 bg-gtw12-acc text-gtw12-onacc font-gtw12-display text-[0.88rem] font-semibold leading-none opacity-45 cursor-not-allowed shadow-none">Disabled</button>
</div>
<div role="group" aria-label="Density" class="flex flex-wrap p-1 rounded-[16px] border border-gtw12-line bg-gtw12-sunk shadow-[inset_0_2px_5px_oklch(0.2_0.03_275/0.12)]">
<input type="radio" name="gtw-12-density" id="gtw-12-d1" checked class="peer/d1 sr-only">
<label for="gtw-12-d1" class="relative isolate overflow-hidden flex-auto inline-flex items-center justify-center min-h-10 px-[0.8rem] rounded-[12px] text-gtw12-mut font-gtw12-display text-[0.83rem] font-semibold leading-none cursor-pointer transition-colors duration-200 peer-checked/d1:bg-gtw12-surf peer-checked/d1:text-gtw12-ink peer-checked/d1:shadow-[inset_0_1px_0_var(--gtw12-hl),0_1px_3px_oklch(0.2_0.03_275/0.18)] peer-focus-visible/d1:outline-2 peer-focus-visible/d1:outline-gtw12-acc peer-focus-visible/d1:outline-offset-2 peer-checked/d1:after:content-[''] after:absolute after:inset-0 after:pointer-events-none after:bg-[image:var(--gtw12-noise)] after:[mix-blend-mode:var(--gtw12-blend)] after:opacity-[var(--gtw12-idle)]">Compact</label>
<input type="radio" name="gtw-12-density" id="gtw-12-d2" class="peer/d2 sr-only">
<label for="gtw-12-d2" class="relative isolate overflow-hidden flex-auto inline-flex items-center justify-center min-h-10 px-[0.8rem] rounded-[12px] text-gtw12-mut font-gtw12-display text-[0.83rem] font-semibold leading-none cursor-pointer transition-colors duration-200 peer-checked/d2:bg-gtw12-surf peer-checked/d2:text-gtw12-ink peer-checked/d2:shadow-[inset_0_1px_0_var(--gtw12-hl),0_1px_3px_oklch(0.2_0.03_275/0.18)] peer-focus-visible/d2:outline-2 peer-focus-visible/d2:outline-gtw12-acc peer-focus-visible/d2:outline-offset-2 peer-checked/d2:after:content-[''] after:absolute after:inset-0 after:pointer-events-none after:bg-[image:var(--gtw12-noise)] after:[mix-blend-mode:var(--gtw12-blend)] after:opacity-[var(--gtw12-idle)]">Regular</label>
<input type="radio" name="gtw-12-density" id="gtw-12-d3" class="peer/d3 sr-only">
<label for="gtw-12-d3" class="relative isolate overflow-hidden flex-auto inline-flex items-center justify-center min-h-10 px-[0.8rem] rounded-[12px] text-gtw12-mut font-gtw12-display text-[0.83rem] font-semibold leading-none cursor-pointer transition-colors duration-200 peer-checked/d3:bg-gtw12-surf peer-checked/d3:text-gtw12-ink peer-checked/d3:shadow-[inset_0_1px_0_var(--gtw12-hl),0_1px_3px_oklch(0.2_0.03_275/0.18)] peer-focus-visible/d3:outline-2 peer-focus-visible/d3:outline-gtw12-acc peer-focus-visible/d3:outline-offset-2 peer-checked/d3:after:content-[''] after:absolute after:inset-0 after:pointer-events-none after:bg-[image:var(--gtw12-noise)] after:[mix-blend-mode:var(--gtw12-blend)] after:opacity-[var(--gtw12-idle)]">Roomy</label>
</div>
<p class="m-0 text-gtw12-mut text-[0.8rem] text-pretty">Idle 0.05 · hover 0.06 · active 0.08 with the highlight inverted.</p>
</section>
<section class="relative isolate overflow-hidden grid gap-4 content-start min-w-0 p-[clamp(1.2rem,3vw,1.7rem)] rounded-[18px] border border-gtw12-line bg-gtw12-surf shadow-[inset_0_1px_0_var(--gtw12-hl),0_12px_30px_-20px_oklch(0.2_0.03_275/0.3)] after:content-[''] after:absolute after:inset-0 after:-z-[1] after:pointer-events-none after:bg-[image:var(--gtw12-noise)] after:opacity-[0.04] after:[mix-blend-mode:var(--gtw12-blend)]" aria-labelledby="gtw-12-h-fields">
<h2 id="gtw-12-h-fields" class="m-0 pb-[0.6rem] border-b border-gtw12-line font-gtw12-display text-[1.05rem] font-semibold leading-tight tracking-[-0.02em]">Fields</h2>
<div class="grid gap-[0.35rem]">
<label for="gtw-12-name" class="font-gtw12-display text-[0.82rem] font-medium leading-[1.3]">Full name</label>
<input type="text" id="gtw-12-name" name="gtw-12-name" placeholder="Ada Lovelace" autocomplete="name" class="min-h-[46px] px-[0.85rem] rounded-[12px] border border-gtw12-line text-gtw12-ink text-[0.95rem] leading-none [background-image:var(--gtw12-noise),linear-gradient(var(--color-gtw12-sunk),var(--color-gtw12-sunk))] [background-blend-mode:var(--gtw12-blend),normal] shadow-[inset_0_2px_5px_oklch(0.2_0.03_275/0.13),inset_0_-1px_0_var(--gtw12-hl)] placeholder:text-[color-mix(in_oklab,var(--color-gtw12-mut)_70%,transparent)] transition-colors duration-200 hover:border-[color-mix(in_oklab,var(--color-gtw12-acc)_45%,var(--color-gtw12-line))] focus-visible:outline-2 focus-visible:outline-gtw12-acc focus-visible:outline-offset-2">
</div>
<div class="grid gap-[0.35rem]">
<label for="gtw-12-mail" class="font-gtw12-display text-[0.82rem] font-medium leading-[1.3]">Work email</label>
<input type="email" id="gtw-12-mail" name="gtw-12-mail" placeholder="ada@example.com" autocomplete="email" required aria-describedby="gtw-12-mail-hint" class="peer/mail min-h-[46px] px-[0.85rem] rounded-[12px] border border-gtw12-line text-gtw12-ink text-[0.95rem] leading-none [background-image:var(--gtw12-noise),linear-gradient(var(--color-gtw12-sunk),var(--color-gtw12-sunk))] [background-blend-mode:var(--gtw12-blend),normal] shadow-[inset_0_2px_5px_oklch(0.2_0.03_275/0.13),inset_0_-1px_0_var(--gtw12-hl)] placeholder:text-[color-mix(in_oklab,var(--color-gtw12-mut)_70%,transparent)] transition-colors duration-200 hover:border-[color-mix(in_oklab,var(--color-gtw12-acc)_45%,var(--color-gtw12-line))] user-invalid:border-gtw12-err focus-visible:outline-2 focus-visible:outline-gtw12-acc focus-visible:outline-offset-2">
<p id="gtw-12-mail-hint" class="m-0 text-[0.8rem] text-gtw12-mut peer-user-invalid/mail:text-gtw12-err">We only email about deploys. Real address required.</p>
</div>
<div class="grid gap-[0.35rem]">
<label for="gtw-12-region" class="font-gtw12-display text-[0.82rem] font-medium leading-[1.3]">Region</label>
<select id="gtw-12-region" name="gtw-12-region" class="appearance-none min-h-[46px] pl-[0.85rem] pr-[2.2rem] rounded-[12px] border border-gtw12-line text-gtw12-ink text-[0.95rem] cursor-pointer [background-image:var(--gtw12-caret),var(--gtw12-noise),linear-gradient(var(--color-gtw12-sunk),var(--color-gtw12-sunk))] [background-position:right_0.9rem_center,0_0,0_0] [background-repeat:no-repeat,repeat,no-repeat] [background-blend-mode:normal,var(--gtw12-blend),normal] shadow-[inset_0_2px_5px_oklch(0.2_0.03_275/0.13),inset_0_-1px_0_var(--gtw12-hl)] transition-colors duration-200 hover:border-[color-mix(in_oklab,var(--color-gtw12-acc)_45%,var(--color-gtw12-line))] focus-visible:outline-2 focus-visible:outline-gtw12-acc focus-visible:outline-offset-2">
<option>us-east-1 · N. Virginia</option>
<option>us-west-2 · Oregon</option>
<option>eu-west-2 · London</option>
<option>ca-central-1 · Montréal</option>
<option>ap-southeast-2 · Sydney</option>
</select>
</div>
</section>
<section class="relative isolate overflow-hidden grid gap-4 content-start min-w-0 p-[clamp(1.2rem,3vw,1.7rem)] rounded-[18px] border border-gtw12-line bg-gtw12-surf shadow-[inset_0_1px_0_var(--gtw12-hl),0_12px_30px_-20px_oklch(0.2_0.03_275/0.3)] after:content-[''] after:absolute after:inset-0 after:-z-[1] after:pointer-events-none after:bg-[image:var(--gtw12-noise)] after:opacity-[0.04] after:[mix-blend-mode:var(--gtw12-blend)]" aria-labelledby="gtw-12-h-misc">
<h2 id="gtw-12-h-misc" class="m-0 pb-[0.6rem] border-b border-gtw12-line font-gtw12-display text-[1.05rem] font-semibold leading-tight tracking-[-0.02em]">Toggles and ranges</h2>
<ul class="list-none m-0 p-0 grid gap-[0.1rem]">
<li class="flex items-center gap-[0.6rem] min-h-11"><input type="checkbox" id="gtw-12-c1" checked class="w-5 h-5 m-0 cursor-pointer"><label for="gtw-12-c1" class="text-[0.92rem] cursor-pointer">Textured surfaces</label></li>
<li class="flex items-center gap-[0.6rem] min-h-11"><input type="checkbox" id="gtw-12-c2" class="w-5 h-5 m-0 cursor-pointer"><label for="gtw-12-c2" class="text-[0.92rem] cursor-pointer">Reduce transparency</label></li>
<li class="flex items-center gap-[0.6rem] min-h-11"><input type="radio" name="gtw-12-r" id="gtw-12-r1" checked class="w-5 h-5 m-0 cursor-pointer"><label for="gtw-12-r1" class="text-[0.92rem] cursor-pointer">Match system theme</label></li>
<li class="flex items-center gap-[0.6rem] min-h-11"><input type="radio" name="gtw-12-r" id="gtw-12-r2" class="w-5 h-5 m-0 cursor-pointer"><label for="gtw-12-r2" class="text-[0.92rem] cursor-pointer">Always dark</label></li>
</ul>
<div class="grid gap-[0.35rem]">
<label for="gtw-12-grain" class="font-gtw12-display text-[0.82rem] font-medium leading-[1.3]">Grain weight</label>
<input type="range" id="gtw-12-grain" name="gtw-12-grain" min="0" max="20" value="6" step="1" class="w-full min-h-11 m-0 bg-transparent cursor-pointer">
<p class="m-0 text-gtw12-mut text-[0.8rem] text-pretty">Native thumb, textured track, <code class="font-gtw12-mono text-[0.9em]">accent-color</code> for the fill.</p>
</div>
</section>
</div>
</main>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-gtw12-bg: #eef0f7;
--color-gtw12-bg: oklch(0.95 0.01 275);
--color-gtw12-surf: #ffffff;
--color-gtw12-surf: oklch(1 0 0);
--color-gtw12-sunk: #e7eaf4;
--color-gtw12-sunk: oklch(0.93 0.012 275);
--color-gtw12-line: #d3d8e8;
--color-gtw12-line: oklch(0.87 0.018 275);
--color-gtw12-ink: #14162a;
--color-gtw12-ink: oklch(0.22 0.04 275);
--color-gtw12-mut: #5c6280;
--color-gtw12-mut: oklch(0.47 0.04 275);
--color-gtw12-acc: #4f46e5;
--color-gtw12-acc: oklch(0.52 0.22 275);
--color-gtw12-onacc: #f2f1fe;
--color-gtw12-onacc: oklch(0.97 0.015 280);
--color-gtw12-err: #b42318;
--color-gtw12-err: oklch(0.5 0.18 27);
--font-gtw12-display: 'Space Grotesk', 'Inter Tight', ui-sans-serif, system-ui, sans-serif;
--font-gtw12-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
}
.gtw-12 {
width: 100%;
min-height: 100svh;
--gtw12-idle: 0.05;
--gtw12-hover: 0.06;
--gtw12-active: 0.08;
--gtw12-sunkw: 0.035;
--gtw12-blend: multiply;
--gtw12-hl: rgba(255, 255, 255, 0.85);
--gtw12-noise: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='150' height='150'%3E%3Cfilter id='gtw12n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='150' height='150' filter='url(%23gtw12n)'/%3E%3C/svg%3E");
--gtw12-caret: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='9' viewBox='0 0 14 9'%3E%3Cpath d='M1 1l6 6 6-6' fill='none' stroke='%235c6280' stroke-width='1.8' stroke-linecap='round'/%3E%3C/svg%3E");
font-family: system-ui, -apple-system, 'Segoe UI', sans-serif;
line-height: 1.55;
accent-color: var(--color-gtw12-acc);
}
.gtw-12:has(.gtw-12-theme:checked) {
--color-gtw12-bg: #0c0d16;
--color-gtw12-bg: oklch(0.16 0.02 275);
--color-gtw12-surf: #15172a;
--color-gtw12-surf: oklch(0.22 0.03 275);
--color-gtw12-sunk: #10121f;
--color-gtw12-sunk: oklch(0.18 0.025 275);
--color-gtw12-line: #262a45;
--color-gtw12-line: oklch(0.3 0.04 275);
--color-gtw12-ink: #ecebfb;
--color-gtw12-ink: oklch(0.94 0.015 280);
--color-gtw12-mut: #9a9ac4;
--color-gtw12-mut: oklch(0.7 0.05 280);
--color-gtw12-acc: #a5b4fc;
--color-gtw12-acc: oklch(0.8 0.11 278);
--color-gtw12-onacc: #10122a;
--color-gtw12-onacc: oklch(0.19 0.04 278);
--color-gtw12-err: #fca5a5;
--color-gtw12-err: oklch(0.78 0.13 22);
--gtw12-blend: soft-light;
--gtw12-hl: rgba(255, 255, 255, 0.12);
--gtw12-idle: 0.07;
--gtw12-hover: 0.085;
--gtw12-active: 0.11;
--gtw12-sunkw: 0.05;
--gtw12-caret: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='9' viewBox='0 0 14 9'%3E%3Cpath d='M1 1l6 6 6-6' fill='none' stroke='%239a9ac4' stroke-width='1.8' stroke-linecap='round'/%3E%3C/svg%3E");
}
@media (prefers-color-scheme: dark) {
.gtw-12 {
--color-gtw12-bg: #0c0d16;
--color-gtw12-bg: oklch(0.16 0.02 275);
--color-gtw12-surf: #15172a;
--color-gtw12-surf: oklch(0.22 0.03 275);
--color-gtw12-sunk: #10121f;
--color-gtw12-sunk: oklch(0.18 0.025 275);
--color-gtw12-line: #262a45;
--color-gtw12-line: oklch(0.3 0.04 275);
--color-gtw12-ink: #ecebfb;
--color-gtw12-ink: oklch(0.94 0.015 280);
--color-gtw12-mut: #9a9ac4;
--color-gtw12-mut: oklch(0.7 0.05 280);
--color-gtw12-acc: #a5b4fc;
--color-gtw12-acc: oklch(0.8 0.11 278);
--color-gtw12-onacc: #10122a;
--color-gtw12-onacc: oklch(0.19 0.04 278);
--color-gtw12-err: #fca5a5;
--color-gtw12-err: oklch(0.78 0.13 22);
--gtw12-blend: soft-light;
--gtw12-hl: rgba(255, 255, 255, 0.12);
--gtw12-idle: 0.07;
--gtw12-hover: 0.085;
--gtw12-active: 0.11;
--gtw12-sunkw: 0.05;
--gtw12-caret: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='9' viewBox='0 0 14 9'%3E%3Cpath d='M1 1l6 6 6-6' fill='none' stroke='%239a9ac4' stroke-width='1.8' stroke-linecap='round'/%3E%3C/svg%3E");
}
.gtw-12:has(.gtw-12-theme:checked) {
--color-gtw12-bg: #eef0f7;
--color-gtw12-bg: oklch(0.95 0.01 275);
--color-gtw12-surf: #ffffff;
--color-gtw12-surf: oklch(1 0 0);
--color-gtw12-sunk: #e7eaf4;
--color-gtw12-sunk: oklch(0.93 0.012 275);
--color-gtw12-line: #d3d8e8;
--color-gtw12-line: oklch(0.87 0.018 275);
--color-gtw12-ink: #14162a;
--color-gtw12-ink: oklch(0.22 0.04 275);
--color-gtw12-mut: #5c6280;
--color-gtw12-mut: oklch(0.47 0.04 275);
--color-gtw12-acc: #4f46e5;
--color-gtw12-acc: oklch(0.52 0.22 275);
--color-gtw12-onacc: #f2f1fe;
--color-gtw12-onacc: oklch(0.97 0.015 280);
--color-gtw12-err: #b42318;
--color-gtw12-err: oklch(0.5 0.18 27);
--gtw12-blend: multiply;
--gtw12-hl: rgba(255, 255, 255, 0.85);
--gtw12-idle: 0.05;
--gtw12-hover: 0.06;
--gtw12-active: 0.08;
--gtw12-sunkw: 0.035;
--gtw12-caret: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='9' viewBox='0 0 14 9'%3E%3Cpath d='M1 1l6 6 6-6' fill='none' stroke='%235c6280' stroke-width='1.8' stroke-linecap='round'/%3E%3C/svg%3E");
}
}
@media (prefers-color-scheme: light) {
.gtw-12:has(.gtw-12-theme:checked) {
--color-gtw12-bg: #0c0d16;
--color-gtw12-bg: oklch(0.16 0.02 275);
--color-gtw12-surf: #15172a;
--color-gtw12-surf: oklch(0.22 0.03 275);
--color-gtw12-sunk: #10121f;
--color-gtw12-sunk: oklch(0.18 0.025 275);
--color-gtw12-line: #262a45;
--color-gtw12-line: oklch(0.3 0.04 275);
--color-gtw12-ink: #ecebfb;
--color-gtw12-ink: oklch(0.94 0.015 280);
--color-gtw12-mut: #9a9ac4;
--color-gtw12-mut: oklch(0.7 0.05 280);
--color-gtw12-acc: #a5b4fc;
--color-gtw12-acc: oklch(0.8 0.11 278);
--color-gtw12-onacc: #10122a;
--color-gtw12-onacc: oklch(0.19 0.04 278);
--color-gtw12-err: #fca5a5;
--color-gtw12-err: oklch(0.78 0.13 22);
--gtw12-blend: soft-light;
--gtw12-hl: rgba(255, 255, 255, 0.12);
--gtw12-idle: 0.07;
--gtw12-hover: 0.085;
--gtw12-active: 0.11;
--gtw12-sunkw: 0.05;
--gtw12-caret: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='9' viewBox='0 0 14 9'%3E%3Cpath d='M1 1l6 6 6-6' fill='none' stroke='%239a9ac4' stroke-width='1.8' stroke-linecap='round'/%3E%3C/svg%3E");
}
}
```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 Textured Button and Input Surfaces
Source: https://codefronts.com/design-styles/tailwind-css-grain-texture/tailwind-textured-button-input-surfaces/
A control kit where every surface is a material: buttons are raised, inputs are recessed, and both carry the same grain token. Press a button and three things change at once — grain weight, highlight direction and shadow depth. The port's wins are active:after: for the pressed texture and peer-user-invalid: for validation copy that cannot drift from the field it describes.
## HTML
```html
<section class="gtw-12 relative block w-full min-h-svh bg-gtw12-bg text-gtw12-ink" aria-labelledby="gtw-12-title">
<div class="absolute top-[clamp(1rem,3vw,1.75rem)] right-[clamp(0.85rem,3vw,1.75rem)] z-40">
<input type="checkbox" id="gtw-12-theme" class="gtw-12-theme peer sr-only">
<label for="gtw-12-theme" class="grid place-items-center w-11 h-11 rounded-full border border-gtw12-line bg-gtw12-surf text-gtw12-ink cursor-pointer shadow-[inset_0_1px_0_var(--gtw12-hl)] peer-focus-visible:outline-2 peer-focus-visible:outline-gtw12-acc peer-focus-visible:outline-offset-2">
<svg viewBox="0 0 24 24" class="w-5 h-5" aria-hidden="true" focusable="false"><circle cx="12" cy="12" r="8.5" fill="none" stroke="currentColor" stroke-width="1.6"/><path d="M12 3.5a8.5 8.5 0 0 0 0 17Z" fill="currentColor"/></svg>
<span class="sr-only">Toggle dark theme</span>
</label>
</div>
<main class="w-[min(76rem,calc(100%-clamp(1.5rem,5vw,4rem)))] mx-auto pt-[clamp(3rem,8vw,5.5rem)] pb-[clamp(2.5rem,6vw,4rem)] grid gap-[clamp(2rem,5vw,3rem)]">
<header class="max-w-[44rem] grid gap-[0.6rem]">
<p class="m-0 font-gtw12-mono text-[0.72rem] font-medium leading-none tracking-[0.18em] uppercase text-gtw12-acc">Control surfaces · 12</p>
<h1 id="gtw-12-title" class="m-0 font-gtw12-display text-[clamp(2rem,5.4vw,3.3rem)] font-semibold leading-[1.03] tracking-[-0.035em] text-balance">Every control is a material</h1>
<p class="m-0 max-w-[38rem] text-gtw12-mut text-[clamp(0.95rem,1.9vw,1.08rem)] text-pretty">Buttons are raised, inputs are recessed, and both carry the same grain. Press a button and three things change at once — grain weight, highlight direction, shadow depth.</p>
</header>
<div class="grid gap-[clamp(1rem,2.4vw,1.5rem)] grid-cols-[repeat(auto-fit,minmax(min(100%,18rem),1fr))] items-start">
<section class="relative isolate overflow-hidden grid gap-4 content-start min-w-0 p-[clamp(1.2rem,3vw,1.7rem)] rounded-[18px] border border-gtw12-line bg-gtw12-surf shadow-[inset_0_1px_0_var(--gtw12-hl),0_12px_30px_-20px_oklch(0.2_0.03_275/0.3)] after:content-[''] after:absolute after:inset-0 after:-z-[1] after:pointer-events-none after:bg-[image:var(--gtw12-noise)] after:opacity-[0.04] after:[mix-blend-mode:var(--gtw12-blend)]" aria-labelledby="gtw-12-h-btn">
<h2 id="gtw-12-h-btn" class="m-0 pb-[0.6rem] border-b border-gtw12-line font-gtw12-display text-[1.05rem] font-semibold leading-tight tracking-[-0.02em]">Buttons</h2>
<div class="flex flex-wrap gap-[0.6rem]">
<button type="button" class="relative isolate overflow-hidden inline-flex items-center justify-center min-h-11 px-[1.2rem] rounded-[12px] border-0 bg-gtw12-acc text-gtw12-onacc font-gtw12-display text-[0.88rem] font-semibold leading-none cursor-pointer shadow-[inset_0_1px_0_rgba(255,255,255,0.24),0_2px_5px_oklch(0.2_0.03_275/0.22)] transition-[box-shadow] duration-200 active:shadow-[inset_0_-1px_0_rgba(255,255,255,0.2),inset_0_2px_6px_oklch(0_0_0/0.3)] focus-visible:outline-2 focus-visible:outline-gtw12-acc focus-visible:outline-offset-2 after:content-[''] after:absolute after:inset-0 after:pointer-events-none after:bg-[image:var(--gtw12-noise)] after:mix-blend-overlay after:opacity-[var(--gtw12-idle)] after:transition-opacity after:duration-150 hover:after:opacity-[var(--gtw12-hover)] active:after:opacity-[var(--gtw12-active)]">Save changes</button>
<button type="button" class="relative isolate overflow-hidden inline-flex items-center justify-center min-h-11 px-[1.2rem] rounded-[12px] border-0 bg-gtw12-sunk text-gtw12-ink font-gtw12-display text-[0.88rem] font-semibold leading-none cursor-pointer shadow-[inset_0_1px_0_var(--gtw12-hl),0_1px_3px_oklch(0.2_0.03_275/0.14)] transition-[box-shadow] duration-200 active:shadow-[inset_0_2px_6px_oklch(0_0_0/0.22)] focus-visible:outline-2 focus-visible:outline-gtw12-acc focus-visible:outline-offset-2 after:content-[''] after:absolute after:inset-0 after:pointer-events-none after:bg-[image:var(--gtw12-noise)] after:[mix-blend-mode:var(--gtw12-blend)] after:opacity-[var(--gtw12-idle)] after:transition-opacity after:duration-150 hover:after:opacity-[var(--gtw12-hover)] active:after:opacity-[var(--gtw12-active)]">Preview</button>
<button type="button" class="inline-flex items-center justify-center min-h-11 px-[1.2rem] rounded-[12px] bg-transparent border border-gtw12-line text-gtw12-mut font-gtw12-display text-[0.88rem] font-semibold leading-none cursor-pointer transition-colors duration-200 hover:text-gtw12-ink hover:border-gtw12-acc focus-visible:outline-2 focus-visible:outline-gtw12-acc focus-visible:outline-offset-2">Cancel</button>
<button type="button" disabled class="inline-flex items-center justify-center min-h-11 px-[1.2rem] rounded-[12px] border-0 bg-gtw12-acc text-gtw12-onacc font-gtw12-display text-[0.88rem] font-semibold leading-none opacity-45 cursor-not-allowed shadow-none">Disabled</button>
</div>
<div role="group" aria-label="Density" class="flex flex-wrap p-1 rounded-[16px] border border-gtw12-line bg-gtw12-sunk shadow-[inset_0_2px_5px_oklch(0.2_0.03_275/0.12)]">
<input type="radio" name="gtw-12-density" id="gtw-12-d1" checked class="peer/d1 sr-only">
<label for="gtw-12-d1" class="relative isolate overflow-hidden flex-auto inline-flex items-center justify-center min-h-10 px-[0.8rem] rounded-[12px] text-gtw12-mut font-gtw12-display text-[0.83rem] font-semibold leading-none cursor-pointer transition-colors duration-200 peer-checked/d1:bg-gtw12-surf peer-checked/d1:text-gtw12-ink peer-checked/d1:shadow-[inset_0_1px_0_var(--gtw12-hl),0_1px_3px_oklch(0.2_0.03_275/0.18)] peer-focus-visible/d1:outline-2 peer-focus-visible/d1:outline-gtw12-acc peer-focus-visible/d1:outline-offset-2 peer-checked/d1:after:content-[''] after:absolute after:inset-0 after:pointer-events-none after:bg-[image:var(--gtw12-noise)] after:[mix-blend-mode:var(--gtw12-blend)] after:opacity-[var(--gtw12-idle)]">Compact</label>
<input type="radio" name="gtw-12-density" id="gtw-12-d2" class="peer/d2 sr-only">
<label for="gtw-12-d2" class="relative isolate overflow-hidden flex-auto inline-flex items-center justify-center min-h-10 px-[0.8rem] rounded-[12px] text-gtw12-mut font-gtw12-display text-[0.83rem] font-semibold leading-none cursor-pointer transition-colors duration-200 peer-checked/d2:bg-gtw12-surf peer-checked/d2:text-gtw12-ink peer-checked/d2:shadow-[inset_0_1px_0_var(--gtw12-hl),0_1px_3px_oklch(0.2_0.03_275/0.18)] peer-focus-visible/d2:outline-2 peer-focus-visible/d2:outline-gtw12-acc peer-focus-visible/d2:outline-offset-2 peer-checked/d2:after:content-[''] after:absolute after:inset-0 after:pointer-events-none after:bg-[image:var(--gtw12-noise)] after:[mix-blend-mode:var(--gtw12-blend)] after:opacity-[var(--gtw12-idle)]">Regular</label>
<input type="radio" name="gtw-12-density" id="gtw-12-d3" class="peer/d3 sr-only">
<label for="gtw-12-d3" class="relative isolate overflow-hidden flex-auto inline-flex items-center justify-center min-h-10 px-[0.8rem] rounded-[12px] text-gtw12-mut font-gtw12-display text-[0.83rem] font-semibold leading-none cursor-pointer transition-colors duration-200 peer-checked/d3:bg-gtw12-surf peer-checked/d3:text-gtw12-ink peer-checked/d3:shadow-[inset_0_1px_0_var(--gtw12-hl),0_1px_3px_oklch(0.2_0.03_275/0.18)] peer-focus-visible/d3:outline-2 peer-focus-visible/d3:outline-gtw12-acc peer-focus-visible/d3:outline-offset-2 peer-checked/d3:after:content-[''] after:absolute after:inset-0 after:pointer-events-none after:bg-[image:var(--gtw12-noise)] after:[mix-blend-mode:var(--gtw12-blend)] after:opacity-[var(--gtw12-idle)]">Roomy</label>
</div>
<p class="m-0 text-gtw12-mut text-[0.8rem] text-pretty">Idle 0.05 · hover 0.06 · active 0.08 with the highlight inverted.</p>
</section>
<section class="relative isolate overflow-hidden grid gap-4 content-start min-w-0 p-[clamp(1.2rem,3vw,1.7rem)] rounded-[18px] border border-gtw12-line bg-gtw12-surf shadow-[inset_0_1px_0_var(--gtw12-hl),0_12px_30px_-20px_oklch(0.2_0.03_275/0.3)] after:content-[''] after:absolute after:inset-0 after:-z-[1] after:pointer-events-none after:bg-[image:var(--gtw12-noise)] after:opacity-[0.04] after:[mix-blend-mode:var(--gtw12-blend)]" aria-labelledby="gtw-12-h-fields">
<h2 id="gtw-12-h-fields" class="m-0 pb-[0.6rem] border-b border-gtw12-line font-gtw12-display text-[1.05rem] font-semibold leading-tight tracking-[-0.02em]">Fields</h2>
<div class="grid gap-[0.35rem]">
<label for="gtw-12-name" class="font-gtw12-display text-[0.82rem] font-medium leading-[1.3]">Full name</label>
<input type="text" id="gtw-12-name" name="gtw-12-name" placeholder="Ada Lovelace" autocomplete="name" class="min-h-[46px] px-[0.85rem] rounded-[12px] border border-gtw12-line text-gtw12-ink text-[0.95rem] leading-none [background-image:var(--gtw12-noise),linear-gradient(var(--color-gtw12-sunk),var(--color-gtw12-sunk))] [background-blend-mode:var(--gtw12-blend),normal] shadow-[inset_0_2px_5px_oklch(0.2_0.03_275/0.13),inset_0_-1px_0_var(--gtw12-hl)] placeholder:text-[color-mix(in_oklab,var(--color-gtw12-mut)_70%,transparent)] transition-colors duration-200 hover:border-[color-mix(in_oklab,var(--color-gtw12-acc)_45%,var(--color-gtw12-line))] focus-visible:outline-2 focus-visible:outline-gtw12-acc focus-visible:outline-offset-2">
</div>
<div class="grid gap-[0.35rem]">
<label for="gtw-12-mail" class="font-gtw12-display text-[0.82rem] font-medium leading-[1.3]">Work email</label>
<input type="email" id="gtw-12-mail" name="gtw-12-mail" placeholder="ada@example.com" autocomplete="email" required aria-describedby="gtw-12-mail-hint" class="peer/mail min-h-[46px] px-[0.85rem] rounded-[12px] border border-gtw12-line text-gtw12-ink text-[0.95rem] leading-none [background-image:var(--gtw12-noise),linear-gradient(var(--color-gtw12-sunk),var(--color-gtw12-sunk))] [background-blend-mode:var(--gtw12-blend),normal] shadow-[inset_0_2px_5px_oklch(0.2_0.03_275/0.13),inset_0_-1px_0_var(--gtw12-hl)] placeholder:text-[color-mix(in_oklab,var(--color-gtw12-mut)_70%,transparent)] transition-colors duration-200 hover:border-[color-mix(in_oklab,var(--color-gtw12-acc)_45%,var(--color-gtw12-line))] user-invalid:border-gtw12-err focus-visible:outline-2 focus-visible:outline-gtw12-acc focus-visible:outline-offset-2">
<p id="gtw-12-mail-hint" class="m-0 text-[0.8rem] text-gtw12-mut peer-user-invalid/mail:text-gtw12-err">We only email about deploys. Real address required.</p>
</div>
<div class="grid gap-[0.35rem]">
<label for="gtw-12-region" class="font-gtw12-display text-[0.82rem] font-medium leading-[1.3]">Region</label>
<select id="gtw-12-region" name="gtw-12-region" class="appearance-none min-h-[46px] pl-[0.85rem] pr-[2.2rem] rounded-[12px] border border-gtw12-line text-gtw12-ink text-[0.95rem] cursor-pointer [background-image:var(--gtw12-caret),var(--gtw12-noise),linear-gradient(var(--color-gtw12-sunk),var(--color-gtw12-sunk))] [background-position:right_0.9rem_center,0_0,0_0] [background-repeat:no-repeat,repeat,no-repeat] [background-blend-mode:normal,var(--gtw12-blend),normal] shadow-[inset_0_2px_5px_oklch(0.2_0.03_275/0.13),inset_0_-1px_0_var(--gtw12-hl)] transition-colors duration-200 hover:border-[color-mix(in_oklab,var(--color-gtw12-acc)_45%,var(--color-gtw12-line))] focus-visible:outline-2 focus-visible:outline-gtw12-acc focus-visible:outline-offset-2">
<option>us-east-1 · N. Virginia</option>
<option>us-west-2 · Oregon</option>
<option>eu-west-2 · London</option>
<option>ca-central-1 · Montréal</option>
<option>ap-southeast-2 · Sydney</option>
</select>
</div>
</section>
<section class="relative isolate overflow-hidden grid gap-4 content-start min-w-0 p-[clamp(1.2rem,3vw,1.7rem)] rounded-[18px] border border-gtw12-line bg-gtw12-surf shadow-[inset_0_1px_0_var(--gtw12-hl),0_12px_30px_-20px_oklch(0.2_0.03_275/0.3)] after:content-[''] after:absolute after:inset-0 after:-z-[1] after:pointer-events-none after:bg-[image:var(--gtw12-noise)] after:opacity-[0.04] after:[mix-blend-mode:var(--gtw12-blend)]" aria-labelledby="gtw-12-h-misc">
<h2 id="gtw-12-h-misc" class="m-0 pb-[0.6rem] border-b border-gtw12-line font-gtw12-display text-[1.05rem] font-semibold leading-tight tracking-[-0.02em]">Toggles and ranges</h2>
<ul class="list-none m-0 p-0 grid gap-[0.1rem]">
<li class="flex items-center gap-[0.6rem] min-h-11"><input type="checkbox" id="gtw-12-c1" checked class="w-5 h-5 m-0 cursor-pointer"><label for="gtw-12-c1" class="text-[0.92rem] cursor-pointer">Textured surfaces</label></li>
<li class="flex items-center gap-[0.6rem] min-h-11"><input type="checkbox" id="gtw-12-c2" class="w-5 h-5 m-0 cursor-pointer"><label for="gtw-12-c2" class="text-[0.92rem] cursor-pointer">Reduce transparency</label></li>
<li class="flex items-center gap-[0.6rem] min-h-11"><input type="radio" name="gtw-12-r" id="gtw-12-r1" checked class="w-5 h-5 m-0 cursor-pointer"><label for="gtw-12-r1" class="text-[0.92rem] cursor-pointer">Match system theme</label></li>
<li class="flex items-center gap-[0.6rem] min-h-11"><input type="radio" name="gtw-12-r" id="gtw-12-r2" class="w-5 h-5 m-0 cursor-pointer"><label for="gtw-12-r2" class="text-[0.92rem] cursor-pointer">Always dark</label></li>
</ul>
<div class="grid gap-[0.35rem]">
<label for="gtw-12-grain" class="font-gtw12-display text-[0.82rem] font-medium leading-[1.3]">Grain weight</label>
<input type="range" id="gtw-12-grain" name="gtw-12-grain" min="0" max="20" value="6" step="1" class="w-full min-h-11 m-0 bg-transparent cursor-pointer">
<p class="m-0 text-gtw12-mut text-[0.8rem] text-pretty">Native thumb, textured track, <code class="font-gtw12-mono text-[0.9em]">accent-color</code> for the fill.</p>
</div>
</section>
</div>
</main>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-gtw12-bg: #eef0f7;
--color-gtw12-bg: oklch(0.95 0.01 275);
--color-gtw12-surf: #ffffff;
--color-gtw12-surf: oklch(1 0 0);
--color-gtw12-sunk: #e7eaf4;
--color-gtw12-sunk: oklch(0.93 0.012 275);
--color-gtw12-line: #d3d8e8;
--color-gtw12-line: oklch(0.87 0.018 275);
--color-gtw12-ink: #14162a;
--color-gtw12-ink: oklch(0.22 0.04 275);
--color-gtw12-mut: #5c6280;
--color-gtw12-mut: oklch(0.47 0.04 275);
--color-gtw12-acc: #4f46e5;
--color-gtw12-acc: oklch(0.52 0.22 275);
--color-gtw12-onacc: #f2f1fe;
--color-gtw12-onacc: oklch(0.97 0.015 280);
--color-gtw12-err: #b42318;
--color-gtw12-err: oklch(0.5 0.18 27);
--font-gtw12-display: 'Space Grotesk', 'Inter Tight', ui-sans-serif, system-ui, sans-serif;
--font-gtw12-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
}
.gtw-12 {
width: 100%;
min-height: 100svh;
--gtw12-idle: 0.05;
--gtw12-hover: 0.06;
--gtw12-active: 0.08;
--gtw12-sunkw: 0.035;
--gtw12-blend: multiply;
--gtw12-hl: rgba(255, 255, 255, 0.85);
--gtw12-noise: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='150' height='150'%3E%3Cfilter id='gtw12n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='150' height='150' filter='url(%23gtw12n)'/%3E%3C/svg%3E");
--gtw12-caret: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='9' viewBox='0 0 14 9'%3E%3Cpath d='M1 1l6 6 6-6' fill='none' stroke='%235c6280' stroke-width='1.8' stroke-linecap='round'/%3E%3C/svg%3E");
font-family: system-ui, -apple-system, 'Segoe UI', sans-serif;
line-height: 1.55;
accent-color: var(--color-gtw12-acc);
}
.gtw-12:has(.gtw-12-theme:checked) {
--color-gtw12-bg: #0c0d16;
--color-gtw12-bg: oklch(0.16 0.02 275);
--color-gtw12-surf: #15172a;
--color-gtw12-surf: oklch(0.22 0.03 275);
--color-gtw12-sunk: #10121f;
--color-gtw12-sunk: oklch(0.18 0.025 275);
--color-gtw12-line: #262a45;
--color-gtw12-line: oklch(0.3 0.04 275);
--color-gtw12-ink: #ecebfb;
--color-gtw12-ink: oklch(0.94 0.015 280);
--color-gtw12-mut: #9a9ac4;
--color-gtw12-mut: oklch(0.7 0.05 280);
--color-gtw12-acc: #a5b4fc;
--color-gtw12-acc: oklch(0.8 0.11 278);
--color-gtw12-onacc: #10122a;
--color-gtw12-onacc: oklch(0.19 0.04 278);
--color-gtw12-err: #fca5a5;
--color-gtw12-err: oklch(0.78 0.13 22);
--gtw12-blend: soft-light;
--gtw12-hl: rgba(255, 255, 255, 0.12);
--gtw12-idle: 0.07;
--gtw12-hover: 0.085;
--gtw12-active: 0.11;
--gtw12-sunkw: 0.05;
--gtw12-caret: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='9' viewBox='0 0 14 9'%3E%3Cpath d='M1 1l6 6 6-6' fill='none' stroke='%239a9ac4' stroke-width='1.8' stroke-linecap='round'/%3E%3C/svg%3E");
}
@media (prefers-color-scheme: dark) {
.gtw-12 {
--color-gtw12-bg: #0c0d16;
--color-gtw12-bg: oklch(0.16 0.02 275);
--color-gtw12-surf: #15172a;
--color-gtw12-surf: oklch(0.22 0.03 275);
--color-gtw12-sunk: #10121f;
--color-gtw12-sunk: oklch(0.18 0.025 275);
--color-gtw12-line: #262a45;
--color-gtw12-line: oklch(0.3 0.04 275);
--color-gtw12-ink: #ecebfb;
--color-gtw12-ink: oklch(0.94 0.015 280);
--color-gtw12-mut: #9a9ac4;
--color-gtw12-mut: oklch(0.7 0.05 280);
--color-gtw12-acc: #a5b4fc;
--color-gtw12-acc: oklch(0.8 0.11 278);
--color-gtw12-onacc: #10122a;
--color-gtw12-onacc: oklch(0.19 0.04 278);
--color-gtw12-err: #fca5a5;
--color-gtw12-err: oklch(0.78 0.13 22);
--gtw12-blend: soft-light;
--gtw12-hl: rgba(255, 255, 255, 0.12);
--gtw12-idle: 0.07;
--gtw12-hover: 0.085;
--gtw12-active: 0.11;
--gtw12-sunkw: 0.05;
--gtw12-caret: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='9' viewBox='0 0 14 9'%3E%3Cpath d='M1 1l6 6 6-6' fill='none' stroke='%239a9ac4' stroke-width='1.8' stroke-linecap='round'/%3E%3C/svg%3E");
}
.gtw-12:has(.gtw-12-theme:checked) {
--color-gtw12-bg: #eef0f7;
--color-gtw12-bg: oklch(0.95 0.01 275);
--color-gtw12-surf: #ffffff;
--color-gtw12-surf: oklch(1 0 0);
--color-gtw12-sunk: #e7eaf4;
--color-gtw12-sunk: oklch(0.93 0.012 275);
--color-gtw12-line: #d3d8e8;
--color-gtw12-line: oklch(0.87 0.018 275);
--color-gtw12-ink: #14162a;
--color-gtw12-ink: oklch(0.22 0.04 275);
--color-gtw12-mut: #5c6280;
--color-gtw12-mut: oklch(0.47 0.04 275);
--color-gtw12-acc: #4f46e5;
--color-gtw12-acc: oklch(0.52 0.22 275);
--color-gtw12-onacc: #f2f1fe;
--color-gtw12-onacc: oklch(0.97 0.015 280);
--color-gtw12-err: #b42318;
--color-gtw12-err: oklch(0.5 0.18 27);
--gtw12-blend: multiply;
--gtw12-hl: rgba(255, 255, 255, 0.85);
--gtw12-idle: 0.05;
--gtw12-hover: 0.06;
--gtw12-active: 0.08;
--gtw12-sunkw: 0.035;
--gtw12-caret: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='9' viewBox='0 0 14 9'%3E%3Cpath d='M1 1l6 6 6-6' fill='none' stroke='%235c6280' stroke-width='1.8' stroke-linecap='round'/%3E%3C/svg%3E");
}
}
@media (prefers-color-scheme: light) {
.gtw-12:has(.gtw-12-theme:checked) {
--color-gtw12-bg: #0c0d16;
--color-gtw12-bg: oklch(0.16 0.02 275);
--color-gtw12-surf: #15172a;
--color-gtw12-surf: oklch(0.22 0.03 275);
--color-gtw12-sunk: #10121f;
--color-gtw12-sunk: oklch(0.18 0.025 275);
--color-gtw12-line: #262a45;
--color-gtw12-line: oklch(0.3 0.04 275);
--color-gtw12-ink: #ecebfb;
--color-gtw12-ink: oklch(0.94 0.015 280);
--color-gtw12-mut: #9a9ac4;
--color-gtw12-mut: oklch(0.7 0.05 280);
--color-gtw12-acc: #a5b4fc;
--color-gtw12-acc: oklch(0.8 0.11 278);
--color-gtw12-onacc: #10122a;
--color-gtw12-onacc: oklch(0.19 0.04 278);
--color-gtw12-err: #fca5a5;
--color-gtw12-err: oklch(0.78 0.13 22);
--gtw12-blend: soft-light;
--gtw12-hl: rgba(255, 255, 255, 0.12);
--gtw12-idle: 0.07;
--gtw12-hover: 0.085;
--gtw12-active: 0.11;
--gtw12-sunkw: 0.05;
--gtw12-caret: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='9' viewBox='0 0 14 9'%3E%3Cpath d='M1 1l6 6 6-6' fill='none' stroke='%239a9ac4' stroke-width='1.8' stroke-linecap='round'/%3E%3C/svg%3E");
}
}
```How this works
Raised and recessed are the same trick, inverted. A button gets a bright inset highlight along its top edge and a small drop shadow below; an input gets an inset shadow along its top edge and a highlight along the bottom. Add the identical grain to both and the eye reads one as a cap and the other as a well. Nothing else changes.
The press is three simultaneous changes. Grain goes from 0.05 to 0.08, the highlight moves from top to bottom, and the shadow inverts from cast to inset. That combination is what makes a flat-design button feel mechanical, and in v4 all three live in active: utilities on the button — no separate :active rule block.
Grain on controls needs a lower number than grain on cards. Small surfaces show texture at roughly twice the apparent strength of large ones, which is why the whole kit runs from four named weights (idle, hover, active, sunk) rather than one global value.
Native controls stay native. The checkboxes, radios and range are real inputs tinted with accent-color — no custom-drawn replacements. You get platform focus, keyboard behaviour, high-contrast mode and forced-colors support for free, and the only thing the design system contributes is a colour.
Make it yours
- Weights:
--gtw12-idle/hover/active/sunk. The dark theme runs about 1.4× higher across the board; that pairing is the shipped default. - Radius:
--gtw12-radius(12px) drives buttons, inputs and the segmented control; the segment wrapper adds 4px so the inner radius nests correctly. - Blend:
--gtw12-blendismultiplyon light andsoft-lighton dark. - Accent:
accent-coloron the root tints every native control at once. One declaration, whole form. - Swap the region options for your own deploy targets — they are ordinary
<option>elements and carry no styling.
Gotchas — read before shipping
- Grain on a small control at the same weight as a large surface looks about twice as strong. Buttons need a lower number than cards.
- Never remove the focus ring to make a textured control look cleaner. The root rule keeps
:focus-visibleat 2px and the segmented control forwards it from the hidden input. appearance-noneon a<select>removes the native arrow — you must supply one, or the control stops looking interactive.user-invalid:(notinvalid:) is the variant you want on a form::invalidmatches an empty required field before the user has typed anything, which flags errors on first paint.accent-colortints the native thumb but cannot texture it. A grained track with a flat thumb is the correct compromise; a fully custom range loses keyboard and platform behaviour.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
accent-color is supported in Chrome 93+, Safari 15.4+ and Firefox 92+. :user-invalid needs Chrome 119, Safari 16.5 and Firefox 88 — below that the field simply never turns red, which is a safe degradation. The quoted floor is Tailwind v4's generated sheet.