GODRICH

CSS 카드 뒤집기 애니메이션 9종 (복붙 20줄)

CSS 카드 뒤집기는 마우스를 올리거나 클릭했을 때 카드가 휙 돌아가거나 반으로 접히면서 반대쪽 내용을 보여주는 애니메이션입니다. 코드를 몰라도 아래 데모에 마우스를 올려보면 바로 무슨 효과인지 알 수 있습니다.

자동 재생 · 눌러서 해당 항목으로 · 전부 한 zip

아홉 개는 인기 순이 아니라 기법이 단순한 것에서 복잡한 것으로, 마지막은 접근성으로 이어지는 순서를 따라갑니다. 가장 기본인 호버 뒤집기(01)와 클릭 뒤집기(02)를 지나, 종이처럼 접고 펼치는 아이디어형 변형(03~04)을 거쳐, 카드 여러 장을 다루는 실전 패턴(05~08)으로 이어지고, 마지막은 같은 동작을 모션을 줄여 재생하는 09번으로 마무리됩니다.

01가로 뒤집기

카드에 마우스를 올리면 Y축을 중심으로 180도 회전해 뒷면이 드러납니다. 클릭 한 번 없이 마우스만 올려도 뒷면이 궁금한 카드에 씁니다.

rotateYperspectivebackface-visibility
.fy-card { perspective: 700px; }
.fy-card__inner {
  transform-style: preserve-3d;
  transition: transform $duration $easing;
}
.fy-card:hover .fy-card__inner { transform: rotateY(180deg); }
.fy-card__face {
  position: absolute;
  inset: 0;
  backface-visibility: hidden;
}
.fy-card__face--back { transform: rotateY(180deg); }

02세로 뒤집기(클릭)

카드를 클릭하면 X축을 중심으로 세로로 뒤집혀 뒷면을 보여줍니다. 터치 기기는 호버가 없어 클릭·탭으로 발동하는 쪽이 확실합니다.

rotateXaria-pressedJS 6줄
var btn = document.querySelector(".fx-card");
btn.addEventListener("click", function () {
  var open = btn.getAttribute("aria-pressed") === "true";
  btn.setAttribute("aria-pressed", String(!open));
  btn.classList.toggle("is-flipped", !open);
});

03반 접기

카드가 가운데 선을 기준으로 반으로 접히며 내용이 안쪽으로 숨습니다. 종이 카드를 닫듯이 여닫을 때 씁니다.

preserve-3drotateYtransform-origin
.fh-half--fold {
  transform-style: preserve-3d;
  transform-origin: left center;
  transition: transform $duration $easing;
}
@keyframes fh-fold {
  0%   { transform: rotateY(0); }
  50%  { transform: rotateY(-172deg); }
  100% { transform: rotateY(0); }
}
.fh-card.is-demo .fh-half--fold {
  animation: fh-fold ($duration * 2.4) $easing infinite;
}

04편지 펼치기

접혀 있던 카드가 편지를 펼치듯 위아래 두 면이 순서대로 펴지며 안의 내용이 드러납니다. 이벤트 초대장처럼 여는 느낌을 주고 싶을 때 씁니다.

rotateXtransition-delaytransform-origin: top
.ul-flap--top { transform-origin: top center; }
.ul-flap--bottom { transform-origin: bottom center; }
.ul-scene.is-demo .ul-flap--top {
  animation: ul-open-top ($duration * 2.6) $easing infinite;
}
.ul-scene.is-demo .ul-flap--bottom {
  animation: ul-open-bottom ($duration * 2.6) $easing infinite;
  animation-delay: 120ms;
}

05카드 더미 섞기

쌓여 있는 카드 더미에서 맨 위 카드가 뒤집히며 물러나 다음 카드가 보입니다. 리뷰나 추천 카드를 순서대로 넘겨볼 때 씁니다.

z-indexrotateYtranslateZ
.ss-card--2 { transform: translateZ(-16px) translateY($sp-2); opacity: .85; }
.ss-card--3 { transform: translateZ(-32px) translateY($sp-4); opacity: .65; }
@keyframes ss-shuffle {
  0%, 30%   { transform: translateZ(0) translateX(0) rotateY(0); opacity: 1; }
  70%       { transform: translateZ(-8px) translateX(-46px) rotateY(-150deg); opacity: .25; }
  71%, 100% { transform: translateZ(0) translateX(0) rotateY(0); opacity: 1; }
}
.ss-stack.is-demo .ss-card--1 { animation: ss-shuffle ($duration * 6) $easing infinite; }

06책장 넘기기

카드 오른쪽 끝을 기준으로 책장을 넘기듯 회전하며 다음 카드가 드러납니다. 그림자가 함께 움직여 종이가 들리는 느낌을 줍니다.

rotateYtransform-origin: rightbox-shadow
.bp-page {
  transform-style: preserve-3d;
  transform-origin: right center;
  transition: transform $duration $easing, box-shadow $duration $easing;
}
.bp-page__face { backface-visibility: hidden; }
.bp-page__face--back { transform: rotateY(180deg); }
.bp-book.is-demo .bp-page {
  animation: bp-turn ($duration * 2.6) $easing infinite;
}

07가격 공개 카드

카드에 마우스를 올리면 뒤집히며 할인된 가격이 뒷면에 나타납니다. 정가와 할인가를 한 카드에서 비교할 때 씁니다.

rotateYbackface-visibility: hidden180ms
.rp-card__inner {
  transform-style: preserve-3d;
  transition: transform $duration $easing;
}
.rp-card:hover .rp-card__inner { transform: rotateY(180deg); }
.rp-card__face {
  position: absolute;
  inset: 0;
  backface-visibility: hidden;
}
.rp-card__face--back { transform: rotateY(180deg); }

084면 큐브

카드 하나가 정육면체처럼 네 면을 가지고 있어, 클릭할 때마다 한 면씩 돌아갑니다. 이미지·설명·가격·구매 버튼을 한 카드 안에 담을 때 씁니다.

preserve-3dtranslateZrotateY
var cube = document.querySelector(".cb-cube");
var step = 0;
cube.addEventListener("click", function () {
  step = (step + 1) % 4;
  cube.style.setProperty("--cb-step", String(step));
});

09모션 줄임 판

같은 카드 뒤집기를 prefers-reduced-motion이 켜진 환경에서 재생하면 회전 대신 부드러운 교차 페이드로 바뀝니다. 이 항목은 그 전환을 직접 보여줍니다.

prefers-reduced-motionopacitytransition
@media (prefers-reduced-motion: reduce) {
  .rm-card:hover .rm-card__inner { transform: none; transition: none; }
  .rm-card:hover .rm-card__face--front {
    opacity: 0;
    transition: opacity $dur-base $ease-out;
  }
  .rm-card:hover .rm-card__face--back {
    opacity: 1;
    transform: none;
    transition: opacity $dur-base $ease-out;
  }
}

카드 뒤집기 애니메이션은 어디서 깨지는가

가장 먼저 눈에 걸린 건 03번 반 접기였습니다. backface-visibility: hidden을 걸어 두면 뒷면은 확실히 숨지만, 회전각이 정확히 90도에 가까운 프레임에서는 앞면이 아직 완전히 사라지기 전이라 원근에 짓눌린 아주 가는 글자 한 줄이 살짝 비쳐 보입니다 — hidden은 면이 90도를 넘어야 완전히 사라지고, 그 직전까지는 원래 각도 그대로 그려지기 때문입니다(MDN backface-visibility 문서 참고). 08번 큐브도 비슷한 함정이 있는데, 네 면을 이어 붙일 때 쓰는 translateZ 값이 카드 한 변의 절반과 정확히 같지 않으면 면과 면 사이에 얇은 틈이 생기거나 서로 겹쳐 보입니다. 아홉 개 파일이 담긴 zip의 압축 비밀번호는 zmahx9ys이고, 압축을 풀면 vanilla 폴더와 react 폴더가 각각 들어 있습니다.

접근성 — 아홉 개가 reduced-motion에서 남기는 것

시스템에서 동작 줄이기를 켜면 반응이 항목마다 다릅니다. 01·02·07번은 transform: none !important로 뒤집기 자체가 막혀, 호버·클릭을 해도 뒷면이 아예 안 보이고 앞면에 그대로 머뭅니다 — 뒷면 정보가 필요하다면 별도 버튼 등 다른 경로를 둬야 합니다. 08번은 클릭으로 상태가 바뀌는 것 자체는 그대로 두고 부드러운 전환만 꺼서, 클릭하면 순간적으로 툭 바뀝니다. 03·04·05·06번은 애초에 호버·클릭 트리거가 없는 자동 재생 루프뿐이라 동작 줄이기와 함께 꺼지는 대상 자체가 없습니다. 09번은 이 대응 자체가 주제라, 회전 대신 opacity 교차 페이드만 남기는 모습을 항상 보여주도록 만들었습니다.

카드 뒤집기 말고 다른 피드백 애니메이션이 궁금하다면 가격표 애니메이션 9종도 참고할 만하고, 전체 목록은 피드백 애니메이션 카테고리에서 모아 볼 수 있습니다.

FAQ — 자주 묻는 질문

카드 뒤집기 CSS는 모든 브라우저에서 똑같이 보이나요?

rotateY·rotateX와 preserve-3d는 최신 Chrome·Edge·Safari·Firefox에서 모두 동작합니다. 다만 아주 오래된 브라우저에서는 3D 회전 없이 카드 내용이 그냥 바뀌어 보일 수 있어, 결제처럼 중요한 화면에는 클릭으로도 같은 정보를 볼 수 있는 대안을 함께 두는 편이 안전합니다.

JS 없이 카드를 뒤집을 수 있나요?

네, 01·07번처럼 :hover로 발동하는 카드는 CSS만으로 충분합니다. 뒤집힌 상태를 계속 기억해야 하는 02·08번은 클릭할 때마다 상태를 저장해야 해서 자바스크립트 6~8줄이 필요합니다.

zip 안의 vanilla와 react 코드가 서로 다른가요?

화면에서 보이는 움직임 자체는 같습니다. vanilla 쪽은 SCSS 파일 맨 위에서 duration·easing·color 세 변수를 직접 고치고, react 쪽은 같은 값을 컴포넌트 props로 넘겨받아 --duration 같은 CSS 커스텀 속성에 연결한다는 점만 다릅니다.

압축 비밀번호를 입력하세요

비밀번호는 이 글 본문 안에 있습니다. 읽다 보면 나옵니다.