KOMPOSE.
Toutes les ressources
Site WebClaude

Reproduire une page produit Apple de A à Z (le démontage du code)

Le problème résolu

Les pages produit d'Apple passent pour hors de portée, alors qu'elles reposent sur cinq couches empilables et toutes reproductibles. Cette ressource ouvre les bundles JavaScript livrés en production sur iPhone 17 Pro, AirPods Pro et MacBook Pro, montre ce que chacune fait vraiment (shaders GLSL maison, scrub vidéo en HLS, moteur de keyframes piloté en CSS), puis donne l'ordre de construction et le prompt complet.

Le prompt à copier

Optimisé avecClaude
You are building an Apple-style product page: a long scroll where the visuals are driven by scroll position rather than by autoplay, and where all motion belongs to one shared system instead of per-component animations.

Stack: Next.js (App Router) + TypeScript, or plain HTML/CSS/JS if no framework is wanted. GSAP + ScrollTrigger for orchestration, hls.js for the scrubbed video, react-three-fiber + three.js only if layer 5 is requested. Never stack two animation libraries.

Build it in this order and do not skip a layer.

LAYER 1: THE TRACK
- Every animated section is a tall container (300vh to 500vh) holding one child with position: sticky; top: 0; height: 100vh.
- Expose a single normalized progress value per section: 0 when the sticky child locks, 1 when it unlocks, computed from getBoundingClientRect. Every other layer reads that number and nothing else.
- One requestAnimationFrame loop for the whole page, never one per component. Never write to the DOM inside a scroll listener.

LAYER 2: SCRUBBED VIDEO
- Use a single  and map progress to video.currentTime. Do not use a PNG or JPEG image sequence: heavier, slower to start, worse on mobile.
- Serve the clip as HLS. On Safari set video.src to the .m3u8 directly (native support), everywhere else attach hls.js. The browser then downloads only the segments the user actually scrolls through.
- Encode with a very short GOP so seeking lands on the right frame:
  ffmpeg -i source.mov -c:v libx264 -crf 20 -g 10 -keyint_min 10 -sc_threshold 0 -an out.mp4
  then segment to HLS with 2 second parts.
- Never call video.play(). The only thing that moves the video is currentTime.

LAYER 3: SMOOTHING
- Never assign a raw scroll value to a style. Pass it through a lerp (current += (target - current) * 0.1) or a spring, and write only the smoothed value.
- Spring for anything that should feel physical (scale, a panel opening, card weight). Lerp for anything that must land exactly on a value (video currentTime, opacity ramps).
- Snap to target when the delta drops below 0.001 and cancel the frame loop, so an idle page costs nothing.

LAYER 4: KEYFRAMES DRIVEN BY CSS
- Do not hardcode animation start and end points in JS. Read them from CSS custom properties on the section, so responsive breakpoints stay in the stylesheet:
  .scene { --copy-start: .15; --copy-end: .40 }
- In JS: sub(progress, start, end) clamped to 0..1, then smoothstep (t*t*(3-2*t)) before applying.
- Compose only from a small set of pure helpers: clamp, lerp, smoothstep, round. No bespoke math per section.

LAYER 5: REAL TIME 3D (only if asked)
- react-three-fiber, one .glb model, a material swap for colorways, camera or object rotation driven by the same progress value, plus pointermove for a small parallax.
- Load the 3D bundle dynamically, and only when the section is within one viewport of the fold.

PERFORMANCE RULES (non negotiable)
- Add will-change through a class only while the section sits within 150vh before and 50vh after the viewport, and remove it outside. A permanent will-change on a long page destroys performance.
- Animate transform and opacity only. Never top, left, width or height.
- decoding="async" everywhere, lazy media below the fold, an inline hero poster so LCP does not wait for the video.
- Budget: if the motion layer ships more than roughly 300 KB of JavaScript, cut a layer.

ACCESSIBILITY
- Read prefers-reduced-motion once at boot, store it in a single flag, expose it as a class on .
- When reduced: sections collapse to normal height, sticky becomes static, the video is replaced by its end frame or poster, copy is simply visible. The page must still tell the whole story.
- Keep copy as real DOM text at all times, never baked into the video.

DELIVERABLE
Build it for: [describe your product, the 3 to 5 moments the scroll should tell, and where the assets come from].
Return the section markup, the CSS including the custom properties per breakpoint, and the single shared animation module.

Une page produit Apple ne repose sur aucune librairie magique. En ouvrant les bundles JavaScript livrés en production, on trouve trois techniques distinctes, empilables, et toutes reproductibles. Ce qui suit vient de l’inspection directe du code d’apple.com, pas d’articles de seconde main.

https://www.apple.com/iphone-17-pro/, https://www.apple.com/airpods-pro/, https://www.apple.com/macbook-pro/

Ce qu’il y a vraiment dans le code

iPhone 17 Pro : un moteur 3D avec ses propres shaders

Le bundle main.built.js pèse 680 Ko et contient une classe ProductViewerWebGL en singleton, avec son chargeur de scène et des réactions MobX. Le plus parlant, ce sont les shaders GLSL écrits à la main :

uniform float focalPoint;
uniform float near;
uniform float far;
layout(location = 0) out vec4 gColor;
layout(location = 1) out vec4 gDepth;

Deux cibles de rendu, une pour la couleur, une pour la profondeur, plus un point de focale : Apple applique une profondeur de champ en post-traitement sur le modèle du téléphone. Le sélecteur de coloris n’est pas un carrousel d’images, il appelle onColorChange sur la scène et remplace le matériau en direct. La rotation est branchée sur pointermove. C’est le niveau le plus ambitieux des trois.

AirPods Pro : le scrub vidéo, en streaming adaptatif

C’est le bundle le plus lourd des trois (881 Ko) et la page la plus pédagogique. Le DOM annonce la couleur : .video-scrub-container, .sticky-image, .scroll-item-driver, .scroll-copy. Le JS contient une méthode scrubKeyframe() qui mappe la position de scroll sur un tableau sequenceKeyframes.

Le détail que la plupart des tutoriels ratent : Apple n’utilise plus de séquence d’images. Le code fait this.isSafari ? this._requestVideo() : this._requestVideoStream() et charge hls.js depuis /ac/libs/hls.js/. Autrement dit, Safari lit la vidéo nativement, les autres navigateurs passent par du HLS adaptatif, et le navigateur ne télécharge que les segments réellement traversés par le scroll. Une séquence de 300 JPEG fait tout l’inverse.

MacBook Pro : le moteur de keyframes maison

La page la moins spectaculaire, et de loin la plus instructive. On y voit le moteur d’animation interne d’Apple à nu : un mini-langage de keyframes, avec son propre parseur d’expressions supportant clamp, lerp, smoothstep, round, cos, sin, atan.

anim.createScrollGroup().addKeyframe(el, {
  start: "a0t - 100vh",
  end:   "lerp(0.756, a0t, a0b - 100vh)"
})

// et surtout, piloté depuis le CSS :
{ start: "css(--hz-hardware-kf-start, a0)",
  end:   "css(--hz-hardware-kf-end, a0)" }

a0t désigne le haut de l’ancre 0, a0b son bas, les décalages sont exprimés en vh. Conséquence directe : les points de départ et de fin des animations se règlent en CSS, donc les breakpoints responsive vivent dans la feuille de style, pas dans le JavaScript.

Deux autres choses s’y lisent. Un système de ressorts (spring.setTarget("scale", ...), snapValue, un preset bounce) plutôt que des courbes d’easing figées. Et surtout une gestion explicite du cycle de vie de will-change, avec _addWillChange et _removeWillChange déclenchés sur la plage { start: "t - 150vh", end: "b + 50vh" }. C’est exactement le détail qui sépare une page fluide d’une page qui fait tourner le ventilateur.

L’ordre de construction, de A à Z

Étape 1 : la trame

Toute section animée est un conteneur haut qui contient un enfant collant. La hauteur du conteneur, c’est la durée du scroll.

<section class="scene">
  <div class="scene-sticky">
    <video class="scene-video" muted playsinline preload="auto"></video>
    <p class="scene-copy">Le texte qui accompagne</p>
  </div>
</section>
.scene { height: 400vh; }
.scene-sticky {
  position: sticky;
  top: 0;
  height: 100vh;
  overflow: clip;
}

Ensuite une seule valeur, normalisée entre 0 et 1, que toutes les autres couches liront. Rien d’autre.

function progress(section) {
  const r = section.getBoundingClientRect()
  const total = r.height - window.innerHeight
  return Math.min(1, Math.max(0, -r.top / total))
}

Étape 2 : le scrub vidéo

Une seule balise video, jamais de play(). La seule chose qui bouge, c’est currentTime.

if (video.canPlayType('application/vnd.apple.mpegurl')) {
  video.src = '/media/scene.m3u8'        // Safari : natif
} else {
  const hls = new Hls({ maxBufferLength: 8 })
  hls.loadSource('/media/scene.m3u8')
  hls.attachMedia(video)
}

L’encodage compte autant que le code. Un GOP court garantit que la recherche tombe sur la bonne image plutôt que sur la dernière image-clé.

ffmpeg -i source.mov -c:v libx264 -crf 20 
  -g 10 -keyint_min 10 -sc_threshold 0 -an out.mp4

Étape 3 : le lissage

La valeur brute du scroll ne doit jamais atterrir directement dans un style. Elle passe par un lerp ou un ressort, et seule la valeur lissée est écrite.

let target = 0, current = 0, raf = null

function onScroll() {
  target = progress(section) * video.duration
  if (!raf) raf = requestAnimationFrame(tick)
}

function tick() {
  current += (target - current) * 0.12
  video.currentTime = current
  if (Math.abs(target - current) < 0.001) {
    current = target
    raf = null                 // page au repos : zéro frame
  } else {
    raf = requestAnimationFrame(tick)
  }
}

Ressort pour ce qui doit paraître physique (une échelle, un panneau qui s’ouvre). Lerp pour ce qui doit atterrir exactement sur une valeur (le currentTime, une rampe d’opacité).

Étape 4 : les keyframes pilotées en CSS

C’est l’idée à voler à la page MacBook Pro. Les bornes de chaque animation vivent en CSS, donc le responsive se règle sans toucher au JavaScript.

.scene { --copy-start: .15; --copy-end: .40; }

@media (max-width: 734px) {
  .scene { --copy-start: .05; --copy-end: .55; }
}
const css = (el, n) => parseFloat(getComputedStyle(el).getPropertyValue(n))
const sub = (p, a, b) => Math.min(1, Math.max(0, (p - a) / (b - a)))
const smoothstep = t => t * t * (3 - 2 * t)

const t = smoothstep(
  sub(p, css(scene, '--copy-start'), css(scene, '--copy-end'))
)
copy.style.opacity = t
copy.style.transform = `translate3d(0, ${(1 - t) * 24}px, 0)`

Quatre helpers purs suffisent : clamp, lerp, smoothstep, sub. Aucune formule sur mesure par section.

Étape 5 : la 3D temps réel, si le budget suit

C’est le niveau iPhone 17 Pro. Un modèle .glb, un changement de matériau pour les coloris, une rotation branchée sur la même valeur de progression, et un léger parallaxe au pointeur. Le bundle 3D se charge en dynamique, seulement quand la section approche du viewport. En dessous de ce budget, une vidéo scrubbée donne 80 % du résultat pour 10 % du coût.

Étape 6 : la performance, là où tout se joue

Reproduire le cycle de vie du will-change d’Apple, avec les mêmes marges :

const io = new IntersectionObserver(
  ([e]) => el.classList.toggle('will-change', e.isIntersecting),
  { rootMargin: '150% 0px 50% 0px' }   // t - 150vh / b + 50vh
)
io.observe(section)

Le reste tient en trois règles : n’animer que transform et opacity, une seule boucle requestAnimationFrame pour toute la page, jamais d’écriture DOM dans un écouteur de scroll. Si la couche animation dépasse environ 300 Ko de JavaScript, il faut retirer une couche.

Étape 7 : l’accessibilité

Les trois pages Apple lisent prefers-reduced-motion au démarrage, le stockent dans un modèle unique et l’exposent en classe sur la page. À reproduire tel quel :

@media (prefers-reduced-motion: reduce) {
  .scene { height: auto; }
  .scene-sticky { position: static; height: auto; }
  .scene-video { display: none; }
  .scene-poster { display: block; }
}

Règle non négociable : le texte reste du vrai texte dans le DOM, jamais incrusté dans la vidéo. Sinon la page ne raconte plus rien une fois le mouvement désactivé, et le référencement disparaît avec.

La checklist avant de livrer

  • Une seule boucle requestAnimationFrame pour tout le site, qui s’arrête quand rien ne bouge.
  • Vidéo en HLS, GOP court, jamais de séquence d’images.
  • Bornes d’animation en variables CSS, pas en constantes JavaScript.
  • will-change ajouté et retiré dynamiquement, jamais posé en dur.
  • Uniquement transform et opacity animés.
  • Poster inline sur le hero pour ne pas faire attendre le LCP.
  • Version prefers-reduced-motion qui raconte encore toute l’histoire.
  • Test réel sur un mobile milieu de gamme, pas sur le simulateur du MacBook.

Pour la couche scroll générique (inertie, révélations, épinglage) qui sert de socle à tout ça, voir la ressource Réussir des animations au scroll de très haute qualité.

Ressources liées

Toutes les ressources
Site WebClaude

Réussir des animations au scroll de très haute qualité

Le smooth scroll installé en trois lignes donne un site mou, qui tremble sur les sections épinglées et qui devient pénible sur mobile. Un scroll de niveau studio, ce sont quatre couches distinctes à régler séparément : inertie, révélation, épinglage, scrub. Ce guide donne les quatre, avec le code, les valeurs qui marchent et le démontage de trois sites qui les réussissent.

›_# Scroll motion layer: build spec # Portable: save it as CLAUDE.md (Claude Code) or AGENTS
Voir la fiche
Site WebClaude

Produire un site à 10 000 € avec l'IA : le pipeline complet

Un client montre igloo.inc et demande un devis. Ces sites paraissent inaccessibles en solo, alors que deux des trois reposent sur une stack très reproductible. Ce guide donne le démontage réel des trois références, ce que 10 000 € achètent vraiment, et le pipeline complet de production avec Claude et les outils externes qui vont avec.

›_# CLAUDE.md - Project constitution for a studio-grade marketing site You are the lead crea
Voir la fiche
Site WebClaude

Comment un site comme likova.space est construit (et le prompt pour le recreer)

Les sites immobilier/architecture comme likova.space paraissent etre une grosse production mais reposent sur une stack simple : pas de framework, Three.js brut avec GLTFLoader pour un vrai modele 3D, mouvement au scroll fait a la main. Ce guide detaille exactement ce qui tourne et donne un prompt pret a l'emploi pour recreer le pattern avec son propre projet.

›_You are building a scroll-narrative marketing site for a physical product (real estate, ar
Voir la fiche
Passer à l'action

On vous construit le site ?

Ces ressources donnent le ton. On peut livrer le site complet, rapide et pensé pour convertir.

Réserver un appelÀ propos de nous