DEV Community

Cover image for Méqui — the story of McDonald's in Brazil, told by a burger that comes apart as you scroll
Lucas Santos Rodrigues
Lucas Santos Rodrigues

Posted on

Méqui — the story of McDonald's in Brazil, told by a burger that comes apart as you scroll

Frontend Challenge Perfect Landing Submission 🍲🥧

This is a submission for Frontend Challenge - Comfort Food Edition, Perfect Landing

What I Built

In Brazil, nobody says "McDonald's". They say Méqui.

On August 30th, 2019, the company quietly changed the sign on two restaurants to read MÉQUI, issued no press release, and let the country figure it out. The mystery spread on its own. The nickname stuck so hard it went on the packaging, the tray liners and the menu.

So the comfort food I picked isn't a dish. It's the burger every Brazilian kid grew up begging for on a Sunday — and the 46 years of history stacked behind it.

🔗 Live site · Source

The page is one continuous scroll: a hero, a giant SVG burger that comes apart layer by layer as it travels down the screen, then a timeline from the first store in Copacabana in 1979 to the day the sign changed.

It's written in Portuguese on purpose. The whole piece is about a country renaming a global brand in its own accent — translating it would defeat the point.

Demo

The burger arrives assembled:

The assembled low-poly burger

…and comes apart as it descends, labelling itself on the way down:

The burger fully separated into seven labelled layers

The one trick worth stealing

The separation is a CSS scroll-driven animation anchored to a named view-timeline. The section is 460svh tall and holds a position: sticky child:

.stage      { view-timeline-name: --burger; block-size: 460svh; }
.stage__pin { position: sticky; inset-block-start: 0; block-size: 100svh; }

.bx-layer {
  animation: peel linear both;
  animation-timeline: --burger;
  animation-range: contain var(--a) contain var(--b);
}
Enter fullscreen mode Exit fullscreen mode

The part that made it click: for a subject taller than the viewport, the contain range runs from "the section's top edge meets the top of the screen" to "its bottom edge meets the bottom of the screen" — which is, point for point, exactly the window in which the sticky child is pinned. So contain 0%contain 100% means "animate only while the burger is parked on screen", with no magic numbers.

Each layer gets its own range, offset 5% from the one above. That stagger is the whole reason the sandwich opens top-down instead of all at once.

One more thing that surprised me: no timeline-scope is needed. The SVG layers are descendants of .stage, and timeline name lookup already walks up the ancestors.

Accessibility and the Firefox problem

Scroll-driven animations are Baseline: limited — Chrome/Edge 115+ and Safari 26+ have them; Firefox is still preview-only. So every scroll-driven rule lives inside @supports (animation-timeline: view()), with a not (...) branch that collapses the 460svh section back to auto height, un-sticks the pin, and shows the burger assembled with all labels visible.

I tested that branch by forcing the @supports query to fail: nothing is hidden, and nobody scrolls through 460svh of nothing.

Other things I cared about:

  • prefers-reduced-motion kills the PSX jitter, the fog and the scanlines. The layer separation stays, because it's the content of the section and the user drives it directly — but the travel is damped to 40% so it isn't a full-screen sweep.
  • On narrow screens the side labels don't fit, so they disappear and the layers come back as a real text list at the bottom of the stage.
  • Skip link, semantic landmarks, no heading-level skips, gold focus rings that survive every background in the palette, scroll-margin so anchor targets don't hide behind the fixed header.
  • The burger SVG is role="img" with a <title>/<desc>; the fog, dither and scanline overlays are aria-hidden and pointer-events: none.

Journey

Two bugs are worth confessing.

The wordmark rendered black. I had background-clip: text for the cel-shading bands plus a filter: drop-shadow() stack for the outline. Chrome loses the text clip when a filter is on the same element. Fix: split the job — solid colour and a text-shadow ring on the base element, gradient clip on an ::after with content: attr(data-txt).

Horizontal scroll on mobile. The stage's grid column was sizing to the SVG's natural width — 509px inside a 369px viewport. I reached for overflow-x: clip on html and it did not stop the viewport scrolling. The actual fix was grid-template-columns: minmax(0, 1fr), because auto tracks grow to max-content. Clipping was treating a symptom.

The whole page ships one line of runtime JavaScript — the import of the CSS that Vite needs as an entry module. Everything else, including the burger, is CSS.

MIT licensed. Fork it, take the contain trick, ignore my colour choices.

Top comments (0)