Pure TailwindMIT licensed

Tailwind Brutalist Input

A hard 2.5px border, zero radius and a flat offset shadow that the field presses into on focus. The whole aesthetic is four utilities, and the RTL flip the CSS original leaves as a gotcha is one rtl: variant here.

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

<div class="tif-28 grid min-h-svh w-full place-items-center bg-tif28-bg px-6 py-14 font-tif28-mono" aria-label="Brutalist input demo">
  <label class="grid w-full max-w-70 gap-2">
    <span class="text-[11px] font-bold tracking-[0.12em] text-tif28-ink">USERNAME_</span>
    <input type="text" name="tif28-user" placeholder="enter handle…" pattern="[a-z0-9_]{3,}" required
      class="w-full rounded-none border-[2.5px] border-tif28-edge bg-tif28-paper px-3.5 py-3 text-[13px] font-bold tracking-[0.06em] text-tif28-edge shadow-[5px_5px_0_var(--color-tif28-acc)] outline-none transition-[transform,box-shadow] duration-100 placeholder:text-tif28-edge/50 focus:translate-x-0.5 focus:translate-y-0.5 focus:shadow-[3px_3px_0_var(--color-tif28-acc)] user-invalid:shadow-[5px_5px_0_var(--color-tif28-bad)] rtl:shadow-[-5px_5px_0_var(--color-tif28-acc)] rtl:focus:-translate-x-0.5 rtl:focus:shadow-[-3px_3px_0_var(--color-tif28-acc)] motion-reduce:transition-none">
    <span class="text-[10px] tracking-[0.08em] text-tif28-ink/60">LOWERCASE_LETTERS_DIGITS_UNDERSCORE · MIN_3</span>
  </label>
</div>
@import "tailwindcss";

@theme {
  --color-tif28-bg: #12121a;
  --color-tif28-bg: oklch(0.19 0.015 285);
  --color-tif28-paper: #f0eeff;
  --color-tif28-paper: oklch(0.95 0.02 290);
  --color-tif28-edge: #1a1a2e;
  --color-tif28-edge: oklch(0.22 0.04 285);
  --color-tif28-ink: #f0eeff;
  --color-tif28-ink: oklch(0.95 0.02 290);
  --color-tif28-acc: #ff6c8a;
  --color-tif28-acc: oklch(0.72 0.17 15);
  --color-tif28-bad: #ff5d6c;
  --color-tif28-bad: oklch(0.69 0.19 20);
  --font-tif28-mono: 'Space Mono', ui-monospace, monospace;
}

.tif-28 {
  width: 100%;
}

.tif-28 ::selection {
  background: var(--color-tif28-acc);
  color: #1a1a2e;
}

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-tif28-bg: #12121a;
  --color-tif28-bg: oklch(0.19 0.015 285);
  --color-tif28-paper: #f0eeff;
  --color-tif28-paper: oklch(0.95 0.02 290);
  --color-tif28-edge: #1a1a2e;
  --color-tif28-edge: oklch(0.22 0.04 285);
  --color-tif28-ink: #f0eeff;
  --color-tif28-ink: oklch(0.95 0.02 290);
  --color-tif28-acc: #ff6c8a;
  --color-tif28-acc: oklch(0.72 0.17 15);
  --color-tif28-bad: #ff5d6c;
  --color-tif28-bad: oklch(0.69 0.19 20);
  --font-tif28-mono: 'Space Mono', ui-monospace, monospace;
}

.tif-28 {
  width: 100%;
}

.tif-28 ::selection {
  background: var(--color-tif28-acc);
  color: #1a1a2e;
}
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+Mono:wght@400;700&display=swap">

What changed in the port

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

  • The RTL gotcha became an rtl: variant trio — the port's recurring pattern: warnings in the CSS collection turn into utilities here.
  • :invalid:not(:placeholder-shown) becomes user-invalid:, and a real pattern was added so the red stamp means something.
  • A hint line was added under the field: a validation state with no stated rule is a guessing game.
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: Tailwind Brutalist Input
Source: https://codefronts.com/components/tailwind-css-input-fields/tailwind-brutalist-input/

A hard 2.5px border, zero radius and a flat offset shadow that the field presses into on focus. The whole aesthetic is four utilities, and the RTL flip the CSS original leaves as a gotcha is one rtl: variant here.
## HTML
```html
<div class="tif-28 grid min-h-svh w-full place-items-center bg-tif28-bg px-6 py-14 font-tif28-mono" aria-label="Brutalist input demo">
  <label class="grid w-full max-w-70 gap-2">
    <span class="text-[11px] font-bold tracking-[0.12em] text-tif28-ink">USERNAME_</span>
    <input type="text" name="tif28-user" placeholder="enter handle…" pattern="[a-z0-9_]{3,}" required
      class="w-full rounded-none border-[2.5px] border-tif28-edge bg-tif28-paper px-3.5 py-3 text-[13px] font-bold tracking-[0.06em] text-tif28-edge shadow-[5px_5px_0_var(--color-tif28-acc)] outline-none transition-[transform,box-shadow] duration-100 placeholder:text-tif28-edge/50 focus:translate-x-0.5 focus:translate-y-0.5 focus:shadow-[3px_3px_0_var(--color-tif28-acc)] user-invalid:shadow-[5px_5px_0_var(--color-tif28-bad)] rtl:shadow-[-5px_5px_0_var(--color-tif28-acc)] rtl:focus:-translate-x-0.5 rtl:focus:shadow-[-3px_3px_0_var(--color-tif28-acc)] motion-reduce:transition-none">
    <span class="text-[10px] tracking-[0.08em] text-tif28-ink/60">LOWERCASE_LETTERS_DIGITS_UNDERSCORE · MIN_3</span>
  </label>
</div>
```
## CSS
```css
@import "tailwindcss";

@theme {
  --color-tif28-bg: #12121a;
  --color-tif28-bg: oklch(0.19 0.015 285);
  --color-tif28-paper: #f0eeff;
  --color-tif28-paper: oklch(0.95 0.02 290);
  --color-tif28-edge: #1a1a2e;
  --color-tif28-edge: oklch(0.22 0.04 285);
  --color-tif28-ink: #f0eeff;
  --color-tif28-ink: oklch(0.95 0.02 290);
  --color-tif28-acc: #ff6c8a;
  --color-tif28-acc: oklch(0.72 0.17 15);
  --color-tif28-bad: #ff5d6c;
  --color-tif28-bad: oklch(0.69 0.19 20);
  --font-tif28-mono: 'Space Mono', ui-monospace, monospace;
}

.tif-28 {
  width: 100%;
}

.tif-28 ::selection {
  background: var(--color-tif28-acc);
  color: #1a1a2e;
}
```

How this works

Press into the shadow. Focus moves the field two pixels toward its own drop and shrinks the drop by the same amount, so the stamp appears to depress:

class="shadow-[5px_5px_0_var(--color-tif28-acc)]
  focus:translate-x-0.5 focus:translate-y-0.5
  focus:shadow-[3px_3px_0_var(--color-tif28-acc)]"

Zero blur is the point. A hard offset shadow reads as a second flat rectangle, not as depth — which is the whole neo-brutalist grammar, and why any blur radius above 0 breaks it.

Make it yours

  • Accent: one bright colour against black. Pink, lime and cyan all work; two accents do not.
  • Weight: 8px 8px 0 for a chunkier stamp, 3px 3px 0 for restraint — change the focus value by the same delta.
  • Type: any monospace. Space Mono and JetBrains Mono both read more modern than Courier.
  • Validation: add pattern and the red stamp fires on a real rule.

Gotchas — read before shipping

  • RTL flips the light source. The rtl: variant is not optional if you ship Arabic or Hebrew.
  • 2.5px borders with zero radius look ragged on 1x displays. Check on a non-retina monitor.
  • user-invalid rather than invalid is what stops the red stamp appearing before the user has typed anything.

Browser support

ChromeSafariFirefoxEdge
119+16.5+128+119+

:user-invalid sets the floor; below it the field simply never turns red, which is a safe fallback.

Techniques used in this demo

Utilitiesfirst-class utility or variant

Search CodeFronts

Loading…