Motion
모션은 무엇이 왜 바뀌었는지, 다음에 무엇을 하면 되는지 이해하도록 도와요.
popup.enter.top / exit.top
listitem.hovered (50ms)
백로그
활성 스프린트
보고서
input.toggle (150ms)이메일 알림 켜짐
나갈 때(100ms)가 들어올 때(150ms)보다 짧아요. 운영체제에서 동작 줄이기를 켜면 바로 바뀌어요.
원칙
- 사람답게 — 움직임은 자연스럽고 은은하고 리듬감 있게. 기계적이지 않고 따뜻하게 느껴져야 해요.
- 분명하게 — 모션은 장식이 아니라 이해를 돕는 층이에요. 시선을 이끌고 흐름을 이어 줘요.
- 누구나 편하게 — 이해를 돕되 불편을 주지 않아요. 움직임을 줄이거나 끌 수 있어야 해요.
- 빠르게 — 속도감과 흐름을 살리고, 팀이 일하는 속도를 늦추지 않아요.
기본 요소
모든 모션은 지속 시간(duration), 가속 곡선(curve), 속성(property) 세 가지로 정해요. Steel 토큰은 컴포넌트와 이벤트 단위로 이름이 붙어 있어서(motion.popup.enter.top), 의도에 맞는 토큰을 고르면 시간과 곡선이 함께 정해져요.
지속 시간
| 종류 | 범위 | 예 |
|---|---|---|
| 상호작용 | 50–150ms | 호버·눌림 상태. 목록 항목 호버 50ms, 버튼 호버 150ms |
| 전환 | 150–400ms | 요소의 등장·퇴장·이동. 드롭다운 등장 150ms, 모달 등장 250ms |
큰 요소일수록 비례감이 느껴지도록 조금 더 길게 써요.
가속 곡선
Ease-out bold · 들어올 때
빠르게 들어와 부드럽게 멈춰요. 패널·플래그 등장.
cubic-bezier(0, 0.4, 0, 1)Ease-out practical · 들어올 때
일상적이고 은은한 등장. 팝업, 호버 배경 전환.
cubic-bezier(0.4, 1, 0.6, 1)Ease-in practical · 나갈 때
천천히 출발해 빠르게 사라져요. 길을 비켜 주는 퇴장.
cubic-bezier(0.6, 0, 0.8, 0.6)Ease-in-out bold · 이동·크기
부드럽게 시작해 살며시 자리잡아요. 모달 확대, 재배치.
cubic-bezier(0.4, 0, 0, 1)곡선 차이가 잘 보이도록 800ms 로 늘려서 재생해요. 실제 토큰은 50–250ms 예요.
속성
| 속성 | 뜻 |
|---|---|
| Scale | 크기가 커지거나 작아져요 |
| Fade | 투명도가 바뀌어요 |
| Slide | X·Y 축으로 움직여요 |
| Color | 배경이나 경계선 색이 바뀌어요 |
토큰
| 토큰 | Duration | Curve | Tailwind | 설명 |
|---|---|---|---|---|
| motion.avatar.enter | 150ms | Ease-out practical | Use for avatar group enter transitions. | |
| motion.avatar.exit | 100ms | Ease-in practical | Use for avatar group exit transitions. | |
| motion.avatar.hovered | 250ms | Spring (linear) | Use for hover state on avatar elements. | |
| motion.blanket.enter | 250ms | Ease-in-out bold | Use for blanket enter transitions. | |
| motion.blanket.exit | 200ms | Ease-in practical | Use for blanket exit transitions. | |
| motion.button.hovered | 150ms | Ease-out practical | Use for button hover state transitions. | |
| motion.button.pressed | 150ms | Ease-out practical | Use for button pressed state transitions. | |
| motion.flag.enter | 250ms | Ease-out bold | Use for flag enter transitions. | |
| motion.flag.exit | 200ms | Ease-in practical | Use for flag exit transitions. | |
| motion.flag.reposition | 250ms | Ease-in-out bold | Use for repositioning flag elements. | |
| motion.form.message.enter | 150ms | Ease-out practical | Use when a form message enters. | |
| motion.form.message.exit | 100ms | Ease-in practical | Use when a form message exits. | |
| motion.input | 150ms | Ease-out practical | Use for input hover, focus, and error state transitions. | |
| motion.input.selection | 150ms | Ease-out practical | Use for checkbox and radio surface, border and indicator state cross-fades. | |
| motion.input.toggle | 150ms | Ease-out practical | Use for toggle track colour, handle slide and icon transitions. | |
| motion.label.enter | 150ms | Ease-out practical | Use for label enter transitions (e.g. tag component). | |
| motion.label.exit | 100ms | Ease-in practical | Use for label exit transitions (e.g. tag component). | |
| motion.listitem.hovered | 50ms | Ease-out practical | Use for list item hover state transitions. | |
| motion.listitem.pressed | 100ms | Ease-out practical | Use for list item pressed state transitions. | |
| motion.listitem.selected | 100ms | Ease-out practical | Use for list item selected state transitions. | |
| motion.modal.enter | 250ms | Ease-in-out bold | Use for modal enter transitions. | |
| motion.modal.exit | 200ms | Ease-in practical | Use for modal exit transitions. | |
| motion.panel.content.enter | 150ms | Ease-out practical | Use for panel content enter. | |
| motion.panel.content.exit | 50ms | Ease-in practical | Use for panel content exit. | |
| motion.panel.enter | 250ms | Ease-out bold | Use for panel enter transitions. | |
| motion.panel.enter.left | 250ms | Ease-out bold | Use for panel enter from the left. | |
| motion.panel.enter.right | 250ms | Ease-out bold | Use for panel enter from the right. | |
| motion.panel.exit | 200ms | Ease-out bold | Use for panel exit transitions. | |
| motion.panel.exit.left | 200ms | Ease-out bold | Use for panel exit towards the left. | |
| motion.panel.exit.right | 200ms | Ease-out bold | Use for panel exit towards the right. | |
| motion.popup.enter.bottom | 150ms | Ease-out practical | Use for popup enter from the bottom. | |
| motion.popup.enter.left | 150ms | Ease-out practical | Use for popup enter from the left. | |
| motion.popup.enter.right | 150ms | Ease-out practical | Use for popup enter from the right. | |
| motion.popup.enter.top | 150ms | Ease-out practical | Use for popup enter from the top: popup, tooltip, dropdown, inline message, inline dialog. | |
| motion.popup.exit.bottom | 100ms | Ease-in practical | Use for popup exit toward the bottom. | |
| motion.popup.exit.left | 100ms | Ease-in practical | Use for popup exit toward the left. | |
| motion.popup.exit.right | 100ms | Ease-in practical | Use for popup exit toward the right. | |
| motion.popup.exit.top | 100ms | Ease-in practical | Use for popup exit toward the top. | |
| motion.sidenav.enter.left | 250ms | Ease-out bold | Use for side nav enter from the left. | |
| motion.sidenav.enter.right | 250ms | Ease-out bold | Use for side nav enter from the right. | |
| motion.sidenav.exit.left | 200ms | Ease-out bold | Use for side nav exit towards the left. | |
| motion.sidenav.exit.right | 200ms | Ease-out bold | Use for side nav exit towards the right. | |
| motion.spotlight.enter | 250ms | Ease-in-out bold | Use for spotlight enter transitions. | |
| motion.spotlight.exit | 200ms | Ease-in practical | Use for spotlight exit transitions. | |
| motion.tab | 150ms | Ease-out practical | Use for tab text color and neutral indicator opacity transitions. | |
| motion.tab.indicator.enter.left | 150ms | Ease-out bold | Use for tab indicators entering from the left. | |
| motion.tab.indicator.enter.right | 150ms | Ease-out bold | Use for tab indicators entering from the right. | |
| motion.tab.indicator.exit.left | 100ms | Ease-in practical | Use for tab indicators exiting toward the left. | |
| motion.tab.indicator.exit.right | 100ms | Ease-in practical | Use for tab indicators exiting toward the right. |
<div className="transition-[opacity,translate] duration-(--ds-motion-popup-enter-top-duration) ease-(--ds-motion-popup-enter-top-curve) motion-reduce:transition-none">
…
</div>사용 규칙
요소 하나
- 핵심 UI 의 모션은 빠르고 은은하게 해서, 상태 변화를 보강하되 시선을 빼앗지 않아요.
- 피드백 상태는 메시지가 잘 전달되도록 조금 더 표현적으로 써요.
- 요소 크기에 맞춰요. 작은 요소는 빠르고 담백하게, 패널과 모달은 조금 더 시간을 줘요.
전체 경험
- 모션은 시선을 이끌거나, 동작에 피드백을 주거나, 온보딩 같은 특별한 순간을 표현할 때 써요.
- "이 모션을 빼면 사용자가 정보나 맥락을 잃나요?" 라고 물어 보세요. 아니라면 빼도 돼요.
- 자주 보는 모션은 짧게 — 하루에 수십 번 보는 상호작용은 150ms 를 넘지 않게 해요.
- 나갈 때는 더 빠르게 — 닫힌 요소가 사용자의 다음 동작을 막지 않도록 퇴장을 등장보다 짧게 해요.
- 바로 시작하기 — 지연이 느껴지지 않도록 모션은 즉시 시작해요.
Do· 한 번에 하나의 초점
여러 요소가 움직일 때는 하나가 이끌고 나머지는 거들게 해요.
Don't· 이유 없이 기다리게 하지 마세요
다음 단계를 막는 긴 장식 모션, 동시에 여러 방향으로 시선을 끄는 애니메이션은 피해요.
동작 줄이기
- 운영체제의 동작 줄이기 설정을 존중해요. 설정이 켜져 있으면 모션을 끄고 즉시 바뀌게 해요. Tailwind 에서는
motion-reduce:transition-none,motion-reduce:animate-none을 붙여요. - 깜빡이거나, 빠르게 흔들리거나, 화면의 넓은 영역을 쓸고 지나가는 모션은 쓰지 않아요.
- 모든 모션을 꺼도 화면을 온전히 쓸 수 있는지 확인해요.