Pure TailwindMIT licensed

Brutalist Sidebar Navigation in Tailwind CSS

Structure as decoration: a black slab with a 4px red rule, condensed 900-weight uppercase links prefixed by monospace //, and a hover that slams to full yellow with black ink. The active row fills solid red and grows an arrow via aria-[current=page]:after:content-['→']. Zero gradients, zero radii — and every state is now visible to keyboard users, not just the mouse.

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="tsm-12 flex w-full min-h-svh border-[3px] border-tsm12-ink bg-tsm12-paper font-tsm12-head text-tsm12-ink" aria-label="Brutalist sidebar navigation demo">
  <nav aria-label="Main" class="flex w-52 shrink-0 flex-col border-r-4 border-tsm12-acc bg-tsm12-ink text-tsm12-paper">
    <div class="border-b-[3px] border-tsm12-acc px-4 py-[18px]">
      <p class="mb-1 font-tsm12-mono text-[10px] tracking-[0.2em] text-tsm12-navmut">EST. 2024</p>
      <p class="text-[28px] leading-none font-black tracking-[-0.02em] uppercase">Brutal</p>
      <p class="mt-1.5 inline-block bg-tsm12-acc px-1.5 py-px text-[11px] font-extrabold tracking-[0.1em] text-white uppercase">Studio</p>
    </div>
    <ul class="flex-1">
      <li><a href="#" class="group/l relative flex min-h-12 items-center border-b border-tsm12-line px-4 text-lg font-extrabold tracking-[0.04em] text-tsm12-navmut uppercase transition-colors duration-100 before:mr-2 before:font-tsm12-mono before:text-xs before:text-tsm12-navmut/60 before:content-['//'] hover:bg-tsm12-yel hover:text-black hover:before:text-black/60 focus-visible:bg-tsm12-yel focus-visible:text-black focus-visible:outline-none aria-[current=page]:border-tsm12-acc aria-[current=page]:bg-tsm12-acc aria-[current=page]:text-tsm12-paper aria-[current=page]:before:text-white/60 aria-[current=page]:after:ml-auto aria-[current=page]:after:font-tsm12-mono aria-[current=page]:after:text-base aria-[current=page]:after:content-['→'] motion-reduce:transition-none" aria-current="page">Home</a></li>
      <li><a href="#" class="group/l relative flex min-h-12 items-center border-b border-tsm12-line px-4 text-lg font-extrabold tracking-[0.04em] text-tsm12-navmut uppercase transition-colors duration-100 before:mr-2 before:font-tsm12-mono before:text-xs before:text-tsm12-navmut/60 before:content-['//'] hover:bg-tsm12-yel hover:text-black hover:before:text-black/60 focus-visible:bg-tsm12-yel focus-visible:text-black focus-visible:outline-none aria-[current=page]:border-tsm12-acc aria-[current=page]:bg-tsm12-acc aria-[current=page]:text-tsm12-paper aria-[current=page]:before:text-white/60 aria-[current=page]:after:ml-auto aria-[current=page]:after:font-tsm12-mono aria-[current=page]:after:text-base aria-[current=page]:after:content-['→'] motion-reduce:transition-none">Work<span class="ml-auto pl-2 font-tsm12-mono text-[11px] font-normal text-tsm12-yel group-hover/l:text-black">12</span></a></li>
      <li><a href="#" class="group/l relative flex min-h-12 items-center border-b border-tsm12-line px-4 text-lg font-extrabold tracking-[0.04em] text-tsm12-navmut uppercase transition-colors duration-100 before:mr-2 before:font-tsm12-mono before:text-xs before:text-tsm12-navmut/60 before:content-['//'] hover:bg-tsm12-yel hover:text-black hover:before:text-black/60 focus-visible:bg-tsm12-yel focus-visible:text-black focus-visible:outline-none aria-[current=page]:border-tsm12-acc aria-[current=page]:bg-tsm12-acc aria-[current=page]:text-tsm12-paper aria-[current=page]:before:text-white/60 aria-[current=page]:after:ml-auto aria-[current=page]:after:font-tsm12-mono aria-[current=page]:after:text-base aria-[current=page]:after:content-['→'] motion-reduce:transition-none">Studio</a></li>
      <li><a href="#" class="group/l relative flex min-h-12 items-center border-b border-tsm12-line px-4 text-lg font-extrabold tracking-[0.04em] text-tsm12-navmut uppercase transition-colors duration-100 before:mr-2 before:font-tsm12-mono before:text-xs before:text-tsm12-navmut/60 before:content-['//'] hover:bg-tsm12-yel hover:text-black hover:before:text-black/60 focus-visible:bg-tsm12-yel focus-visible:text-black focus-visible:outline-none aria-[current=page]:border-tsm12-acc aria-[current=page]:bg-tsm12-acc aria-[current=page]:text-tsm12-paper aria-[current=page]:before:text-white/60 aria-[current=page]:after:ml-auto aria-[current=page]:after:font-tsm12-mono aria-[current=page]:after:text-base aria-[current=page]:after:content-['→'] motion-reduce:transition-none">Journal<span class="ml-auto pl-2 font-tsm12-mono text-[11px] font-normal text-tsm12-yel group-hover/l:text-black">05</span></a></li>
      <li><a href="#" class="group/l relative flex min-h-12 items-center border-b border-tsm12-line px-4 text-lg font-extrabold tracking-[0.04em] text-tsm12-navmut uppercase transition-colors duration-100 before:mr-2 before:font-tsm12-mono before:text-xs before:text-tsm12-navmut/60 before:content-['//'] hover:bg-tsm12-yel hover:text-black hover:before:text-black/60 focus-visible:bg-tsm12-yel focus-visible:text-black focus-visible:outline-none aria-[current=page]:border-tsm12-acc aria-[current=page]:bg-tsm12-acc aria-[current=page]:text-tsm12-paper aria-[current=page]:before:text-white/60 aria-[current=page]:after:ml-auto aria-[current=page]:after:font-tsm12-mono aria-[current=page]:after:text-base aria-[current=page]:after:content-['→'] motion-reduce:transition-none">Contact</a></li>
    </ul>
    <div class="border-t-[3px] border-tsm12-acc px-4 py-3">
      <p class="mb-1 font-tsm12-mono text-[10px] tracking-[0.15em] text-tsm12-navmut">// USER</p>
      <p class="text-sm font-extrabold tracking-[0.05em] uppercase">J. Haas</p>
    </div>
  </nav>
  <div class="min-w-0 flex-1">
    <div class="flex items-baseline gap-3 border-b-[3px] border-tsm12-ink px-6 py-4">
      <span class="font-tsm12-mono text-xs text-tsm12-acc">[01]</span>
      <h1 class="text-[clamp(28px,4vw,40px)] leading-none font-black tracking-[-0.01em] uppercase">Homepage</h1>
    </div>
    <div class="p-6">
      <p class="mb-6 max-w-sm text-base leading-snug font-semibold text-tsm12-mut text-pretty">Raw borders, zero gradients, high-contrast type — and a thick red rule as the only decoration. Hover or Tab the menu.</p>
      <div class="grid max-w-md grid-cols-2 border-[3px] border-tsm12-ink">
        <div class="border-tsm12-ink p-4 odd:border-r-2 nth-[-n+2]:border-b-2"><p class="text-[clamp(26px,4vw,36px)] leading-none font-black tracking-[-0.03em]">24</p><p class="mt-1.5 font-tsm12-mono text-[11px] tracking-[0.1em] text-tsm12-mut uppercase">Projects</p></div>
        <div class="border-tsm12-ink p-4 odd:border-r-2 nth-[-n+2]:border-b-2"><p class="text-[clamp(26px,4vw,36px)] leading-none font-black tracking-[-0.03em]">08</p><p class="mt-1.5 font-tsm12-mono text-[11px] tracking-[0.1em] text-tsm12-mut uppercase">Clients</p></div>
        <div class="border-tsm12-ink p-4 odd:border-r-2 nth-[-n+2]:border-b-2"><p class="text-[clamp(26px,4vw,36px)] leading-none font-black tracking-[-0.03em]">6yr</p><p class="mt-1.5 font-tsm12-mono text-[11px] tracking-[0.1em] text-tsm12-mut uppercase">Experience</p></div>
        <div class="border-tsm12-ink p-4 odd:border-r-2 nth-[-n+2]:border-b-2"><p class="text-[clamp(26px,4vw,36px)] leading-none font-black tracking-[-0.03em]">100%</p><p class="mt-1.5 font-tsm12-mono text-[11px] tracking-[0.1em] text-tsm12-mut uppercase">Remote</p></div>
      </div>
    </div>
  </div>
</section>
@import "tailwindcss";

@theme {
  --color-tsm12-paper: #f5f0e8;
  --color-tsm12-paper: oklch(0.957 0.012 79.8);
  --color-tsm12-ink: #0a0a0a;
  --color-tsm12-ink: oklch(0.145 0 0);
  --color-tsm12-acc: #e63329;
  --color-tsm12-acc: oklch(0.604 0.216 28.7);
  --color-tsm12-yel: #f7e733;
  --color-tsm12-yel: oklch(0.913 0.181 104);
  --color-tsm12-line: #262626;
  --color-tsm12-line: oklch(0.269 0 0);
  --color-tsm12-navmut: #a8a8a8;
  --color-tsm12-navmut: oklch(0.732 0 0);
  --color-tsm12-mut: #3d3d3d;
  --color-tsm12-mut: oklch(0.36 0 0);
  --font-tsm12-head: 'Barlow Condensed', 'Arial Narrow', system-ui, sans-serif;
  --font-tsm12-mono: 'Space 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. */

.tsm-12 {
  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-tsm12-paper: #f5f0e8;
  --color-tsm12-paper: oklch(0.957 0.012 79.8);
  --color-tsm12-ink: #0a0a0a;
  --color-tsm12-ink: oklch(0.145 0 0);
  --color-tsm12-acc: #e63329;
  --color-tsm12-acc: oklch(0.604 0.216 28.7);
  --color-tsm12-yel: #f7e733;
  --color-tsm12-yel: oklch(0.913 0.181 104);
  --color-tsm12-line: #262626;
  --color-tsm12-line: oklch(0.269 0 0);
  --color-tsm12-navmut: #a8a8a8;
  --color-tsm12-navmut: oklch(0.732 0 0);
  --color-tsm12-mut: #3d3d3d;
  --color-tsm12-mut: oklch(0.36 0 0);
  --font-tsm12-head: 'Barlow Condensed', 'Arial Narrow', system-ui, sans-serif;
  --font-tsm12-mono: 'Space 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. */

.tsm-12 {
  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=Barlow+Condensed:wght@600;700;800;900&family=Space+Mono:wght@400;700&display=swap">

What changed in the port

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

  • The ::before { content:'//' } and active ::after { content:'→' } rules became before:content-['//'] and aria-[current=page]:after:content-['→'] — the arrow is now tied to the page state, not a class.
  • The :nth-child(even) / :nth-child(n+3) border-removal rules became odd:border-r-2 nth-[-n+2]:border-b-2 — add-only rules instead of add-then-remove.
  • UI upgrade from the CSS original: hover now inverts to a solid yellow slab with black ink (the stronger print-brutalist move the original suggested but didn't ship), and focus-visible: gets the same treatment — the original had no focus style.
  • UI upgrade: nav text went from #666 on black (3.7:1) to #a8a8a8 (8.6:1), counters are flex-aligned instead of absolutely positioned, and the stat numerals scale with clamp().
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: Brutalist Sidebar Navigation in Tailwind CSS
Source: https://codefronts.com/navigation/tailwind-css-side-menus/brutalist-sidebar-navigation-tailwind-css/

Structure as decoration: a black slab with a 4px red rule, condensed 900-weight uppercase links prefixed by monospace //, and a hover that slams to full yellow with black ink. The active row fills solid red and grows an arrow via aria-[current=page]:after:content-['→']. Zero gradients, zero radii — and every state is now visible to keyboard users, not just the mouse.
## HTML
```html
<section class="tsm-12 flex w-full min-h-svh border-[3px] border-tsm12-ink bg-tsm12-paper font-tsm12-head text-tsm12-ink" aria-label="Brutalist sidebar navigation demo">
  <nav aria-label="Main" class="flex w-52 shrink-0 flex-col border-r-4 border-tsm12-acc bg-tsm12-ink text-tsm12-paper">
    <div class="border-b-[3px] border-tsm12-acc px-4 py-[18px]">
      <p class="mb-1 font-tsm12-mono text-[10px] tracking-[0.2em] text-tsm12-navmut">EST. 2024</p>
      <p class="text-[28px] leading-none font-black tracking-[-0.02em] uppercase">Brutal</p>
      <p class="mt-1.5 inline-block bg-tsm12-acc px-1.5 py-px text-[11px] font-extrabold tracking-[0.1em] text-white uppercase">Studio</p>
    </div>
    <ul class="flex-1">
      <li><a href="#" class="group/l relative flex min-h-12 items-center border-b border-tsm12-line px-4 text-lg font-extrabold tracking-[0.04em] text-tsm12-navmut uppercase transition-colors duration-100 before:mr-2 before:font-tsm12-mono before:text-xs before:text-tsm12-navmut/60 before:content-['//'] hover:bg-tsm12-yel hover:text-black hover:before:text-black/60 focus-visible:bg-tsm12-yel focus-visible:text-black focus-visible:outline-none aria-[current=page]:border-tsm12-acc aria-[current=page]:bg-tsm12-acc aria-[current=page]:text-tsm12-paper aria-[current=page]:before:text-white/60 aria-[current=page]:after:ml-auto aria-[current=page]:after:font-tsm12-mono aria-[current=page]:after:text-base aria-[current=page]:after:content-['→'] motion-reduce:transition-none" aria-current="page">Home</a></li>
      <li><a href="#" class="group/l relative flex min-h-12 items-center border-b border-tsm12-line px-4 text-lg font-extrabold tracking-[0.04em] text-tsm12-navmut uppercase transition-colors duration-100 before:mr-2 before:font-tsm12-mono before:text-xs before:text-tsm12-navmut/60 before:content-['//'] hover:bg-tsm12-yel hover:text-black hover:before:text-black/60 focus-visible:bg-tsm12-yel focus-visible:text-black focus-visible:outline-none aria-[current=page]:border-tsm12-acc aria-[current=page]:bg-tsm12-acc aria-[current=page]:text-tsm12-paper aria-[current=page]:before:text-white/60 aria-[current=page]:after:ml-auto aria-[current=page]:after:font-tsm12-mono aria-[current=page]:after:text-base aria-[current=page]:after:content-['→'] motion-reduce:transition-none">Work<span class="ml-auto pl-2 font-tsm12-mono text-[11px] font-normal text-tsm12-yel group-hover/l:text-black">12</span></a></li>
      <li><a href="#" class="group/l relative flex min-h-12 items-center border-b border-tsm12-line px-4 text-lg font-extrabold tracking-[0.04em] text-tsm12-navmut uppercase transition-colors duration-100 before:mr-2 before:font-tsm12-mono before:text-xs before:text-tsm12-navmut/60 before:content-['//'] hover:bg-tsm12-yel hover:text-black hover:before:text-black/60 focus-visible:bg-tsm12-yel focus-visible:text-black focus-visible:outline-none aria-[current=page]:border-tsm12-acc aria-[current=page]:bg-tsm12-acc aria-[current=page]:text-tsm12-paper aria-[current=page]:before:text-white/60 aria-[current=page]:after:ml-auto aria-[current=page]:after:font-tsm12-mono aria-[current=page]:after:text-base aria-[current=page]:after:content-['→'] motion-reduce:transition-none">Studio</a></li>
      <li><a href="#" class="group/l relative flex min-h-12 items-center border-b border-tsm12-line px-4 text-lg font-extrabold tracking-[0.04em] text-tsm12-navmut uppercase transition-colors duration-100 before:mr-2 before:font-tsm12-mono before:text-xs before:text-tsm12-navmut/60 before:content-['//'] hover:bg-tsm12-yel hover:text-black hover:before:text-black/60 focus-visible:bg-tsm12-yel focus-visible:text-black focus-visible:outline-none aria-[current=page]:border-tsm12-acc aria-[current=page]:bg-tsm12-acc aria-[current=page]:text-tsm12-paper aria-[current=page]:before:text-white/60 aria-[current=page]:after:ml-auto aria-[current=page]:after:font-tsm12-mono aria-[current=page]:after:text-base aria-[current=page]:after:content-['→'] motion-reduce:transition-none">Journal<span class="ml-auto pl-2 font-tsm12-mono text-[11px] font-normal text-tsm12-yel group-hover/l:text-black">05</span></a></li>
      <li><a href="#" class="group/l relative flex min-h-12 items-center border-b border-tsm12-line px-4 text-lg font-extrabold tracking-[0.04em] text-tsm12-navmut uppercase transition-colors duration-100 before:mr-2 before:font-tsm12-mono before:text-xs before:text-tsm12-navmut/60 before:content-['//'] hover:bg-tsm12-yel hover:text-black hover:before:text-black/60 focus-visible:bg-tsm12-yel focus-visible:text-black focus-visible:outline-none aria-[current=page]:border-tsm12-acc aria-[current=page]:bg-tsm12-acc aria-[current=page]:text-tsm12-paper aria-[current=page]:before:text-white/60 aria-[current=page]:after:ml-auto aria-[current=page]:after:font-tsm12-mono aria-[current=page]:after:text-base aria-[current=page]:after:content-['→'] motion-reduce:transition-none">Contact</a></li>
    </ul>
    <div class="border-t-[3px] border-tsm12-acc px-4 py-3">
      <p class="mb-1 font-tsm12-mono text-[10px] tracking-[0.15em] text-tsm12-navmut">// USER</p>
      <p class="text-sm font-extrabold tracking-[0.05em] uppercase">J. Haas</p>
    </div>
  </nav>
  <div class="min-w-0 flex-1">
    <div class="flex items-baseline gap-3 border-b-[3px] border-tsm12-ink px-6 py-4">
      <span class="font-tsm12-mono text-xs text-tsm12-acc">[01]</span>
      <h1 class="text-[clamp(28px,4vw,40px)] leading-none font-black tracking-[-0.01em] uppercase">Homepage</h1>
    </div>
    <div class="p-6">
      <p class="mb-6 max-w-sm text-base leading-snug font-semibold text-tsm12-mut text-pretty">Raw borders, zero gradients, high-contrast type — and a thick red rule as the only decoration. Hover or Tab the menu.</p>
      <div class="grid max-w-md grid-cols-2 border-[3px] border-tsm12-ink">
        <div class="border-tsm12-ink p-4 odd:border-r-2 nth-[-n+2]:border-b-2"><p class="text-[clamp(26px,4vw,36px)] leading-none font-black tracking-[-0.03em]">24</p><p class="mt-1.5 font-tsm12-mono text-[11px] tracking-[0.1em] text-tsm12-mut uppercase">Projects</p></div>
        <div class="border-tsm12-ink p-4 odd:border-r-2 nth-[-n+2]:border-b-2"><p class="text-[clamp(26px,4vw,36px)] leading-none font-black tracking-[-0.03em]">08</p><p class="mt-1.5 font-tsm12-mono text-[11px] tracking-[0.1em] text-tsm12-mut uppercase">Clients</p></div>
        <div class="border-tsm12-ink p-4 odd:border-r-2 nth-[-n+2]:border-b-2"><p class="text-[clamp(26px,4vw,36px)] leading-none font-black tracking-[-0.03em]">6yr</p><p class="mt-1.5 font-tsm12-mono text-[11px] tracking-[0.1em] text-tsm12-mut uppercase">Experience</p></div>
        <div class="border-tsm12-ink p-4 odd:border-r-2 nth-[-n+2]:border-b-2"><p class="text-[clamp(26px,4vw,36px)] leading-none font-black tracking-[-0.03em]">100%</p><p class="mt-1.5 font-tsm12-mono text-[11px] tracking-[0.1em] text-tsm12-mut uppercase">Remote</p></div>
      </div>
    </div>
  </div>
</section>
```
## CSS
```css
@import "tailwindcss";

@theme {
  --color-tsm12-paper: #f5f0e8;
  --color-tsm12-paper: oklch(0.957 0.012 79.8);
  --color-tsm12-ink: #0a0a0a;
  --color-tsm12-ink: oklch(0.145 0 0);
  --color-tsm12-acc: #e63329;
  --color-tsm12-acc: oklch(0.604 0.216 28.7);
  --color-tsm12-yel: #f7e733;
  --color-tsm12-yel: oklch(0.913 0.181 104);
  --color-tsm12-line: #262626;
  --color-tsm12-line: oklch(0.269 0 0);
  --color-tsm12-navmut: #a8a8a8;
  --color-tsm12-navmut: oklch(0.732 0 0);
  --color-tsm12-mut: #3d3d3d;
  --color-tsm12-mut: oklch(0.36 0 0);
  --font-tsm12-head: 'Barlow Condensed', 'Arial Narrow', system-ui, sans-serif;
  --font-tsm12-mono: 'Space 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. */

.tsm-12 {
  width: 100%;
  min-height: 100svh;
}
```

How this works

Ornament is generated, state is semantic. The // prefix and → suffix are pseudo-elements, so the accessible name of each link is just its word. The arrow is gated on aria-current, not a class:

<a aria-current="page" class="… aria-[current=page]:bg-tsm12-acc
  aria-[current=page]:after:ml-auto
  aria-[current=page]:after:content-['→']">

The stat grid draws its own rules. Inside a 3px-bordered box, odd:border-r-2 rules the left column and nth-[-n+2]:border-b-2 rules the first row — the internal cross appears without touching the outer edge.

Brutal but accessible. Hover and focus share one treatment (yellow slab, black ink), so the keyboard gets the same unmissable signal as the pointer.

Make it yours

  • Newspaper brutalism: swap --color-tsm12-acc to yellow and --color-tsm12-yel to red.
  • Slab serif: replace Barlow Condensed with Playfair Display in --font-tsm12-head for a different sub-genre.
  • Visible grid: add bg-[repeating-linear-gradient(90deg,oklch(0_0_0/.05)_0_1px,transparent_1px_40px)] to the main area.
  • Hard shadow on the stage: shadow-[8px_8px_0_var(--color-tsm12-ink)].

Gotchas — read before shipping

  • Arbitrary content-[…] values with spaces need underscores: content-['01_/'].
  • Pure black on pure white causes halation on OLED — the tokens use #0a0a0a on #f5f0e8.
  • Condensed display fonts reflow when they load; keep display=swap and a condensed fallback stack.

Browser support

ChromeSafariFirefoxEdge
111+16.4+128+111+

Deliberately old-school CSS — borders, backgrounds, generated content; the floor is v4's own sheet.

Techniques used in this demo

Utilitiesfirst-class utility or variant

Search CodeFronts

Loading…