32 Tailwind CSS Tabs
03 / 32
Pure TailwindMIT licensed

Neo-Brutalist Tailwind Tabs with Press Effect

Heavy uppercase keys with hard 4px offset shadows. The checked tab presses flush: it translates exactly as far as its shadow shrinks, so the shadow's far edge never moves and the key appears to sink into the page. The panel is a brutalist card of its own.

Live Demo
Plain CSSTailwind v4
Try it

Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time

Untitled UI React · Affiliate link

Get Untitled UI Pro

The code

<section class="ttb-03 group/tabs grid min-h-svh w-full place-items-center bg-ttb03-bg bg-[radial-gradient(var(--color-ttb03-dot)_1px,transparent_1px)] bg-size-[18px_18px] px-6 py-14 font-ttb03-sans text-ttb03-ink" aria-label="Neo-brutalist press tabs demo">
  <div class="grid w-full max-w-lg justify-items-center gap-8">
    <div role="radiogroup" aria-label="Workflow stage" class="flex w-full gap-4">
      <label class="flex min-h-12 flex-1 cursor-pointer items-center justify-center border-3 border-ttb03-ink bg-white text-[13px] font-bold tracking-[0.12em] uppercase shadow-[4px_4px_0_var(--color-ttb03-ink)] transition-[translate,box-shadow,background-color,color] duration-150 ease-out hover:bg-ttb03-soft active:translate-x-0.5 active:translate-y-0.5 active:shadow-[2px_2px_0_var(--color-ttb03-ink)] has-checked:translate-x-1 has-checked:translate-y-1 has-checked:bg-ttb03-ink has-checked:text-ttb03-bg has-checked:shadow-[0_0_0_var(--color-ttb03-ink)] has-focus-visible:outline-3 has-focus-visible:outline-offset-4 has-focus-visible:outline-dashed has-focus-visible:outline-ttb03-ink motion-reduce:transition-none"><input class="sr-only" type="radio" name="ttb03" id="ttb03-r1" checked>Build</label>
      <label class="flex min-h-12 flex-1 cursor-pointer items-center justify-center border-3 border-ttb03-ink bg-white text-[13px] font-bold tracking-[0.12em] uppercase shadow-[4px_4px_0_var(--color-ttb03-ink)] transition-[translate,box-shadow,background-color,color] duration-150 ease-out hover:bg-ttb03-soft active:translate-x-0.5 active:translate-y-0.5 active:shadow-[2px_2px_0_var(--color-ttb03-ink)] has-checked:translate-x-1 has-checked:translate-y-1 has-checked:bg-ttb03-ink has-checked:text-ttb03-bg has-checked:shadow-[0_0_0_var(--color-ttb03-ink)] has-focus-visible:outline-3 has-focus-visible:outline-offset-4 has-focus-visible:outline-dashed has-focus-visible:outline-ttb03-ink motion-reduce:transition-none"><input class="sr-only" type="radio" name="ttb03" id="ttb03-r2">Ship</label>
      <label class="flex min-h-12 flex-1 cursor-pointer items-center justify-center border-3 border-ttb03-ink bg-white text-[13px] font-bold tracking-[0.12em] uppercase shadow-[4px_4px_0_var(--color-ttb03-ink)] transition-[translate,box-shadow,background-color,color] duration-150 ease-out hover:bg-ttb03-soft active:translate-x-0.5 active:translate-y-0.5 active:shadow-[2px_2px_0_var(--color-ttb03-ink)] has-checked:translate-x-1 has-checked:translate-y-1 has-checked:bg-ttb03-ink has-checked:text-ttb03-bg has-checked:shadow-[0_0_0_var(--color-ttb03-ink)] has-focus-visible:outline-3 has-focus-visible:outline-offset-4 has-focus-visible:outline-dashed has-focus-visible:outline-ttb03-ink motion-reduce:transition-none"><input class="sr-only" type="radio" name="ttb03" id="ttb03-r3">Iterate</label>
    </div>
    <div class="w-full border-3 border-ttb03-ink bg-white p-5 shadow-[6px_6px_0_var(--color-ttb03-ink)]">
      <div class="hidden group-has-[#ttb03-r1:checked]/tabs:block"><p class="font-ttb03-mono text-[11px] font-bold tracking-[0.1em] uppercase">Step 01 / 03</p><p class="mt-2 text-lg leading-snug font-bold">Build — assemble the parts, wire the data, make it run.</p></div>
      <div class="hidden group-has-[#ttb03-r2:checked]/tabs:block"><p class="font-ttb03-mono text-[11px] font-bold tracking-[0.1em] uppercase">Step 02 / 03</p><p class="mt-2 text-lg leading-snug font-bold">Ship — push it to production while it's still a little rough.</p></div>
      <div class="hidden group-has-[#ttb03-r3:checked]/tabs:block"><p class="font-ttb03-mono text-[11px] font-bold tracking-[0.1em] uppercase">Step 03 / 03</p><p class="mt-2 text-lg leading-snug font-bold">Iterate — measure what people actually do, then refine.</p></div>
    </div>
    <p aria-hidden="true" class="border-2 border-ttb03-ink bg-white px-3.5 py-2 font-ttb03-mono text-[11.5px] font-bold">shadow-[4px_4px_0] · has-checked:translate-1 · active: half-press</p>
  </div>
</section>
@import "tailwindcss";

@theme {
  --color-ttb03-bg: #fdeeb8;
  --color-ttb03-bg: oklch(0.948 0.071 94.4);
  --color-ttb03-dot: #e9d48a;
  --color-ttb03-dot: oklch(0.871 0.096 94.1);
  --color-ttb03-soft: #fff6d6;
  --color-ttb03-soft: oklch(0.972 0.043 94.1);
  --color-ttb03-ink: #0b0b0b;
  --color-ttb03-ink: oklch(0.150 0 0);
  --font-ttb03-sans: 'Space Grotesk', system-ui, sans-serif;
  --font-ttb03-mono: 'JetBrains 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. */

.ttb-03 {
  width: 100%;
  min-height: 100svh;
}

Setup — do this once per project

Skip this if your project already runs Tailwind v4.

1Load Tailwind
<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.

2Add the stylesheetRequiredthe @theme block is what makes the custom utilities exist
@import "tailwindcss";

@theme {
  --color-ttb03-bg: #fdeeb8;
  --color-ttb03-bg: oklch(0.948 0.071 94.4);
  --color-ttb03-dot: #e9d48a;
  --color-ttb03-dot: oklch(0.871 0.096 94.1);
  --color-ttb03-soft: #fff6d6;
  --color-ttb03-soft: oklch(0.972 0.043 94.1);
  --color-ttb03-ink: #0b0b0b;
  --color-ttb03-ink: oklch(0.150 0 0);
  --font-ttb03-sans: 'Space Grotesk', system-ui, sans-serif;
  --font-ttb03-mono: 'JetBrains 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. */

.ttb-03 {
  width: 100%;
  min-height: 100svh;
}
3Fonts used by this demooptional — skip if you have your own type stack
<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..700&family=JetBrains+Mono:wght@400;500;700&display=swap">

What changed in the port

Which parts became utilities, which stayed CSS, and why.

  • input:checked + .tt23b with transform: translate(4px,4px) became has-checked:translate-x-1 has-checked:translate-y-1; the shadow collapse is a zero-offset shadow so it still interpolates.
  • The :has() panel selectors became group-has-[#ttb03-rN:checked]/tabs:block on each panel.
  • UI upgrade from the CSS original: an active: half-press gives feedback the moment the pointer goes down, before the radio flips; the panel became a step card with its own hard shadow.
  • UI upgrade: Space Grotesk replaces the system sans, a dotted grid texture sits on the stage, and keys are 48px tall (were 42px).
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
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: Neo-Brutalist Tailwind Tabs with Press Effect
Source: https://codefronts.com/navigation/tailwind-css-tabs/neo-brutalist-tailwind-tabs-press-effect/

Heavy uppercase keys with hard 4px offset shadows. The checked tab presses flush: it translates exactly as far as its shadow shrinks, so the shadow's far edge never moves and the key appears to sink into the page. The panel is a brutalist card of its own.
## HTML
```html
<section class="ttb-03 group/tabs grid min-h-svh w-full place-items-center bg-ttb03-bg bg-[radial-gradient(var(--color-ttb03-dot)_1px,transparent_1px)] bg-size-[18px_18px] px-6 py-14 font-ttb03-sans text-ttb03-ink" aria-label="Neo-brutalist press tabs demo">
  <div class="grid w-full max-w-lg justify-items-center gap-8">
    <div role="radiogroup" aria-label="Workflow stage" class="flex w-full gap-4">
      <label class="flex min-h-12 flex-1 cursor-pointer items-center justify-center border-3 border-ttb03-ink bg-white text-[13px] font-bold tracking-[0.12em] uppercase shadow-[4px_4px_0_var(--color-ttb03-ink)] transition-[translate,box-shadow,background-color,color] duration-150 ease-out hover:bg-ttb03-soft active:translate-x-0.5 active:translate-y-0.5 active:shadow-[2px_2px_0_var(--color-ttb03-ink)] has-checked:translate-x-1 has-checked:translate-y-1 has-checked:bg-ttb03-ink has-checked:text-ttb03-bg has-checked:shadow-[0_0_0_var(--color-ttb03-ink)] has-focus-visible:outline-3 has-focus-visible:outline-offset-4 has-focus-visible:outline-dashed has-focus-visible:outline-ttb03-ink motion-reduce:transition-none"><input class="sr-only" type="radio" name="ttb03" id="ttb03-r1" checked>Build</label>
      <label class="flex min-h-12 flex-1 cursor-pointer items-center justify-center border-3 border-ttb03-ink bg-white text-[13px] font-bold tracking-[0.12em] uppercase shadow-[4px_4px_0_var(--color-ttb03-ink)] transition-[translate,box-shadow,background-color,color] duration-150 ease-out hover:bg-ttb03-soft active:translate-x-0.5 active:translate-y-0.5 active:shadow-[2px_2px_0_var(--color-ttb03-ink)] has-checked:translate-x-1 has-checked:translate-y-1 has-checked:bg-ttb03-ink has-checked:text-ttb03-bg has-checked:shadow-[0_0_0_var(--color-ttb03-ink)] has-focus-visible:outline-3 has-focus-visible:outline-offset-4 has-focus-visible:outline-dashed has-focus-visible:outline-ttb03-ink motion-reduce:transition-none"><input class="sr-only" type="radio" name="ttb03" id="ttb03-r2">Ship</label>
      <label class="flex min-h-12 flex-1 cursor-pointer items-center justify-center border-3 border-ttb03-ink bg-white text-[13px] font-bold tracking-[0.12em] uppercase shadow-[4px_4px_0_var(--color-ttb03-ink)] transition-[translate,box-shadow,background-color,color] duration-150 ease-out hover:bg-ttb03-soft active:translate-x-0.5 active:translate-y-0.5 active:shadow-[2px_2px_0_var(--color-ttb03-ink)] has-checked:translate-x-1 has-checked:translate-y-1 has-checked:bg-ttb03-ink has-checked:text-ttb03-bg has-checked:shadow-[0_0_0_var(--color-ttb03-ink)] has-focus-visible:outline-3 has-focus-visible:outline-offset-4 has-focus-visible:outline-dashed has-focus-visible:outline-ttb03-ink motion-reduce:transition-none"><input class="sr-only" type="radio" name="ttb03" id="ttb03-r3">Iterate</label>
    </div>
    <div class="w-full border-3 border-ttb03-ink bg-white p-5 shadow-[6px_6px_0_var(--color-ttb03-ink)]">
      <div class="hidden group-has-[#ttb03-r1:checked]/tabs:block"><p class="font-ttb03-mono text-[11px] font-bold tracking-[0.1em] uppercase">Step 01 / 03</p><p class="mt-2 text-lg leading-snug font-bold">Build — assemble the parts, wire the data, make it run.</p></div>
      <div class="hidden group-has-[#ttb03-r2:checked]/tabs:block"><p class="font-ttb03-mono text-[11px] font-bold tracking-[0.1em] uppercase">Step 02 / 03</p><p class="mt-2 text-lg leading-snug font-bold">Ship — push it to production while it's still a little rough.</p></div>
      <div class="hidden group-has-[#ttb03-r3:checked]/tabs:block"><p class="font-ttb03-mono text-[11px] font-bold tracking-[0.1em] uppercase">Step 03 / 03</p><p class="mt-2 text-lg leading-snug font-bold">Iterate — measure what people actually do, then refine.</p></div>
    </div>
    <p aria-hidden="true" class="border-2 border-ttb03-ink bg-white px-3.5 py-2 font-ttb03-mono text-[11.5px] font-bold">shadow-[4px_4px_0] · has-checked:translate-1 · active: half-press</p>
  </div>
</section>
```
## CSS
```css
@import "tailwindcss";

@theme {
  --color-ttb03-bg: #fdeeb8;
  --color-ttb03-bg: oklch(0.948 0.071 94.4);
  --color-ttb03-dot: #e9d48a;
  --color-ttb03-dot: oklch(0.871 0.096 94.1);
  --color-ttb03-soft: #fff6d6;
  --color-ttb03-soft: oklch(0.972 0.043 94.1);
  --color-ttb03-ink: #0b0b0b;
  --color-ttb03-ink: oklch(0.150 0 0);
  --font-ttb03-sans: 'Space Grotesk', system-ui, sans-serif;
  --font-ttb03-mono: 'JetBrains 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. */

.ttb-03 {
  width: 100%;
  min-height: 100svh;
}
```

How this works

The press is arithmetic. Move the key by N pixels and shrink the shadow offset by N; the far edge of the shadow stays put:

shadow-[4px_4px_0_var(--color-ttb03-ink)]
active:translate-x-0.5 active:translate-y-0.5 active:shadow-[2px_2px_0_…]
has-checked:translate-x-1 has-checked:translate-y-1 has-checked:shadow-[0_0_0_…]

Animate a zero shadow, not none. shadow-none would snap; a 0-offset shadow of the same colour interpolates.

The translate property (not transform) is what v4's translate utilities write, so the transition list names translate.

Make it yours

  • Swap the warning yellow (--color-ttb03-bg) for mint or punk pink; keep the ink at near-black so the press metaphor holds.
  • Heavier press: 6px shadow and translate-x-1.5 translate-y-1.5.
  • Soft-brutalist: add rounded-md — the shadow follows the radius.

Gotchas — read before shipping

  • A will-change: transform ancestor can blur the hard shadow edge in Safari — keep it off parents.
  • Keep horizontal padding on the stage so the 4px shadow never clips at the viewport edge.
  • Pressed keys read as "on" only because colour inverts too; colour-blind users still get the position change.

Browser support

ChromeSafariFirefoxEdge
111+16.4+128+111+

Standalone translate is Chrome 104 / Safari 14.1 / Firefox 72; the floor is Tailwind v4's sheet.

Techniques used in this demo

Utilitiesfirst-class utility or variant

Search CodeFronts

Loading…