Brutalist web design, written as values

Brutalism specified rather than described: a 1.5 type scale, a sixteen pixel grid, two pixel borders, five colours and zero transition duration.

Massive raw blocks of thick unpolished glass stacked with hard unrounded edges, concrete grey and lit by harsh acid yellow light
Nothing is softened and nothing is hidden. Both of those are specifications.

Brutalism is the one look whose specification is already written: it is the browser's own default stylesheet, held on purpose. At a 16px root, the CSS sample stylesheet sets h1 at 2em with 0.67em margins, h2 at 1.5em with 0.83em, h3 at 1.17em, h4 at 1em, h5 at 0.83em and h6 at 0.67em. That resolves to 32, 24, 19, 16, 13 and 11 pixels, which is a modular ladder somebody designed in the nineties and nobody has improved.

Links are #0000EE unvisited and #551A8B visited in Chrome, Firefox and Safari. On white those measure 9.40:1 and 11.01:1. The defaults were never illegible. They were only ever unfashionable.

most AI developed stuff is just so insanely soulless crap I can instantly tell and instinctively close the tab

A developer on Hacker News

Brutalism is the direct answer to that complaint, and it is worth saying why before the values: a page that hides nothing cannot look like it was assembled by something with no opinion. The call, up front: the style is a list of refusals, and refusals have to be stated as rules or a model will quietly override every one. That is what the Thanor library writes down.

What the style refuses, as rules#

Brutalism on the web attached itself to the term through Pascal Deville's brutalistwebsites.com, and it has stayed coherent because the discipline is subtractive. These are prohibitions, and each one has to be named explicitly because each one is a default somewhere:

  • border-radius: 0 on every element. No exceptions for avatars or buttons.
  • No filter: blur(), no backdrop-filter, no frosted anything.
  • No gradient of any kind, including as an image overlay.
  • No blurred box-shadow. Offset shadows with 0 blur are permitted.
  • No web font unless it is the point of the page. System stacks otherwise.
  • No opacity below 1 on text, ever. If it should be quieter, change the colour.
  • No centred body copy. Everything ranges left, including headings.
  • Links are underlined and buttons look like buttons, so that nothing has to be hovered to be discovered.
  • Content is readable at 320px wide with no horizontal scroll.

The last two are the pair that separates brutalism from carelessness. A page can be raw and still be completely navigable, and that combination is the actual craft in the style. A brutalist page is hostile to decoration, not to the reader.

Three type stacks, and you pick exactly one for the whole site:

  • Grotesque: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif
  • Document: "Times New Roman", Times, serif
  • Terminal: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace

Because none of those load a font file, the whole page can render in one round trip, which is the performance argument the style has always made for itself.

The scale, a perfect fifth#

Ratio 1.5 from 20px. Two steps and the size has more than doubled, which is the point: brutalist hierarchy is a shout followed by a whisper, with nothing in between to soften the transition.

StepSizeLine heightWeightUsed for
-113px20px400meta, footnotes, nav
020px32px400body copy
130px36px700h3
245px48px700h2
368px68px700h1
4101px96px700the one headline per page

Two weights, 400 and 700, and the jump between them is part of the hierarchy rather than a decoration on top of it. Tracking stays at 0 everywhere, including on the 101px step, because optically corrected letterfit is a refinement and refinement is the thing being refused.

  • Line height at step 4 is 96px, which is less than the font size. Negative leading is legitimate here and it is the strongest available signal that a human set the type.
  • Body copy measure: min(100% - 32px, 72ch). Wider than editorial on purpose.
  • text-transform: uppercase on step -1 only, with 0.08em tracking, which is the one tracking value in the system.
  • No step between 0 and 1. The empty band from 20px to 30px is where a generated page puts four sizes, and removing it is most of the look.

Five colours, four of them defaults#

RoleHexContrast on whiteWhere it goes
Ground#FFFFFFthe page
Ink#00000021:1all type, all borders
Link#0000EE9.40:1unvisited links
Visited#551A8B11.01:1visited links, and yes, keep it
Alarm#FF3B003.57:1fills and rules only, never body text

Four of those five are values the browser already ships, and using them is the single most legible statement the style makes. Keeping the visited colour is the test of whether you mean it. Almost every site in the last fifteen years has removed it, which is why a page that keeps it feels like a document rather than a product.

The alarm colour sits at 3.57:1, which fails the 4.5:1 threshold for body text and clears 3:1 for large text and graphical objects. So it is a fill behind black type, a 2px rule, or type at step 3 and above. Write that restriction into the spec, because an unguided model will happily set a caption in it and the caption will be the one thing on the page nobody can read.

A heavy glass structure with its internal supporting framework fully exposed on the outside, every joint and beam deliberately visible
Four of these five colours ship with every browser. Choosing them is still a choice.

Borders, shadows and the sixteen pixel grid#

The spacing unit is 16px, coarse on purpose, and the ladder is 16, 32, 48, 64, 96, 128. There is no 8px and no 4px. A style built on undisguised structure cannot also do fine adjustment, and the coarseness is what makes the alignment visible from across a room.

  • Border: 2px solid #000000. One weight for every border on the site, including inputs, cards, images and the page frame.
  • Shadow: 6px 6px 0 #000000. No blur, no alpha. Applied to buttons and raised blocks only, and the offset does not change on hover.
  • Grid: display: grid, gap: 0. Each cell takes a 2px border, with margin-right: -2px and margin-bottom: -2px so adjacent borders collapse into one line rather than doubling to 4px.
  • Section padding: 64px top and bottom on desktop, 48px on tablet, 32px on a phone, with a 2px rule between sections instead of a colour change.
  • Page gutter: 16px. Flat, not fluid. The text touching close to the edge is a feature.
  • Images: 2px border, no radius, no shadow, and a visible alt text fallback rather than a grey placeholder box.
  • Inputs: 2px border, 48px tall, 0 radius, and a focus state that swaps the border to the alarm colour rather than adding a ring.

The collapsed border is the detail that separates a brutalist grid from a page of divs with borders on them. Double borders at every internal join are the default outcome and they read as a mistake, which in a style with no decoration to distract you is the only thing left to look at.

Motion, and the order of a page#

transition: none. Every state change lands in one frame. Hover on a link inverts it to white on #000000 with no fade, hover on a button moves nothing, and focus draws a 2px solid outline at 2px offset in the alarm colour.

If you want the change to read as deliberately mechanical rather than merely instant, there is one permitted alternative: transition: background-color 80ms steps(2, end). Two discrete frames, so the change is visible without being eased. That is the only easing value in the system and cubic-bezier does not appear anywhere.

The sequence, which is shorter than a marketing page because half the sections a marketing page has are decoration:

  1. Name and one line, step 4 and step 0, at the top of the document with no hero image and no viewport height trick.
  2. The thing itself, immediately: the product, the list, the demo. No preamble section.
  3. Plain facts, as a bordered table. Tables are honest and brutalism likes them.
  4. Contact, as a real mailto link in #0000EE, not a form.
  5. A footer that is a list of links, underlined, unstyled, in the default colours, with the page's own byte weight printed at the end of it if you want the performance claim on the record.

Proof and testimonials are omitted entirely. A style whose argument is that nothing is hidden cannot then arrange social pressure, and a brutalist page with a five star review carousel has broken its own premise more thoroughly than a rounded corner ever could.

The verdict on brutalism as a spec#

Brutalism is the cheapest of these looks to specify and the easiest to get wrong, because every single value in it is something a model will overwrite from its defaults. Ask for brutalism unprompted and you get rounded cards, a soft grey background, a blurred shadow, Inter, and a 300ms fade on nine sections, which is the house style wearing a black border.

So the prompt carries prohibitions, not adjectives. Those nine refusals are the specification, and they work because a prohibition is checkable: search the stylesheet for border-radius, blur, gradient and cubic-bezier, and any hit means the brief was not followed. Ten seconds, which is more than can be said for most design feedback.

The set, complete:

  • 1.5 from 20px, six steps, two weights, 0 tracking.
  • 16px unit, 2px borders, 0 radius, 6px 6px 0 shadows with no blur.
  • #FFFFFF, #000000, #0000EE, #551A8B, #FF3B00, with the alarm colour restricted to fills and large type.
  • transition: none, or 80ms steps(2, end), and nothing else.
  • Five sections, no proof section, links underlined and visited links visible.

Every prompt in the Thanor library carries its refusals as explicit rules rather than hoping the model infers them, which is the difference between a brutalist page and a soft page with a hard font. For the other end of the same discipline, the editorial specification is as strict and far more readable, and the awwwards style recipe spends the decoration budget instead of refusing it. Then browse the Thanor library.

Questions this raises

What actually defines brutalist web design?

Undisguised structure. Visible borders, no rounded corners, no blur, no gradients, system typefaces, default link colours, and interactions that happen instantly rather than easing. Every one of those is a value you can write down.

What are the default browser link colours?

Unvisited links are #0000EE and visited links are #551A8B in Chrome, Firefox and Safari. On white they measure 9.40:1 and 11.01:1, which is why the defaults were never a legibility problem.

What type scale works for brutalism?

A perfect fifth, ratio 1.5, from 20px. That gives 13, 20, 30, 45, 68 and 101 pixels. Two steps and the size has more than doubled, which is the violent hierarchy the look depends on.

What is the brutalist shadow?

A hard offset with no blur and no transparency, 6px 6px 0 #000000. A blurred shadow simulates depth that is not there, and simulating is the one thing the style refuses.

Should a brutalist site have animation?

No easing. Set transition to none and let state changes land in a single frame, or use steps(2, end) at 80ms if you want the change to read as mechanical rather than instant.

Can a model build this without being told the values?

No. Asked for brutalism a model produces rounded cards and a soft gradient, because those are its defaults. The Thanor library is available now and its prompts name the borders, the scale and the zero duration outright.

Part of

The expensive look

What makes a site read as expensive, written as values rather than adjectives: the type scale, the spacing, the curve and its duration, and the order the sections run in.