06 / 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
Tailwind Mobile Ad Banner Close Button (48px Tap Target)
The IAB rule in one component: a 20px visual dot inflated to a 48×48 tap target with p-3.5 and pulled back into the corner with -mt-3.5 -me-3.5. The original's "set --debug:0" instruction became a real toggle: a checkbox the stage reads with group-has-[:checked]/stage:, so the dashed hit-area overlay switches on and off with zero JavaScript.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="tcb-06 group/stage w-full min-h-svh grid place-items-center bg-tcb06-bg px-6 py-12 font-tcb06-sans text-tcb06-ink" aria-label="Mobile ad banner close button demo">
<div class="grid w-full justify-items-center gap-7">
<div class="flex w-full max-w-[22.5rem] flex-col items-center gap-5">
<div class="relative h-16 w-full rounded-xl bg-linear-100 from-tcb06-a to-tcb06-b shadow-[0_14px_30px_-16px_var(--color-tcb06-shadow)]">
<div class="flex h-full flex-col justify-center gap-1 px-4 text-white">
<span class="text-[15px] leading-none font-extrabold tracking-[0.06em]">SUMMER SALE</span>
<span class="text-xs">Up to 60% off — today only</span>
</div>
<span class="absolute end-2 bottom-1.5 rounded-sm bg-black/35 px-1 text-[9px] font-semibold tracking-wide text-white">AD</span>
<button type="button" aria-label="Close ad" class="group absolute top-0 end-0 -mt-3.5 -me-3.5 cursor-pointer rounded-[14px] p-3.5 outline-offset-[-2px] group-has-[#tcb-06-debug:checked]/stage:outline-[1.5px] group-has-[#tcb-06-debug:checked]/stage:outline-dashed group-has-[#tcb-06-debug:checked]/stage:outline-tcb06-acc focus-visible:outline-3 focus-visible:outline-solid focus-visible:outline-offset-0 focus-visible:outline-tcb06-acc">
<span aria-hidden="true" class="relative block size-5 rounded-full bg-[rgb(20_16_14/0.72)] ring-1 ring-white/50 transition duration-150 before:absolute before:top-1/2 before:left-1/2 before:h-[1.5px] before:w-2.5 before:-translate-1/2 before:rotate-45 before:rounded-full before:bg-white after:absolute after:top-1/2 after:left-1/2 after:h-[1.5px] after:w-2.5 after:-translate-1/2 after:-rotate-45 after:rounded-full after:bg-white group-hover:scale-110 group-active:scale-90 motion-reduce:transition-none"></span>
</button>
</div>
<label class="flex min-h-11 cursor-pointer items-center gap-3 rounded-full bg-white px-4 text-[13px] font-medium shadow-sm ring-1 ring-black/8 has-focus-visible:outline-2 has-focus-visible:outline-offset-2 has-focus-visible:outline-tcb06-acc">
<input id="tcb-06-debug" type="checkbox" checked class="peer sr-only">
<span aria-hidden="true" class="relative h-5 w-9 rounded-full bg-tcb06-ink/25 transition-colors after:absolute after:top-0.5 after:left-0.5 after:size-4 after:rounded-full after:bg-white after:shadow after:transition-transform peer-checked:bg-tcb06-acc peer-checked:after:translate-x-4 motion-reduce:transition-none motion-reduce:after:transition-none"></span>
Show the 48×48 tap target
</label>
</div>
<p aria-hidden="true" class="rounded-full bg-white/70 px-3.5 py-2 font-tcb06-mono text-[11.5px] text-tcb06-mut ring-1 ring-black/8">p-3.5 -mt-3.5 -me-3.5 · group-has-[:checked]/stage:</p>
</div>
</section><section class="tcb-06 group/stage w-full min-h-svh grid place-items-center bg-tcb06-bg px-6 py-12 font-tcb06-sans text-tcb06-ink" aria-label="Mobile ad banner close button demo">
<div class="grid w-full justify-items-center gap-7">
<div class="flex w-full max-w-[22.5rem] flex-col items-center gap-5">
<div class="relative h-16 w-full rounded-xl bg-linear-100 from-tcb06-a to-tcb06-b shadow-[0_14px_30px_-16px_var(--color-tcb06-shadow)]">
<div class="flex h-full flex-col justify-center gap-1 px-4 text-white">
<span class="text-[15px] leading-none font-extrabold tracking-[0.06em]">SUMMER SALE</span>
<span class="text-xs">Up to 60% off — today only</span>
</div>
<span class="absolute end-2 bottom-1.5 rounded-sm bg-black/35 px-1 text-[9px] font-semibold tracking-wide text-white">AD</span>
<button type="button" aria-label="Close ad" class="group absolute top-0 end-0 -mt-3.5 -me-3.5 cursor-pointer rounded-[14px] p-3.5 outline-offset-[-2px] group-has-[#tcb-06-debug:checked]/stage:outline-[1.5px] group-has-[#tcb-06-debug:checked]/stage:outline-dashed group-has-[#tcb-06-debug:checked]/stage:outline-tcb06-acc focus-visible:outline-3 focus-visible:outline-solid focus-visible:outline-offset-0 focus-visible:outline-tcb06-acc">
<span aria-hidden="true" class="relative block size-5 rounded-full bg-[rgb(20_16_14/0.72)] ring-1 ring-white/50 transition duration-150 before:absolute before:top-1/2 before:left-1/2 before:h-[1.5px] before:w-2.5 before:-translate-1/2 before:rotate-45 before:rounded-full before:bg-white after:absolute after:top-1/2 after:left-1/2 after:h-[1.5px] after:w-2.5 after:-translate-1/2 after:-rotate-45 after:rounded-full after:bg-white group-hover:scale-110 group-active:scale-90 motion-reduce:transition-none"></span>
</button>
</div>
<label class="flex min-h-11 cursor-pointer items-center gap-3 rounded-full bg-white px-4 text-[13px] font-medium shadow-sm ring-1 ring-black/8 has-focus-visible:outline-2 has-focus-visible:outline-offset-2 has-focus-visible:outline-tcb06-acc">
<input id="tcb-06-debug" type="checkbox" checked class="peer sr-only">
<span aria-hidden="true" class="relative h-5 w-9 rounded-full bg-tcb06-ink/25 transition-colors after:absolute after:top-0.5 after:left-0.5 after:size-4 after:rounded-full after:bg-white after:shadow after:transition-transform peer-checked:bg-tcb06-acc peer-checked:after:translate-x-4 motion-reduce:transition-none motion-reduce:after:transition-none"></span>
Show the 48×48 tap target
</label>
</div>
<p aria-hidden="true" class="rounded-full bg-white/70 px-3.5 py-2 font-tcb06-mono text-[11.5px] text-tcb06-mut ring-1 ring-black/8">p-3.5 -mt-3.5 -me-3.5 · group-has-[:checked]/stage:</p>
</div>
</section>@import "tailwindcss";
@theme {
--color-tcb06-bg: #f1ede6;
--color-tcb06-bg: oklch(0.94 0.01 80);
--color-tcb06-ink: #34291f;
--color-tcb06-ink: oklch(0.3 0.03 60);
--color-tcb06-mut: #6b5d4f;
--color-tcb06-mut: oklch(0.49 0.03 60);
--color-tcb06-a: #e2622c;
--color-tcb06-a: oklch(0.64 0.19 40);
--color-tcb06-b: #c9303a;
--color-tcb06-b: oklch(0.55 0.2 20);
--color-tcb06-acc: #b33a1c;
--color-tcb06-acc: oklch(0.52 0.17 35);
--color-tcb06-shadow: #8e3a1f99;
--color-tcb06-shadow: oklch(0.45 0.15 30 / 0.6);
--font-tcb06-sans: 'Geist', system-ui, sans-serif;
--font-tcb06-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-06 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-tcb06-bg: #f1ede6;
--color-tcb06-bg: oklch(0.94 0.01 80);
--color-tcb06-ink: #34291f;
--color-tcb06-ink: oklch(0.3 0.03 60);
--color-tcb06-mut: #6b5d4f;
--color-tcb06-mut: oklch(0.49 0.03 60);
--color-tcb06-a: #e2622c;
--color-tcb06-a: oklch(0.64 0.19 40);
--color-tcb06-b: #c9303a;
--color-tcb06-b: oklch(0.55 0.2 20);
--color-tcb06-acc: #b33a1c;
--color-tcb06-acc: oklch(0.52 0.17 35);
--color-tcb06-shadow: #8e3a1f99;
--color-tcb06-shadow: oklch(0.45 0.15 30 / 0.6);
--font-tcb06-sans: 'Geist', system-ui, sans-serif;
--font-tcb06-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-06 {
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-tcb06-bg: #f1ede6;
--color-tcb06-bg: oklch(0.94 0.01 80);
--color-tcb06-ink: #34291f;
--color-tcb06-ink: oklch(0.3 0.03 60);
--color-tcb06-mut: #6b5d4f;
--color-tcb06-mut: oklch(0.49 0.03 60);
--color-tcb06-a: #e2622c;
--color-tcb06-a: oklch(0.64 0.19 40);
--color-tcb06-b: #c9303a;
--color-tcb06-b: oklch(0.55 0.2 20);
--color-tcb06-acc: #b33a1c;
--color-tcb06-acc: oklch(0.52 0.17 35);
--color-tcb06-shadow: #8e3a1f99;
--color-tcb06-shadow: oklch(0.45 0.15 30 / 0.6);
--font-tcb06-sans: 'Geist', system-ui, sans-serif;
--font-tcb06-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-06 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-tcb06-bg: #f1ede6;
--color-tcb06-bg: oklch(0.94 0.01 80);
--color-tcb06-ink: #34291f;
--color-tcb06-ink: oklch(0.3 0.03 60);
--color-tcb06-mut: #6b5d4f;
--color-tcb06-mut: oklch(0.49 0.03 60);
--color-tcb06-a: #e2622c;
--color-tcb06-a: oklch(0.64 0.19 40);
--color-tcb06-b: #c9303a;
--color-tcb06-b: oklch(0.55 0.2 20);
--color-tcb06-acc: #b33a1c;
--color-tcb06-acc: oklch(0.52 0.17 35);
--color-tcb06-shadow: #8e3a1f99;
--color-tcb06-shadow: oklch(0.45 0.15 30 / 0.6);
--font-tcb06-sans: 'Geist', system-ui, sans-serif;
--font-tcb06-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-06 {
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.
outline: calc(var(--debug) * 1.5px) dashedbecame a real control: a labelled checkbox read withgroup-has-[#tcb-06-debug:checked]/stage:.- UI upgrade from the CSS original: the debug overlay is now a keyboard-operable switch instead of a code edit, and the creative gained the "AD" disclosure label that ad policies require.
- UI upgrade: the dot gained a
ring-1 ring-white/50edge so it stays visible on dark creatives, and the note copy rose fromoklch(0.5)to0.49on a whiter pill for 6: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: Tailwind Mobile Ad Banner Close Button (48px Tap Target)
Source: https://codefronts.com/components/tailwind-css-close-buttons/tailwind-mobile-ad-banner-close-button-tap-target/
The IAB rule in one component: a 20px visual dot inflated to a 48×48 tap target with p-3.5 and pulled back into the corner with -mt-3.5 -me-3.5. The original's "set --debug:0" instruction became a real toggle: a checkbox the stage reads with group-has-[:checked]/stage:, so the dashed hit-area overlay switches on and off with zero JavaScript.
## HTML
```html
<section class="tcb-06 group/stage w-full min-h-svh grid place-items-center bg-tcb06-bg px-6 py-12 font-tcb06-sans text-tcb06-ink" aria-label="Mobile ad banner close button demo">
<div class="grid w-full justify-items-center gap-7">
<div class="flex w-full max-w-[22.5rem] flex-col items-center gap-5">
<div class="relative h-16 w-full rounded-xl bg-linear-100 from-tcb06-a to-tcb06-b shadow-[0_14px_30px_-16px_var(--color-tcb06-shadow)]">
<div class="flex h-full flex-col justify-center gap-1 px-4 text-white">
<span class="text-[15px] leading-none font-extrabold tracking-[0.06em]">SUMMER SALE</span>
<span class="text-xs">Up to 60% off — today only</span>
</div>
<span class="absolute end-2 bottom-1.5 rounded-sm bg-black/35 px-1 text-[9px] font-semibold tracking-wide text-white">AD</span>
<button type="button" aria-label="Close ad" class="group absolute top-0 end-0 -mt-3.5 -me-3.5 cursor-pointer rounded-[14px] p-3.5 outline-offset-[-2px] group-has-[#tcb-06-debug:checked]/stage:outline-[1.5px] group-has-[#tcb-06-debug:checked]/stage:outline-dashed group-has-[#tcb-06-debug:checked]/stage:outline-tcb06-acc focus-visible:outline-3 focus-visible:outline-solid focus-visible:outline-offset-0 focus-visible:outline-tcb06-acc">
<span aria-hidden="true" class="relative block size-5 rounded-full bg-[rgb(20_16_14/0.72)] ring-1 ring-white/50 transition duration-150 before:absolute before:top-1/2 before:left-1/2 before:h-[1.5px] before:w-2.5 before:-translate-1/2 before:rotate-45 before:rounded-full before:bg-white after:absolute after:top-1/2 after:left-1/2 after:h-[1.5px] after:w-2.5 after:-translate-1/2 after:-rotate-45 after:rounded-full after:bg-white group-hover:scale-110 group-active:scale-90 motion-reduce:transition-none"></span>
</button>
</div>
<label class="flex min-h-11 cursor-pointer items-center gap-3 rounded-full bg-white px-4 text-[13px] font-medium shadow-sm ring-1 ring-black/8 has-focus-visible:outline-2 has-focus-visible:outline-offset-2 has-focus-visible:outline-tcb06-acc">
<input id="tcb-06-debug" type="checkbox" checked class="peer sr-only">
<span aria-hidden="true" class="relative h-5 w-9 rounded-full bg-tcb06-ink/25 transition-colors after:absolute after:top-0.5 after:left-0.5 after:size-4 after:rounded-full after:bg-white after:shadow after:transition-transform peer-checked:bg-tcb06-acc peer-checked:after:translate-x-4 motion-reduce:transition-none motion-reduce:after:transition-none"></span>
Show the 48×48 tap target
</label>
</div>
<p aria-hidden="true" class="rounded-full bg-white/70 px-3.5 py-2 font-tcb06-mono text-[11.5px] text-tcb06-mut ring-1 ring-black/8">p-3.5 -mt-3.5 -me-3.5 · group-has-[:checked]/stage:</p>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tcb06-bg: #f1ede6;
--color-tcb06-bg: oklch(0.94 0.01 80);
--color-tcb06-ink: #34291f;
--color-tcb06-ink: oklch(0.3 0.03 60);
--color-tcb06-mut: #6b5d4f;
--color-tcb06-mut: oklch(0.49 0.03 60);
--color-tcb06-a: #e2622c;
--color-tcb06-a: oklch(0.64 0.19 40);
--color-tcb06-b: #c9303a;
--color-tcb06-b: oklch(0.55 0.2 20);
--color-tcb06-acc: #b33a1c;
--color-tcb06-acc: oklch(0.52 0.17 35);
--color-tcb06-shadow: #8e3a1f99;
--color-tcb06-shadow: oklch(0.45 0.15 30 / 0.6);
--font-tcb06-sans: 'Geist', system-ui, sans-serif;
--font-tcb06-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-06 {
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: Tailwind Mobile Ad Banner Close Button (48px Tap Target)
Source: https://codefronts.com/components/tailwind-css-close-buttons/tailwind-mobile-ad-banner-close-button-tap-target/
The IAB rule in one component: a 20px visual dot inflated to a 48×48 tap target with p-3.5 and pulled back into the corner with -mt-3.5 -me-3.5. The original's "set --debug:0" instruction became a real toggle: a checkbox the stage reads with group-has-[:checked]/stage:, so the dashed hit-area overlay switches on and off with zero JavaScript.
## HTML
```html
<section class="tcb-06 group/stage w-full min-h-svh grid place-items-center bg-tcb06-bg px-6 py-12 font-tcb06-sans text-tcb06-ink" aria-label="Mobile ad banner close button demo">
<div class="grid w-full justify-items-center gap-7">
<div class="flex w-full max-w-[22.5rem] flex-col items-center gap-5">
<div class="relative h-16 w-full rounded-xl bg-linear-100 from-tcb06-a to-tcb06-b shadow-[0_14px_30px_-16px_var(--color-tcb06-shadow)]">
<div class="flex h-full flex-col justify-center gap-1 px-4 text-white">
<span class="text-[15px] leading-none font-extrabold tracking-[0.06em]">SUMMER SALE</span>
<span class="text-xs">Up to 60% off — today only</span>
</div>
<span class="absolute end-2 bottom-1.5 rounded-sm bg-black/35 px-1 text-[9px] font-semibold tracking-wide text-white">AD</span>
<button type="button" aria-label="Close ad" class="group absolute top-0 end-0 -mt-3.5 -me-3.5 cursor-pointer rounded-[14px] p-3.5 outline-offset-[-2px] group-has-[#tcb-06-debug:checked]/stage:outline-[1.5px] group-has-[#tcb-06-debug:checked]/stage:outline-dashed group-has-[#tcb-06-debug:checked]/stage:outline-tcb06-acc focus-visible:outline-3 focus-visible:outline-solid focus-visible:outline-offset-0 focus-visible:outline-tcb06-acc">
<span aria-hidden="true" class="relative block size-5 rounded-full bg-[rgb(20_16_14/0.72)] ring-1 ring-white/50 transition duration-150 before:absolute before:top-1/2 before:left-1/2 before:h-[1.5px] before:w-2.5 before:-translate-1/2 before:rotate-45 before:rounded-full before:bg-white after:absolute after:top-1/2 after:left-1/2 after:h-[1.5px] after:w-2.5 after:-translate-1/2 after:-rotate-45 after:rounded-full after:bg-white group-hover:scale-110 group-active:scale-90 motion-reduce:transition-none"></span>
</button>
</div>
<label class="flex min-h-11 cursor-pointer items-center gap-3 rounded-full bg-white px-4 text-[13px] font-medium shadow-sm ring-1 ring-black/8 has-focus-visible:outline-2 has-focus-visible:outline-offset-2 has-focus-visible:outline-tcb06-acc">
<input id="tcb-06-debug" type="checkbox" checked class="peer sr-only">
<span aria-hidden="true" class="relative h-5 w-9 rounded-full bg-tcb06-ink/25 transition-colors after:absolute after:top-0.5 after:left-0.5 after:size-4 after:rounded-full after:bg-white after:shadow after:transition-transform peer-checked:bg-tcb06-acc peer-checked:after:translate-x-4 motion-reduce:transition-none motion-reduce:after:transition-none"></span>
Show the 48×48 tap target
</label>
</div>
<p aria-hidden="true" class="rounded-full bg-white/70 px-3.5 py-2 font-tcb06-mono text-[11.5px] text-tcb06-mut ring-1 ring-black/8">p-3.5 -mt-3.5 -me-3.5 · group-has-[:checked]/stage:</p>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tcb06-bg: #f1ede6;
--color-tcb06-bg: oklch(0.94 0.01 80);
--color-tcb06-ink: #34291f;
--color-tcb06-ink: oklch(0.3 0.03 60);
--color-tcb06-mut: #6b5d4f;
--color-tcb06-mut: oklch(0.49 0.03 60);
--color-tcb06-a: #e2622c;
--color-tcb06-a: oklch(0.64 0.19 40);
--color-tcb06-b: #c9303a;
--color-tcb06-b: oklch(0.55 0.2 20);
--color-tcb06-acc: #b33a1c;
--color-tcb06-acc: oklch(0.52 0.17 35);
--color-tcb06-shadow: #8e3a1f99;
--color-tcb06-shadow: oklch(0.45 0.15 30 / 0.6);
--font-tcb06-sans: 'Geist', system-ui, sans-serif;
--font-tcb06-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-06 {
width: 100%;
min-height: 100svh;
}
```How this works
Target size is padding, not drawing. The dot is 20px; the button around it is 20 + 2×14 = 48px:
<button class="absolute top-0 end-0 -mt-3.5 -me-3.5 p-3.5 …">
<span class="block size-5 rounded-full bg-black/72 …">The toggle reads from above. The stage is a named group; the button styles itself when that group has a checked debug box. No script, no class, and the checkbox is a real, labelled control.
Make it yours
- IAB exact: keep the visual at
size-5and the target at ≥48px; many networks also require the button sit outside the creative — use-mt-6 -me-6. - Ship it: delete the checkbox row; the
group-has-[…]classes become inert. - Light creatives: swap the dot to
bg-white/85and the bars tobg-black.
Gotchas — read before shipping
- An absolutely positioned hit area can overlap neighbouring controls — leave ≥24px of clear space around it (WCAG 2.5.8's spacing exception).
group-has-[…]needs a named group when groups nest; the/stagesuffix keeps this one unambiguous.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
:has() is Chrome 105 / Safari 15.4 / Firefox 121; the floor is Tailwind v4.