Live preview needs JavaScript. The code below works without any dependencies.
Prompt for your AI assistant
Paste this into Claude, Cursor or ChatGPT. The settings update as you move the controls above.
Build "Loud landing slider" for my site using the Enrichr Library element at https://enrichr.dev/elements/loud-landing-slider.html.
Read the machine-readable spec and source at https://enrichr.dev/elements/loud-landing-slider.json (markup, CSS, JavaScript, parameters).
Requirements: vanilla JavaScript and CSS, no new dependencies; keep the behaviour of the source unchanged; respect prefers-reduced-motion; work on touch screens; keep it accessible.
Settings:
- line1: MAKE
- line2: IT
- line3: MOVE
- ticker: MAKE, SOMETHING, LOUD, THAT, MOVES, ON THE, WEB
- cursor: on
- tickerSeconds: 22
- spinSeconds: 25
- slideSeconds: 0.45
- accent: #e54c1e
- highlight: #e6ff55
- background: #0c1e18
- text: #fbf8f1
Verify: reduced motion shows no movement, it works at 375px wide, there are no console errors, and scrolling stays smooth.The same spec as JSON, for tools that read it directly: /elements/loud-landing-slider.json.
Code
Mount it on an element that contains the markup. It returns an object with update(options) and destroy().
HTML
<div class="ef-lls">
<div class="ef-lls__cursor" aria-hidden="true"></div>
<p class="ef-lls__sr ef-lls__tsr">Make something loud that moves on the web</p>
<div class="ef-lls__ticker" aria-hidden="true"><div class="ef-lls__tin"></div></div>
<nav class="ef-lls__nav" aria-label="Example site">
<a class="ef-lls__logo" href="#"><svg class="ef-lls__mark" viewBox="0 0 50 50" aria-hidden="true" focusable="false"><path fill="currentColor" d="m34.213 25.361 c0 2.655 1.381 4.808 3.086 4.808 0.781 0 1.494 -0.452 2.038 -1.198 0.267 -0.366 0.688 -0.589 1.142 -0.589 h0.054 c0.774 0 1.401 0.627 1.401 1.401 v10.65 h-10.651 c-0.774 0 -1.401 0.627 -1.401 1.401 v0.054 c0 0.453 0.222 0.874 0.589 1.141 0.746 0.544 1.198 1.257 1.198 2.038 0 1.705 -2.152 3.086 -4.808 3.086 -2.655 0 -4.808 -1.381 -4.808 -3.086 0 -0.781 0.452 -1.494 1.198 -2.038 0.366 -0.267 0.589 -0.688 0.589 -1.142 v-0.054 c0 -0.774 -0.627 -1.401 -1.401 -1.401 h-10.65 v-10.651 c0 -0.774 -0.627 -1.401 -1.401 -1.401 h-0.054 c-0.453 0 -0.875 0.222 -1.142 0.589 -0.543 0.746 -1.257 1.198 -2.038 1.198 -1.705 0 -3.086 -2.152 -3.086 -4.808 0 -2.655 1.381 -4.808 3.086 -4.808 0.781 0 1.495 0.452 2.038 1.198 0.267 0.366 0.688 0.589 1.141 0.589 h0.054 c0.774 0 1.401 -0.627 1.401 -1.401 v-10.65 h10.65 c0.774 0 1.401 -0.627 1.401 -1.401 v-0.054 c0 -0.453 -0.222 -0.875 -0.589 -1.142 -0.746 -0.543 -1.198 -1.257 -1.198 -2.038 0 -1.705 2.152 -3.086 4.808 -3.086 2.655 0 4.808 1.381 4.808 3.086 0 0.781 -0.452 1.495 -1.198 2.038 -0.366 0.267 -0.589 0.688 -0.589 1.141 v0.054 c0 0.774 0.627 1.401 1.401 1.401 h10.65 v10.65 c0 0.774 -0.627 1.401 -1.401 1.401 h-0.053 c-0.453 0 -0.874 -0.222 -1.141 -0.589 -0.544 -0.746 -1.257 -1.198 -2.038 -1.198 -1.705 0 -3.086 2.152 -3.086 4.808 z"/></svg><span>Enrichr</span></a>
<ul class="ef-lls__links">
<li class="ef-lls__li"><a class="ef-lls__link ef-lls__tolineup" href="#">Lineup</a></li>
<li class="ef-lls__li"><a class="ef-lls__link" href="#">About</a></li>
<li class="ef-lls__li"><a class="ef-lls__link ef-lls__cart" href="#">Get started</a></li>
</ul>
</nav>
<section class="ef-lls__hero">
<div class="ef-lls__blob" aria-hidden="true"></div>
<div class="ef-lls__blob2" aria-hidden="true"></div>
<div class="ef-lls__text">
<div class="ef-lls__eyebrow"><span class="ef-lls__dot" aria-hidden="true"></span><span class="ef-lls__et">Now building • All day every day</span></div>
<h2 class="ef-lls__h1"><span class="ef-lls__l1">MAKE</span><br><span class="ef-lls__l2">IT</span><br><span class="ef-lls__l3">MOVE</span></h2>
<p class="ef-lls__subp">Motion, colour and a little noise for your next landing page. Copy it, tweak it, ship it.</p>
<div class="ef-lls__pills" role="group" aria-label="Colourway of the spinning piece">
<button type="button" class="ef-lls__pill is-active" aria-pressed="true">Lime</button>
<button type="button" class="ef-lls__pill" aria-pressed="false">Cream</button>
<button type="button" class="ef-lls__pill" aria-pressed="false">Terracotta</button>
<button type="button" class="ef-lls__pill" aria-pressed="false">Violet</button>
<button type="button" class="ef-lls__pill" aria-pressed="false">Green</button>
</div>
<div class="ef-lls__btns">
<button type="button" class="ef-lls__bp ef-lls__tolineup">See the lineup ↓</button>
<a class="ef-lls__bs" href="#">What's inside?</a>
</div>
<div class="ef-lls__chips">
<div class="ef-lls__chip"><div class="ef-lls__cv">60fps</div><div class="ef-lls__cl">Target</div></div>
<div class="ef-lls__chip"><div class="ef-lls__cv">3</div><div class="ef-lls__cl">Files</div></div>
<div class="ef-lls__chip"><div class="ef-lls__cv">0</div><div class="ef-lls__cl">Libraries</div></div>
</div>
</div>
<div class="ef-lls__imgwrap">
<div class="ef-lls__ring">
<button type="button" class="ef-lls__prod" aria-label="Next colourway"><svg class="ef-lls__piece" viewBox="0 0 50 50" aria-hidden="true" focusable="false"><path fill="currentColor" d="m34.213 25.361 c0 2.655 1.381 4.808 3.086 4.808 0.781 0 1.494 -0.452 2.038 -1.198 0.267 -0.366 0.688 -0.589 1.142 -0.589 h0.054 c0.774 0 1.401 0.627 1.401 1.401 v10.65 h-10.651 c-0.774 0 -1.401 0.627 -1.401 1.401 v0.054 c0 0.453 0.222 0.874 0.589 1.141 0.746 0.544 1.198 1.257 1.198 2.038 0 1.705 -2.152 3.086 -4.808 3.086 -2.655 0 -4.808 -1.381 -4.808 -3.086 0 -0.781 0.452 -1.494 1.198 -2.038 0.366 -0.267 0.589 -0.688 0.589 -1.142 v-0.054 c0 -0.774 -0.627 -1.401 -1.401 -1.401 h-10.65 v-10.651 c0 -0.774 -0.627 -1.401 -1.401 -1.401 h-0.054 c-0.453 0 -0.875 0.222 -1.142 0.589 -0.543 0.746 -1.257 1.198 -2.038 1.198 -1.705 0 -3.086 -2.152 -3.086 -4.808 0 -2.655 1.381 -4.808 3.086 -4.808 0.781 0 1.495 0.452 2.038 1.198 0.267 0.366 0.688 0.589 1.141 0.589 h0.054 c0.774 0 1.401 -0.627 1.401 -1.401 v-10.65 h10.65 c0.774 0 1.401 -0.627 1.401 -1.401 v-0.054 c0 -0.453 -0.222 -0.875 -0.589 -1.142 -0.746 -0.543 -1.198 -1.257 -1.198 -2.038 0 -1.705 2.152 -3.086 4.808 -3.086 2.655 0 4.808 1.381 4.808 3.086 0 0.781 -0.452 1.495 -1.198 2.038 -0.366 0.267 -0.589 0.688 -0.589 1.141 v0.054 c0 0.774 0.627 1.401 1.401 1.401 h10.65 v10.65 c0 0.774 -0.627 1.401 -1.401 1.401 h-0.053 c-0.453 0 -0.874 -0.222 -1.141 -0.589 -0.544 -0.746 -1.257 -1.198 -2.038 -1.198 -1.705 0 -3.086 2.152 -3.086 4.808 z"/></svg></button>
</div>
<div class="ef-lls__badge" aria-hidden="true">Iconic</div>
</div>
</section>
<section class="ef-lls__lineup">
<div class="ef-lls__head">
<h3 class="ef-lls__h2">THE<br><em>LINEUP</em></h3>
<p class="ef-lls__hp">Five sections, zero filler. Pick one, or try all of them. We won't judge.</p>
</div>
<div class="ef-lls__viewport" role="region" aria-roledescription="carousel" aria-label="The lineup, use the left and right arrow keys" tabindex="0">
<div class="ef-lls__track">
<article class="ef-lls__card ef-lls__c1" aria-label="1 of 5: Heroes"><span class="ef-lls__tag">Loud</span><img class="ef-lls__img" src="/img/library/corgi-1.jpg" alt="" loading="lazy" decoding="async" draggable="false"><div class="ef-lls__bottom"><div class="ef-lls__name">Heroes</div><p class="ef-lls__desc">Big type, bigger motion. The first thing anyone sees on a page.</p><div class="ef-lls__foot"><div class="ef-lls__price">No. 01</div><button type="button" class="ef-lls__add">OPEN +</button></div></div></article>
<article class="ef-lls__card ef-lls__c2" aria-label="2 of 5: Text"><span class="ef-lls__tag">Kinetic</span><img class="ef-lls__img" src="/img/library/corgi-2.jpg" alt="" loading="lazy" decoding="async" draggable="false"><div class="ef-lls__bottom"><div class="ef-lls__name">Text</div><p class="ef-lls__desc">Letters that rise, scatter and settle back into their places.</p><div class="ef-lls__foot"><div class="ef-lls__price">No. 02</div><button type="button" class="ef-lls__add">OPEN +</button></div></div></article>
<article class="ef-lls__card ef-lls__c3" aria-label="3 of 5: Galleries"><span class="ef-lls__tag">Swipeable</span><img class="ef-lls__img" src="/img/library/corgi-3.jpg" alt="" loading="lazy" decoding="async" draggable="false"><div class="ef-lls__bottom"><div class="ef-lls__name">Galleries</div><p class="ef-lls__desc">Cards, carousels and fans you can drag around with one finger.</p><div class="ef-lls__foot"><div class="ef-lls__price">No. 03</div><button type="button" class="ef-lls__add">OPEN +</button></div></div></article>
<article class="ef-lls__card ef-lls__c4" aria-label="4 of 5: Backgrounds"><span class="ef-lls__tag">Ambient</span><img class="ef-lls__img" src="/img/library/corgi-6.jpg" alt="" loading="lazy" decoding="async" draggable="false"><div class="ef-lls__bottom"><div class="ef-lls__name">Backgrounds</div><p class="ef-lls__desc">Shaders, dots and waves that sit quietly behind all of it.</p><div class="ef-lls__foot"><div class="ef-lls__price">No. 04</div><button type="button" class="ef-lls__add">OPEN +</button></div></div></article>
<article class="ef-lls__card ef-lls__c5" aria-label="5 of 5: Bands"><span class="ef-lls__tag">Fan fave</span><img class="ef-lls__img" src="/img/library/corgi-4.jpg" alt="" loading="lazy" decoding="async" draggable="false"><div class="ef-lls__bottom"><div class="ef-lls__name">Bands</div><p class="ef-lls__desc">Full-width strips for stats, quotes, tickers and big numbers.</p><div class="ef-lls__foot"><div class="ef-lls__price">No. 05</div><button type="button" class="ef-lls__add">OPEN +</button></div></div></article>
</div>
</div>
<div class="ef-lls__controls">
<button type="button" class="ef-lls__ctrl ef-lls__prev" aria-label="Previous card"><svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M19 12H5M11 6l-6 6 6 6" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"/></svg></button>
<button type="button" class="ef-lls__ctrl ef-lls__next" aria-label="Next card"><svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M5 12h14M13 6l6 6-6 6" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"/></svg></button>
<div class="ef-lls__dots"></div>
<p class="ef-lls__sr ef-lls__live" aria-live="polite"></p>
</div>
</section>
</div>CSS
/* Enrichr Library · https://enrichr.dev */
.ef-lls { --ef-lls-bg: #0c1e18; --ef-lls-acc: #e54c1e; --ef-lls-hi: #e6ff55; --ef-lls-cream: #fbf8f1; --ef-lls-tick: 22s; --ef-lls-spin: 25s; --ef-lls-slide: 0.45s; --ef-lls-piece: #e6ff55; position: relative; width: 100%; box-sizing: border-box; overflow: hidden; border-radius: 1rem; background: var(--ef-lls-bg); color: var(--ef-lls-cream); font-family: 'Fira Code', ui-monospace, monospace; container-type: inline-size; }
.ef-lls *, .ef-lls *::before, .ef-lls *::after { box-sizing: border-box; }
.ef-lls.is-cursor, .ef-lls.is-cursor * { cursor: none; }
.ef-lls .ef-lls__sr { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
.ef-lls button { font: inherit; }
.ef-lls :focus-visible { outline: 3px solid var(--ef-lls-hi); outline-offset: 3px; }
/* custom cursor: follows the pointer inside the element */
.ef-lls .ef-lls__cursor { position: absolute; left: 0; top: 0; z-index: 9999; display: none; width: 20px; height: 20px; border-radius: 50%; background: var(--ef-lls-hi); pointer-events: none; transform: translate(-50%, -50%); transition: transform 0.1s ease, width 0.2s, height 0.2s; mix-blend-mode: difference; }
.ef-lls .ef-lls__cursor.is-visible { display: block; }
.ef-lls .ef-lls__cursor.is-big { width: 60px; height: 60px; }
/* ticker */
.ef-lls .ef-lls__ticker { position: relative; z-index: 100; overflow: hidden; white-space: nowrap; background: var(--ef-lls-bg); border-bottom: 1px solid rgba(255, 255, 255, 0.06); }
.ef-lls .ef-lls__tin { display: inline-flex; align-items: center; animation: ef-lls-ticker var(--ef-lls-tick) linear infinite; }
.ef-lls .ef-lls__tw { padding: 12px 28px 12px 0; font-family: 'Bebas Neue', 'Parkinsans', sans-serif; font-size: 1.5rem; line-height: 1.2; letter-spacing: 0.18em; white-space: nowrap; }
.ef-lls .ef-lls__tw.is-a { color: var(--ef-lls-acc); }
.ef-lls .ef-lls__tw.is-h { color: var(--ef-lls-hi); }
.ef-lls .ef-lls__tw.is-c { color: color-mix(in srgb, var(--ef-lls-cream) 25%, transparent); }
.ef-lls .ef-lls__tsep { padding: 0 20px 0 0; color: var(--ef-lls-acc); font-size: 1.2rem; opacity: 0.5; }
@keyframes ef-lls-ticker { 0% { transform: translateX(0); } 100% { transform: translateX(-50%); } }
/* nav */
.ef-lls .ef-lls__nav { position: relative; z-index: 10; display: flex; align-items: center; justify-content: space-between; padding: 18px 40px; }
.ef-lls .ef-lls__logo { display: flex; align-items: center; gap: 10px; color: var(--ef-lls-hi); font-family: 'Parkinsans', 'Space Grotesk', sans-serif; font-size: 1.5rem; font-weight: 700; letter-spacing: -0.02em; text-decoration: none; }
.ef-lls .ef-lls__mark { display: block; width: 1.4em; height: 1.4em; }
.ef-lls .ef-lls__links { display: flex; align-items: center; gap: 30px; margin: 0; padding: 0; list-style: none; }
.ef-lls .ef-lls__li { margin: 0; padding: 0; list-style: none; opacity: 1; }
.ef-lls .ef-lls__link { color: var(--ef-lls-cream); font-family: 'Bebas Neue', 'Parkinsans', sans-serif; font-size: 1.2rem; letter-spacing: 0.15em; text-decoration: none; transition: color 0.2s; }
.ef-lls .ef-lls__link:hover { color: var(--ef-lls-acc); }
.ef-lls .ef-lls__link.ef-lls__cart { padding: 8px 20px; border-radius: 100px; background: var(--ef-lls-acc); color: var(--ef-lls-bg); font-size: 1.1rem; }
.ef-lls .ef-lls__link.ef-lls__cart:hover { background: var(--ef-lls-hi); color: var(--ef-lls-bg); }
/* hero */
.ef-lls .ef-lls__hero { position: relative; display: grid; grid-template-columns: 1fr 1fr; align-items: center; gap: 20px; min-height: 34rem; padding: 20px 40px 50px; overflow: hidden; }
.ef-lls .ef-lls__hero::before { content: ""; position: absolute; inset: 0; pointer-events: none; background-image: linear-gradient(rgba(255, 255, 255, 0.03) 1px, transparent 1px), linear-gradient(90deg, rgba(255, 255, 255, 0.03) 1px, transparent 1px); background-size: 40px 40px; }
.ef-lls .ef-lls__blob { position: absolute; top: -100px; right: -100px; width: 600px; height: 600px; border-radius: 50%; pointer-events: none; background: radial-gradient(circle, color-mix(in srgb, var(--ef-lls-acc) 25%, transparent) 0%, transparent 70%); animation: ef-lls-pulse 5s ease-in-out infinite; }
.ef-lls .ef-lls__blob2 { position: absolute; bottom: -50px; left: 100px; width: 400px; height: 400px; border-radius: 50%; pointer-events: none; background: radial-gradient(circle, rgba(26, 122, 76, 0.3) 0%, transparent 70%); animation: ef-lls-pulse 7s ease-in-out infinite reverse; }
@keyframes ef-lls-pulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.1); } }
.ef-lls .ef-lls__text { position: relative; z-index: 2; }
.ef-lls .ef-lls__eyebrow { display: inline-flex; align-items: center; gap: 10px; margin-bottom: 20px; padding: 6px 16px; border: 1px solid var(--ef-lls-hi); border-radius: 100px; background: color-mix(in srgb, var(--ef-lls-hi) 12%, transparent); }
.ef-lls .ef-lls__et { color: var(--ef-lls-hi); font-size: 0.72rem; letter-spacing: 0.2em; text-transform: uppercase; }
.ef-lls .ef-lls__dot { width: 6px; height: 6px; border-radius: 50%; background: var(--ef-lls-hi); animation: ef-lls-blink 1s step-end infinite; }
@keyframes ef-lls-blink { 0%, 100% { opacity: 1; } 50% { opacity: 0; } }
.ef-lls .ef-lls__h1 { margin: 0; padding: 0; opacity: 1; color: var(--ef-lls-cream); font-family: 'Bebas Neue', 'Parkinsans', sans-serif; font-size: clamp(4.5rem, 9cqw, 9rem); font-weight: 400; line-height: 0.88; letter-spacing: -0.01em; text-transform: none; }
.ef-lls .ef-lls__l1 { color: var(--ef-lls-acc); }
.ef-lls .ef-lls__l3 { color: var(--ef-lls-hi); }
.ef-lls .ef-lls__subp { max-width: 360px; margin: 16px 0 22px; padding: 0; opacity: 1; color: color-mix(in srgb, var(--ef-lls-cream) 55%, transparent); font-size: 0.82rem; line-height: 1.7; }
.ef-lls .ef-lls__pills { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 26px; }
.ef-lls .ef-lls__pill { padding: 5px 12px; border: 1px solid rgba(251, 248, 241, 0.15); border-radius: 100px; background: transparent; color: rgba(251, 248, 241, 0.5); font-size: 0.62rem; letter-spacing: 0.08em; text-transform: uppercase; cursor: pointer; user-select: none; transition: border-color 0.2s, color 0.2s, background 0.2s; }
.ef-lls .ef-lls__pill:hover { border-color: var(--ef-lls-acc); color: var(--ef-lls-acc); }
.ef-lls .ef-lls__pill.is-active { border-color: var(--ef-lls-acc); background: color-mix(in srgb, var(--ef-lls-acc) 12%, transparent); color: var(--ef-lls-acc); }
.ef-lls .ef-lls__btns { display: flex; flex-wrap: wrap; gap: 14px; margin-bottom: 28px; }
.ef-lls .ef-lls__bp, .ef-lls .ef-lls__bs { display: inline-block; padding: 14px 32px; border-radius: 100px; font-family: 'Bebas Neue', 'Parkinsans', sans-serif; font-size: 1.25rem; line-height: 1.2; letter-spacing: 0.15em; text-decoration: none; cursor: pointer; }
.ef-lls .ef-lls__bp { border: none; background: var(--ef-lls-acc); color: var(--ef-lls-bg); transition: transform 0.2s, box-shadow 0.2s; }
.ef-lls .ef-lls__bp:hover { transform: translateY(-3px) scale(1.03); box-shadow: 0 0 40px color-mix(in srgb, var(--ef-lls-acc) 50%, transparent); }
.ef-lls .ef-lls__bs { border: 1.5px solid rgba(251, 248, 240, 0.25); background: transparent; color: var(--ef-lls-cream); transition: border-color 0.2s, color 0.2s; }
.ef-lls .ef-lls__bs:hover { border-color: var(--ef-lls-hi); color: var(--ef-lls-hi); }
.ef-lls .ef-lls__chips { display: flex; flex-wrap: wrap; gap: 12px; }
.ef-lls .ef-lls__chip { padding: 8px 14px; border: 1px solid rgba(255, 255, 255, 0.1); border-radius: 10px; background: rgba(255, 255, 255, 0.05); }
.ef-lls .ef-lls__cv { color: var(--ef-lls-hi); font-family: 'Bebas Neue', 'Parkinsans', sans-serif; font-size: 1.1rem; line-height: 1; letter-spacing: 0.05em; }
.ef-lls .ef-lls__cl { color: rgba(251, 248, 241, 0.45); font-size: 0.6rem; line-height: 1.3; letter-spacing: 0.12em; text-transform: uppercase; }
/* the spinning ring with the Enrichr piece */
.ef-lls .ef-lls__imgwrap { position: relative; z-index: 2; display: flex; align-items: center; justify-content: center; height: 100%; }
.ef-lls .ef-lls__ring { position: relative; display: flex; align-items: center; justify-content: center; width: 420px; height: 420px; border: 1px dashed rgba(255, 255, 255, 0.1); border-radius: 50%; background: radial-gradient(circle at 35% 35%, color-mix(in srgb, var(--ef-lls-hi) 15%, transparent), color-mix(in srgb, var(--ef-lls-acc) 10%, transparent)); animation: ef-lls-spin var(--ef-lls-spin) linear infinite; }
@keyframes ef-lls-spin { to { transform: rotate(360deg); } }
/* the pen gives its image both a reverse spin and a float on transform; the float wins, so the image turns with the ring */
.ef-lls .ef-lls__prod { position: relative; z-index: 2; display: block; padding: 0; border: none; background: none; color: var(--ef-lls-piece); cursor: pointer; filter: drop-shadow(0 20px 40px color-mix(in srgb, var(--ef-lls-acc) 40%, transparent)); transition: opacity 0.25s ease; animation: ef-lls-float 4s ease-in-out infinite; }
.ef-lls .ef-lls__prod:focus-visible { outline: none; }
.ef-lls .ef-lls__prod:focus-visible .ef-lls__piece { outline: 3px solid var(--ef-lls-hi); outline-offset: 6px; border-radius: 1rem; }
.ef-lls .ef-lls__piece { display: block; width: 330px; height: 330px; }
@keyframes ef-lls-float { 0%, 100% { transform: translateY(0) rotate(360deg); } 50% { transform: translateY(-15px) rotate(360deg); } }
.ef-lls .ef-lls__badge { position: absolute; top: 28px; right: 20px; z-index: 6; padding: 8px 16px; border-radius: 100px; background: var(--ef-lls-hi); color: var(--ef-lls-bg); font-family: 'Parkinsans', 'Space Grotesk', sans-serif; font-size: 0.85rem; font-weight: 800; font-style: italic; transform: rotate(8deg); box-shadow: 3px 3px 0 rgba(0, 0, 0, 0.3); animation: ef-lls-wiggle 2s ease-in-out infinite; }
@keyframes ef-lls-wiggle { 0%, 100% { transform: rotate(8deg); } 50% { transform: rotate(12deg); } }
/* the lineup slider */
.ef-lls .ef-lls__lineup { padding: 80px 0 60px; }
.ef-lls .ef-lls__head { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: 20px; margin-bottom: 50px; padding: 0 40px; }
.ef-lls .ef-lls__h2 { margin: 0; padding: 0; opacity: 1; color: var(--ef-lls-cream); font-family: 'Bebas Neue', 'Parkinsans', sans-serif; font-size: clamp(3rem, 7cqw, 6rem); font-weight: 400; line-height: 0.9; letter-spacing: normal; text-transform: none; }
.ef-lls .ef-lls__h2 em { font-style: normal; color: var(--ef-lls-acc); }
.ef-lls .ef-lls__hp { max-width: 280px; margin: 0; padding: 0; opacity: 1; color: rgba(251, 248, 241, 0.45); font-size: 0.78rem; line-height: 1.7; }
.ef-lls .ef-lls__viewport { margin-top: -60px; padding: 100px 0 40px; overflow: hidden; outline: none; touch-action: pan-y; }
.ef-lls .ef-lls__viewport:focus-visible { box-shadow: inset 0 0 0 3px var(--ef-lls-hi); }
.ef-lls .ef-lls__track { display: flex; gap: 24px; padding: 0 40px; user-select: none; will-change: transform; transition: transform var(--ef-lls-slide) cubic-bezier(0.25, 0.46, 0.45, 0.94); }
.ef-lls .ef-lls__track.is-drag { transition: none; cursor: grabbing; }
.ef-lls .ef-lls__card { position: relative; display: flex; flex-direction: column; flex-shrink: 0; align-items: center; justify-content: flex-end; min-width: 280px; height: 400px; padding: 28px; overflow: visible; border-radius: 28px; transition: transform 0.3s ease; }
.ef-lls .ef-lls__card:hover { transform: translateY(-12px) rotate(-1deg); }
.ef-lls .ef-lls__card:nth-child(even):hover { transform: translateY(-12px) rotate(1deg); }
.ef-lls .ef-lls__c1 { --ef-lls-chip: #d7dcd5; background: linear-gradient(160deg, #1f2b26, #16201c); border: 1px solid rgba(215, 220, 213, 0.2); }
.ef-lls .ef-lls__c2 { --ef-lls-chip: #a98ef2; background: linear-gradient(160deg, #241347, #190d33); border: 1px solid rgba(140, 100, 230, 0.3); }
.ef-lls .ef-lls__c3 { --ef-lls-chip: #e6ff55; background: linear-gradient(160deg, #2c3300, #212600); border: 1px solid rgba(230, 255, 85, 0.3); }
.ef-lls .ef-lls__c4 { --ef-lls-chip: #f07a52; background: linear-gradient(160deg, #3a160b, #2a0f07); border: 1px solid rgba(229, 76, 30, 0.3); }
.ef-lls .ef-lls__c5 { --ef-lls-chip: #6fd29c; background: linear-gradient(160deg, #003d28, #002a1c); border: 1px solid rgba(26, 122, 76, 0.5); }
.ef-lls .ef-lls__img { position: absolute; top: -70px; left: 50%; z-index: 2; display: block; height: 260px; aspect-ratio: 3 / 4; object-fit: cover; border-radius: 22px; pointer-events: none; transform: translateX(-50%); filter: drop-shadow(0 15px 30px rgba(0, 0, 0, 0.5)); transition: height 0.4s ease, top 0.4s ease; }
.ef-lls .ef-lls__c4 .ef-lls__img { object-position: 68% 50%; }
.ef-lls .ef-lls__c5 .ef-lls__img { object-position: 30% 50%; }
.ef-lls .ef-lls__card:hover .ef-lls__img { top: -90px; height: 295px; }
.ef-lls .ef-lls__tag { position: absolute; top: 18px; right: 18px; z-index: 3; padding: 4px 10px; border: 1px solid currentColor; border-radius: 100px; color: var(--ef-lls-chip); font-size: 0.62rem; letter-spacing: 0.15em; text-transform: uppercase; }
.ef-lls .ef-lls__bottom { position: relative; z-index: 2; width: 100%; }
.ef-lls .ef-lls__name { margin-bottom: 6px; color: var(--ef-lls-cream); font-family: 'Bebas Neue', 'Parkinsans', sans-serif; font-size: 2.2rem; line-height: 1; letter-spacing: 0.08em; }
.ef-lls .ef-lls__desc { margin: 0 0 16px; padding: 0; opacity: 1; color: rgba(251, 248, 241, 0.5); font-size: 0.67rem; line-height: 1.5; }
.ef-lls .ef-lls__foot { display: flex; align-items: center; justify-content: space-between; }
.ef-lls .ef-lls__price { color: var(--ef-lls-hi); font-family: 'Bebas Neue', 'Parkinsans', sans-serif; font-size: 1.6rem; }
.ef-lls .ef-lls__add { padding: 10px 20px; border: none; border-radius: 100px; background: var(--ef-lls-acc); color: var(--ef-lls-bg); font-family: 'Bebas Neue', 'Parkinsans', sans-serif; font-size: 1rem; letter-spacing: 0.1em; cursor: pointer; transition: transform 0.2s, background 0.2s; }
.ef-lls .ef-lls__add:hover { transform: scale(1.08); background: color-mix(in srgb, var(--ef-lls-acc) 80%, #fff); }
.ef-lls .ef-lls__controls { display: flex; align-items: center; gap: 20px; margin-top: 10px; padding: 0 40px; }
.ef-lls .ef-lls__ctrl { display: flex; align-items: center; justify-content: center; width: 52px; height: 52px; padding: 0; border: 1.5px solid rgba(251, 248, 241, 0.15); border-radius: 50%; background: transparent; color: var(--ef-lls-cream); cursor: pointer; transition: border-color 0.2s, background 0.2s, color 0.2s; }
.ef-lls .ef-lls__ctrl svg { width: 1.2rem; height: 1.2rem; }
.ef-lls .ef-lls__ctrl:hover { border-color: var(--ef-lls-acc); background: var(--ef-lls-acc); color: var(--ef-lls-bg); }
.ef-lls .ef-lls__dots { display: flex; gap: 8px; }
.ef-lls .ef-lls__sdot { position: relative; width: 8px; height: 8px; padding: 0; border: none; border-radius: 100px; background: rgba(251, 248, 241, 0.2); cursor: pointer; transition: background 0.3s, width 0.3s; }
.ef-lls .ef-lls__sdot::before { content: ""; position: absolute; inset: -10px -4px; }
.ef-lls .ef-lls__sdot.is-active { width: 28px; background: var(--ef-lls-acc); }
/* paused off screen, and still for reduced motion */
.ef-lls.is-paused .ef-lls__tin, .ef-lls.is-paused .ef-lls__blob, .ef-lls.is-paused .ef-lls__blob2, .ef-lls.is-paused .ef-lls__dot,
.ef-lls.is-paused .ef-lls__ring, .ef-lls.is-paused .ef-lls__prod, .ef-lls.is-paused .ef-lls__badge { animation-play-state: paused; }
@media (prefers-reduced-motion: reduce) {
.ef-lls .ef-lls__tin, .ef-lls .ef-lls__blob, .ef-lls .ef-lls__blob2, .ef-lls .ef-lls__dot, .ef-lls .ef-lls__ring, .ef-lls .ef-lls__prod, .ef-lls .ef-lls__badge { animation: none; }
.ef-lls .ef-lls__track, .ef-lls .ef-lls__card, .ef-lls .ef-lls__img { transition: none; }
}
@container (max-width: 800px) {
.ef-lls .ef-lls__nav { padding: 14px 20px; }
.ef-lls .ef-lls__links { gap: 18px; }
.ef-lls .ef-lls__li:not(:last-child) { display: none; }
.ef-lls .ef-lls__hero { grid-template-columns: 1fr; min-height: auto; padding: 20px 20px 40px; text-align: center; }
.ef-lls .ef-lls__imgwrap { order: -1; margin-bottom: 10px; }
.ef-lls .ef-lls__ring { width: 260px; height: 260px; }
.ef-lls .ef-lls__piece { width: 200px; height: 200px; }
.ef-lls .ef-lls__badge { top: 10px; right: calc(50% - 160px); }
.ef-lls .ef-lls__eyebrow { margin: 0 auto 16px; }
.ef-lls .ef-lls__subp { margin-left: auto; margin-right: auto; }
.ef-lls .ef-lls__pills, .ef-lls .ef-lls__btns, .ef-lls .ef-lls__chips { justify-content: center; }
.ef-lls .ef-lls__lineup { padding: 60px 0 40px; }
.ef-lls .ef-lls__head, .ef-lls .ef-lls__track, .ef-lls .ef-lls__controls { padding-left: 20px; padding-right: 20px; }
}
@container (max-width: 480px) {
.ef-lls .ef-lls__tw { font-size: 1.1rem; }
.ef-lls .ef-lls__nav { padding: 12px 16px; }
.ef-lls .ef-lls__logo { font-size: 1.2rem; }
.ef-lls .ef-lls__hero { padding: 16px 16px 36px; }
.ef-lls .ef-lls__h1 { font-size: clamp(3.5rem, 14cqw, 5rem); }
.ef-lls .ef-lls__ring { width: 220px; height: 220px; }
.ef-lls .ef-lls__piece { width: 170px; height: 170px; }
.ef-lls .ef-lls__badge { display: none; }
.ef-lls .ef-lls__subp { font-size: 0.78rem; }
.ef-lls .ef-lls__card { min-width: 240px; height: 360px; }
.ef-lls .ef-lls__img { top: -60px; height: 220px; }
.ef-lls .ef-lls__card:hover .ef-lls__img { top: -75px; height: 250px; }
}JavaScript
// Enrichr Library · https://enrichr.dev
// Loud landing slider: a full landing hero with a marquee ticker on top, a big three-colour headline, a spinning ring that
// carries a floating Enrichr piece, and a "lineup" of cards in a draggable slider below. The ticker scrolls four copies
// of its words by half its width every 22 s, so it loops with no seam. The ring turns once every 25 s and the piece rides
// it while bobbing 15px every 4 s; the badge wiggles between 8 and 12 degrees; two glows breathe; the status dot blinks.
// The colourway pills (or a click on the piece) fade it out for 220 ms and back in a new colour. The slider moves one card
// width plus the 24px gap at a time on a 0.45 s ease-out curve, from the buttons, dots, arrow keys, or a drag or swipe
// past 60px. Inside the element a round cursor follows the pointer in difference blend and grows over anything clickable.
// Reduced motion stops every loop and the slide easing; CSS loops pause off screen and in hidden tabs.
// mount(root, { line1, line2, line3, ticker (comma separated), cursor ("on" | "off"), tickerSeconds, spinSeconds,
// slideSeconds, accent, highlight, background, text })
function mount(root, o) {
var reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
var box = root.querySelector(".ef-lls");
var cursor = root.querySelector(".ef-lls__cursor");
var tin = root.querySelector(".ef-lls__tin");
var tsr = root.querySelector(".ef-lls__tsr");
var prod = root.querySelector(".ef-lls__prod");
var pills = [].slice.call(root.querySelectorAll(".ef-lls__pill"));
var lineup = root.querySelector(".ef-lls__lineup");
var viewport = root.querySelector(".ef-lls__viewport");
var track = root.querySelector(".ef-lls__track");
var cards = [].slice.call(track.querySelectorAll(".ef-lls__card"));
var dotsWrap = root.querySelector(".ef-lls__dots");
var live = root.querySelector(".ef-lls__live");
var COLS = ["#e6ff55", "#fbf8f1", "#e54c1e", "#7a46d8", "#1a7a4c"];
var BIG = "a, button, .ef-lls__card, .ef-lls__pill";
var opts = {}, offs = [], dots = [], total = cards.length, current = 0, heroIdx = 0, swapTimer = 0;
var isDragging = false, dragStartX = 0, dragCurrentX = 0, baseOffset = 0, dragId = null;
var io = null, ro = null, visible = true, tickSig = "";
function on(el, ev, fn, opt) { el.addEventListener(ev, fn, opt); offs.push(function () { el.removeEventListener(ev, fn, opt); }); }
function num(v, d) { v = Number(v); return isFinite(v) ? v : d; }
// ticker: copies of the phrase, colours rotating accent / highlight / faint, swapping the first two each copy. The pen
// uses four copies and slides half of them (two) every 22 s. Short phrases get more copies (always a multiple of four,
// so the colours line up at the seam) and a longer loop, so the row stays full and moves at the same speed.
var copies = 4;
function buildTicker() {
var words = String(opts.ticker).split(",").map(function (s) { return s.trim(); }).filter(Boolean);
if (!words.length) words = ["ENRICHR"];
tin.textContent = "";
for (var c = 0; c < copies; c++) {
words.forEach(function (w, j) {
var k = j % 3, s = document.createElement("span");
s.className = "ef-lls__tw " + (k === 2 ? "is-c" : (k === 0) === (c % 2 === 0) ? "is-a" : "is-h");
s.textContent = w.toUpperCase();
tin.appendChild(s);
});
var sep = document.createElement("span"); sep.className = "ef-lls__tsep"; sep.textContent = "✦"; tin.appendChild(sep);
}
tsr.textContent = words.join(" ");
tickerTiming();
}
function fitTicker() {
var one = tin.scrollWidth / copies, need = one > 0 ? Math.max(4, 4 * Math.ceil(2 * box.clientWidth / one / 4)) : 4;
if (need !== copies) { copies = need; buildTicker(); }
}
function tickerTiming() { box.style.setProperty("--ef-lls-tick", (Math.max(2, num(opts.tickerSeconds, 22)) * copies / 4) + "s"); }
// custom cursor, inside the element only
on(box, "pointermove", function (e) {
if (opts.cursor !== "on" || e.pointerType !== "mouse") return;
var r = box.getBoundingClientRect();
cursor.style.left = (e.clientX - r.left) + "px";
cursor.style.top = (e.clientY - r.top) + "px";
if (!cursor.classList.contains("is-visible")) { cursor.classList.add("is-visible"); box.classList.add("is-cursor"); }
});
on(box, "pointerover", function (e) { cursor.classList.toggle("is-big", !!(e.target.closest && e.target.closest(BIG))); });
on(box, "pointerleave", function () { cursor.classList.remove("is-visible", "is-big"); box.classList.remove("is-cursor"); });
// hero colourways: fade out, swap after 220 ms, fade back in
function switchFlavor(idx) {
heroIdx = idx;
prod.style.opacity = "0";
clearTimeout(swapTimer);
swapTimer = setTimeout(function () { box.style.setProperty("--ef-lls-piece", COLS[heroIdx]); prod.style.opacity = "1"; }, reduce ? 0 : 220);
pills.forEach(function (p, i) { p.classList.toggle("is-active", i === heroIdx); p.setAttribute("aria-pressed", i === heroIdx ? "true" : "false"); });
}
on(prod, "click", function () { switchFlavor((heroIdx + 1) % COLS.length); });
pills.forEach(function (p, i) { on(p, "click", function () { switchFlavor(i); }); });
// slider
cards.forEach(function (_, i) {
var d = document.createElement("button"); d.type = "button";
d.className = "ef-lls__sdot" + (i === 0 ? " is-active" : "");
d.setAttribute("aria-label", "Show card " + (i + 1));
d.addEventListener("click", function () { goTo(i, true); });
dotsWrap.appendChild(d); dots.push(d);
});
function cardWidth() { return cards[0].offsetWidth + 24; }
function applyTranslate(px, animate) {
track.style.transition = animate ? "" : "none";
track.style.transform = "translateX(" + px + "px)";
}
function goTo(index, announce) {
current = Math.max(0, Math.min(total - 1, index));
baseOffset = -(current * cardWidth());
applyTranslate(baseOffset, true);
dots.forEach(function (d, i) { d.classList.toggle("is-active", i === current); if (i === current) d.setAttribute("aria-current", "true"); else d.removeAttribute("aria-current"); });
cards.forEach(function (c, i) { c.setAttribute("aria-hidden", i < current ? "true" : "false"); });
if (announce) live.textContent = "Card " + (current + 1) + " of " + total;
}
function onDragStart(e) {
if (e.pointerType === "mouse" && e.button !== 0) return;
isDragging = true; dragId = e.pointerId;
dragStartX = dragCurrentX = e.clientX;
track.classList.add("is-drag");
if (e.pointerType === "mouse") e.preventDefault();
}
function onDragMove(e) {
if (!isDragging || e.pointerId !== dragId) return;
dragCurrentX = e.clientX;
applyTranslate(baseOffset + (dragCurrentX - dragStartX), false);
}
function onDragEnd(e) {
if (!isDragging || (e && e.pointerId !== dragId)) return;
isDragging = false;
track.classList.remove("is-drag");
var diff = dragCurrentX - dragStartX;
if (diff < -60) goTo(current + 1, true); else if (diff > 60) goTo(current - 1, true); else goTo(current);
}
on(track, "pointerdown", onDragStart);
on(window, "pointermove", onDragMove);
on(window, "pointerup", onDragEnd);
on(window, "pointercancel", onDragEnd);
on(track, "click", function (e) { if (Math.abs(dragCurrentX - dragStartX) > 5) { e.preventDefault(); e.stopPropagation(); } }, true);
on(root.querySelector(".ef-lls__next"), "click", function () { goTo(current + 1, true); });
on(root.querySelector(".ef-lls__prev"), "click", function () { goTo(current - 1, true); });
on(viewport, "keydown", function (e) {
if (e.key === "ArrowRight") { e.preventDefault(); goTo(current + 1, true); }
else if (e.key === "ArrowLeft") { e.preventDefault(); goTo(current - 1, true); }
});
// "See the lineup" and the Lineup link jump to the slider, like the pen's #shakes anchors; other # links stay put
on(box, "click", function (e) {
var t = e.target.closest && e.target.closest(".ef-lls__tolineup, a[href='#']");
if (!t) return;
e.preventDefault();
if (t.classList.contains("ef-lls__tolineup")) lineup.scrollIntoView({ behavior: reduce ? "auto" : "smooth", block: "start" });
});
function pause() { box.classList.toggle("is-paused", !visible || document.hidden); }
on(document, "visibilitychange", pause);
function update(partial) {
opts = Object.assign({
line1: "MAKE", line2: "IT", line3: "MOVE", ticker: "MAKE, SOMETHING, LOUD, THAT, MOVES, ON THE, WEB", cursor: "on",
tickerSeconds: 22, spinSeconds: 25, slideSeconds: 0.45,
accent: "#e54c1e", highlight: "#e6ff55", background: "#0c1e18", text: "#fbf8f1"
}, opts, partial || {});
root.querySelector(".ef-lls__l1").textContent = opts.line1;
root.querySelector(".ef-lls__l2").textContent = opts.line2;
root.querySelector(".ef-lls__l3").textContent = opts.line3;
if (opts.ticker !== tickSig) { tickSig = opts.ticker; copies = 4; buildTicker(); fitTicker(); }
if (opts.cursor !== "on") { cursor.classList.remove("is-visible", "is-big"); box.classList.remove("is-cursor"); }
tickerTiming();
box.style.setProperty("--ef-lls-spin", Math.max(2, num(opts.spinSeconds, 25)) + "s");
box.style.setProperty("--ef-lls-slide", Math.max(0, num(opts.slideSeconds, 0.45)) + "s");
box.style.setProperty("--ef-lls-acc", opts.accent);
box.style.setProperty("--ef-lls-hi", opts.highlight);
box.style.setProperty("--ef-lls-bg", opts.background);
box.style.setProperty("--ef-lls-cream", opts.text);
}
update(o);
goTo(0);
if ("IntersectionObserver" in window) { io = new IntersectionObserver(function (en) { visible = en[0].isIntersecting; pause(); }); io.observe(box); }
if ("ResizeObserver" in window) { ro = new ResizeObserver(function () { fitTicker(); if (!isDragging) goTo(current); }); ro.observe(box); }
return {
update: update,
destroy: function () {
clearTimeout(swapTimer);
if (io) io.disconnect(); if (ro) ro.disconnect();
offs.forEach(function (f) { f(); }); offs = [];
}
};
}
// Usage
var fx = mount(document.querySelector("#my-root"), {"line1":"MAKE","line2":"IT","line3":"MOVE","ticker":"MAKE, SOMETHING, LOUD, THAT, MOVES, ON THE, WEB","cursor":"on","tickerSeconds":22,"spinSeconds":25,"slideSeconds":0.45,"accent":"#e54c1e","highlight":"#e6ff55","background":"#0c1e18","text":"#fbf8f1"});How it works
Most of the motion is CSS keyframes, as in the original pen. The ticker holds four copies of the phrase and slides left by half its own width every 22 seconds, so the second half lands exactly where the first began and the loop has no seam; short phrases get more copies and a longer loop so the row stays full at the same speed. The ring turns once every 25 seconds and the Enrichr piece inside it bobs 15px every 4 seconds; the pen gave its image a counter-spin and the float on the same transform property, the float wins, so the piece turns with the ring, and that is kept. The badge wiggles between 8 and 12 degrees, two glows breathe and the status dot blinks. The colourway pills fade the piece out, swap its colour after 220 milliseconds and fade it back. The slider moves one card width plus the 24px gap per step on a 0.45 second ease-out; a drag or swipe past 60px changes card, a shorter one springs back. Inside the element a round cursor follows the pointer in difference blend and grows to 60px over anything clickable.
Accessibility and performance
With prefers-reduced-motion every loop stops (ticker, ring, float, wiggle, glows, blink) and the slider moves without easing. The ticker is hidden from screen readers and its phrase is given once as plain text. The slider is a focusable region that the Left and Right arrow keys move, its Previous, Next and dot controls are real buttons with labels, and changes are announced politely. The colourway pills are toggle buttons. The custom cursor only appears for a mouse, and it can be turned off so the normal pointer shows.
The loops are CSS animations on transform and opacity, and they are paused while the element is off screen or the tab is hidden. The only JavaScript work is on input: pointer moves for the cursor and drag, and clicks. Card photos load lazily.
| Section | Hero |
|---|---|
| Techniques | Carousel, Pointer interaction, Text animation |
| Stack | Vanilla JS, CSS |
| Works with | React, Next.js, Vanilla |
| Dependencies | None |
| Status | tested |
| On CodePen | Open the live pen on our CodePen account; fork it to experiment. |
| Inspired by | Landing page with CSS slider, marquee ticker and custom cursor by Enrichr on CodePen. Original code, written from scratch. |
Find this technique on a real site
Want to see this technique in the wild? Check any site with the free tools, then build your own version from this page.
Check which animation library a site uses · Extract a site's design tokens · Browse site breakdowns
Licence and provenance
Free to use in personal and client projects during the preview. Final licence terms will be published here before any paid tier starts; ask before reselling the code as a collection.
This is original code. The technique is a common one that we studied by measuring award-winning sites with Enrichr; no third-party code, shaders, fonts or images are used.
More elements
FAQ
Where are the photos from?
Photos: Alvan Nee and fatty corgi on Unsplash, used under the Unsplash License. Each card's img can point at your own product shots; the frame crops them to 3 by 4.
Why does the piece turn with the ring instead of staying upright?
That is how the original behaves: its image had a counter-spin and a float on the same transform, and the float overrides the spin. To keep the piece upright, add the counter-rotation to the float keyframes.
Can I use it without the custom cursor?
Yes. Set Custom cursor to Off and the normal pointer is used everywhere.
How do I change the ticker?
Type the words separated by commas. Every third word is faint, and the other two swap between the accent and highlight colours on each repeat.