Skip to content

Brut

Brut is a free, loud, animated neo-brutalist site for anything you launch: an app, a product, a service, a side project, a small studio or an event. Thick ink outlines, hard shadows without blur, flat colour blocks in lime, pink and sky, a heavy grotesk for titles and a mono for labels, all on graph paper that runs edge to edge. The sections are generic on purpose, so the same site works for almost anything. The demo is a fictional time-tracking app, Punchcard.

Brut comes in two variants, chosen when you create the site: files/Sanity (static pages, any host) and EmDash (--cms emdash, rendered on request on Cloudflare). The pages are the same in both.

Page Edit it in
Home Pages → Home page (a list of sections)
Pricing (plans with a monthly/yearly switch, every plan’s basics, a demo band, questions) Pages → Pricing page
About (a picture, the story with facts, the team, values, a closing call) Pages → About page
Blog (a tag filter, every post, the newsletter) Pages → Blog page
A post (tags, author, date, the cover, the text, more posts) Content → Blog posts
Contact (the form, demo booking, questions) Pages → Contact page
Name, announcement bar, navigation, footer columns, profiles Pages → Site settings
Buttons, labels, chat, emails and game texts Content → Interface texts
Privacy, terms Pages → Legal

In titles, words wrapped in *stars* sit on a crooked highlighter block in the accent that drops onto them as they scroll in, as in “Track time *without* the guilt trip”.

The hero is a wall of Bauhaus tiles across the whole window: quarter circles, half moons, circles, rings, triangles, leaves, stripes and checks in your three colors, paper and ink. On load the wall stamps itself in tile by tile; then the shapes turn a quarter at a time in hard steps, in a wave that runs across the wall, and some tiles flip over. A tile you point at turns at once. The headline card sits in the middle with the sticker, the line, two buttons and the small print, all there from the first frame. The tiles are pure CSS, so they cost nothing to load, and they follow your colors. With reduced motion the wall stands still.

Smaller walls of the same tiles sit beside every page title, in the newsletter card and around the closing call.

The home page is built from these sections, in any order:

  • Hero: the tile machine.
  • Ticker: two marquee bands crossing in an X, one in the accent, one in ink.
  • Features: cards in a color each (accent, second, third, paper or ink), one or two columns wide, each with a little working drawing: a stopwatch that ticks, bars that grow, a bell that rings, a receipt that prints, people who nod, lightning that flashes, a lock that snaps, a plug that goes in.
  • How it works: numbered steps with a picture each; they stick and pile on top of each other as you scroll.
  • Showcase: framed pictures (4:5) on a strip that slides sideways as you scroll down. Where scroll-driven animation isn’t available, the strip scrolls sideways by itself.
  • Numbers: big blocks on an ink band; the numbers count up as they scroll in.
  • Quotes: stickers on a wall, each turned a little, with initials instead of photos.
  • Plans: the plans from the pricing page.
  • Questions: chunky boxes that open on click.
  • Newsletter and Closing call: a big block of the accent between two rows of turning tiles.

Each plan (Pages → Pricing page → Plans) has a price per month and per month billed yearly: 0 shows “Free”, an empty price shows “Let’s talk”. The monthly/yearly switch appears when some plan has a different yearly price. Put a Stripe Price id (or a Lemon Squeezy variant id) for each interval in Online checkout and the button starts a checkout with 14 days free; without ids the button follows the plan’s link. Search engines get the plans as offers of the product.

Everything works without keys; each feature switches on with its variables in .env.example.

Feature Where Service
Book a demo Contact page, loads on click; a preview of the calendar until connected Cal.com
Plans Pricing page and the home page (subscriptions with a trial) Stripe or Lemon Squeezy
Newsletter Home page, blog and every post, with a welcome email Supabase or Mailchimp
Contact form Contact page; the topic is written above the message Resend
AI chat Every page; answers about the product, plans and setup Claude or OpenAI
Search ⌘K or the search button: the home page, pricing, about, posts, contact built in, or Algolia
Analytics Every page; Google Analytics only after consent Plausible, Umami, Google Analytics, PostHog
Pictures Production builds Cloudinary (optional)

src/content/singletons/brand.json (Pages → Brand in Keystatic) holds three fills: the accent (lime: buttons, highlights), the second (pink) and the third (sky). Text on a fill is always ink, so each fill is lightened until ink on it reads; a color that can’t get there stops the build with a message. Brand also sets the fonts (titles, text and labels), the corners (square, slightly rounded or round) and the size of the hard shadows. Cards, steps, quotes, plans and posts each pick a fill in their content, so you can recolor without code. The site opens in its light theme (warm graph paper); the toggle switches to the dark one (near-black graph paper) and the browser remembers it.

Brut is ready for more languages: see Languages.

A small game, Four up: four in a row against the machine. Drop a disc into a column with a click or tap, keys 1–7, or the arrow keys and Enter; four of yours in a row, a column or a diagonal wins. Three games, and the machine looks further ahead each time. A win is worth 100 points less 2 for every move over four (at least 40), a draw 20. The best score is kept in the visitor’s browser.

  • AGENTS.md describes the project for coding assistants (Claude Code reads it through CLAUDE.md, Cursor through .cursor/rules/).
  • /llms.txt lists your pages, plans and posts for AI assistants, in every language.
  • Pages carry JSON-LD: the company as an Organization, the product with its plans as offers, posts as BlogPostings and questions as a FAQPage.
Terminal window
pnpm dev # local site, editor at /keystatic
pnpm build # production build
pnpm cms sanity schema|seed # move content to Sanity (see Content → Sanity)

In the EmDash variant: npm run dev, npm run deploy (Cloudflare) and npm run emdash:schema (after changing the content model).