02 / 28
All 28 in 28 Tailwind CSS Close Buttons
- 01Tailwind Modal Close Button (Top-Right Corner X)
- 02Accessible Tailwind Close Button with Focus Ring (WCAG)
- 03iOS-Style Tailwind Tab Close Button
- 04Tailwind Floating Chat Widget Close Button
- 05Tailwind Cookie Consent Banner Dismiss Button
- 06Tailwind Mobile Ad Banner Close Button (48px Tap Target)
- 07Neumorphic Tailwind Close Button (Soft UI)
- 08Tailwind Hamburger to X Menu Button Animation
- 09Tailwind Video Player Overlay Close Button
- 10Tailwind Drawer Close Button with Native Dialog
- 11Origami Fold Tailwind Close Button
- 12Lock Tumbler Tailwind Close Button (@property)
- 13Rotate on Hover Tailwind Close Button
- 14Magnetic Cursor Tailwind Close Button
- 15Liquid Goo Tailwind Close Button (SVG Filter)
- 16Glitch Effect Tailwind Close Button
- 17Trash Compactor Tailwind Close Button
- 18Material Ripple Tailwind Close Button
- 19Particle Burst Tailwind Close Button
- 20Spring Snap Tailwind Close Button
- 21Neon Glow Tailwind Close Button
- 22Corner Ribbon Tailwind Close Button
- 233D Fold Away Tailwind Close Button
- 24Hold to Confirm Tailwind Close Button
- 25Tailwind Toast Notification Dismiss Button
- 26Tailwind Lightbox Image Close Button
- 27Brutalist Tailwind Close Button
- 28Galaxy Vortex Tailwind Close Button
Accessible Tailwind Close Button with Focus Ring (WCAG)
A close button that passes WCAG 2.4.7 and 2.4.11 on any surface: focus-visible:outline-3 plus a two-stop focus-visible:shadow-[…] halo paints ink-on-white-on-ink, so the ring reads on both the light and dark panels. The border and × are currentColor, so one class list serves both themes, and forced-colors: hands the glyph to the system palette in Windows High Contrast.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="tcb-02 w-full min-h-svh grid place-items-center bg-tcb02-bg px-6 py-12 font-tcb02-sans" aria-label="Accessible focus ring close button demo">
<div class="grid w-full justify-items-center gap-7">
<div class="@container w-full max-w-[30rem]">
<div class="grid grid-cols-2 overflow-hidden rounded-[1.25rem] shadow-[0_24px_54px_-30px_var(--color-tcb02-shadow)] ring-1 ring-black/5 @max-[22rem]:grid-cols-1">
<div class="flex flex-col items-center gap-4 bg-white px-5 py-10 text-tcb02-lmut">
<span class="text-[11px] font-semibold tracking-[0.12em] uppercase">Light surface</span>
<button type="button" aria-label="Close light panel" class="relative size-11 cursor-pointer rounded-xl border-[1.5px] border-current transition-colors duration-150 before:absolute before:top-1/2 before:left-1/2 before:h-0.5 before:w-4 before:-translate-1/2 before:rotate-45 before:rounded-full before:bg-current after:absolute after:top-1/2 after:left-1/2 after:h-0.5 after:w-4 after:-translate-1/2 after:-rotate-45 after:rounded-full after:bg-current hover:bg-current/10 focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-tcb02-ring focus-visible:shadow-[0_0_0_6px_white,0_0_0_9px_var(--color-tcb02-ring)] active:scale-95 forced-colors:border-[ButtonText] forced-colors:before:bg-[ButtonText] forced-colors:after:bg-[ButtonText] motion-reduce:transition-none"></button>
<span class="flex items-center gap-1.5 text-xs">Press <kbd class="rounded-md border border-current/30 px-1.5 py-0.5 font-tcb02-mono text-[11px]">Tab</kbd></span>
</div>
<div class="flex flex-col items-center gap-4 bg-tcb02-dark px-5 py-10 text-tcb02-dmut">
<span class="text-[11px] font-semibold tracking-[0.12em] uppercase">Dark surface</span>
<button type="button" aria-label="Close dark panel" class="relative size-11 cursor-pointer rounded-xl border-[1.5px] border-current transition-colors duration-150 before:absolute before:top-1/2 before:left-1/2 before:h-0.5 before:w-4 before:-translate-1/2 before:rotate-45 before:rounded-full before:bg-current after:absolute after:top-1/2 after:left-1/2 after:h-0.5 after:w-4 after:-translate-1/2 after:-rotate-45 after:rounded-full after:bg-current hover:bg-current/10 focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-tcb02-ring focus-visible:shadow-[0_0_0_6px_white,0_0_0_9px_var(--color-tcb02-ring)] active:scale-95 forced-colors:border-[ButtonText] forced-colors:before:bg-[ButtonText] forced-colors:after:bg-[ButtonText] motion-reduce:transition-none"></button>
<span class="flex items-center gap-1.5 text-xs">then <kbd class="rounded-md border border-current/30 px-1.5 py-0.5 font-tcb02-mono text-[11px]">Tab</kbd> again</span>
</div>
</div>
</div>
<p aria-hidden="true" class="rounded-full bg-white/70 px-3.5 py-2 font-tcb02-mono text-[11.5px] text-tcb02-lmut ring-1 ring-black/8">focus-visible:outline-3 · shadow halo · forced-colors:</p>
</div>
</section><section class="tcb-02 w-full min-h-svh grid place-items-center bg-tcb02-bg px-6 py-12 font-tcb02-sans" aria-label="Accessible focus ring close button demo">
<div class="grid w-full justify-items-center gap-7">
<div class="@container w-full max-w-[30rem]">
<div class="grid grid-cols-2 overflow-hidden rounded-[1.25rem] shadow-[0_24px_54px_-30px_var(--color-tcb02-shadow)] ring-1 ring-black/5 @max-[22rem]:grid-cols-1">
<div class="flex flex-col items-center gap-4 bg-white px-5 py-10 text-tcb02-lmut">
<span class="text-[11px] font-semibold tracking-[0.12em] uppercase">Light surface</span>
<button type="button" aria-label="Close light panel" class="relative size-11 cursor-pointer rounded-xl border-[1.5px] border-current transition-colors duration-150 before:absolute before:top-1/2 before:left-1/2 before:h-0.5 before:w-4 before:-translate-1/2 before:rotate-45 before:rounded-full before:bg-current after:absolute after:top-1/2 after:left-1/2 after:h-0.5 after:w-4 after:-translate-1/2 after:-rotate-45 after:rounded-full after:bg-current hover:bg-current/10 focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-tcb02-ring focus-visible:shadow-[0_0_0_6px_white,0_0_0_9px_var(--color-tcb02-ring)] active:scale-95 forced-colors:border-[ButtonText] forced-colors:before:bg-[ButtonText] forced-colors:after:bg-[ButtonText] motion-reduce:transition-none"></button>
<span class="flex items-center gap-1.5 text-xs">Press <kbd class="rounded-md border border-current/30 px-1.5 py-0.5 font-tcb02-mono text-[11px]">Tab</kbd></span>
</div>
<div class="flex flex-col items-center gap-4 bg-tcb02-dark px-5 py-10 text-tcb02-dmut">
<span class="text-[11px] font-semibold tracking-[0.12em] uppercase">Dark surface</span>
<button type="button" aria-label="Close dark panel" class="relative size-11 cursor-pointer rounded-xl border-[1.5px] border-current transition-colors duration-150 before:absolute before:top-1/2 before:left-1/2 before:h-0.5 before:w-4 before:-translate-1/2 before:rotate-45 before:rounded-full before:bg-current after:absolute after:top-1/2 after:left-1/2 after:h-0.5 after:w-4 after:-translate-1/2 after:-rotate-45 after:rounded-full after:bg-current hover:bg-current/10 focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-tcb02-ring focus-visible:shadow-[0_0_0_6px_white,0_0_0_9px_var(--color-tcb02-ring)] active:scale-95 forced-colors:border-[ButtonText] forced-colors:before:bg-[ButtonText] forced-colors:after:bg-[ButtonText] motion-reduce:transition-none"></button>
<span class="flex items-center gap-1.5 text-xs">then <kbd class="rounded-md border border-current/30 px-1.5 py-0.5 font-tcb02-mono text-[11px]">Tab</kbd> again</span>
</div>
</div>
</div>
<p aria-hidden="true" class="rounded-full bg-white/70 px-3.5 py-2 font-tcb02-mono text-[11.5px] text-tcb02-lmut ring-1 ring-black/8">focus-visible:outline-3 · shadow halo · forced-colors:</p>
</div>
</section>@import "tailwindcss";
@theme {
--color-tcb02-bg: #e6e9f0;
--color-tcb02-bg: oklch(0.93 0.01 260);
--color-tcb02-dark: #1c2233;
--color-tcb02-dark: oklch(0.22 0.03 265);
--color-tcb02-lmut: #4d5163;
--color-tcb02-lmut: oklch(0.43 0.025 265);
--color-tcb02-dmut: #c5cad8;
--color-tcb02-dmut: oklch(0.84 0.02 265);
--color-tcb02-ring: #3346c9;
--color-tcb02-ring: oklch(0.45 0.19 265);
--color-tcb02-shadow: #33384d80;
--color-tcb02-shadow: oklch(0.35 0.05 265 / 0.5);
--font-tcb02-sans: 'Geist', system-ui, sans-serif;
--font-tcb02-mono: 'Geist Mono', ui-monospace, monospace;
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's utilities, so the
stage is sized before the browser build compiles. */
.tcb-02 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-tcb02-bg: #e6e9f0;
--color-tcb02-bg: oklch(0.93 0.01 260);
--color-tcb02-dark: #1c2233;
--color-tcb02-dark: oklch(0.22 0.03 265);
--color-tcb02-lmut: #4d5163;
--color-tcb02-lmut: oklch(0.43 0.025 265);
--color-tcb02-dmut: #c5cad8;
--color-tcb02-dmut: oklch(0.84 0.02 265);
--color-tcb02-ring: #3346c9;
--color-tcb02-ring: oklch(0.45 0.19 265);
--color-tcb02-shadow: #33384d80;
--color-tcb02-shadow: oklch(0.35 0.05 265 / 0.5);
--font-tcb02-sans: 'Geist', system-ui, sans-serif;
--font-tcb02-mono: 'Geist Mono', ui-monospace, monospace;
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's utilities, so the
stage is sized before the browser build compiles. */
.tcb-02 {
width: 100%;
min-height: 100svh;
}Setup — do this once per project
Skip this if your project already runs Tailwind v4.
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>Compiles in the browser — perfect for a CodePen or a quick test, not for production.
npm install tailwindcss @tailwindcss/vite
npm install tailwindcss @tailwindcss/vite// vite.config.js ("type": "module" in package.json)
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()],
})// vite.config.js ("type": "module" in package.json)
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()],
})Any v4 setup works — Vite, PostCSS or the CLI. Nothing in this demo is bundler-specific.
@import "tailwindcss";
@theme {
--color-tcb02-bg: #e6e9f0;
--color-tcb02-bg: oklch(0.93 0.01 260);
--color-tcb02-dark: #1c2233;
--color-tcb02-dark: oklch(0.22 0.03 265);
--color-tcb02-lmut: #4d5163;
--color-tcb02-lmut: oklch(0.43 0.025 265);
--color-tcb02-dmut: #c5cad8;
--color-tcb02-dmut: oklch(0.84 0.02 265);
--color-tcb02-ring: #3346c9;
--color-tcb02-ring: oklch(0.45 0.19 265);
--color-tcb02-shadow: #33384d80;
--color-tcb02-shadow: oklch(0.35 0.05 265 / 0.5);
--font-tcb02-sans: 'Geist', system-ui, sans-serif;
--font-tcb02-mono: 'Geist Mono', ui-monospace, monospace;
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's utilities, so the
stage is sized before the browser build compiles. */
.tcb-02 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-tcb02-bg: #e6e9f0;
--color-tcb02-bg: oklch(0.93 0.01 260);
--color-tcb02-dark: #1c2233;
--color-tcb02-dark: oklch(0.22 0.03 265);
--color-tcb02-lmut: #4d5163;
--color-tcb02-lmut: oklch(0.43 0.025 265);
--color-tcb02-dmut: #c5cad8;
--color-tcb02-dmut: oklch(0.84 0.02 265);
--color-tcb02-ring: #3346c9;
--color-tcb02-ring: oklch(0.45 0.19 265);
--color-tcb02-shadow: #33384d80;
--color-tcb02-shadow: oklch(0.35 0.05 265 / 0.5);
--font-tcb02-sans: 'Geist', system-ui, sans-serif;
--font-tcb02-mono: 'Geist Mono', ui-monospace, monospace;
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's utilities, so the
stage is sized before the browser build compiles. */
.tcb-02 {
width: 100%;
min-height: 100svh;
}<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Geist:wght@400..800&family=Geist+Mono:wght@400..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=Geist:wght@400..800&family=Geist+Mono:wght@400..600&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
- The
@media (forced-colors:active)block became threeforced-colors:utilities on the element they change. color-mix(in oklab, currentColor 10%, transparent)becamehover:bg-current/10— the opacity modifier works oncurrentColortoo.- UI upgrade from the CSS original: the two panels now carry surface labels and real
<kbd>Tab</kbd>hints, each button has a distinctaria-label(the original announced two identical "Close panel" buttons), and the grid stacks under 22rem with a container query. - UI upgrade: dark-panel copy rose from
oklch(0.8)to0.84and light-panel copy darkened to0.43; both now clear 7:1.
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: Accessible Tailwind Close Button with Focus Ring (WCAG)
Source: https://codefronts.com/components/tailwind-css-close-buttons/accessible-tailwind-close-button-focus-ring/
A close button that passes WCAG 2.4.7 and 2.4.11 on any surface: focus-visible:outline-3 plus a two-stop focus-visible:shadow-[…] halo paints ink-on-white-on-ink, so the ring reads on both the light and dark panels. The border and × are currentColor, so one class list serves both themes, and forced-colors: hands the glyph to the system palette in Windows High Contrast.
## HTML
```html
<section class="tcb-02 w-full min-h-svh grid place-items-center bg-tcb02-bg px-6 py-12 font-tcb02-sans" aria-label="Accessible focus ring close button demo">
<div class="grid w-full justify-items-center gap-7">
<div class="@container w-full max-w-[30rem]">
<div class="grid grid-cols-2 overflow-hidden rounded-[1.25rem] shadow-[0_24px_54px_-30px_var(--color-tcb02-shadow)] ring-1 ring-black/5 @max-[22rem]:grid-cols-1">
<div class="flex flex-col items-center gap-4 bg-white px-5 py-10 text-tcb02-lmut">
<span class="text-[11px] font-semibold tracking-[0.12em] uppercase">Light surface</span>
<button type="button" aria-label="Close light panel" class="relative size-11 cursor-pointer rounded-xl border-[1.5px] border-current transition-colors duration-150 before:absolute before:top-1/2 before:left-1/2 before:h-0.5 before:w-4 before:-translate-1/2 before:rotate-45 before:rounded-full before:bg-current after:absolute after:top-1/2 after:left-1/2 after:h-0.5 after:w-4 after:-translate-1/2 after:-rotate-45 after:rounded-full after:bg-current hover:bg-current/10 focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-tcb02-ring focus-visible:shadow-[0_0_0_6px_white,0_0_0_9px_var(--color-tcb02-ring)] active:scale-95 forced-colors:border-[ButtonText] forced-colors:before:bg-[ButtonText] forced-colors:after:bg-[ButtonText] motion-reduce:transition-none"></button>
<span class="flex items-center gap-1.5 text-xs">Press <kbd class="rounded-md border border-current/30 px-1.5 py-0.5 font-tcb02-mono text-[11px]">Tab</kbd></span>
</div>
<div class="flex flex-col items-center gap-4 bg-tcb02-dark px-5 py-10 text-tcb02-dmut">
<span class="text-[11px] font-semibold tracking-[0.12em] uppercase">Dark surface</span>
<button type="button" aria-label="Close dark panel" class="relative size-11 cursor-pointer rounded-xl border-[1.5px] border-current transition-colors duration-150 before:absolute before:top-1/2 before:left-1/2 before:h-0.5 before:w-4 before:-translate-1/2 before:rotate-45 before:rounded-full before:bg-current after:absolute after:top-1/2 after:left-1/2 after:h-0.5 after:w-4 after:-translate-1/2 after:-rotate-45 after:rounded-full after:bg-current hover:bg-current/10 focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-tcb02-ring focus-visible:shadow-[0_0_0_6px_white,0_0_0_9px_var(--color-tcb02-ring)] active:scale-95 forced-colors:border-[ButtonText] forced-colors:before:bg-[ButtonText] forced-colors:after:bg-[ButtonText] motion-reduce:transition-none"></button>
<span class="flex items-center gap-1.5 text-xs">then <kbd class="rounded-md border border-current/30 px-1.5 py-0.5 font-tcb02-mono text-[11px]">Tab</kbd> again</span>
</div>
</div>
</div>
<p aria-hidden="true" class="rounded-full bg-white/70 px-3.5 py-2 font-tcb02-mono text-[11.5px] text-tcb02-lmut ring-1 ring-black/8">focus-visible:outline-3 · shadow halo · forced-colors:</p>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tcb02-bg: #e6e9f0;
--color-tcb02-bg: oklch(0.93 0.01 260);
--color-tcb02-dark: #1c2233;
--color-tcb02-dark: oklch(0.22 0.03 265);
--color-tcb02-lmut: #4d5163;
--color-tcb02-lmut: oklch(0.43 0.025 265);
--color-tcb02-dmut: #c5cad8;
--color-tcb02-dmut: oklch(0.84 0.02 265);
--color-tcb02-ring: #3346c9;
--color-tcb02-ring: oklch(0.45 0.19 265);
--color-tcb02-shadow: #33384d80;
--color-tcb02-shadow: oklch(0.35 0.05 265 / 0.5);
--font-tcb02-sans: 'Geist', system-ui, sans-serif;
--font-tcb02-mono: 'Geist Mono', ui-monospace, monospace;
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's utilities, so the
stage is sized before the browser build compiles. */
.tcb-02 {
width: 100%;
min-height: 100svh;
}
```Here's a working CSS demo from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Accessible Tailwind Close Button with Focus Ring (WCAG)
Source: https://codefronts.com/components/tailwind-css-close-buttons/accessible-tailwind-close-button-focus-ring/
A close button that passes WCAG 2.4.7 and 2.4.11 on any surface: focus-visible:outline-3 plus a two-stop focus-visible:shadow-[…] halo paints ink-on-white-on-ink, so the ring reads on both the light and dark panels. The border and × are currentColor, so one class list serves both themes, and forced-colors: hands the glyph to the system palette in Windows High Contrast.
## HTML
```html
<section class="tcb-02 w-full min-h-svh grid place-items-center bg-tcb02-bg px-6 py-12 font-tcb02-sans" aria-label="Accessible focus ring close button demo">
<div class="grid w-full justify-items-center gap-7">
<div class="@container w-full max-w-[30rem]">
<div class="grid grid-cols-2 overflow-hidden rounded-[1.25rem] shadow-[0_24px_54px_-30px_var(--color-tcb02-shadow)] ring-1 ring-black/5 @max-[22rem]:grid-cols-1">
<div class="flex flex-col items-center gap-4 bg-white px-5 py-10 text-tcb02-lmut">
<span class="text-[11px] font-semibold tracking-[0.12em] uppercase">Light surface</span>
<button type="button" aria-label="Close light panel" class="relative size-11 cursor-pointer rounded-xl border-[1.5px] border-current transition-colors duration-150 before:absolute before:top-1/2 before:left-1/2 before:h-0.5 before:w-4 before:-translate-1/2 before:rotate-45 before:rounded-full before:bg-current after:absolute after:top-1/2 after:left-1/2 after:h-0.5 after:w-4 after:-translate-1/2 after:-rotate-45 after:rounded-full after:bg-current hover:bg-current/10 focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-tcb02-ring focus-visible:shadow-[0_0_0_6px_white,0_0_0_9px_var(--color-tcb02-ring)] active:scale-95 forced-colors:border-[ButtonText] forced-colors:before:bg-[ButtonText] forced-colors:after:bg-[ButtonText] motion-reduce:transition-none"></button>
<span class="flex items-center gap-1.5 text-xs">Press <kbd class="rounded-md border border-current/30 px-1.5 py-0.5 font-tcb02-mono text-[11px]">Tab</kbd></span>
</div>
<div class="flex flex-col items-center gap-4 bg-tcb02-dark px-5 py-10 text-tcb02-dmut">
<span class="text-[11px] font-semibold tracking-[0.12em] uppercase">Dark surface</span>
<button type="button" aria-label="Close dark panel" class="relative size-11 cursor-pointer rounded-xl border-[1.5px] border-current transition-colors duration-150 before:absolute before:top-1/2 before:left-1/2 before:h-0.5 before:w-4 before:-translate-1/2 before:rotate-45 before:rounded-full before:bg-current after:absolute after:top-1/2 after:left-1/2 after:h-0.5 after:w-4 after:-translate-1/2 after:-rotate-45 after:rounded-full after:bg-current hover:bg-current/10 focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-tcb02-ring focus-visible:shadow-[0_0_0_6px_white,0_0_0_9px_var(--color-tcb02-ring)] active:scale-95 forced-colors:border-[ButtonText] forced-colors:before:bg-[ButtonText] forced-colors:after:bg-[ButtonText] motion-reduce:transition-none"></button>
<span class="flex items-center gap-1.5 text-xs">then <kbd class="rounded-md border border-current/30 px-1.5 py-0.5 font-tcb02-mono text-[11px]">Tab</kbd> again</span>
</div>
</div>
</div>
<p aria-hidden="true" class="rounded-full bg-white/70 px-3.5 py-2 font-tcb02-mono text-[11.5px] text-tcb02-lmut ring-1 ring-black/8">focus-visible:outline-3 · shadow halo · forced-colors:</p>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tcb02-bg: #e6e9f0;
--color-tcb02-bg: oklch(0.93 0.01 260);
--color-tcb02-dark: #1c2233;
--color-tcb02-dark: oklch(0.22 0.03 265);
--color-tcb02-lmut: #4d5163;
--color-tcb02-lmut: oklch(0.43 0.025 265);
--color-tcb02-dmut: #c5cad8;
--color-tcb02-dmut: oklch(0.84 0.02 265);
--color-tcb02-ring: #3346c9;
--color-tcb02-ring: oklch(0.45 0.19 265);
--color-tcb02-shadow: #33384d80;
--color-tcb02-shadow: oklch(0.35 0.05 265 / 0.5);
--font-tcb02-sans: 'Geist', system-ui, sans-serif;
--font-tcb02-mono: 'Geist Mono', ui-monospace, monospace;
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's utilities, so the
stage is sized before the browser build compiles. */
.tcb-02 {
width: 100%;
min-height: 100svh;
}
```How this works
One ring, three layers. The outline is the primary indicator; the shadow adds a white gap and a second ink ring outside it:
focus-visible:outline-3 focus-visible:outline-offset-2
focus-visible:outline-tcb02-ring
focus-visible:shadow-[0_0_0_6px_white,0_0_0_9px_var(--color-tcb02-ring)]On white the inner outline carries the contrast; on navy the white gap does. Either way the indicator clears 3:1 against its neighbours, which is what WCAG 2.4.11 measures.
Themes via currentColor. Both panels use the same button markup — the only difference is text-tcb02-lmut vs text-tcb02-dmut on the panel.
Make it yours
- Tighter ring: drop the shadow and keep
outline-3 outline-offset-2if every surface is light. - Square it up:
rounded-xl→rounded-fullfor a circular variant — the ring follows the radius. - Brand ring: change
--color-tcb02-ring; keep it ≥3:1 against white.
Gotchas — read before shipping
focus:would show the ring on mouse click too;focus-visible:only shows it for keyboard and assistive-tech focus.outline-hiddenin v4 keeps an invisible outline for forced-colors mode;outline-noneremoves it — never use either without a replacement.- Box-shadow rings are dropped in forced-colors mode, which is why the outline stays the primary indicator.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
:focus-visible and forced-colors are universal in evergreen engines; the floor is Tailwind v4's generated sheet.