18 / 21
All 21 in 21 Tailwind CSS Pricing Sections
- 01Tailwind Classic Three Tier Pricing Section
- 02Tailwind Highlighted Popular Pricing Tier
- 03Tailwind Feature Comparison Pricing Table
- 04Tailwind Monthly Yearly Billing Toggle Pricing
- 05Tailwind Usage Slider Pricing Estimator
- 06Tailwind Single Premium Pricing Card
- 07Tailwind Bento Pricing Grid
- 08Tailwind Vertical Tier Timeline Pricing
- 09Tailwind Brutalist Print Pricing Zine
- 10Tailwind Glass Aurora Pricing Cards
- 11Tailwind Per-Seat Pricing Calculator
- 12Tailwind Animated Conic Border Pricing Cards
- 13Tailwind Editorial Two Column Pricing List
- 14Tailwind 3D Tilt Perspective Pricing Cards
- 15Tailwind Synthwave Neon Pricing Section
- 16Tailwind Pay What You Want Pricing Card
- 17Tailwind Currency Switcher Pricing Section
- 18Tailwind Social Proof Marquee Pricing
- 19Tailwind Feature Accordion Pricing Tiers
- 20Tailwind Cursor Spotlight Pricing Cards
- 21Tailwind Annual Savings Stripe Pricing
Tailwind Social Proof Marquee Pricing
A seamless logo marquee above the tiers — social proof first, decision second — on slate with a lime accent. The port's interesting part is not the animation but the containment: the marquee's w-max track will grow the whole section to 2000px unless the wrapper is explicitly bounded, and in utilities that bound is three classes you can actually see.
Complete Tailwind CSS landing page templates in HTML, React, Next.js and Vue · from $49
Cruip · Affiliate link
The code
<section class="tps-18 relative w-full min-h-svh overflow-hidden [contain:layout_paint] bg-tps18-bg font-tps18-sans text-tps18-body px-[clamp(20px,4vw,40px)] pt-[clamp(20px,4vw,36px)] pb-[clamp(28px,5vw,48px)]" aria-label="Pricing">
<div class="relative overflow-hidden [contain:layout_paint] min-w-0 max-w-screen -mx-[clamp(20px,4vw,40px)] mb-[clamp(20px,4vw,32px)] py-3.5 bg-tps18-bg/60 border-y border-tps18-lime/18 mask-x-from-88% mask-x-to-100%" aria-label="Customers using this product">
<div class="flex w-max gap-14 whitespace-nowrap will-change-transform animate-tps18-roll motion-reduce:animate-none">
<span class="shrink-0 font-tps18-mono text-sm font-bold tracking-[0.18em] text-tps18-logo">◇ NORTHWIND</span>
<span class="shrink-0 font-tps18-mono text-sm font-bold tracking-[0.18em] text-tps18-logo">▲ APEX.IO</span>
<span class="shrink-0 font-tps18-mono text-sm font-bold tracking-[0.18em] text-tps18-logo">★ PULSE</span>
<span class="shrink-0 font-tps18-mono text-sm font-bold tracking-[0.18em] text-tps18-logo">⬣ HEXLAB</span>
<span class="shrink-0 font-tps18-mono text-sm font-bold tracking-[0.18em] text-tps18-logo">◯ ORBITA</span>
<span class="shrink-0 font-tps18-mono text-sm font-bold tracking-[0.18em] text-tps18-logo">◤ FLINT</span>
<span class="shrink-0 font-tps18-mono text-sm font-bold tracking-[0.18em] text-tps18-logo">⬢ MERIDIAN</span>
<span class="shrink-0 font-tps18-mono text-sm font-bold tracking-[0.18em] text-tps18-logo" aria-hidden="true">◇ NORTHWIND</span>
<span class="shrink-0 font-tps18-mono text-sm font-bold tracking-[0.18em] text-tps18-logo" aria-hidden="true">▲ APEX.IO</span>
<span class="shrink-0 font-tps18-mono text-sm font-bold tracking-[0.18em] text-tps18-logo" aria-hidden="true">★ PULSE</span>
<span class="shrink-0 font-tps18-mono text-sm font-bold tracking-[0.18em] text-tps18-logo" aria-hidden="true">⬣ HEXLAB</span>
<span class="shrink-0 font-tps18-mono text-sm font-bold tracking-[0.18em] text-tps18-logo" aria-hidden="true">◯ ORBITA</span>
<span class="shrink-0 font-tps18-mono text-sm font-bold tracking-[0.18em] text-tps18-logo" aria-hidden="true">◤ FLINT</span>
<span class="shrink-0 font-tps18-mono text-sm font-bold tracking-[0.18em] text-tps18-logo" aria-hidden="true">⬢ MERIDIAN</span>
</div>
</div>
<header class="text-center mb-[clamp(20px,4vw,32px)]">
<span class="inline-block mb-2.5 text-xs font-semibold tracking-[0.08em] text-tps18-lime" aria-label="Rated 4.9 out of 5">★★★★★ 4.9 from 12,400 teams</span>
<h2 class="m-0 text-[clamp(24px,3.6vw,36px)] font-bold tracking-[-0.02em] text-white">Pricing teams actually pay for.</h2>
</header>
<div class="grid grid-cols-1 md:grid-cols-[repeat(3,minmax(0,1fr))] items-center gap-3.5 max-w-[360px] md:max-w-[940px] mx-auto">
<article class="relative flex flex-col px-5 pt-[22px] pb-[18px] rounded-[12px] bg-tps18-card border border-tps18-lime/12">
<h3 class="m-0 mb-3 text-sm font-bold uppercase tracking-[0.1em] text-tps18-head">Free</h3>
<div class="flex items-baseline gap-1 mb-[18px] text-[38px] font-extrabold tracking-[-0.03em] text-white"><em class="not-italic text-[0.5em] pr-0.5 opacity-70">$</em>0<span class="ml-1.5 text-[0.32em] font-medium opacity-65">forever</span></div>
<ul class="list-none p-0 m-0 mb-[18px] flex-1 text-[13px]/[1.95] text-tps18-body">
<li class="before:content-['\2713'] before:inline-block before:w-5 before:font-bold before:text-tps18-lime">1 project</li>
<li class="before:content-['\2713'] before:inline-block before:w-5 before:font-bold before:text-tps18-lime">5 GB storage</li>
<li class="before:content-['\2713'] before:inline-block before:w-5 before:font-bold before:text-tps18-lime">Community</li>
</ul>
<a href="#free" class="block text-center px-4 py-2.5 rounded-[8px] bg-tps18-lime/10 text-tps18-lime border border-tps18-lime/28 text-[13px] font-semibold no-underline transition-[background-color,color] duration-[180ms] hover:bg-tps18-lime hover:text-tps18-bg focus-visible:outline-2 focus-visible:outline-tps18-lime focus-visible:outline-offset-[3px] motion-reduce:transition-none">Start free</a>
</article>
<article class="relative flex flex-col px-5 pt-[22px] pb-[18px] rounded-[12px] bg-linear-180 from-tps18-card to-tps18-moss border border-tps18-lime shadow-[inset_0_0_0_1px_--theme(--color-tps18-lime),0_14px_36px_--alpha(--color-tps18-lime/18)]">
<span class="absolute -top-[11px] left-1/2 -translate-x-1/2 whitespace-nowrap px-2.75 py-1 rounded-full bg-tps18-lime text-tps18-bg text-[10.5px] font-bold uppercase tracking-[0.08em]">Most teams pick this</span>
<h3 class="m-0 mb-3 text-sm font-bold uppercase tracking-[0.1em] text-tps18-lime-pale">Team</h3>
<div class="flex items-baseline gap-1 mb-[18px] text-[38px] font-extrabold tracking-[-0.03em] text-white"><em class="not-italic text-[0.5em] pr-0.5 opacity-70">$</em>32<span class="ml-1.5 text-[0.32em] font-medium opacity-65">/mo</span></div>
<ul class="list-none p-0 m-0 mb-[18px] flex-1 text-[13px]/[1.95] text-tps18-body">
<li class="before:content-['\2713'] before:inline-block before:w-5 before:font-bold before:text-tps18-lime">Unlimited projects</li>
<li class="before:content-['\2713'] before:inline-block before:w-5 before:font-bold before:text-tps18-lime">Priority support</li>
<li class="before:content-['\2713'] before:inline-block before:w-5 before:font-bold before:text-tps18-lime">SSO + roles</li>
</ul>
<a href="#team" class="block text-center px-4 py-2.5 rounded-[8px] bg-tps18-lime text-tps18-bg border border-tps18-lime text-[13px] font-semibold no-underline transition-[background-color] duration-[180ms] hover:bg-tps18-lime-pale focus-visible:outline-2 focus-visible:outline-tps18-lime focus-visible:outline-offset-[3px] motion-reduce:transition-none">Choose Team</a>
</article>
<article class="relative flex flex-col px-5 pt-[22px] pb-[18px] rounded-[12px] bg-tps18-card border border-tps18-lime/12">
<h3 class="m-0 mb-3 text-sm font-bold uppercase tracking-[0.1em] text-tps18-head">Enterprise</h3>
<div class="flex items-baseline gap-1 mb-[18px] text-[38px] font-extrabold tracking-[-0.03em] text-white"><em class="not-italic text-[0.5em] pr-0.5 opacity-70">$</em>0<span class="ml-1.5 text-[0.32em] font-medium opacity-65">custom</span></div>
<ul class="list-none p-0 m-0 mb-[18px] flex-1 text-[13px]/[1.95] text-tps18-body">
<li class="before:content-['\2713'] before:inline-block before:w-5 before:font-bold before:text-tps18-lime">Everything in Team</li>
<li class="before:content-['\2713'] before:inline-block before:w-5 before:font-bold before:text-tps18-lime">Dedicated CSM</li>
<li class="before:content-['\2713'] before:inline-block before:w-5 before:font-bold before:text-tps18-lime">SLA 99.99%</li>
</ul>
<a href="#ent" class="block text-center px-4 py-2.5 rounded-[8px] bg-tps18-lime/10 text-tps18-lime border border-tps18-lime/28 text-[13px] font-semibold no-underline transition-[background-color,color] duration-[180ms] hover:bg-tps18-lime hover:text-tps18-bg focus-visible:outline-2 focus-visible:outline-tps18-lime focus-visible:outline-offset-[3px] motion-reduce:transition-none">Talk to sales</a>
</article>
</div>
</section><section class="tps-18 relative w-full min-h-svh overflow-hidden [contain:layout_paint] bg-tps18-bg font-tps18-sans text-tps18-body px-[clamp(20px,4vw,40px)] pt-[clamp(20px,4vw,36px)] pb-[clamp(28px,5vw,48px)]" aria-label="Pricing">
<div class="relative overflow-hidden [contain:layout_paint] min-w-0 max-w-screen -mx-[clamp(20px,4vw,40px)] mb-[clamp(20px,4vw,32px)] py-3.5 bg-tps18-bg/60 border-y border-tps18-lime/18 mask-x-from-88% mask-x-to-100%" aria-label="Customers using this product">
<div class="flex w-max gap-14 whitespace-nowrap will-change-transform animate-tps18-roll motion-reduce:animate-none">
<span class="shrink-0 font-tps18-mono text-sm font-bold tracking-[0.18em] text-tps18-logo">◇ NORTHWIND</span>
<span class="shrink-0 font-tps18-mono text-sm font-bold tracking-[0.18em] text-tps18-logo">▲ APEX.IO</span>
<span class="shrink-0 font-tps18-mono text-sm font-bold tracking-[0.18em] text-tps18-logo">★ PULSE</span>
<span class="shrink-0 font-tps18-mono text-sm font-bold tracking-[0.18em] text-tps18-logo">⬣ HEXLAB</span>
<span class="shrink-0 font-tps18-mono text-sm font-bold tracking-[0.18em] text-tps18-logo">◯ ORBITA</span>
<span class="shrink-0 font-tps18-mono text-sm font-bold tracking-[0.18em] text-tps18-logo">◤ FLINT</span>
<span class="shrink-0 font-tps18-mono text-sm font-bold tracking-[0.18em] text-tps18-logo">⬢ MERIDIAN</span>
<span class="shrink-0 font-tps18-mono text-sm font-bold tracking-[0.18em] text-tps18-logo" aria-hidden="true">◇ NORTHWIND</span>
<span class="shrink-0 font-tps18-mono text-sm font-bold tracking-[0.18em] text-tps18-logo" aria-hidden="true">▲ APEX.IO</span>
<span class="shrink-0 font-tps18-mono text-sm font-bold tracking-[0.18em] text-tps18-logo" aria-hidden="true">★ PULSE</span>
<span class="shrink-0 font-tps18-mono text-sm font-bold tracking-[0.18em] text-tps18-logo" aria-hidden="true">⬣ HEXLAB</span>
<span class="shrink-0 font-tps18-mono text-sm font-bold tracking-[0.18em] text-tps18-logo" aria-hidden="true">◯ ORBITA</span>
<span class="shrink-0 font-tps18-mono text-sm font-bold tracking-[0.18em] text-tps18-logo" aria-hidden="true">◤ FLINT</span>
<span class="shrink-0 font-tps18-mono text-sm font-bold tracking-[0.18em] text-tps18-logo" aria-hidden="true">⬢ MERIDIAN</span>
</div>
</div>
<header class="text-center mb-[clamp(20px,4vw,32px)]">
<span class="inline-block mb-2.5 text-xs font-semibold tracking-[0.08em] text-tps18-lime" aria-label="Rated 4.9 out of 5">★★★★★ 4.9 from 12,400 teams</span>
<h2 class="m-0 text-[clamp(24px,3.6vw,36px)] font-bold tracking-[-0.02em] text-white">Pricing teams actually pay for.</h2>
</header>
<div class="grid grid-cols-1 md:grid-cols-[repeat(3,minmax(0,1fr))] items-center gap-3.5 max-w-[360px] md:max-w-[940px] mx-auto">
<article class="relative flex flex-col px-5 pt-[22px] pb-[18px] rounded-[12px] bg-tps18-card border border-tps18-lime/12">
<h3 class="m-0 mb-3 text-sm font-bold uppercase tracking-[0.1em] text-tps18-head">Free</h3>
<div class="flex items-baseline gap-1 mb-[18px] text-[38px] font-extrabold tracking-[-0.03em] text-white"><em class="not-italic text-[0.5em] pr-0.5 opacity-70">$</em>0<span class="ml-1.5 text-[0.32em] font-medium opacity-65">forever</span></div>
<ul class="list-none p-0 m-0 mb-[18px] flex-1 text-[13px]/[1.95] text-tps18-body">
<li class="before:content-['\2713'] before:inline-block before:w-5 before:font-bold before:text-tps18-lime">1 project</li>
<li class="before:content-['\2713'] before:inline-block before:w-5 before:font-bold before:text-tps18-lime">5 GB storage</li>
<li class="before:content-['\2713'] before:inline-block before:w-5 before:font-bold before:text-tps18-lime">Community</li>
</ul>
<a href="#free" class="block text-center px-4 py-2.5 rounded-[8px] bg-tps18-lime/10 text-tps18-lime border border-tps18-lime/28 text-[13px] font-semibold no-underline transition-[background-color,color] duration-[180ms] hover:bg-tps18-lime hover:text-tps18-bg focus-visible:outline-2 focus-visible:outline-tps18-lime focus-visible:outline-offset-[3px] motion-reduce:transition-none">Start free</a>
</article>
<article class="relative flex flex-col px-5 pt-[22px] pb-[18px] rounded-[12px] bg-linear-180 from-tps18-card to-tps18-moss border border-tps18-lime shadow-[inset_0_0_0_1px_--theme(--color-tps18-lime),0_14px_36px_--alpha(--color-tps18-lime/18)]">
<span class="absolute -top-[11px] left-1/2 -translate-x-1/2 whitespace-nowrap px-2.75 py-1 rounded-full bg-tps18-lime text-tps18-bg text-[10.5px] font-bold uppercase tracking-[0.08em]">Most teams pick this</span>
<h3 class="m-0 mb-3 text-sm font-bold uppercase tracking-[0.1em] text-tps18-lime-pale">Team</h3>
<div class="flex items-baseline gap-1 mb-[18px] text-[38px] font-extrabold tracking-[-0.03em] text-white"><em class="not-italic text-[0.5em] pr-0.5 opacity-70">$</em>32<span class="ml-1.5 text-[0.32em] font-medium opacity-65">/mo</span></div>
<ul class="list-none p-0 m-0 mb-[18px] flex-1 text-[13px]/[1.95] text-tps18-body">
<li class="before:content-['\2713'] before:inline-block before:w-5 before:font-bold before:text-tps18-lime">Unlimited projects</li>
<li class="before:content-['\2713'] before:inline-block before:w-5 before:font-bold before:text-tps18-lime">Priority support</li>
<li class="before:content-['\2713'] before:inline-block before:w-5 before:font-bold before:text-tps18-lime">SSO + roles</li>
</ul>
<a href="#team" class="block text-center px-4 py-2.5 rounded-[8px] bg-tps18-lime text-tps18-bg border border-tps18-lime text-[13px] font-semibold no-underline transition-[background-color] duration-[180ms] hover:bg-tps18-lime-pale focus-visible:outline-2 focus-visible:outline-tps18-lime focus-visible:outline-offset-[3px] motion-reduce:transition-none">Choose Team</a>
</article>
<article class="relative flex flex-col px-5 pt-[22px] pb-[18px] rounded-[12px] bg-tps18-card border border-tps18-lime/12">
<h3 class="m-0 mb-3 text-sm font-bold uppercase tracking-[0.1em] text-tps18-head">Enterprise</h3>
<div class="flex items-baseline gap-1 mb-[18px] text-[38px] font-extrabold tracking-[-0.03em] text-white"><em class="not-italic text-[0.5em] pr-0.5 opacity-70">$</em>0<span class="ml-1.5 text-[0.32em] font-medium opacity-65">custom</span></div>
<ul class="list-none p-0 m-0 mb-[18px] flex-1 text-[13px]/[1.95] text-tps18-body">
<li class="before:content-['\2713'] before:inline-block before:w-5 before:font-bold before:text-tps18-lime">Everything in Team</li>
<li class="before:content-['\2713'] before:inline-block before:w-5 before:font-bold before:text-tps18-lime">Dedicated CSM</li>
<li class="before:content-['\2713'] before:inline-block before:w-5 before:font-bold before:text-tps18-lime">SLA 99.99%</li>
</ul>
<a href="#ent" class="block text-center px-4 py-2.5 rounded-[8px] bg-tps18-lime/10 text-tps18-lime border border-tps18-lime/28 text-[13px] font-semibold no-underline transition-[background-color,color] duration-[180ms] hover:bg-tps18-lime hover:text-tps18-bg focus-visible:outline-2 focus-visible:outline-tps18-lime focus-visible:outline-offset-[3px] motion-reduce:transition-none">Talk to sales</a>
</article>
</div>
</section>@import "tailwindcss";
@theme {
--color-tps18-bg: #0f172a;
--color-tps18-bg: oklch(0.21 0.04 265);
--color-tps18-card: #1e293b;
--color-tps18-card: oklch(0.28 0.04 264);
--color-tps18-moss: #243f1a;
--color-tps18-moss: oklch(0.32 0.08 135);
--color-tps18-body: #b8c1d1;
--color-tps18-body: oklch(0.81 0.02 265);
--color-tps18-head: #cbd5e1;
--color-tps18-head: oklch(0.87 0.02 262);
--color-tps18-logo: #94a3b8;
--color-tps18-logo: oklch(0.71 0.03 258);
--color-tps18-lime: #a3e635;
--color-tps18-lime: oklch(0.85 0.2 128);
--color-tps18-lime-pale: #d9f99d;
--color-tps18-lime-pale: oklch(0.94 0.12 124);
--font-tps18-mono: 'JetBrains Mono', monospace;
--font-tps18-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
--animate-tps18-roll: tps18-roll 28s linear infinite;
}
/* Keyframes have no utility form — the shorthand is a theme token
(--animate-tps18-roll → animate-tps18-roll), the body lives here.
The -50% travel is what makes the duplicated track loop seamlessly. */
@keyframes tps18-roll {
to {
transform: translateX(-50%);
}
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's
w-full min-h-svh, so the section is sized correctly in the moment before the
browser build has compiled its utilities. */
.tps-18 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-tps18-bg: #0f172a;
--color-tps18-bg: oklch(0.21 0.04 265);
--color-tps18-card: #1e293b;
--color-tps18-card: oklch(0.28 0.04 264);
--color-tps18-moss: #243f1a;
--color-tps18-moss: oklch(0.32 0.08 135);
--color-tps18-body: #b8c1d1;
--color-tps18-body: oklch(0.81 0.02 265);
--color-tps18-head: #cbd5e1;
--color-tps18-head: oklch(0.87 0.02 262);
--color-tps18-logo: #94a3b8;
--color-tps18-logo: oklch(0.71 0.03 258);
--color-tps18-lime: #a3e635;
--color-tps18-lime: oklch(0.85 0.2 128);
--color-tps18-lime-pale: #d9f99d;
--color-tps18-lime-pale: oklch(0.94 0.12 124);
--font-tps18-mono: 'JetBrains Mono', monospace;
--font-tps18-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
--animate-tps18-roll: tps18-roll 28s linear infinite;
}
/* Keyframes have no utility form — the shorthand is a theme token
(--animate-tps18-roll → animate-tps18-roll), the body lives here.
The -50% travel is what makes the duplicated track loop seamlessly. */
@keyframes tps18-roll {
to {
transform: translateX(-50%);
}
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's
w-full min-h-svh, so the section is sized correctly in the moment before the
browser build has compiled its utilities. */
.tps-18 {
width: 100%;
min-height: 100svh;
}Setup — do this once per project
Skip this if your project already runs Tailwind v4.
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>Compiles in the browser — perfect for a CodePen or a quick test, not for production.
npm install tailwindcss @tailwindcss/vite
npm install tailwindcss @tailwindcss/vite// vite.config.js ("type": "module" in package.json)
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()],
})// vite.config.js ("type": "module" in package.json)
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()],
})Any v4 setup works — Vite, PostCSS or the CLI. Nothing in this demo is bundler-specific.
@import "tailwindcss";
@theme {
--color-tps18-bg: #0f172a;
--color-tps18-bg: oklch(0.21 0.04 265);
--color-tps18-card: #1e293b;
--color-tps18-card: oklch(0.28 0.04 264);
--color-tps18-moss: #243f1a;
--color-tps18-moss: oklch(0.32 0.08 135);
--color-tps18-body: #b8c1d1;
--color-tps18-body: oklch(0.81 0.02 265);
--color-tps18-head: #cbd5e1;
--color-tps18-head: oklch(0.87 0.02 262);
--color-tps18-logo: #94a3b8;
--color-tps18-logo: oklch(0.71 0.03 258);
--color-tps18-lime: #a3e635;
--color-tps18-lime: oklch(0.85 0.2 128);
--color-tps18-lime-pale: #d9f99d;
--color-tps18-lime-pale: oklch(0.94 0.12 124);
--font-tps18-mono: 'JetBrains Mono', monospace;
--font-tps18-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
--animate-tps18-roll: tps18-roll 28s linear infinite;
}
/* Keyframes have no utility form — the shorthand is a theme token
(--animate-tps18-roll → animate-tps18-roll), the body lives here.
The -50% travel is what makes the duplicated track loop seamlessly. */
@keyframes tps18-roll {
to {
transform: translateX(-50%);
}
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's
w-full min-h-svh, so the section is sized correctly in the moment before the
browser build has compiled its utilities. */
.tps-18 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-tps18-bg: #0f172a;
--color-tps18-bg: oklch(0.21 0.04 265);
--color-tps18-card: #1e293b;
--color-tps18-card: oklch(0.28 0.04 264);
--color-tps18-moss: #243f1a;
--color-tps18-moss: oklch(0.32 0.08 135);
--color-tps18-body: #b8c1d1;
--color-tps18-body: oklch(0.81 0.02 265);
--color-tps18-head: #cbd5e1;
--color-tps18-head: oklch(0.87 0.02 262);
--color-tps18-logo: #94a3b8;
--color-tps18-logo: oklch(0.71 0.03 258);
--color-tps18-lime: #a3e635;
--color-tps18-lime: oklch(0.85 0.2 128);
--color-tps18-lime-pale: #d9f99d;
--color-tps18-lime-pale: oklch(0.94 0.12 124);
--font-tps18-mono: 'JetBrains Mono', monospace;
--font-tps18-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
--animate-tps18-roll: tps18-roll 28s linear infinite;
}
/* Keyframes have no utility form — the shorthand is a theme token
(--animate-tps18-roll → animate-tps18-roll), the body lives here.
The -50% travel is what makes the duplicated track loop seamlessly. */
@keyframes tps18-roll {
to {
transform: translateX(-50%);
}
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's
w-full min-h-svh, so the section is sized correctly in the moment before the
browser build has compiled its utilities. */
.tps-18 {
width: 100%;
min-height: 100svh;
}<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@700&family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@700&family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
- The CSS version needs an eight-line comment to explain why the marquee wrapper carries
max-width: 100vw; min-width: 0. Ported, the fix isoverflow-hidden min-w-0 max-w-screensitting on the element next to thew-maxtrack that causes the problem — the cause and the cure one line apart. - The prefixed
mask-imagepair becomesmask-x-from-88% mask-x-to-100%; v4's mask utilities emit the-webkit-spelling, so the edge fade is one pair of classes. border-blockhas no utility, butborder-yis the same declaration and reads better next to the rest of the class list.margin: 0 calc(-1 * clamp(20px,4vw,40px))becomes-mx-[clamp(20px,4vw,40px)]— the negated form of the section's own padding utility, which makes the full-bleed relationship obvious.- The duplicated half of the track gets
aria-hidden="true". The CSS version reads all fourteen names to a screen reader; the port reads seven. --animate-tps18-rollin@themeplus the@keyframesbody in CSS is the same honest split as the other animated demos: the shorthand is tokenisable, the keyframe body is not.
Here's a working CSS demo from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Tailwind Social Proof Marquee Pricing
Source: https://codefronts.com/layouts/tailwind-css-pricing-sections/tailwind-social-proof-marquee-pricing/
A seamless logo marquee above the tiers — social proof first, decision second — on slate with a lime accent. The port's interesting part is not the animation but the containment: the marquee's w-max track will grow the whole section to 2000px unless the wrapper is explicitly bounded, and in utilities that bound is three classes you can actually see.
## HTML
```html
<section class="tps-18 relative w-full min-h-svh overflow-hidden [contain:layout_paint] bg-tps18-bg font-tps18-sans text-tps18-body px-[clamp(20px,4vw,40px)] pt-[clamp(20px,4vw,36px)] pb-[clamp(28px,5vw,48px)]" aria-label="Pricing">
<div class="relative overflow-hidden [contain:layout_paint] min-w-0 max-w-screen -mx-[clamp(20px,4vw,40px)] mb-[clamp(20px,4vw,32px)] py-3.5 bg-tps18-bg/60 border-y border-tps18-lime/18 mask-x-from-88% mask-x-to-100%" aria-label="Customers using this product">
<div class="flex w-max gap-14 whitespace-nowrap will-change-transform animate-tps18-roll motion-reduce:animate-none">
<span class="shrink-0 font-tps18-mono text-sm font-bold tracking-[0.18em] text-tps18-logo">◇ NORTHWIND</span>
<span class="shrink-0 font-tps18-mono text-sm font-bold tracking-[0.18em] text-tps18-logo">▲ APEX.IO</span>
<span class="shrink-0 font-tps18-mono text-sm font-bold tracking-[0.18em] text-tps18-logo">★ PULSE</span>
<span class="shrink-0 font-tps18-mono text-sm font-bold tracking-[0.18em] text-tps18-logo">⬣ HEXLAB</span>
<span class="shrink-0 font-tps18-mono text-sm font-bold tracking-[0.18em] text-tps18-logo">◯ ORBITA</span>
<span class="shrink-0 font-tps18-mono text-sm font-bold tracking-[0.18em] text-tps18-logo">◤ FLINT</span>
<span class="shrink-0 font-tps18-mono text-sm font-bold tracking-[0.18em] text-tps18-logo">⬢ MERIDIAN</span>
<span class="shrink-0 font-tps18-mono text-sm font-bold tracking-[0.18em] text-tps18-logo" aria-hidden="true">◇ NORTHWIND</span>
<span class="shrink-0 font-tps18-mono text-sm font-bold tracking-[0.18em] text-tps18-logo" aria-hidden="true">▲ APEX.IO</span>
<span class="shrink-0 font-tps18-mono text-sm font-bold tracking-[0.18em] text-tps18-logo" aria-hidden="true">★ PULSE</span>
<span class="shrink-0 font-tps18-mono text-sm font-bold tracking-[0.18em] text-tps18-logo" aria-hidden="true">⬣ HEXLAB</span>
<span class="shrink-0 font-tps18-mono text-sm font-bold tracking-[0.18em] text-tps18-logo" aria-hidden="true">◯ ORBITA</span>
<span class="shrink-0 font-tps18-mono text-sm font-bold tracking-[0.18em] text-tps18-logo" aria-hidden="true">◤ FLINT</span>
<span class="shrink-0 font-tps18-mono text-sm font-bold tracking-[0.18em] text-tps18-logo" aria-hidden="true">⬢ MERIDIAN</span>
</div>
</div>
<header class="text-center mb-[clamp(20px,4vw,32px)]">
<span class="inline-block mb-2.5 text-xs font-semibold tracking-[0.08em] text-tps18-lime" aria-label="Rated 4.9 out of 5">★★★★★ 4.9 from 12,400 teams</span>
<h2 class="m-0 text-[clamp(24px,3.6vw,36px)] font-bold tracking-[-0.02em] text-white">Pricing teams actually pay for.</h2>
</header>
<div class="grid grid-cols-1 md:grid-cols-[repeat(3,minmax(0,1fr))] items-center gap-3.5 max-w-[360px] md:max-w-[940px] mx-auto">
<article class="relative flex flex-col px-5 pt-[22px] pb-[18px] rounded-[12px] bg-tps18-card border border-tps18-lime/12">
<h3 class="m-0 mb-3 text-sm font-bold uppercase tracking-[0.1em] text-tps18-head">Free</h3>
<div class="flex items-baseline gap-1 mb-[18px] text-[38px] font-extrabold tracking-[-0.03em] text-white"><em class="not-italic text-[0.5em] pr-0.5 opacity-70">$</em>0<span class="ml-1.5 text-[0.32em] font-medium opacity-65">forever</span></div>
<ul class="list-none p-0 m-0 mb-[18px] flex-1 text-[13px]/[1.95] text-tps18-body">
<li class="before:content-['\2713'] before:inline-block before:w-5 before:font-bold before:text-tps18-lime">1 project</li>
<li class="before:content-['\2713'] before:inline-block before:w-5 before:font-bold before:text-tps18-lime">5 GB storage</li>
<li class="before:content-['\2713'] before:inline-block before:w-5 before:font-bold before:text-tps18-lime">Community</li>
</ul>
<a href="#free" class="block text-center px-4 py-2.5 rounded-[8px] bg-tps18-lime/10 text-tps18-lime border border-tps18-lime/28 text-[13px] font-semibold no-underline transition-[background-color,color] duration-[180ms] hover:bg-tps18-lime hover:text-tps18-bg focus-visible:outline-2 focus-visible:outline-tps18-lime focus-visible:outline-offset-[3px] motion-reduce:transition-none">Start free</a>
</article>
<article class="relative flex flex-col px-5 pt-[22px] pb-[18px] rounded-[12px] bg-linear-180 from-tps18-card to-tps18-moss border border-tps18-lime shadow-[inset_0_0_0_1px_--theme(--color-tps18-lime),0_14px_36px_--alpha(--color-tps18-lime/18)]">
<span class="absolute -top-[11px] left-1/2 -translate-x-1/2 whitespace-nowrap px-2.75 py-1 rounded-full bg-tps18-lime text-tps18-bg text-[10.5px] font-bold uppercase tracking-[0.08em]">Most teams pick this</span>
<h3 class="m-0 mb-3 text-sm font-bold uppercase tracking-[0.1em] text-tps18-lime-pale">Team</h3>
<div class="flex items-baseline gap-1 mb-[18px] text-[38px] font-extrabold tracking-[-0.03em] text-white"><em class="not-italic text-[0.5em] pr-0.5 opacity-70">$</em>32<span class="ml-1.5 text-[0.32em] font-medium opacity-65">/mo</span></div>
<ul class="list-none p-0 m-0 mb-[18px] flex-1 text-[13px]/[1.95] text-tps18-body">
<li class="before:content-['\2713'] before:inline-block before:w-5 before:font-bold before:text-tps18-lime">Unlimited projects</li>
<li class="before:content-['\2713'] before:inline-block before:w-5 before:font-bold before:text-tps18-lime">Priority support</li>
<li class="before:content-['\2713'] before:inline-block before:w-5 before:font-bold before:text-tps18-lime">SSO + roles</li>
</ul>
<a href="#team" class="block text-center px-4 py-2.5 rounded-[8px] bg-tps18-lime text-tps18-bg border border-tps18-lime text-[13px] font-semibold no-underline transition-[background-color] duration-[180ms] hover:bg-tps18-lime-pale focus-visible:outline-2 focus-visible:outline-tps18-lime focus-visible:outline-offset-[3px] motion-reduce:transition-none">Choose Team</a>
</article>
<article class="relative flex flex-col px-5 pt-[22px] pb-[18px] rounded-[12px] bg-tps18-card border border-tps18-lime/12">
<h3 class="m-0 mb-3 text-sm font-bold uppercase tracking-[0.1em] text-tps18-head">Enterprise</h3>
<div class="flex items-baseline gap-1 mb-[18px] text-[38px] font-extrabold tracking-[-0.03em] text-white"><em class="not-italic text-[0.5em] pr-0.5 opacity-70">$</em>0<span class="ml-1.5 text-[0.32em] font-medium opacity-65">custom</span></div>
<ul class="list-none p-0 m-0 mb-[18px] flex-1 text-[13px]/[1.95] text-tps18-body">
<li class="before:content-['\2713'] before:inline-block before:w-5 before:font-bold before:text-tps18-lime">Everything in Team</li>
<li class="before:content-['\2713'] before:inline-block before:w-5 before:font-bold before:text-tps18-lime">Dedicated CSM</li>
<li class="before:content-['\2713'] before:inline-block before:w-5 before:font-bold before:text-tps18-lime">SLA 99.99%</li>
</ul>
<a href="#ent" class="block text-center px-4 py-2.5 rounded-[8px] bg-tps18-lime/10 text-tps18-lime border border-tps18-lime/28 text-[13px] font-semibold no-underline transition-[background-color,color] duration-[180ms] hover:bg-tps18-lime hover:text-tps18-bg focus-visible:outline-2 focus-visible:outline-tps18-lime focus-visible:outline-offset-[3px] motion-reduce:transition-none">Talk to sales</a>
</article>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tps18-bg: #0f172a;
--color-tps18-bg: oklch(0.21 0.04 265);
--color-tps18-card: #1e293b;
--color-tps18-card: oklch(0.28 0.04 264);
--color-tps18-moss: #243f1a;
--color-tps18-moss: oklch(0.32 0.08 135);
--color-tps18-body: #b8c1d1;
--color-tps18-body: oklch(0.81 0.02 265);
--color-tps18-head: #cbd5e1;
--color-tps18-head: oklch(0.87 0.02 262);
--color-tps18-logo: #94a3b8;
--color-tps18-logo: oklch(0.71 0.03 258);
--color-tps18-lime: #a3e635;
--color-tps18-lime: oklch(0.85 0.2 128);
--color-tps18-lime-pale: #d9f99d;
--color-tps18-lime-pale: oklch(0.94 0.12 124);
--font-tps18-mono: 'JetBrains Mono', monospace;
--font-tps18-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
--animate-tps18-roll: tps18-roll 28s linear infinite;
}
/* Keyframes have no utility form — the shorthand is a theme token
(--animate-tps18-roll → animate-tps18-roll), the body lives here.
The -50% travel is what makes the duplicated track loop seamlessly. */
@keyframes tps18-roll {
to {
transform: translateX(-50%);
}
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's
w-full min-h-svh, so the section is sized correctly in the moment before the
browser build has compiled its utilities. */
.tps-18 {
width: 100%;
min-height: 100svh;
}
```Here's a working CSS demo from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Tailwind Social Proof Marquee Pricing
Source: https://codefronts.com/layouts/tailwind-css-pricing-sections/tailwind-social-proof-marquee-pricing/
A seamless logo marquee above the tiers — social proof first, decision second — on slate with a lime accent. The port's interesting part is not the animation but the containment: the marquee's w-max track will grow the whole section to 2000px unless the wrapper is explicitly bounded, and in utilities that bound is three classes you can actually see.
## HTML
```html
<section class="tps-18 relative w-full min-h-svh overflow-hidden [contain:layout_paint] bg-tps18-bg font-tps18-sans text-tps18-body px-[clamp(20px,4vw,40px)] pt-[clamp(20px,4vw,36px)] pb-[clamp(28px,5vw,48px)]" aria-label="Pricing">
<div class="relative overflow-hidden [contain:layout_paint] min-w-0 max-w-screen -mx-[clamp(20px,4vw,40px)] mb-[clamp(20px,4vw,32px)] py-3.5 bg-tps18-bg/60 border-y border-tps18-lime/18 mask-x-from-88% mask-x-to-100%" aria-label="Customers using this product">
<div class="flex w-max gap-14 whitespace-nowrap will-change-transform animate-tps18-roll motion-reduce:animate-none">
<span class="shrink-0 font-tps18-mono text-sm font-bold tracking-[0.18em] text-tps18-logo">◇ NORTHWIND</span>
<span class="shrink-0 font-tps18-mono text-sm font-bold tracking-[0.18em] text-tps18-logo">▲ APEX.IO</span>
<span class="shrink-0 font-tps18-mono text-sm font-bold tracking-[0.18em] text-tps18-logo">★ PULSE</span>
<span class="shrink-0 font-tps18-mono text-sm font-bold tracking-[0.18em] text-tps18-logo">⬣ HEXLAB</span>
<span class="shrink-0 font-tps18-mono text-sm font-bold tracking-[0.18em] text-tps18-logo">◯ ORBITA</span>
<span class="shrink-0 font-tps18-mono text-sm font-bold tracking-[0.18em] text-tps18-logo">◤ FLINT</span>
<span class="shrink-0 font-tps18-mono text-sm font-bold tracking-[0.18em] text-tps18-logo">⬢ MERIDIAN</span>
<span class="shrink-0 font-tps18-mono text-sm font-bold tracking-[0.18em] text-tps18-logo" aria-hidden="true">◇ NORTHWIND</span>
<span class="shrink-0 font-tps18-mono text-sm font-bold tracking-[0.18em] text-tps18-logo" aria-hidden="true">▲ APEX.IO</span>
<span class="shrink-0 font-tps18-mono text-sm font-bold tracking-[0.18em] text-tps18-logo" aria-hidden="true">★ PULSE</span>
<span class="shrink-0 font-tps18-mono text-sm font-bold tracking-[0.18em] text-tps18-logo" aria-hidden="true">⬣ HEXLAB</span>
<span class="shrink-0 font-tps18-mono text-sm font-bold tracking-[0.18em] text-tps18-logo" aria-hidden="true">◯ ORBITA</span>
<span class="shrink-0 font-tps18-mono text-sm font-bold tracking-[0.18em] text-tps18-logo" aria-hidden="true">◤ FLINT</span>
<span class="shrink-0 font-tps18-mono text-sm font-bold tracking-[0.18em] text-tps18-logo" aria-hidden="true">⬢ MERIDIAN</span>
</div>
</div>
<header class="text-center mb-[clamp(20px,4vw,32px)]">
<span class="inline-block mb-2.5 text-xs font-semibold tracking-[0.08em] text-tps18-lime" aria-label="Rated 4.9 out of 5">★★★★★ 4.9 from 12,400 teams</span>
<h2 class="m-0 text-[clamp(24px,3.6vw,36px)] font-bold tracking-[-0.02em] text-white">Pricing teams actually pay for.</h2>
</header>
<div class="grid grid-cols-1 md:grid-cols-[repeat(3,minmax(0,1fr))] items-center gap-3.5 max-w-[360px] md:max-w-[940px] mx-auto">
<article class="relative flex flex-col px-5 pt-[22px] pb-[18px] rounded-[12px] bg-tps18-card border border-tps18-lime/12">
<h3 class="m-0 mb-3 text-sm font-bold uppercase tracking-[0.1em] text-tps18-head">Free</h3>
<div class="flex items-baseline gap-1 mb-[18px] text-[38px] font-extrabold tracking-[-0.03em] text-white"><em class="not-italic text-[0.5em] pr-0.5 opacity-70">$</em>0<span class="ml-1.5 text-[0.32em] font-medium opacity-65">forever</span></div>
<ul class="list-none p-0 m-0 mb-[18px] flex-1 text-[13px]/[1.95] text-tps18-body">
<li class="before:content-['\2713'] before:inline-block before:w-5 before:font-bold before:text-tps18-lime">1 project</li>
<li class="before:content-['\2713'] before:inline-block before:w-5 before:font-bold before:text-tps18-lime">5 GB storage</li>
<li class="before:content-['\2713'] before:inline-block before:w-5 before:font-bold before:text-tps18-lime">Community</li>
</ul>
<a href="#free" class="block text-center px-4 py-2.5 rounded-[8px] bg-tps18-lime/10 text-tps18-lime border border-tps18-lime/28 text-[13px] font-semibold no-underline transition-[background-color,color] duration-[180ms] hover:bg-tps18-lime hover:text-tps18-bg focus-visible:outline-2 focus-visible:outline-tps18-lime focus-visible:outline-offset-[3px] motion-reduce:transition-none">Start free</a>
</article>
<article class="relative flex flex-col px-5 pt-[22px] pb-[18px] rounded-[12px] bg-linear-180 from-tps18-card to-tps18-moss border border-tps18-lime shadow-[inset_0_0_0_1px_--theme(--color-tps18-lime),0_14px_36px_--alpha(--color-tps18-lime/18)]">
<span class="absolute -top-[11px] left-1/2 -translate-x-1/2 whitespace-nowrap px-2.75 py-1 rounded-full bg-tps18-lime text-tps18-bg text-[10.5px] font-bold uppercase tracking-[0.08em]">Most teams pick this</span>
<h3 class="m-0 mb-3 text-sm font-bold uppercase tracking-[0.1em] text-tps18-lime-pale">Team</h3>
<div class="flex items-baseline gap-1 mb-[18px] text-[38px] font-extrabold tracking-[-0.03em] text-white"><em class="not-italic text-[0.5em] pr-0.5 opacity-70">$</em>32<span class="ml-1.5 text-[0.32em] font-medium opacity-65">/mo</span></div>
<ul class="list-none p-0 m-0 mb-[18px] flex-1 text-[13px]/[1.95] text-tps18-body">
<li class="before:content-['\2713'] before:inline-block before:w-5 before:font-bold before:text-tps18-lime">Unlimited projects</li>
<li class="before:content-['\2713'] before:inline-block before:w-5 before:font-bold before:text-tps18-lime">Priority support</li>
<li class="before:content-['\2713'] before:inline-block before:w-5 before:font-bold before:text-tps18-lime">SSO + roles</li>
</ul>
<a href="#team" class="block text-center px-4 py-2.5 rounded-[8px] bg-tps18-lime text-tps18-bg border border-tps18-lime text-[13px] font-semibold no-underline transition-[background-color] duration-[180ms] hover:bg-tps18-lime-pale focus-visible:outline-2 focus-visible:outline-tps18-lime focus-visible:outline-offset-[3px] motion-reduce:transition-none">Choose Team</a>
</article>
<article class="relative flex flex-col px-5 pt-[22px] pb-[18px] rounded-[12px] bg-tps18-card border border-tps18-lime/12">
<h3 class="m-0 mb-3 text-sm font-bold uppercase tracking-[0.1em] text-tps18-head">Enterprise</h3>
<div class="flex items-baseline gap-1 mb-[18px] text-[38px] font-extrabold tracking-[-0.03em] text-white"><em class="not-italic text-[0.5em] pr-0.5 opacity-70">$</em>0<span class="ml-1.5 text-[0.32em] font-medium opacity-65">custom</span></div>
<ul class="list-none p-0 m-0 mb-[18px] flex-1 text-[13px]/[1.95] text-tps18-body">
<li class="before:content-['\2713'] before:inline-block before:w-5 before:font-bold before:text-tps18-lime">Everything in Team</li>
<li class="before:content-['\2713'] before:inline-block before:w-5 before:font-bold before:text-tps18-lime">Dedicated CSM</li>
<li class="before:content-['\2713'] before:inline-block before:w-5 before:font-bold before:text-tps18-lime">SLA 99.99%</li>
</ul>
<a href="#ent" class="block text-center px-4 py-2.5 rounded-[8px] bg-tps18-lime/10 text-tps18-lime border border-tps18-lime/28 text-[13px] font-semibold no-underline transition-[background-color,color] duration-[180ms] hover:bg-tps18-lime hover:text-tps18-bg focus-visible:outline-2 focus-visible:outline-tps18-lime focus-visible:outline-offset-[3px] motion-reduce:transition-none">Talk to sales</a>
</article>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tps18-bg: #0f172a;
--color-tps18-bg: oklch(0.21 0.04 265);
--color-tps18-card: #1e293b;
--color-tps18-card: oklch(0.28 0.04 264);
--color-tps18-moss: #243f1a;
--color-tps18-moss: oklch(0.32 0.08 135);
--color-tps18-body: #b8c1d1;
--color-tps18-body: oklch(0.81 0.02 265);
--color-tps18-head: #cbd5e1;
--color-tps18-head: oklch(0.87 0.02 262);
--color-tps18-logo: #94a3b8;
--color-tps18-logo: oklch(0.71 0.03 258);
--color-tps18-lime: #a3e635;
--color-tps18-lime: oklch(0.85 0.2 128);
--color-tps18-lime-pale: #d9f99d;
--color-tps18-lime-pale: oklch(0.94 0.12 124);
--font-tps18-mono: 'JetBrains Mono', monospace;
--font-tps18-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
--animate-tps18-roll: tps18-roll 28s linear infinite;
}
/* Keyframes have no utility form — the shorthand is a theme token
(--animate-tps18-roll → animate-tps18-roll), the body lives here.
The -50% travel is what makes the duplicated track loop seamlessly. */
@keyframes tps18-roll {
to {
transform: translateX(-50%);
}
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's
w-full min-h-svh, so the section is sized correctly in the moment before the
browser build has compiled its utilities. */
.tps-18 {
width: 100%;
min-height: 100svh;
}
```How this works
The seamless loop is a duplicated track plus a -50% travel. Seven logos are listed twice; the animation moves the track by half its own width, so the second copy lands exactly where the first began:
@theme { --animate-tps18-roll: tps18-roll 28s linear infinite; }
@keyframes tps18-roll { to { transform: translateX(-50%); } }No JS, no measuring, and the seam is mathematically invisible as long as the two halves are identical.
The containment is the real lesson. The track needs w-max to lay its logos out on one line. That intrinsic width propagates upward, and a wrapper that inherits it makes the section about 2000px wide with a horizontal scrollbar on the body. Three utilities stop it: overflow-hidden to clip, min-w-0 to refuse the intrinsic minimum, max-w-screen to cap the maximum. The CSS version needs an eight-line comment to explain the same thing.
The rating is a label, not a decoration. aria-label="Rated 4.9 out of 5" on the star string, and aria-label on the marquee saying what the logos are — without them a screen reader hears five star glyphs and fourteen company names with no context.
Reduced motion stops the scroll and nothing else. motion-reduce:animate-none leaves the first seven logos visible and static, which is a perfectly good logo wall — the duplicate simply sits off the clipped edge.
Make it yours
- Speed: the
28sin--animate-tps18-roll. Faster than about 20s and the logos become unreadable, which defeats the purpose. - Logo count: any number, as long as the list is duplicated exactly. The
-50%travel depends on the two halves being identical. - Gap:
gap-14on the track. Wider gaps need more logos to fill the viewport before the seam comes round. - Edge fade:
mask-x-from-88%. Push it to 70% for a heavier vignette, or drop the mask for hard edges. - Accent:
--color-tps18-limedrives the rating, the bullets, the featured card and every CTA. One accent token, six uses.
Gotchas — read before shipping
overflow-hidden,min-w-0andmax-w-screenon the marquee wrapper are all three load-bearing. Drop any one and thew-maxtrack can size the section to its own intrinsic width and add a horizontal scrollbar to the page.- The two halves of the track must be identical. Add a logo to one and the loop visibly jumps once per cycle.
motion-reduce:animate-noneis mandatory here: a continuously scrolling strip is the canonical vestibular trigger, and it is also the hardest content to read while moving.- Give the marquee an
aria-label. Fourteen mono-spaced company names with no context is noise to a screen reader, and the duplicated half is read too. [contain:layout_paint]on both the section and the marquee keeps a 28s infinite animation from invalidating layout outside its own box.- The featured card's heading is
text-tps18-lime-pale, not the lime accent. Full-strength lime on the dark green gradient is only about 3:1 at 14px.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
mask-image is Baseline 2023 and Tailwind's mask-* utilities emit the -webkit- spelling; without mask support the marquee simply has hard edges. Everything else — keyframes, contain, will-change — is long-standing.