Depth Carousel Hero
A tactile carousel with real depth. Four subjects occupy four roles — center
(large, sharp), left, right (small, blurred, to the sides), and back (tiny,
behind). Clicking an arrow rotates every subject to the next role while the
background color, scales, blurs, and opacities all crossfade together.
Before building
Read /mnt/skills/public/frontend-design/SKILL.md. Collect: 3–4 ITEMS, each
with an image src, a bg color, and (optional) a panel color; a BRAND
label; a giant GHOST_TEXT (e.g. "3D SHAPE"); a bottom-left blurb; and a
bottom-right CTA word. Fonts: a heavy display face (Anton) for the ghost text +
CTA; Inter for body.
Stack
React + Vite + TypeScript, Tailwind, lucide-react (ArrowLeft, ArrowRight).
Single App.tsx. Preload all images on mount via new Image().
State & role logic
const [active, setActive] = React.useState(0);
const [locked, setLocked] = React.useState(false);
const n = ITEMS.length; // 4
const navigate = (dir: "next" | "prev") => {
if (locked) return;
setLocked(true);
setActive(p => dir === "next" ? (p + 1) % n : (p + n - 1) % n);
setTimeout(() => setLocked(false), 650);
};
const roles = {
center: active,
left: (active + n - 1) % n,
right: (active + 1) % n,
back: (active + 2) % n,
};
Per-role styling (the depth illusion)
Each item is position: absolute, aspectRatio: "0.6 / 1", image
objectFit: contain; objectPosition: bottom center. Transition every property
over 650ms cubic-bezier(0.4,0,0.2,1) with willChange: transform,filter,opacity,left.
| Role |
transform |
blur |
opacity |
z |
left |
height (desktop / mobile) |
bottom |
| center |
translateX(-50%) scale(1.68 / 1.25) |
0 |
1 |
20 |
50% |
92% / 60% |
0 / 22% |
| left |
translateX(-50%) scale(1) |
2px |
0.85 |
10 |
30% / 20% |
28% / 16% |
12% / 32% |
| right |
same as left |
2px |
0.85 |
10 |
70% / 80% |
28% / 16% |
12% / 32% |
| back |
translateX(-50%) scale(1) |
4px |
1 |
5 |
50% |
22% / 13% |
12% / 32% |
Root wrapper: backgroundColor: ITEMS[active].bg, transition
background-color 650ms cubic-bezier(0.4,0,0.2,1), relative w-full overflow-hidden, inner height: 100vh.
The scenery
- Ghost text (
z-2, absolute inset-x-0 flex justify-center, top: 18%,
pointer-events-none select-none): Anton, fontSize: clamp(90px,28vw,380px),
white, lineHeight:1, uppercase, letterSpacing:-0.02em, whiteSpace:nowrap.
The subjects (z-3) overlap it.
- Grain overlay (
z-50, pointer-events-none): an inline SVG feTurbulence
fractal-noise data URI, baseFrequency 0.9 numOctaves 4, container
opacity 0.4, backgroundSize:200px 200px, repeat.
- Brand label top-left; blurb + two round arrow buttons bottom-left
(
w-12 h-12 sm:w-16 sm:h-16, transparent, 2px white border, hover scale 1.08
bg-white/12); CTA link bottom-right in Anton + ArrowRight.
Responsive
Track isMobile = window.innerWidth < 640 on resize and switch the scale/
height/bottom values per the table. Ghost text and CTA scale via clamp().
Quality checklist
1---2name: depth-carousel-hero3description: Build a playful 3D-style carousel hero where 3–4 subjects (product shots, character figurines, app screens) sit at depth positions — one big in the center, others smaller to the sides and behind — and rotate through those roles with a smooth crossfade when you click arrows. The whole background color changes per active item, a giant "ghost" display word sits behind the subjects, and a film-grain overlay adds texture. Use this skill whenever the user wants a carousel, a rotating showcase, a "coverflow"-style gallery, cycling product/character cards, or a colorful, toy-like hero that swaps subjects. Prefer this when the content is a small set of hero objects meant to be flipped through.4---56# Depth Carousel Hero78A tactile carousel with real depth. Four subjects occupy four roles — center9(large, sharp), left, right (small, blurred, to the sides), and back (tiny,10behind). Clicking an arrow rotates every subject to the next role while the11background color, scales, blurs, and opacities all crossfade together.1213## Before building14Read `/mnt/skills/public/frontend-design/SKILL.md`. Collect: 3–4 `ITEMS`, each15with an image `src`, a `bg` color, and (optional) a `panel` color; a `BRAND`16label; a giant `GHOST_TEXT` (e.g. "3D SHAPE"); a bottom-left blurb; and a17bottom-right CTA word. Fonts: a heavy display face (Anton) for the ghost text +18CTA; Inter for body.1920## Stack21React + Vite + TypeScript, Tailwind, `lucide-react` (`ArrowLeft`, `ArrowRight`).22Single `App.tsx`. Preload all images on mount via `new Image()`.2324## State & role logic25```tsx26const [active, setActive] = React.useState(0);27const [locked, setLocked] = React.useState(false);28const n = ITEMS.length; // 429const navigate = (dir: "next" | "prev") => {30 if (locked) return;31 setLocked(true);32 setActive(p => dir === "next" ? (p + 1) % n : (p + n - 1) % n);33 setTimeout(() => setLocked(false), 650);34};35const roles = {36 center: active,37 left: (active + n - 1) % n,38 right: (active + 1) % n,39 back: (active + 2) % n,40};41```4243## Per-role styling (the depth illusion)44Each item is `position: absolute`, `aspectRatio: "0.6 / 1"`, image45`objectFit: contain; objectPosition: bottom center`. Transition every property46over `650ms cubic-bezier(0.4,0,0.2,1)` with `willChange: transform,filter,opacity,left`.4748| Role | transform | blur | opacity | z | left | height (desktop / mobile) | bottom |49|------|-----------|------|---------|---|------|---------------------------|--------|50| center | `translateX(-50%) scale(1.68 / 1.25)` | 0 | 1 | 20 | 50% | 92% / 60% | 0 / 22% |51| left | `translateX(-50%) scale(1)` | 2px | 0.85 | 10 | 30% / 20% | 28% / 16% | 12% / 32% |52| right | same as left | 2px | 0.85 | 10 | 70% / 80% | 28% / 16% | 12% / 32% |53| back | `translateX(-50%) scale(1)` | 4px | 1 | 5 | 50% | 22% / 13% | 12% / 32% |5455Root wrapper: `backgroundColor: ITEMS[active].bg`, transition56`background-color 650ms cubic-bezier(0.4,0,0.2,1)`, `relative w-full57overflow-hidden`, inner `height: 100vh`.5859## The scenery60- **Ghost text** (`z-2`, `absolute inset-x-0 flex justify-center`, `top: 18%`,61 `pointer-events-none select-none`): Anton, `fontSize: clamp(90px,28vw,380px)`,62 white, `lineHeight:1`, uppercase, `letterSpacing:-0.02em`, `whiteSpace:nowrap`.63 The subjects (`z-3`) overlap it.64- **Grain overlay** (`z-50`, `pointer-events-none`): an inline SVG `feTurbulence`65 fractal-noise data URI, `baseFrequency 0.9 numOctaves 4`, container66 `opacity 0.4`, `backgroundSize:200px 200px`, repeat.67- **Brand label** top-left; **blurb + two round arrow buttons** bottom-left68 (`w-12 h-12 sm:w-16 sm:h-16`, transparent, 2px white border, hover `scale 1.08`69 + `bg-white/12`); **CTA link** bottom-right in Anton + `ArrowRight`.7071## Responsive72Track `isMobile = window.innerWidth < 640` on resize and switch the scale/73height/bottom values per the table. Ghost text and CTA scale via `clamp()`.7475## Quality checklist76- [ ] Clicking arrows rotates all subjects through center→right→back→left.77- [ ] Background color, scales, blurs, opacities crossfade together (650ms).78- [ ] Input is locked during the 650ms transition (no double-fire).79- [ ] Ghost display word sits behind; grain overlay on top.80- [ ] Images preloaded; subjects anchored to the bottom.81- [ ] Mobile uses the smaller scale/position set.