움직임이 좋다는 것을 배우고 나면 누구나 모든 것을 움직이고 싶은 욕심에 빠집니다. 하지만 넘치는 움직임은 사방에서 동시에 말을 거는 사람들 같아서, 하나하나는 반가워도 한꺼번에 겪으면 그저 소음이 되지요. 그 욕심이 화면을 어떻게 망치는지, 나쁜 예와 절제한 예를 CSS로 나란히 놓고 보겠습니다.
88.1 지속시간이 길면 굼떠 보인다
화려해 보이려고 전환을 길게 늘이면 사용자는 매번 그 움직임이 끝나기를 기다려야 합니다. 아래 버튼은 1.2s나 걸립니다.
.btn {
transition: transform 1.2s ease;
}
.btn:hover {
transform: scale(1.1);
}
손끝에 답하는 반응은 짧을수록 경쾌합니다. 지속시간을 .15s로 줄이고 크기 변화도 은은하게 낮추면 훨씬 빠릿빠릿해집니다.
.btn {
transition: transform .15s ease;
}
.btn:hover {
transform: scale(1.04);
}
자주 마주치는 반응일수록 짧고 미묘해야 합니다. 화려함은 그것을 마주하는 빈도와 반비례해야 한다는 것을 기억합니다.
88.2 모든 요소를 움직이지 않기
편하다는 이유로 전역 선택자에 all을 전환하면, 뜻하지 않은 속성까지 죄다 애니메이션되어 화면이 정신 사납고 무거워집니다.
* {
transition: all .3s ease;
}
움직일 요소와 움직일 속성을 콕 집어 정합니다. 무엇이 움직이는지 코드만 봐도 드러나고, 뜻밖의 전환도 사라집니다.
.btn,
.card,
.link {
transition: background-color .15s ease, transform .15s ease;
}
움직임은 고요한 바탕 위에 놓일 때 힘을 얻습니다. 화면 대부분을 차분하게 두어야 정작 중요한 움직임이 도드라집니다.
88.3 무한 반복으로 주의를 흩지 않기
눈길을 끌겠다고 요소를 끝없이 흔들면, 사용자는 글에 집중하지 못하고 그 움직임에 눈이 붙들립니다. 아래 버튼은 쉼 없이 좌우로 까딱거립니다.
@keyframes wobble {
0%, 100% { transform: rotate(-3deg); }
50% { transform: rotate(3deg); }
}
.cta {
animation: wobble 1s ease-in-out infinite;
}
정말 눈길이 필요한 순간에만, 클래스가 붙을 때 한두 번 까딱이고 멈추게 합니다. infinite 대신 정해진 횟수를 주는 것만으로 소음이 신호로 바뀝니다.
.cta.alert {
animation: wobble .4s ease-in-out 2;
}
필요할 때 짧게 두 번 흔들고 조용해지니, 그 움직임이 도리어 또렷하게 눈에 들어옵니다.
88.4 과한 딜레이로 반응을 늦추지 않기
멋을 부리려 시작 전에 긴 뜸을 두면, 사용자는 손을 댔는데도 한참 아무 일이 없어 고장인 줄 압니다. 아래 메뉴는 .6s나 기다립니다.
.menu {
transition: opacity .2s ease .6s;
}
손짓에 답하는 반응은 즉각 시작해야 합니다. 딜레이를 걷어 내면 누른 그 순간 화면이 대답합니다.
.menu {
transition: opacity .2s ease;
}
사용자가 서둘러 일을 처리하려는 자리일수록, 움직임이 길을 막지 않도록 더 조심합니다.
88.5 튀는 바운스 남발하지 않기
알림 하나가 요란하게 통통 튀며 끝없이 반복되면 즐거움은 금세 성가심으로 바뀝니다. 아래 토스트가 그런 경우입니다.
@keyframes bounce {
0% { transform: translateY(0); }
30% { transform: translateY(-30px); }
50% { transform: translateY(0); }
70% { transform: translateY(-15px); }
100% { transform: translateY(0); }
}
.toast {
animation: bounce .8s ease infinite;
}
같은 등장이라도 은은하게 한 번 떠오르는 페이드면 충분합니다. 조용히 나타났다 제 할 일을 하는 편이 훨씬 어른스럽습니다.
@keyframes fade-in {
from { opacity: 0; transform: translateY(6px); }
to { opacity: 1; transform: translateY(0); }
}
.toast {
animation: fade-in .2s ease-out;
}
6px만 올라오며 스르르 밝아지니 눈에 거슬리지 않으면서도 새 소식이 왔다는 사실은 또렷이 전해집니다.
88.6 스크롤마다 요란하게 등장하지 않기
내려갈 때마다 요소가 크게 솟구치고 비틀리며 나타나면, 읽는 흐름이 자꾸 끊깁니다. 아래는 과한 스크롤 등장입니다.
.reveal {
opacity: 0;
transform: translateY(60px) scale(0.8) rotate(-4deg);
transition: transform .8s ease, opacity .8s ease;
}
.reveal.in {
opacity: 1;
transform: none;
}
등장은 차분할수록 좋습니다. 이동 거리를 12px로 줄이고 회전과 확대를 걷어 내면, 거슬리지 않으면서 내용이 부드럽게 자리를 잡습니다.
.reveal {
opacity: 0;
transform: translateY(12px);
transition: opacity .3s ease, transform .3s ease;
}
.reveal.in {
opacity: 1;
transform: none;
}
은은한 페이드 하나면 충분합니다. 스크롤은 읽으러 내려가는 길이지 구경거리를 지나는 길이 아니니까요.
88.7 색을 쉼 없이 바꾸지 않기
배지 색을 끝없이 무지개처럼 돌리면 화려해 보이지만, 사용자의 눈은 그 자리에 붙들려 정작 할 일을 잊습니다. 아래가 그런 경우입니다.
@keyframes rainbow {
to { filter: hue-rotate(360deg); }
}
.badge {
animation: rainbow 3s linear infinite;
}
색은 상태가 바뀔 때만 잔잔하게 움직이면 충분합니다. 손을 올렸을 때 한 단계 짙어지는 정도가 눈에 편하고 뜻도 분명합니다.
.badge {
background: #2563eb;
transition: background-color .15s ease;
}
.badge:hover {
background: #1d4ed8;
}
쉼 없이 도는 색은 소음이고, 필요한 순간에만 바뀌는 색은 신호입니다. 움직임이든 색이든 절제할 때 비로소 제 몫을 합니다.
88.8 정리
과한 움직임은 즐거움을 소음으로, 도움을 방해로, 도드라짐을 소란으로 바꿉니다. 지속시간은 짧게, 움직일 것만 골라, 무한 반복과 긴 딜레이를 걷어 내고, 튀는 바운스 대신 은은한 페이드로 두는 절제가 그 답입니다. 마지막 안전망으로 움직임 줄이기 설정까지 존중해 준다면, 화면은 비로소 누구에게나 편안해집니다.
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: .01ms !important;
animation-iteration-count: 1 !important;
transition-duration: .01ms !important;
}
}