3D Tilt Scroll Reveal Cards 3Dチルトスクロールカードリビール
Cards pivot upright with tactile 3D perspective transforms upon scrolling into viewport. CSS 3Dパースペクティブとtransform-originを活用した触感豊かなスクロールイン演出。
ライブデモ Live Demo
概要・用途・特徴Overview, Usage & Features
何ができるかWhat it does
A 3D perspective scroll effect where cards enter leaning backward in depth space and rotate upright as they enter the view.
スクロールに応じて奥側に傾いていたカードが、視界中央に入ると滑らかに起き上がってフラットに着地する3Dスクロールリビール演出です。
どこで使うかWhere to use
Ideal for landing page feature sections, Apple-inspired device showcases, portfolio case studies, and modern marketing sites.
製品機能のプレゼンテーション、モダンなSaaSのトップページ、実績紹介カードなど、視覚的インパクトを与えたいエリアに適しています。
特徴Key features
Hardware-accelerated CSS 3D transforms, customizable perspective focal distance, smooth cubic-bezier easing, prefers-reduced-motion fallback.
GPUアクセラレーションによるCSS 3D変形、パースペクティブ距離の調整、上質なcubic-bezierイージング、prefers-reduced-motion時の静的表示。
調整可能パラメータ Adjustable Parameters
| Parameter | Default | Description |
|---|---|---|
perspective | 1000px | 3D perspective focal depth |
accent-color | #38bdf8 | Accent highlight for active cards |
実装コード Implementation Code
<div class="m021-scroll-viewport">
<div class="m021-cards-list">
<div class="m021-card is-flat"><h4>Next-Gen Engine</h4><p>Details...</p></div>
<div class="m021-card is-tilted"><h4>Spatial Depth</h4><p>Details...</p></div>
</div>
</div>
.m021-scroll-viewport {
height: 320px;
overflow-y: auto;
padding: 36px 20px 48px;
perspective: var(--m021-perspective, 1000px);
perspective-origin: center center;
}
.m021-cards-list {
display: flex;
flex-direction: column;
gap: 20px;
transform-style: preserve-3d;
}
.m021-card {
padding: 22px 24px;
border-radius: 14px;
background: linear-gradient(135deg, rgba(30, 41, 59, 0.75), rgba(15, 23, 42, 0.85));
border: 1px solid rgba(148, 163, 184, 0.15);
transform-origin: center bottom;
transform-style: preserve-3d;
transition: transform 0.5s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.5s ease;
}
.m021-card.is-tilted {
opacity: 0.35;
transform: rotateX(32deg) translateY(24px) scale(0.92);
}
.m021-card.is-flat {
opacity: 1;
transform: rotateX(0deg) translateY(0) scale(1);
border-color: rgba(56, 189, 248, 0.35);
}
import React, { useRef, useEffect } from 'react';
import './M-021.css';
export default function TiltScrollRevealCards({
cards = [],
perspective = 1000,
accentColor = '#38bdf8'
}) {
const viewportRef = useRef(null);
useEffect(() => {
const vp = viewportRef.current;
if (!vp) return;
const onScroll = () => {
const items = vp.querySelectorAll('.m021-card');
const vBottom = vp.scrollTop + vp.clientHeight;
items.forEach(card => {
if (vBottom - card.offsetTop > 90) {
card.classList.remove('is-tilted');
card.classList.add('is-flat');
} else {
card.classList.remove('is-flat');
card.classList.add('is-tilted');
}
});
};
vp.addEventListener('scroll', onScroll, { passive: true });
onScroll();
return () => vp.removeEventListener('scroll', onScroll);
}, []);
return (
<div className="m021-stage" style={{ '--m021-perspective': `${perspective}px`, '--m021-card-accent': accentColor }}>
<div className="m021-scroll-viewport" ref={viewportRef}>
<div className="m021-cards-list">
{cards.map((c, i) => (
<div key={i} className="m021-card is-tilted">
<h4>{c.title}</h4>
<p>{c.desc}</p>
</div>
))}
</div>
</div>
</div>
);
}
.m021-stage {
position: relative;
width: 100%;
max-width: 620px;
border-radius: 16px;
background: #090d16;
border: 1px solid rgba(148, 163, 184, 0.2);
overflow: hidden;
}
.m021-scroll-viewport {
height: 320px;
overflow-y: auto;
padding: 36px 20px 48px;
perspective: var(--m021-perspective, 1000px);
perspective-origin: center center;
}
.m021-cards-list {
display: flex;
flex-direction: column;
gap: 20px;
transform-style: preserve-3d;
}
.m021-card {
padding: 22px 24px;
border-radius: 14px;
background: linear-gradient(135deg, rgba(30, 41, 59, 0.75), rgba(15, 23, 42, 0.85));
border: 1px solid rgba(148, 163, 184, 0.15);
transform-origin: center bottom;
transform-style: preserve-3d;
transition: transform 0.5s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.5s ease;
}
.m021-card.is-tilted {
opacity: 0.35;
transform: rotateX(32deg) translateY(24px) scale(0.92);
}
.m021-card.is-flat {
opacity: 1;
transform: rotateX(0deg) translateY(0) scale(1);
}
仕組みとカスタマイズHow It Works & Customization
仕組みHow it works
Assigns perspective to the scroll container and rotateX / scale transforms to each card based on its viewport entry threshold.
親コンテナにperspectiveを設定し、カードの下端を基準点(transform-origin: center bottom)として、スクロール進入時にrotateX(32deg)から0degへ遷移させます。
カスタマイズ方法Customization
Adjust perspective depth with --m021-perspective and accent highlight colors with --m021-card-accent.
CSSカスタムプロパティ(--m021-perspective, --m021-card-accent)で奥行き感の強さやアクセントカラーを自由に調整できます。
注意点Caveats
Ensure preserve-3d is set properly and avoid overflow: hidden on intermediate wrapper elements that would flatten the 3D context.
カードの中間親要素に意図しないoverflowやtransformを付与すると3Dコンテキストが平坦化(flatten)されるため階層構造に注意してください。
よくある質問 Frequently Asked Questions
Can I adjust the angle of rotation? 傾斜の角度(傾き度合い)を変更できますか?
Yes, modify rotateX(32deg) in the .is-tilted CSS selector to any desired degrees such as 15deg or 45deg.
はい、.is-tilted セレクタ内の rotateX(32deg) の数値を変更することで、緩やかな15度やダイナミックな45度など自由に調整できます。
How does it behave under prefers-reduced-motion? prefers-reduced-motion設定ではどのように動作しますか?
The 3D transform is bypassed and replaced with a clean 2D fade-in for motion sensitivity.
3D回転とスケール変化が無効化され、シンプルなフェードインのみでアクセシブルに表示されます。
AIへの指示テンプレート AI Prompt Template
以下をAIに貼り付けると実装を依頼できます。 Paste the following into your AI assistant to request implementation.