1. Mechanisme — kopieer 1-op-1, geen styling-keuzes
// Mechanisme: cta-shader-orb-kinetic (techniek #36 — Custom GLSL fragment shader)
// WebGL2 fullscreen quad in ronde clip. Radiale gradient + sin-wave puls op uTime.
// Kleur cycelt zwart -> acid -> coral via 3 sin-waves met phase-offset.
// Auto-running rAF loop, geen click nodig. CTA-button er overheen.
// Fallback: animated CSS radial-gradient orb met @keyframes pulse.
import gsap from 'https://esm.sh/[email protected]';
const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
const root = document.querySelector('.shader-orb-kinetic');
if (root) {
const canvas = root.querySelector('canvas');
const fallback = root.querySelector('.orb-fallback');
const gl = canvas && canvas.getContext('webgl2', { antialias: true, premultipliedAlpha: true });
if (!gl) {
if (fallback) fallback.style.display = 'block';
if (canvas) canvas.style.display = 'none';
} else {
// ... compile vs+fs, link program, fullscreen quad VBO,
// uniform uT (time) + uRes, draw loop via rAF.
}
} 3. Styling-template — verplicht eigen invulling per merk
/* Styling: cta-shader-orb-kinetic — black + acid yellow + coral, Archivo Black, oversize */
:root {
--block-bg: #0A0A0A;
--block-fg: #FFE600;
--block-accent: #FF4D2E;
}
.cta-orb-kinetic-section {
background: var(--block-bg);
color: var(--block-fg);
font-family: 'Archivo Black', 'Anton', sans-serif;
display: grid;
grid-template-columns: 1fr 1fr;
gap: clamp(2rem, 5vw, 4rem);
padding: clamp(3rem, 6vw, 6rem);
align-items: center;
min-height: 80vh;
}
.orb-eyebrow {
font-family: 'Archivo', sans-serif; font-weight: 700;
font-size: 0.8rem; letter-spacing: 0.2em; text-transform: uppercase;
color: var(--block-accent); margin: 0 0 1.5rem;
}
.orb-title {
font-size: clamp(4rem, 12vw, 12rem); line-height: 0.85;
letter-spacing: -0.05em; margin: 0; text-transform: uppercase;
}
.orb-sub {
font-family: 'Archivo', sans-serif; font-weight: 500;
font-size: clamp(1rem, 1.4vw, 1.25rem); line-height: 1.4;
max-width: 28ch; margin: 2rem 0;
color: rgba(255, 230, 0, 0.7);
}
.orb-cta {
display: inline-block;
font-family: 'Archivo Black', sans-serif;
font-size: clamp(1.25rem, 2vw, 1.75rem);
color: var(--block-bg); background: var(--block-fg);
padding: 1.25rem 2.5rem; text-decoration: none; text-transform: uppercase;
transition: background 0.2s, color 0.2s;
}
.orb-cta:hover { background: var(--block-accent); color: var(--block-fg); }
.shader-orb-kinetic { position: relative; aspect-ratio: 1; width: 100%; max-width: 520px; }
.shader-orb-kinetic canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
.shader-orb-kinetic .orb-fallback {
position: absolute; inset: 0; display: none; border-radius: 50%;
background: radial-gradient(circle at 50% 50%, #FFE600 0%, #FF4D2E 50%, #0A0A0A 100%);
animation: orbPulse 1.2s ease-in-out infinite;
}
@keyframes orbPulse {
0%, 100% { transform: scale(0.92); }
50% { transform: scale(1); }
}