084面キューブ
カード1枚が立方体のように4つの面を持ち、クリックするたびに1面ずつ回転します。画像・説明・価格・CTAを1枚に収めるときに使います。
使いどころ商品画像・説明・価格・購入ボタンを順番に見せるカード、ステップ型案内カード
preserve-3dtranslateZrotateY

<main class="stage">
<div class="cb-scene">
<button type="button" class="cb-cube is-demo" aria-label="次の面を見る">
<span class="cb-face cb-face--1">写真</span>
<span class="cb-face cb-face--2">説明</span>
<span class="cb-face cb-face--3">$29</span>
<span class="cb-face cb-face--4">購入</span>
</button>
</div>
</main>@use "../../../../site/static/demo-tokens" as *;
// ── 변수 3개 ──────────────────────────────────────────
$duration: $dur-enter;
$easing: $ease-spring;
$color: $subject-blue;
@include stage($stage-paper);
$cb-size: 110px;
$cb-half: 55px;
.cb-scene { perspective: 700px; }
.cb-cube {
--cb-step: 0;
position: relative;
width: $cb-size;
height: $cb-size;
border: 0;
padding: 0;
background: none;
cursor: pointer;
transform-style: preserve-3d;
transform: rotateY(calc(var(--cb-step) * -90deg));
transition: transform $duration $easing;
}
.cb-cube:focus-visible { outline: 3px solid $subject-ink; outline-offset: $sp-2; }
.cb-face {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
border-radius: $r-card;
backface-visibility: hidden;
font: 700 14px/1 -apple-system, "Segoe UI", sans-serif;
color: $subject-cream;
}
.cb-face--1 { background: $subject-blue-deep; transform: rotateY(0deg) translateZ(#{$cb-half}); }
.cb-face--2 { background: rgba(23, 20, 26, .82); transform: rotateY(90deg) translateZ(#{$cb-half}); }
.cb-face--3 { background: $subject-blue-deep; transform: rotateY(180deg) translateZ(#{$cb-half}); }
.cb-face--4 { background: rgba(23, 20, 26, .82); transform: rotateY(270deg) translateZ(#{$cb-half}); }
// 데모 루프: 클릭 대신 네 면을 순서대로 자동 회전
@keyframes cb-spin {
0%, 15% { transform: rotateY(0); }
30%, 40% { transform: rotateY(-90deg); }
55%, 65% { transform: rotateY(-180deg); }
80%, 90% { transform: rotateY(-270deg); }
100% { transform: rotateY(-360deg); }
}
.cb-cube.is-demo { animation: cb-spin ($duration * 5) $easing infinite; }
@media (prefers-reduced-motion: reduce) {
.cb-cube { animation: none !important; transition: none !important; }
.cb-cube.is-demo { animation: none !important; }
}html, body {
margin: 0;
overflow: hidden;
}
.stage {
box-sizing: border-box;
min-height: 100vh;
display: grid;
place-items: center;
margin: 0;
background: #ffffff;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
padding: clamp(8px, 4vw, 24px);
}
.cb-scene {
perspective: 700px;
}
.cb-cube {
--cb-step: 0;
position: relative;
width: 110px;
height: 110px;
border: 0;
padding: 0;
background: none;
cursor: pointer;
transform-style: preserve-3d;
transform: rotateY(calc(var(--cb-step) * -90deg));
transition: transform 600ms cubic-bezier(0.2, 0.8, 0.2, 1);
}
.cb-cube:focus-visible {
outline: 3px solid #17141a;
outline-offset: 8px;
}
.cb-face {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
border-radius: 16px;
backface-visibility: hidden;
font: 700 14px/1 -apple-system, "Segoe UI", sans-serif;
color: #fff7e6;
}
.cb-face--1 {
background: #0a4fe8;
transform: rotateY(0deg) translateZ(55px);
}
.cb-face--2 {
background: rgba(23, 20, 26, 0.82);
transform: rotateY(90deg) translateZ(55px);
}
.cb-face--3 {
background: #0a4fe8;
transform: rotateY(180deg) translateZ(55px);
}
.cb-face--4 {
background: rgba(23, 20, 26, 0.82);
transform: rotateY(270deg) translateZ(55px);
}
@keyframes cb-spin {
0%, 15% {
transform: rotateY(0);
}
30%, 40% {
transform: rotateY(-90deg);
}
55%, 65% {
transform: rotateY(-180deg);
}
80%, 90% {
transform: rotateY(-270deg);
}
100% {
transform: rotateY(-360deg);
}
}
.cb-cube.is-demo {
animation: cb-spin 3000ms cubic-bezier(0.2, 0.8, 0.2, 1) infinite;
}
@media (prefers-reduced-motion: reduce) {
.cb-cube {
animation: none !important;
transition: none !important;
}
.cb-cube.is-demo {
animation: none !important;
}
}var cube = document.querySelector(".cb-cube");
var step = 0;
cube.addEventListener("click", function () {
step = (step + 1) % 4;
cube.style.setProperty("--cb-step", String(step));
});import { CSSProperties, useState } from "react";
import styles from "./CubeFourFaces.module.scss";
export interface CubeFourFacesProps {
duration?: string;
easing?: string;
faces?: [string, string, string, string];
}
export default function CubeFourFaces({
duration = "600ms",
easing = "cubic-bezier(.2, .8, .2, 1)",
faces = ["Photo", "Details", "$29", "Buy"],
}: CubeFourFacesProps) {
const [step, setStep] = useState(0);
const style = { "--duration": duration, "--easing": easing, "--step": step } as CSSProperties;
return (
<div className={styles.scene}>
<button
type="button"
className={styles.cube}
style={style}
aria-label="Show next face"
onClick={() => setStep((s) => s + 1)}
>
{faces.map((f, i) => (
<span key={f} className={`${styles.face} ${styles["face" + i]}`}>{f}</span>
))}
</button>
</div>
);
}08 4面キューブ — 計測 累積面積 12.789% · 動いたフレーム 12/23GODRICH オリジナル · SCSS と React は zip に同梱 · カード フリップ アニメーション 9選【コピペ20行】
このデモが属する記事
カード フリップ アニメーション 9選【コピペ20行】カード フリップ アニメーションとは、マウスを乗せたりクリックしたりするとカードがくるっと回転したり半分に折れたりして、裏側の内容が現れる効果のことです。コードが分からなくても、下のデモにマウスを乗せてみれば何が起きているか一目で分かります この記事のデモをすべて見る


