büro otto.
Build noteA studio site, dissected

how büro was made

A short account of building the Büro Otto site — a Zürich design studio in the Swiss International tradition. The premise was singular: prove precision. This page is laid out on the very same grid it describes.

System
12 col · 8pt
Type
Inter
Palette
red · ink · paper
Assets
none
Passes
03
Console err
0
01

Brief & concept

The brief asked for a Zürich studio with a strict twelve-column exposed grid, a Helvetica-adjacent face, a red/black/white palette, a massive lowercase logotype, and a project index of numbered rows. One instruction did the heavy lifting: the design must demonstrate precision — aligned baselines, perfect optical margins — with exactly one playful crack.

I read that as a constraint, not a mood. A site that claims Swiss discipline but eyeballs its spacing is worse than one that never tried. So the governing decision came first: every measurement in the build would be a multiple of a single 8-point unit, and I would build a live overlay to prove it on screen. The playful crack — the red dot in the logo reacting to scroll — had to earn its place against that rigour, not undercut it.

The fictional studio needed to feel real. It became Büro Otto, founded MMIX, six people, at Löwenstrasse 11 in Zürich — with a project index (Kunsthaus Wende, Tonhalle Saison, a Brockmann reissue) written like a real archive rather than filler.

02

Design decisions

The unit is the whole argument

Rather than hand-tuning spacing, I declared one variable and expressed every gap, pad, and margin as a multiple of it. This is what makes the grid overlay land exactly on content edges instead of near them — the difference between looking Swiss and being Swiss.

:rootthe 8pt system
:root{
  --u: 8px;        /* base 8pt unit */
  --gutter: 24px;  /* 3 × --u */
  --cols: 12;
}
/* every space is a multiple of --u, never a raw px */
.hero{ padding-top: calc(var(--u)*14); }
.prow{ padding: calc(var(--u)*3) 0; }

Palette & type

Three values only: ink #111, paper #f4f2ee (a warm off-white, never pure), and a single signal red #e4231f. Inter carries everything, tracked tight (-.045em) at display sizes for the Helvetica-adjacent feel the brief wanted, and left loose at caption sizes for legibility. Years and indices use tabular figures so numeric columns align to the pixel.

Restraint as the flex

The hardest decision was subtraction. Early on the capability lists ended with a decorative middot; under scrutiny it read as an artifact, not a mark, so it went. The whitespace below say hello is deliberate — a Swiss composition earns its silence.

03

Signature techniques

The red dot, on a spring

The logo's red "o" is a physics object. A requestAnimationFrame loop tracks scroll velocity, kicks the dot vertically, then lets a spring pull it back to rest with damping — so it overshoots and settles like real mass. This is the one crack in the precision, and it only runs when motion is allowed.

scroll-velocity dotthe playful crack
const spring=0.14, damp=0.72, kick=0.9, maxDisp=42;
function tick(){
  vel += -scrollVel*0.06*kick;   // velocity kicks it
  scrollVel *= 0.80;
  vel += (restY - y)*spring;      // spring back
  vel *= damp;                    // lose energy
  y += vel;
  y = Math.max(-maxDisp, Math.min(maxDisp, y));
  disk.style.transform = `translate(-50%, calc(-50% + ${y}px))`;
  requestAnimationFrame(tick);
}

The grid, made visible

Pressing g reveals the whole system at once: twelve red columns, an 8-point baseline backdrop, and a live readout of the exact column width. It is proof, offered to anyone who wants it.

the "g" toggleproof on demand
window.addEventListener('keydown', e => {
  if (e.key === 'g' || e.key === 'G') toggleGrid();
});
function toggleGrid(){
  grid.classList.toggle('on');      // 12 columns
  baseRows.classList.toggle('on');  // 8pt baseline
  gread.classList.toggle('on');     // live col-width
  pulseDisk();                          // dot acknowledges
}

The index that floods the frame

Each project row carries a data-flash colour. On hover the entire viewport floods with that field, the row inverts to white, and its name slides right to reveal a red arrow — a printed-broadsheet gesture translated to the screen.

04

Assets & provenance

There are no assets. No images, no icons, no illustration, no external files beyond one webfont. Everything you see is type, rule, and grid: the logotype is live text with a CSS disk; the favicon is an inline SVG data-URI; the "flash" fields are solid colour; the baseline overlay is a repeating linear-gradient.

That is faithful to the tradition. Swiss International Style was built for the printing press with type and space as its only reliable materials — so the most honest homage ships nothing you couldn't set in metal. The single dependency is Inter, loaded from Google Fonts with display=swap; the copy is invented but plausible, written to read like a real studio's archive.

05

Three passes

Pass 01

Setting the rhythm

FoundScreenshots showed a ~200px dead zone stranding the logotype below the meta column, trailing middots dangling off each capability list like typos, and the two contact info blocks floating apart on different grid rows.

ChangedTightened the hero's vertical rhythm by two units, removed the middots, and began verifying the column overlay against real content edges. Upgrade added: the 8pt baseline backdrop and the live column-width readout.

Pass 02

Resolving the contact block

FoundThe two right-column info blocks were both baseline-anchored in the same grid and drifted into a disconnected float, with a large gap between "General" and "Studio".

ChangedWrapped them into one right-aligned stack with a hairline divider echoing the "say hello" baseline, so the composition reads as one gesture. Upgrade added: a red-dot pulse animation that fires the moment the grid is invoked.

Pass 03

Polish & proof

FoundInteractive elements lacked visible keyboard focus, and the grid overlay needed one final alignment check against the twelve columns at both breakpoints.

ChangedAdded :focus-visible rings on every link and the huge contact target, re-verified the overlay landing exactly on content edges, and confirmed a clean mobile stack with zero console errors.

06

Reproduce this

  1. Declare one spacing unit (--u: 8px) and a gutter that is a multiple of it. Never write a raw pixel gap again — express everything as calc(var(--u)*n).
  2. Commit to three colours: ink, a warm paper (not pure white), one signal accent. Put the accent only where it means something.
  3. Set one tight display face. Track it hard (-.04em) at size, loose at caption size, and switch on tabular figures for any numeric column.
  4. Build a grid overlay you can toggle. If your columns don't land on your content edges, your spacing is decorative, not systematic — fix it until they do.
  5. Earn one moment of play. Give it real physics (a spring with damping beats a linear tween) and gate it behind prefers-reduced-motion.
  6. Add the craft layer: ::selection, :focus-visible, an inline-SVG favicon, meta & og tags, and a mobile stack that keeps the hierarchy.
  7. Screenshot and critique like a hostile art director — at 1440 and 390 — then fix, three passes, until nothing reads as a default.