---
title: "BRAND Spotify 2026 · Recipe: scrolling-hero"
url: https://memocore.ai/memos/6ae42ecccb977478f04eb81b
project: "Brand Styles"
tags: "brand-spotify"
updated_at: "2026-10-30T05:54:33.192Z"
---

# BRAND Spotify 2026 · Recipe: scrolling-hero

HEADER
brand: Spotify; design_date: 2026-10; kind: effect; used_in: Page: product-marketing S1-S2
captured: https://www.spotify.com/us/premium/ 2026-10-30, US via Google Translate proxy; proof: "country":"US", "market":"us", query market us, USD prices ($12.99); the proxy exit itself geolocates TW, and the market comes from the /us/ path
evidence: measured on that URL unless marked; values from CSS module ScrollingHero, intro and from premium.voyager GSAP code

LOOK
A dark #121212 page with a huge black-weight wide headline on the left and a big round Spotify symbol on the right, cut out of the dark surface so a video plays through it. The last word of the headline changes every 2 seconds by sliding vertically inside a one-line slot, in italic. On scroll the dark surface fades while the symbol scales up, until the video fills the whole screen behind the next text, then the page continues on solid #121212.

STRUCTURE
Back to front:
1 fixed video container: top var(--hero-banner-offset, 0), full width, height calc(100vh - banner offset), video object-fit cover
2 mask layer: absolute, 100% x 100%, background --background-base; mask-image is two layers (solid gradient + logo PNG) combined so the logo is a hole
3 content: intro section 100vh, then descriptor text blocks
4 sticky bottom controls: scroll indicator left, round play/pause control right

KEY PARAMETERS
container: min-height 150vh; background --background-base
mask-size desktop >=1024: 100%, 53%; mask-x calc(100% - 25px), and from 1310px the code uses 100px from the edge; mask-y 50%
mask mobile <1024: 100%, var(--hero-logo-size), where size = max(150px, min(80vw, space left above the copy)); mask-x 50%, mask-y 0%; --hero-copy-height 400px; --hero-logo-gap 8px
intro: height 100vh; desktop padding 100px 25px by CSS, measured text left edge 100 at 1440 (inset grows to 100 from 1310), align start; mobile padding 16px 16px 0, centered, title margin-top 65%
headline: SpotifyMixUITitleVariable (font-stretch axis 50%-150%), weight 900, stretch 156.25% computed, line-height normal, first line starts at y 248 at 1440. Size: measured 128px at 1440 although the token says 6rem (--encore-text-size-larger-5 is set to 6rem at >=768, the computed value on the live page is 128px). Mobile 2.5rem from 390px, 2rem below.
keyword slot: height 1lh, overflow hidden, container-type inline-size; each word absolute, italic, font-size min(1em, calc(115cqi / var(--keyword-chars, 10)))
keywords US: music, artists, fans, live events, videos
subheadline: 20px/1.5, max-width 510px, margin-top 24px
CTA: 2 medium pills 48 tall, gap 16px, stacked on mobile with centered buttons
legal text: 300px wide, margin 24px auto, 12px #b3b3b3 (est colour)
countdown chip above CTA: "Offer ends in 3 days" with bell icon (seasonal)
play/pause: round 48, icon 16px, bottom right

MOTION
keywords: GSAP timeline repeat -1. For each word: hold 1.5s, then current yPercent 0 -> -100 and next yPercent 100 -> 0 together, GSAP default 0.5s. The timeline carries ease power2.inOut in its vars, which GSAP does not pass to child tweens, so children run the default power1.out (inf).
mask on scroll: timeline fromTo opacity 1, scale 1, mask at start position -> opacity 0, scale 3, --mask-x 50%, --mask-y 50%, ease none
trigger: body, start top top, end bottom top, scrub 1. Collapsed hero variant: hero element, start top top, end bottom top, scrub 0.3
after the hero: ScrollTrigger start bottom top sets the video container display none, restores it on leave back
resize: rebuild after 50ms debounce, ScrollTrigger refresh after 100ms

STATES
hover: CTA buttons as Overview button-primary and button-secondary
keyboard: headline has aria-label with base plus all keywords; visible words are aria-hidden
touch and mobile: same scrub, logo above the headline with about 4px between, text centered
reduced motion: the animation code returns early, first keyword stays visible, mask static
loading fallback: dark #121212 surface with the logo hole until the video plays

HOW TO BUILD
Fixed video behind a full-size element whose mask cuts a logo-shaped hole. Animate scale and opacity of that element by scroll position with scrub. Rotate the keywords with a simple looping timeline inside a one-line clipped list. Use a round abstract shape of your own instead of the Spotify symbol. With Archivo as the substitute use 136px, line-height 1.1.

PITFALLS
Headline must be wide and black weight; a regular grotesk loses the look. The word slot must clip to exactly 1lh. Scale is applied to the whole mask layer, so the logo grows from its own position toward the center. The surface colour must match the page so no edge is seen.

SCREENSHOTS
- file: spotify_2026_product-marketing_d1440_s1-hero.jpeg
  shows: hero at scroll 0, viewport 1440x900, word "music", announcement bar above the header
  source: live via US proxy 2026-10-30
  key values: H1 128px/900 stretch 156%; green CTA #1ed760; bg #121212
  do not copy: logo, video, photos; use placeholders
- file: spotify_2026_scrolling-hero_d1440_mid.jpeg
  shows: scroll 700, video almost full screen, surface faded
  source: live via US proxy 2026-10-30
  key values: mask scale between 1 and 3
  do not copy: video
- file: spotify_2026_product-marketing_m390_s1-hero.jpeg
  shows: mobile hero 390x844, symbol above centered text, word "fans", stacked buttons
  source: live via US proxy 2026-10-30
  key values: H1 2.5rem token on 390; buttons centered
  do not copy: logo, video
