CSS 화면 전환 효과 9종 — 화면이 바뀌는 순간 모음
CSS 화면 전환 효과는 페이지를 새로 불러오지 않고 한 화면 안에서 무엇이 다른 모습으로 바뀔 때, 그 바뀜을 부드럽게 눈에 보여 주는 장치입니다.
자동 재생 · 눌러서 해당 항목으로 · 전부 한 zip
- 01 브라인드 살 열림 등장
- 02 폴라로이드 그네 등장
- 03 격자↔목록 뷰 전환
- 04 큰 이미지 슬라이드 교체
- 05 필터 칩 슬롯 채움
- 06 장바구니로 날아가는 담기
- 07 티켓 찢김 확인
- 08 단계 방향 슬라이드
- 09 대각선 장면 교체
아홉 개는 인기 순서가 아니라 방문자가 화면에서 바뀜을 만나는 순서로 놓았습니다. 처음 장면이 브라인드처럼 열리고(01) 사진이 그네처럼 걸려 들어오고(02), 둘러보는 방식이 격자와 목록 사이를 오가고(03) 눈길 끄는 큰 사진이 같은 자리에서 교체되고(04), 고른 조건이 칩으로 슬롯에 날아가 채워지고(05), 담은 상품이 점이 되어 장바구니까지 포물선을 그려 날아가고(06), 확인 순간 티켓이 찢어지고(07), 폼이 단계가 바뀌는 방향에 맞춰 흐르다가(08), 마지막에 장면이 대각선으로 쓸리듯 통째로 교체됩니다(09).
| 번호 | 이름 | 무엇이 움직이나 | JS |
|---|---|---|---|
| 01 | 브라인드 살 열림 등장 | 살 6개 scaleY 계단 접힘 |
없음 |
| 02 | 폴라로이드 그네 등장 | 카드 rotate 감쇠 진동 |
없음 |
| 03 | 격자↔목록 뷰 전환 | 카드 translate 좌표 |
없음 |
| 04 | 큰 이미지 슬라이드 교체 | 장면 translateX 교차 |
없음 |
| 05 | 필터 칩 슬롯 채움 | 칩 translate 비행 |
없음 |
| 06 | 장바구니로 날아가는 담기 | 점 이중 translate 포물선 |
없음 |
| 07 | 티켓 찢김 확인 | 두 반 rotate 분리 |
없음 |
| 08 | 단계 방향 슬라이드 | 패널 translateX ±100% |
없음 |
| 09 | 대각선 장면 교체 | skew 판 통과 |
없음 |
01브라인드 살 열림 등장
페이지가 열리면 가로 브라인드 여섯 개가 위쪽부터 차례로 접히며 덮고 있던 밝은 문구 패널을 드러냅니다. 히어로 문구나 이벤트 공지가 휙 하고 한 번에 바뀌는 게 아니라 살 하나씩 편안하게 열리는 것이 차이입니다.
.reveal__slat {
transform-origin: top center;
transform: scaleY(1);
animation: slat-open $duration $easing both;
}
@keyframes slat-open {
from { transform: scaleY(1); }
to { transform: scaleY(0); }
}
// 살마다 90ms 뒤로 밀어 계단처럼
.reveal__slat:nth-child(2) { animation-delay: 90ms; }
.reveal__slat:nth-child(3) { animation-delay: 180ms; }
02폴라로이드 그네 등장
상단 한 점에 매달린 폴라로이드 사진 카드가 기울어진 채 떨어져 내려와 좌우로 몇 번 흔들리다 반듯하게 멈춥니다. 사진 갤러리 첫 화면이나 여행 기록 썸네일에 붙이면 정적인 사진 페이지에 온기가 생깁니다.
.swing {
transform-origin: top center; // 핀 한 점이 회전 축
animation: swing-in $duration $easing both;
}
@keyframes swing-in {
0% { transform: translateY(-70px) rotate(18deg); opacity: 0; }
38% { transform: rotate(-11deg); }
56% { transform: rotate(7deg); }
72% { transform: rotate(-4deg); }
100% { transform: rotate(0deg); }
}
03격자↔목록 뷰 전환
같은 상품 카드 세 장이 가로로 나란히 놓은 격자 배치와 한 줄씩 쌓은 목록 배치 사이를 떠나지 않고 미끄러져 오갑니다. 토글 손잡이와 카드가 같은 박자로 함께 움직이므로 누르는 즉시 화면이 살아 있다는 느낌을 줍니다.
.vs__card { transition: transform $duration $easing; }
// % 는 카드 자기 크기 기준 — 격자: 오른쪽 열로 한 카드 폭만큼
.vs__card--2 { transform: translate(calc(100% + 8px), 0); }
.vs__card--3 { transform: translate(0, calc(100% + 10px)); }
// 목록: 같은 카드가 한 줄씩 아래로
#vs-list:checked ~ .vs__board .vs__card--2 { transform: translate(0, calc(100% + 10px)); }
#vs-list:checked ~ .vs__board .vs__card--3 { transform: translate(0, calc(200% + 20px)); }
04큰 이미지 슬라이드 교체
아래 썸네일을 고르면 지금 보이던 큰 사진은 왼쪽으로 빠지고 새 사진이 오른쪽에서 들어와 같은 자리를 차지합니다. 상품 상세 페이지에서 사진만 깜빡 바뀌는 대신 흐르듯 교체되어 고르는 재미가 붙습니다.
.gal__scene {
position: absolute; inset: 0;
transform: translateX(110%); // 대기: 오른쪽 밖
transition: transform $duration $easing, opacity $duration $easing;
}
#gal-2:checked ~ .gal__frame .gal__scene--2 { transform: translateX(0); }
#gal-2:checked ~ .gal__frame .gal__scene--1 { transform: translateX(-110%); } // 왼쪽으로 퇴장
05필터 칩 슬롯 채움
색상 칩을 고르면 그 칩이 제자리에서 위쪽 빈 점선 슬롯으로 통째로 날아가 크기 그대로 착지합니다. 필터를 골랐다는 것이 글자가 아니라 물건이 옮겨진 것으로 보이므로 쇼핑 화면에서 특히 잘 읽힙니다.
.pick__chip { transition: transform $duration $easing; }
// 칩마다 슬롯까지의 거리가 다르다 — 칩2는 96px 왼쪽·76px 위
#pick-2:checked ~ .pick__row .pick__chip--2 {
transform: translate(-96px, -76px);
z-index: 2; // 날아가는 칩이 위에 뜬다
}
#pick-2:checked ~ .pick__slot { border-color: rgba(255,255,255,.8); }
06장바구니로 날아가는 담기
담기 버튼을 누르면 상품색 원형 점 하나가 상품 카드에서 오른쪽 위 장바구니까지 포물선을 그리며 날아가 옅어지며 사라지고, 개수 배지가 통통 튀어 올라 담겼다는 것을 알립니다. 점이 떠나는 출발점과 도착점이 같은 화면에 있으니 사용자는 자신의 동작이 어디로 갔는지 눈으로 따라갑니다.
.cart__fly { transition: transform $duration $easing; } // 바깥 요소: x 등속
.cart__fly-y { transition: transform $duration $easing; } // 안 요소: y 이징
// 착지 좌표는 getBoundingClientRect 실측값 — 배지 중심에 맞춘 숫자다
#cart-add:checked ~ .cart__fly { transform: translateX(273px); opacity: 0; }
#cart-add:checked ~ .cart__fly .cart__fly-y { transform: translateY(-71px); }
#cart-add:checked ~ .cart__bag .cart__badge { transform: scale(1.35); }
07티켓 찢김 확인
확인을 누르는 순간 티켓 가운데 점선이 밝아지고 왼쪽 반과 오른쪽 반이 반대 방향으로 기울며 벌어져, 찢긴 두 쪽이 화면에서 따로 빠집니다. 주문 완료나 체크인 같은 되돌릴 수 없는 순간을 안내 문구 한 줄보다 확실하게 각인시킵니다.
.tear__half { transition: transform $duration $easing, opacity $duration $easing; }
#tear-ok:checked ~ .tear__ticket .tear__half--l {
transform: translate(-52px, 26px) rotate(-9deg); // 왼쪽 반: 왼쪽 아래로
opacity: 0;
}
#tear-ok:checked ~ .tear__ticket .tear__half--r {
transform: translate(44px, 30px) rotate(8deg); // 오른쪽 반: 오른쪽 아래로
opacity: 0;
}
08단계 방향 슬라이드
다단계 폼에서 다음 단계로 넘어가면 지금 패널은 왼쪽으로 빠지고 새 패널이 오른쪽에서 들어오며, 이전으로 돌아가면 방향이 정확히 반대가 됩니다. 화살표를 읽지 않아도 흐름 방향이 눈에 보이므로 몇 단계짜리 폼이든 길을 잃지 않습니다.
.wiz__panel { transition: transform $duration $easing, opacity $duration $easing; }
.wiz__panel--2 { transform: translateX(100%); opacity: 0; } // 2단계의 집: 오른쪽
// 뒤로 돌아가면 1단계는 왼쪽 집으로 빠지고 2단계는 오른쪽 집으로 돌아간다 — 방향이 반대가 된다
#wiz-2:checked ~ .wiz__window .wiz__panel--1 { transform: translateX(-100%); opacity: 0; }
#wiz-2:checked ~ .wiz__window .wiz__panel--2 { transform: translateX(0); opacity: 1; }
#wiz-1:checked ~ .wiz__window .wiz__panel--1 { transform: translateX(0); opacity: 1; }
09대각선 장면 교체
화면을 비스듬히 가른 두 판이 이어서 같은 방향으로 프레임을 쓸고 지나가면서 뒤에 숨어 있던 다음 장면을 한 번에 드러냅니다. 랜딩 페이지에서 계절이나 시즌 장면을 바꿀 때 페이지를 새로 고침하는 대신 이 동작 하나로 극적인 전환을 만들 수 있습니다.
.scene__wipe {
top: -20%; bottom: -20%; left: -45%;
width: 190%;
transform: skewX(-14deg) translateX(-115%); // 비스듬히 눕힌 판
transition: transform $duration $easing;
}
#scene-swap:checked ~ .scene__frame .scene__wipe { transform: skewX(-14deg) translateX(115%); }
.scene__inner { transition: opacity $dur-quick linear calc(#{$duration} / 2); } // 판이 덮는 중간에 교체
어디서 깨지는가 — 함정 하나
06 장바구니 비행을 처음 만들 때 점이 분명히 움직이는데 도착점이 어딘가 어긋나 있었습니다. 눈짐작으로 카드에서 장바구니까지 거리를 계산해 translateX(247px)를 넣었던 것인데, Playwright로 두 요소의 실제 좌표를 재니 장바구니 배지의 중심이 예상보다 오른쪽에 있었고 점은 배지에 26px 못 미쳐 허공에 사라지고 있었습니다. getBoundingClientRect로 출발점과 도착점 중심을 재어 translateX(273px)·translateY(-71px)로 고쳤더니 점이 배지 한가운데에 정확히 착지했습니다. 같은 검사에서 장바구니 손잡이를 그리는 가상 요소가 부모 위치 기준을 못 잡아 무대 왼쪽 위에 떠 있던 것도 발견해 부모에 position: relative를 한 줄 넣어 붙였습니다. 날아가는 동작은 출발점과 도착점을 눈으로 맞추지 말고 좌표로 재야 합니다. 이 아홉 개 파일의 압축 비밀번호는 2aq7sbjt이며 전부 그런 실측을 거친 뒤에 올라온 것입니다.
접근성
아홉 개 전부 prefers-reduced-motion: reduce에서는 움직임만 끄고 결과 상태를 남깁니다. 01은 브라인드가 접힌 채 문구가 바로 보이고, 02는 사진이 흔들리지 않고 반듯하게 걸리며, 03·04·08은 카드·장면·패널이 전환 없이 즉시 목표 배치로 바뀌고, 05는 칩이 즉시 슬롯에 얹히며, 06은 점이 순간 이동하고 배지만 커지며, 07은 티켓이 찢어지는 연출 없이 즉시 사라지고, 09는 판이 통과하지 않고 장면만 바뀝니다. 클릭 동작은 전부 실제 라디오·체크박스와 연결된 라벨이라 키보드와 스크린 리더로도 조작되고, 장식 요소는 aria-hidden으로 읽는 순서에서 뺐습니다. transform 전환이 왜 부드러운지는 MDN transform 문서에서, 움직임 줄이기 설정의 의미는 MDN prefers-reduced-motion 문서에서 확인할 수 있습니다. 페이지 사이를 넘어가는 전환은 이전 글 CSS 페이지 전환 애니메이션 9종에, 탭 패널 전환은 탭 메뉴 전환 모음에 따로 정리했습니다.
FAQ
자바스크립트를 정말 한 줄도 안 써도 됩니까?
이 아홉 개는 전부 CSS만으로 돌아갑니다. 클릭이 필요한 것은 화면에 보이지 않는 라디오·체크박스를 두고 :checked 상태로 CSS가 따라가게 만든 방식이라, 붙여 넣는 순간 그대로 동작합니다. React 판은 상태 관리를 위해 useState를 씁니다.
기존 사이트에 붙이려면 무엇을 바꿔야 합니까?
항목마다 움직이는 부분의 클래스 이름과 거리 값만 여러분 화면 크기에 맞게 고치면 됩니다. 특히 05 칩 비행과 06 장바구니 비행은 칩·카드·장바구니의 실제 위치에 따라 translate 숫자가 달라지니, 개발자 도구에서 요소 좌표를 재고 넣는 것이 정확합니다.
뷰 전환 API로도 같은 효과를 낼 수 있습니까?
뷰 전환 API는 문서나 큰 덩어리가 통째로 바뀌는 순간에 쓰는 브라우저 기능이라 이 글의 작은 상태 전환에는 맞지 않고, 아직 브라우저마다 지원 정도가 다릅니다. 이 글의 아홉 개는 지원 편차 없이 어디서나 같은 모습으로 돌아가도록 transform과 opacity만 썼습니다.