예제 하나로 보는 transition
transition은 브라우저에게 "이 속성이 바뀌면 즉시 바꾸지 말고 이만큼의 시간 동안 애니메이션해"라고 알려 줘요. :hover에만 두지 말고 요소의 일반 규칙에 두세요.
첫 번째 버튼에 마우스를 올려 보세요. 0.4초 동안 색이 초록으로 바뀌고 4px 올라가며, 포인터를 떼면 같은 트랜지션이 거꾸로 재생돼요. 두 번째 버튼은 transition: none이라서 즉시 바뀌어요. 키보드 사용자는 :focus-visible로 같은 효과를 받아요.
transition 단축 속성
transition: background-color 0.3s ease-in-out 0.1s;
/* property duration timing delay */
| 개별 속성 | 정하는 것 | 기본값 |
|---|---|---|
transition-property | 애니메이션할 속성(all 또는 목록) | all |
transition-duration | 얼마 동안, s나 ms로 | 0s |
transition-timing-function | 속도 곡선 | ease |
transition-delay | 시작 전 대기(음수도 가능) | 0s |
지속 시간의 기본값이 0s라서 지정하기 전까지는 아무것도 애니메이션되지 않아요. 단축 속성에서 첫 번째 시간은 항상 지속 시간이고 두 번째는 지연이에요. 속성마다 다른 설정을 주려면 쉼표로 구분하세요.
.card {
transition: transform 0.2s ease-out, box-shadow 0.4s ease-out;
}
타이밍 함수 나란히 보기
타이밍 함수는 트랜지션 동안 속도가 어떻게 바뀔지 정해요. Play를 누르면 일곱 개 막대가 모두 같은 1.5초 동안 같은 거리를 움직여요.
| 타이밍 함수 | 느낌 |
|---|---|
linear | 일정한 속도, 기계적 |
ease (기본값) | 빠르게 시작, 부드럽게 멈춤 |
ease-in | 느리게 시작, 빠르게 끝: 떠나는 것에 어울려요 |
ease-out | 빠르게 시작, 느리게 끝: 도착하는 것에 어울려요 |
ease-in-out | 양 끝에서 느림 |
cubic-bezier(x1, y1, x2, y2) | 나만의 곡선. y가 1보다 크면 지나쳤다가 돌아와요 |
steps(n) | 중간 프레임 없이 n개의 같은 단계로 뛰어요 |
hover 효과와 메뉴에는 150ms에서 300ms 사이의 ease-out이 반응이 빠르게 느껴져요.
트랜지션할 수 있는 것
중간값이 있는 속성은 트랜지션할 수 있어요. 색, 길이(width, padding, margin), opacity, transform, box-shadow, border-radius, filter, background-color예요. display나 font-family처럼 키워드 사이를 뛰는 속성은 즉시 바뀌어요.
height: auto가 가장 많이 부딪히는 경우예요. height: 0에서 height: auto로 열리는 패널은 기본적으로 애니메이션되지 않아요. 브라우저가 0과 "auto" 사이의 값을 계산하지 않기 때문이에요. 믿을 만한 우회 방법은 행 하나가 0fr에서 1fr로 가는 grid 래퍼예요.
aria-expanded는 스크린 리더 사용자에게 패널이 열렸는지 알려 줘요. 애니메이션만으로는 알 수 없어요.
트랜지션이 끝날 때 코드 실행하기
transitionend 이벤트는 트랜지션이 끝날 때 속성마다 한 번씩 발생해요. 포커스를 옮기거나 요소를 지우는 것처럼 움직임 뒤에 뭔가를 할 때 쓰세요.
움직임 줄이기 설정 존중하기
화면의 움직임 때문에 어지럽거나 산만해져서 시스템 설정에서 "동작 줄이기"를 켜는 사람들이 있어요. 미디어 쿼리로 색과 불투명도 페이드는 남기고 큰 움직임은 빼세요.
@media (prefers-reduced-motion: reduce) {
.btn { transition: background-color 0.2s ease; } /* no movement, only the color */
}
트랜지션을 부드럽게 유지하기
transform과 opacity는 애니메이션 비용이 가장 적은 속성이에요. 브라우저가 레이아웃을 다시 계산하지 않고 요소를 옮기고 흐리게 할 수 있어요. width, height, top, margin을 트랜지션하면 매 프레임마다 페이지 레이아웃을 다시 해서 느린 휴대폰에서 끊길 수 있어요. 무언가를 옮길 때는 left나 margin보다 transform: translate()를 쓰세요. 함수들은 transform 페이지에서 다뤄요.
자주 하는 실수
:hover규칙에만transition. 효과가 들어갈 때는 애니메이션되지만 hover가 끝나면 확 돌아와요. 기본 규칙에transition을 두세요.- 단위 없는 지속 시간.
transition: opacity 300은 잘못된 값이에요.300ms나0.3s로 쓰세요. transition: all. 의도하지 않은 것까지 바뀌는 모든 속성을 애니메이션하고, 레이아웃 변화를 굼뜨게 만들 수 있어요. 속성을 나열하세요.display: none이 페이드될 거라고 기대하기.display는 즉시 바뀌어요. 대신opacity를 페이드하세요(페이드가 끝나면visibility: hidden을 지정하세요).height: auto로 트랜지션하기. 뛰어 버려요. 위의grid-template-rows기법을 쓰세요.- 작은 상호작용에 긴 지속 시간. 버튼이나 메뉴에서 약 400ms를 넘으면 느리게 느껴져요.
자주 묻는 질문
CSS transition은 어떻게 동작하나요?
요소에 어떤 속성을 얼마 동안 애니메이션할지 선언해요. 예를 들면 transition: background-color 0.3s ease;예요. 그 속성의 값이 바뀌면(:hover, 클래스, JavaScript로) 브라우저가 즉시 바꾸는 대신 옛 값에서 새 값으로 애니메이션해요.
transition 단축 속성의 값 순서는 어떻게 되나요?
transition: property duration timing-function delay;예요. 예를 들면 transition: opacity 0.3s ease-in 0.1s;예요. 엄격한 규칙은 첫 번째 시간 값이 지속 시간이고 두 번째가 지연이라는 것뿐이에요. 여러 트랜지션은 쉼표로 구분하세요.
CSS transition이 동작하지 않는 이유는 무엇인가요?
흔한 원인이에요. transition이 :hover 규칙에만 있거나(들어갈 때만 애니메이션되고 확 돌아와요), 애니메이션할 수 없는 속성이거나(display, 또는 auto로 가는 height), 지속 시간에 단위가 없거나(0.3s 대신 0.3), 요소가 페이지에 추가되는 순간 값이 바뀌는 경우예요.
height: auto를 트랜지션할 수 있나요?
모든 브라우저에서 일반 height 트랜지션으로는 안 돼요. auto로 보간하려면 새 속성 interpolate-size가 필요한데, 아직 모든 곳에서 지원하지는 않아요. 흔한 우회 방법은 grid 래퍼의 grid-template-rows를 0fr에서 1fr로 애니메이션하거나, max-height를 콘텐츠보다 큰 값으로 애니메이션하는 거예요.
CSS에서 transition과 animation은 무엇이 다른가요?
transition은 속성이 바뀔 때 두 값 사이에서 한 번 실행돼요. animation은 @keyframes를 쓰고, 단계를 여러 개 가질 수 있으며, 페이지가 로드되면 스스로 시작하고, 반복하거나 무한히 돌 수 있어요.