← Back to the site
Design & build guide · ~10 min read

How the Alto Roasters site was brewed.

A complete walkthrough of the concept, palette, typography, the scroll-drawn steam thread, the bag-label product cards, the imagery art direction, and how to deploy the whole thing — no build step, just three files and taste.

01The concept

Alto is a fictional specialty coffee roastery in the vein of Onyx or La Cabra: modern craft, not rustic café. No chalkboards, no burlap-texture backgrounds, no steaming-mug clip art. The organizing idea is simple:

Coffee-bag-label graphic design, brought to the web. Specialty roasters put their best design on the bag — origin, altitude, process, tasting notes, big friendly numerals. Each product card on the site is a bag label: color band, index numeral, mono data table, tasting-note pills, a roast meter.

Everything else follows from that: mono type for anything that reads like data, a chunky soft serif for anything that reads like a voice, and one loud accent color used sparingly enough to stay loud.

02Palette

Rich espresso brown + oat cream, with cobalt blue as the unexpected pop (persimmon was the alternate; cobalt reads more "design studio", less "harvest festival"). Caramel bridges the two on dark sections.

Espresso#2a160c — ink & dark bg
Espresso deep#1d0f08 — footer
Paper#faf3e5 — page bg
Cream#f6eddb — cards
Oat#e9dcc2 — soft accents
Cobalt#2b3fe4 — the pop
Caramel#c2814e — warm accent
Tobacco#7a5c3e — muted labels

Contrast rules: body text is espresso on paper (≈13:1). Cobalt text on cream is reserved for labels 12px+ and darkened to #1f30c7 when it has to carry small text. On cobalt and espresso backgrounds, text is always cream/oat.

03Typography

RoleFaceWhy
DisplayFraunces (variable, opsz 9–144)A chunky soft-serif with real personality — wonky italics, huge optical range. At opsz 144 the headlines get that dense, jammy "printed label" feel.
BodyKarlaA warm, slightly quirky grotesque. Friendly without being childish.
DataSpline Sans MonoSoft-cornered mono for altitude, process, ratios, hours — anything that would be stamped on a bag.

One trick worth stealing: italicizing exactly one word per headline in cobalt (notice, filler, smoke) gives every section a consistent editorial wink without a single extra element.

04The scroll-drawn steam thread

The signature motion: a single cobalt line that meanders down the entire page like steam rising off a pour, drawing itself as you scroll. It's one SVG path stretched over the page:

<!-- absolutely positioned behind the content, full page height -->
<div id="thread-wrap" aria-hidden="true">
  <svg viewBox="0 0 1440 8000" preserveAspectRatio="none">
    <path id="steam-path" d="M 980 -20 C 1060 240, 820 420, ..."/>
  </svg>
</div>
// classic line-draw, scrubbed to the whole document
const len = path.getTotalLength();
gsap.set(path, { strokeDasharray: len, strokeDashoffset: len });
gsap.to(path, {
  strokeDashoffset: 0, ease: 'none',
  scrollTrigger: { trigger: document.body,
    start: 'top top', end: 'bottom bottom', scrub: 0.6 }
});

05Bag-label cards + tilt hover

Each card is plain HTML: a color band (numeral + origin), a name, a dashed-rule mono spec list, tasting-note pills, a roast meter, and a price row. Two motion details make them feel physical:

Satisfying hover tilt

card.addEventListener('mousemove', e => {
  const r = card.getBoundingClientRect();
  const px = (e.clientX - r.left) / r.width  - .5;
  const py = (e.clientY - r.top)  / r.height - .5;
  card.style.transform =
    `translateY(-10px) rotateX(${-py*7}deg) rotateY(${px*8}deg)`;
});

Roast meter that fills on scroll

A bordered track with a striped fill (repeating-linear-gradient at -45°). Width starts at 0 and animates to data-fill with power4.out when the card enters the viewport — same pattern as a skill bar, but the striping makes it read as "roast level" rather than UI chrome.

06Paper grain, stickers, tape

07Art direction — the imagery

All photography was generated with Nano Banana Pro via the Higgsfield MCP, then compressed to JPEG (sips -s format jpeg -s formatOptions 74). Each image came from a carefully art-directed brief — lighting, lens feel, palette, composition and mood specified shot by shot (warm golden light, espresso-brown + cream, editorial/documentary, appetizing, no text, no people or hands only) — and iterated until it landed already color-graded to the site.

The full prompt library is proprietary. The shot-by-shot briefs behind these six images are part of our production kit — available to clients on request.

08Other techniques worth stealing

09Deploying to Netlify

The site is three static files plus an assets folder — no build step, so deployment is drag-and-drop simple.

  1. Drag-and-drop: go to app.netlify.com/drop and drop the alto-roasters folder. Done — you get a live URL in seconds.
  2. CLI:
    npm i -g netlify-cli
    cd alto-roasters
    netlify deploy --prod --dir .
  3. Git-connected: push the folder to a repo, "Import from Git" in Netlify, set publish directory to alto-roasters and leave the build command empty.

Optional but nice: add a _headers file for long-cache on /assets/*, and set Cache-Control: public, max-age=31536000, immutable since the images are content-hashed by name in practice.