DEV Community

Cover image for Giving My One-Off Web Experiments an Actual Brand System
Andy Schelb
Andy Schelb

Posted on AI-assisted

Giving My One-Off Web Experiments an Actual Brand System

How I stopped every new Plaid Labs experiment from looking like a different AI-generated site, and built a shared brand mark + design system instead.

The problem: every new experiment looked like a different app

I've got a small constellation of side projects living under Plaid Labs โ€” Daily Doodle, a couple of maker tools, and whatever I spin up next on a random night. Each one got built independently, usually in a single sitting, which meant each one also got its own accidental visual identity. Different fonts. Different spacing instincts. Different "vibe."

None of them looked bad exactly. They just didn't look like they belonged to each other.

I posted a link to one thing and someone said, oh did you vibe code that? As if that was a negative I should be ashamed of. I get it not everything Ai is good and it made me think. Am I just ok with how this is or do I want to control the look and branding of this?

Step one: a brand mark that actually travels

The first fix was small and dumb-simple on paper: every Plaid Labs page needed the same little signature in the footer. Not a logo file โ€” I wanted something typographic, something that would render consistently anywhere without shipping an image asset.

What I landed on:

<a class="plaid-mark" href="https://theplaidscientist.github.io/">
  <span class="plaid-mark-row">
    <span class="plaid-mark-lede">a</span>
    <span class="plaid-mark-beaker" aria-hidden="true">๐Ÿงช</span>
    <span class="plaid-mark-word">Plaid Labs</span>
  </span>
  <span class="plaid-mark-sub">
    <span>e</span><span>x</span><span>p</span><span>e</span><span>r</span><span>i</span><span>m</span><span>e</span><span>n</span><span>t</span>
  </span>
</a>
Enter fullscreen mode Exit fullscreen mode

Two lines: a ๐Ÿงช Plaid Labs, then experiment underneath, letter-spaced out to match the width of the line above it. A couple of details I had to fight the browser for, in case they save you the same headache:

The emoji was rendering smaller than the capital letter next to it. Emoji glyphs sit smaller within their own em-box than a capital letter does โ€” a beaker sized at 1em next to bold text at the same font-size visually reads as noticeably smaller than the "P" beside it. Bumping it to 1.4em and nudging the baseline with transform: translateY(0.11em) got it to actually match the cap-height.

Major Mono Display โ€” the font I used for "experiment" โ€” looks completely different in uppercase vs. lowercase. Typed in caps, some of its glyphs render as strange filled-in blocks. Typed in lowercase, it renders as these clean, readable outlined letterforms. So the word is authored in lowercase in the HTML even though it reads like a stylized all-caps treatment โ€” that's the font doing the work, not a text-transform.

Stretching "experiment" to match the width of the line above, at any size: instead of guessing at letter-spacing values, I split the word into ten individual <span> letters and put them in a flex row with justify-content: space-between. Whatever width the row above renders at, the letters spread themselves to fill it exactly. No magic numbers, no breaking at different screen sizes.

Major Mono Display only ships one weight (Regular) โ€” so font-weight: bold does nothing. -webkit-text-stroke fakes it instead:

.plaid-mark-sub {
  font-family: "Major Mono Display", ui-monospace, monospace;
  font-weight: 700;
  -webkit-text-stroke: 0.55px currentColor; /* this is what actually bolds it */
}
Enter fullscreen mode Exit fullscreen mode

Step two: a real design system, not just a shared footer

The footer mark fixed the "who made this" signature, but the pages themselves still had no shared language. So I wrote out an actual spec โ€” colors, three type roles, production CSS โ€” and made it the default for anything new I build:

  • Palette: dark charcoal background (#181818), clinical off-white text (#E1E1E1), one accent โ€” a terminal green (#48C774) โ€” spent deliberately instead of smeared everywhere.
  • Type, three roles:
    • A bold display face reserved only for the page's own title/branding โ€” not every heading, just the one moment that's actually the page introducing itself.
    • A monospace face for tags, badges, status readouts โ€” real structural information, not decoration.
    • A neutral body face for everything else โ€” paragraphs, UI, ordinary section headers.

The point of scoping the display face to just the page title was deliberate. My first pass applied it to every h1/h2/h3, and it immediately stopped feeling special โ€” it just became "the font," which is exactly the generic-template trap I was trying to avoid in the first place.

I really just didn't want all my stuff to look like it was made without a human input.

The part where the font choice backfired

I initially speced the display face as Syne at weight 700/800 โ€” bold, sculptural, "avant-garde lab" energy. It looked great in isolation.

Then I shipped it on a real page โ€” a little "Image Cutout" tool โ€” and it looked stretchy. Wide, flared, almost like the letters had been horizontally scaled. My first assumption was a CSS bug: bad font-stretch, a broken fallback, something synthetic going on.

It wasn't a bug. That's just what Syne looks like at heavy weight โ€” wide counters and flared terminals are the actual design of the typeface. It's a deliberate, characterful choice for the right context. It just wasn't the right context sitting directly above plain body text at a normal reading size.

Rather than argue with myself about it, I built a quick side-by-side comparison page: the exact same heading, in Syne (as shipped), a toned-down Syne, and three alternatives with a similar bold/technical energy but tighter, more normal letter proportions. Landed on Bricolage Grotesque โ€” bold, a little experimental, but nothing about it reads as "stretched."

What I actually shipped

The design system now lives as a spec I reuse on every new experiment by default, with two explicit exceptions: Disco Doodle and the main Plaid Labs landing page both keep their own established look, because they're not "new" โ€” they already have an identity worth protecting.

:root {
  --bg-dark: #181818;
  --text-main: #E1E1E1;
  --accent-green: #48C774;

  --font-display: 'Bricolage Grotesque', sans-serif; /* page title/branding only */
  --font-mono: 'JetBrains Mono', monospace;           /* tags, badges, status */
  --font-body: 'Public Sans', sans-serif;              /* everything else */
}
Enter fullscreen mode Exit fullscreen mode

Made it a skill

I asked Claude to remember all the choices we made and create a repeatable skill that would basically be a branding guide that auto triggers every times we create a new site to publish.

None of this was hard, individually. Emoji sizing, font-weight faking, letter-spacing math โ€” each one's a five-minute CSS fix. The thing that actually mattered was treating "brand consistency across side projects" as a real spec worth writing down once, instead of re-deciding it from scratch every time I open a new repo.

I'm super excited to have each new thing we create feel like its mine. Like it has my signature on it so it doesn't just look the same as something anyone else has created.

Top comments (0)