Tailwind CSS transition, animation, motion-reduce 사용 기준 정리

2026.04.29·수정 2026.09.13·약 20분·작성: 해비·블로그 소개

이 글에서 정리하는 내용

Tailwind CSS v4 기준으로 transition, animation, motion-reduce를 정리합니다. 버튼 hover처럼 상태가 바뀌는 움직임, 로딩 스피너처럼 시간 흐름을 가진 움직임, 사용자의 모션 감소 설정을 고려해야 하는 움직임을 구분해 봅니다.

움직임 효과를 넣을 때 먼저 나눌 기준

Tailwind CSS에서 transition과 animation을 상태 변화와 시간 흐름 기준으로 구분하는 인포그래픽

Tailwind CSS에서 움직임을 넣을 때 처음 눈에 들어오는 것은 클래스 이름입니다. transition, duration-300, ease-in-out, animate-spin, animate-pulse처럼 바로 붙일 수 있는 유틸리티가 많습니다. 그런데 실제 화면에서는 클래스 이름보다 먼저 봐야 할 것이 있습니다. 이 움직임이 사용자의 행동에 반응하는 변화인지, 화면이 스스로 반복하는 동작인지, 줄여도 되는 장식인지부터 나눠야 합니다.

버튼 hover는 사용자가 마우스를 올렸을 때 색상이 바뀌는 짧은 변화입니다. 카드 hover는 그림자와 위치가 함께 바뀌는 상태 변화입니다. 로딩 스피너는 사용자의 hover와 상관없이 계속 회전해야 하는 시간 기반 동작입니다. 알림 배지의 ping 효과는 사용자의 시선을 끌기 위한 반복 애니메이션입니다. 모두 움직임이지만 처리 기준은 다릅니다.

이 차이를 나누지 않고 모든 요소에 transition duration-300 ease-in-out을 붙이면 처음에는 화면이 부드러워 보입니다. 하지만 버튼, 카드, 모달, 드롭다운, 알림까지 늘어나면 페이지 전체가 계속 흔들리는 느낌이 날 수 있습니다. 특히 transform 기반 움직임이 여러 곳에 들어가면 사용자가 내용을 읽기 전에 UI가 먼저 시선을 빼앗습니다.

그래서 Tailwind에서 motion을 다룰 때는 세 가지 질문을 먼저 둡니다. 첫째, 이 변화는 hover, focus, open 상태처럼 값이 바뀌는 순간인가. 그렇다면 transition 계열이 중심입니다. 둘째, 이 효과는 로딩이나 알림처럼 시간 흐름 자체가 의미를 가지는가. 그렇다면 animate-* 계열이 맞습니다. 셋째, 이 움직임은 사용자의 모션 감소 설정에서 줄여야 하는가. 여기서 motion-safe:motion-reduce:를 같이 검토합니다.

구분 어울리는 상황 대표 클래스
transition hover, focus, open, disabled처럼 상태가 바뀌는 순간 transition-colors, transition-transform, duration-200
animation 로딩, 알림, pulse처럼 시간 흐름이 있는 동작 animate-spin, animate-pulse, animate-ping
motion-reduce 사용자가 모션 감소 설정을 켰을 때 움직임을 줄이는 처리 motion-reduce:animate-none, motion-safe:animate-spin

transition은 상태 변화에 붙이는 기준

transition은 요소의 값이 바뀔 때 그 변화를 끊어 보이지 않게 이어주는 역할을 합니다. 버튼 배경색이 바뀌거나, 카드 그림자가 강해지거나, 드롭다운이 투명도와 위치를 바꾸며 열리는 상황이 여기에 들어갑니다. 기준은 “움직임을 넣고 싶다”가 아니라 “상태 변화가 갑자기 끊겨 보이는가”입니다.

자주 생기는 문제는 transition을 기본값처럼 붙이는 습관입니다. 작은 버튼 하나에서는 큰 문제가 없어 보이지만, 컴포넌트가 커지면 의도하지 않은 속성까지 전환 대상이 될 수 있습니다. 색상만 바뀌는 버튼이라면 transition-colors가 더 분명하고, 카드 위치만 살짝 움직일 계획이라면 transition-transform이 더 읽기 쉽습니다.

버튼은 색상 변화부터 좁혀 보기

부분 예제 — 앞뒤 설명에 해당하는 코드만 적용하세요. 다른 예제와 합친 전체 프로젝트가 아닙니다.

<button
  class="rounded-lg bg-slate-900 px-4 py-2 text-sm font-medium text-white transition-colors duration-200 hover:bg-slate-700 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-slate-900 disabled:cursor-not-allowed disabled:opacity-50"
>
  저장하기
</button>

이 버튼에서 실제로 부드럽게 바뀌어야 하는 값은 배경색입니다. 그래서 transition 전체보다 transition-colors가 더 맞습니다. duration-200은 사용자가 hover 상태를 바로 인식할 정도로 짧고, 버튼 클릭 흐름을 방해하지 않는 수준입니다. 저장 버튼처럼 반복해서 누르는 UI라면 화려한 움직임보다 반응이 빠르게 느껴지는 쪽이 더 중요합니다.

focus-visible은 transition보다 우선순위가 높게 봐야 하는 상태입니다. 키보드 사용자가 현재 포커스 위치를 확인해야 하므로 outline을 과하게 흐리거나 늦게 나타나게 만들면 조작 흐름이 나빠집니다. hover는 부드럽게 처리하더라도 focus 표시 자체는 분명해야 합니다.

카드 hover는 transform이 정말 필요한지 먼저 보기

부분 예제 — 앞뒤 설명에 해당하는 코드만 적용하세요. 다른 예제와 합친 전체 프로젝트가 아닙니다.

<a
  href="/products/coffee-mug"
  class="group block rounded-2xl border border-slate-200 bg-white p-5 shadow-sm transition duration-200 ease-out hover:-translate-y-1 hover:shadow-md focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-slate-900 motion-reduce:hover:translate-y-0"
>
  <span class="block text-sm text-slate-500">Kitchen</span>
  <strong class="mt-2 block text-lg text-slate-950">Ceramic mug</strong>
  <span
    class="mt-3 block text-sm text-slate-600 transition-colors group-hover:text-slate-900"
    >자세히 보기</span
  >
</a>

카드 hover에서 hover:-translate-y-1은 눈에 잘 띕니다. 다만 카드가 많은 그리드에서는 모든 카드가 위아래로 움직일 수 있으므로 화면이 쉽게 산만해집니다. 상품 목록처럼 카드가 반복되는 화면이라면 transform을 약하게 쓰거나, 그림자와 border 변화만으로 hover를 표현하는 방식도 후보가 됩니다.

여기서는 motion-reduce:hover:translate-y-0도 함께 넣었습니다. 사용자의 모션 감소 설정을 고려하면 hover 상태에서 카드가 위로 움직이는 효과를 제거할 수 있습니다. 색상이나 그림자 변화까지 모두 없앨 필요는 없지만, 위치 이동처럼 시야 흐름에 영향을 주는 motion은 줄이는 기준을 남겨두는 게 좋습니다.

animation은 시간 흐름이 있는 동작에 쓰는 기준

animation은 상태가 바뀌는 순간보다 시간 흐름 자체가 의미를 가질 때 사용합니다. 로딩 중인 아이콘이 계속 회전하거나, 알림 배지가 퍼지듯 강조되거나, skeleton UI가 pulse처럼 흐르는 경우입니다. 사용자의 hover가 없어도 움직임이 계속되거나, keyframes 흐름이 필요한 경우라고 보면 됩니다.

Tailwind의 기본 animation 유틸리티는 자주 쓰이는 패턴을 빠르게 적용하기 좋습니다. animate-spin은 로딩 아이콘, animate-ping은 알림 강조, animate-pulse는 로딩 placeholder, animate-bounce는 짧은 주의 환기에 자주 쓰입니다. 다만 기본 제공 클래스가 있다고 해서 모든 상태에 넣을 필요는 없습니다. 반복 애니메이션은 한 화면 안의 개수와 지속 시간을 같이 봐야 합니다.

로딩 스피너는 움직임을 켜는 조건도 같이 보기

부분 예제 — 앞뒤 설명에 해당하는 코드만 적용하세요. 다른 예제와 합친 전체 프로젝트가 아닙니다.

<button
  class="inline-flex items-center gap-2 rounded-lg bg-slate-900 px-4 py-2 text-sm font-medium text-white disabled:cursor-wait disabled:opacity-70"
  disabled
>
  <svg class="size-4 motion-safe:animate-spin" viewBox="0 0 24 24" aria-hidden="true">
    <circle
      class="opacity-25"
      cx="12"
      cy="12"
      r="10"
      stroke="currentColor"
      stroke-width="4"
      fill="none"
    ></circle>
    <path
      class="opacity-75"
      fill="currentColor"
      d="M4 12a8 8 0 018-8v4a4 4 0 00-4 4H4z"
    ></path>
  </svg>
  저장 중
</button>

로딩 스피너는 animate-spin을 가장 자주 떠올리는 예시입니다. 하지만 여기서는 바로 animate-spin을 쓰지 않고 motion-safe:animate-spin을 붙였습니다. 사용자가 모션 감소를 요청하지 않은 환경에서만 회전 애니메이션을 켜겠다는 의미가 더 분명합니다.

로딩 상태를 꼭 회전으로만 표현할 필요는 없습니다. 버튼 텍스트를 “저장 중”으로 바꾸고, disabled 상태를 함께 처리하면 모션이 없어도 사용자는 상태를 이해할 수 있습니다. motion 대응은 애니메이션을 끄는 데서 끝나지 않고, 움직임이 사라져도 상태 정보가 남아 있는지까지 확인해야 합니다.

알림 배지는 강조가 필요한 곳에만 제한하기

부분 예제 — 앞뒤 설명에 해당하는 코드만 적용하세요. 다른 예제와 합친 전체 프로젝트가 아닙니다.

<span class="relative inline-flex">
  <span
    class="absolute inline-flex h-full w-full rounded-full bg-rose-400 opacity-75 motion-safe:animate-ping"
  ></span>
  <span class="relative inline-flex size-3 rounded-full bg-rose-500"></span>
</span>

animate-ping은 작은 영역에서도 시선을 강하게 끕니다. 그래서 알림 배지, 실시간 상태, 새 메시지처럼 사용자가 놓치면 안 되는 변화에 제한해서 쓰는 편이 낫습니다. 리스트의 모든 항목에 ping 효과를 넣으면 강조가 아니라 소음에 가까워집니다.

반복 애니메이션은 한 화면에서 개수를 세어보는 습관이 필요합니다. 스피너 하나는 괜찮지만, skeleton 카드 10개에 모두 pulse가 들어가고 알림 배지까지 ping이 돌면 페이지가 계속 흔들리는 느낌을 줍니다. 실제 UI에서는 “움직이는 요소가 동시에 몇 개인가”가 체감 품질에 영향을 줍니다.

motion-reduce는 접근성 설정을 반영하는 기준

motion-reduce는 단순히 애니메이션을 싫어하는 사용자를 위한 옵션이 아닙니다. 사용자가 운영체제나 브라우저 환경에서 모션 감소를 설정했다면, 웹 UI도 그 선호를 반영해야 합니다. 강한 이동, 회전, 확대, 시차 효과는 일부 사용자에게 피로감을 줄 수 있습니다.

Tailwind에서는 motion-reduce:motion-safe: 변형을 사용해 reduced motion 환경을 나눌 수 있습니다. motion-safe:는 모션 감소 요청이 없는 환경에서만 움직임을 적용하고, motion-reduce:는 모션 감소 환경에서 대체 스타일을 적용할 때 사용합니다.

움직임을 없앨지, 줄일지, 대체할지 나누기

부분 예제 — 앞뒤 설명에 해당하는 코드만 적용하세요. 다른 예제와 합친 전체 프로젝트가 아닙니다.

<div
  class="rounded-2xl border border-slate-200 bg-white p-6 opacity-0 transition duration-300 ease-out data-[open=true]:opacity-100 motion-safe:data-[open=true]:translate-y-0 motion-safe:data-[open=false]:translate-y-2 motion-reduce:transition-opacity"
  data-open="true"
>
  <h3 class="text-lg font-semibold text-slate-950">필터 설정</h3>
  <p class="mt-2 text-sm text-slate-600">카테고리와 정렬 기준을 선택합니다.</p>
</div>

모달이나 패널이 열릴 때 아래에서 위로 올라오는 효과는 흔히 쓰입니다. 하지만 reduced motion 환경에서는 위치 이동을 줄이고 opacity 변화만 남기는 방식이 더 안정적입니다. 위 예시에서는 기본 전환은 유지하되, transform 기반 이동은 motion-safe: 조건 안에 넣고, reduced motion 환경에서는 motion-reduce:transition-opacity로 전환 대상을 좁혔습니다.

모든 움직임을 무조건 animate-none으로 끄는 방식만 있는 것은 아닙니다. 로딩 스피너처럼 회전 자체가 부담스러울 수 있는 요소는 애니메이션을 끄고 텍스트 상태를 남길 수 있습니다. 모달처럼 열림 상태를 알려야 하는 요소는 이동을 줄이고 opacity 중심으로 바꿀 수 있습니다. 알림처럼 강조가 필요한 요소는 ping 대신 색상, badge 숫자, 텍스트 라벨로 대체할 수 있습니다.

상황 기본 motion reduced motion 대응
버튼 hover 색상, 그림자 변화 대부분 유지 가능
카드 hover translate, shadow 변화 translate 제거, shadow 또는 border만 유지
로딩 스피너 계속 회전 회전 제거, 텍스트 상태 유지
모달 등장 opacity와 translate 조합 translate 제거, opacity 중심 전환
알림 ping 반복 확산 효과 색상, 숫자, 라벨로 대체

실제 UI에서 다시 확인할 체크포인트

Tailwind CSS motion-reduce와 motion-safe를 사용해 접근성 기준으로 움직임을 줄이는 체크포인트 정리 이미지

Tailwind motion 클래스를 붙이기 전에 먼저 바뀌는 속성을 확인해야 합니다. 색상만 바뀌는지, 투명도도 바뀌는지, transform까지 들어가는지에 따라 transition-colors, transition-opacity, transition-transform, transition 중 선택이 달라집니다. 처음부터 넓은 transition을 붙이면 빠르지만, 나중에 수정할 때 어떤 변화가 의도된 것인지 흐려집니다.

두 번째로 duration을 확인합니다. 버튼이나 링크 hover는 짧아야 합니다. 사용자가 클릭하려는 순간에 전환이 길게 이어지면 반응이 둔하게 느껴집니다. 반대로 모달이나 패널처럼 화면 구조가 바뀌는 요소는 너무 짧으면 갑자기 튀어나오는 느낌이 날 수 있습니다. 그래서 duration-150, duration-200, duration-300 정도를 UI 성격에 따라 나누어 쓰는 기준을 두면 관리하기 쉽습니다.

세 번째로 easing을 확인합니다. ease-out은 나타나는 움직임에 자주 어울리고, ease-in은 사라지는 움직임에서 자연스럽게 느껴질 때가 많습니다. ease-in-out은 들어오고 나가는 양쪽 움직임이 모두 있는 경우에 무난합니다. 다만 모든 버튼에 ease-in-out을 습관처럼 붙이면 코드만 길어지고 기준은 남지 않습니다.

네 번째로 움직이는 요소의 개수를 봅니다. 페이지 안에서 동시에 움직이는 요소가 많을수록 사용자는 어디를 봐야 할지 놓치기 쉽습니다. 관리자 화면의 저장 버튼, 테이블 row hover, toast 알림, skeleton loading이 한 번에 움직이면 각각은 작은 효과라도 전체 화면은 산만해질 수 있습니다.

마지막으로 reduced motion 상태에서 정보가 사라지지 않는지 확인합니다. 애니메이션을 끄면 로딩 중인지 모르게 되는 구조라면 텍스트, aria 속성, disabled 상태, 시각적 라벨을 함께 보강해야 합니다. motion을 줄이는 작업은 장식을 제거하는 일이 아니라, 움직임 없이도 같은 정보를 전달하도록 UI를 다시 정리하는 작업입니다.

상태 변화와 반복 동작을 따로 확인합니다

본문 transition 예제는 상태를 바꿔 전후 값을 비교하고 animation 예제는 상태를 유지한 채 시간에 따른 변화를 확인합니다. 운영체제 또는 개발자 도구에서 동작 줄이기 설정도 적용하세요.

풀이 기준과 확인 결과

transition은 속성값의 변화 사이를 연결하고 animation은 키프레임 흐름을 실행합니다. motion-reduce는 사용자의 줄이기 선호에 맞춰 동작을 조정할 때 사용합니다.

움직임을 줄였을 때도 처리 중·성공·실패를 텍스트로 구분할 수 있어야 합니다. 단순한 장식 동작은 없어도 사용 가능한지 확인하세요.

정리

Tailwind CSS에서 transitionanimation은 같은 움직임처럼 보이지만 적용 기준이 다릅니다. transition은 상태가 바뀌는 순간을 부드럽게 연결하는 데 쓰고, animation은 시간 흐름 자체가 필요한 동작에 씁니다. 버튼 hover와 로딩 스피너를 같은 기준으로 보면 코드가 금방 섞입니다.

움직임을 넣을 때는 먼저 바뀌는 속성을 좁혀야 합니다. 색상만 바뀐다면 transition-colors, 투명도만 바뀐다면 transition-opacity, 위치 변화가 필요하다면 transition-transform을 고려합니다. 반복 동작이 필요할 때만 animate-spin, animate-pulse, animate-ping 같은 animation 유틸리티를 씁니다.

그리고 motion을 넣었다면 줄이는 기준도 같이 남겨야 합니다. motion-safe:는 움직임을 허용할 수 있는 환경에서만 애니메이션을 켜는 방식이고, motion-reduce:는 사용자가 모션 감소를 요청했을 때 대체 스타일을 제공하는 방식입니다. 카드가 살짝 올라가는 효과, 스피너 회전, 모달 등장 이동처럼 transform과 반복이 들어간 효과는 특히 이 기준을 먼저 확인해야 합니다.

다음에 Tailwind로 움직임을 넣을 때는 클래스 이름보다 질문을 먼저 두는 쪽이 오래 갑니다. 이 변화는 상태 변화인가, 반복 애니메이션인가, 사용자가 모션을 줄였을 때도 필요한 정보가 남는가. 이 세 가지를 통과하면 transition, animation, motion-reduce를 훨씬 덜 흔들리게 고를 수 있습니다.

같이 읽으면 좋은 글

이 글이 도움이 되었나요?

조회 중

Tailwind CSS 학습 순서

필수 15개 · 전체 22개

읽음 기록 관리

전체 과정 목차 (22개)
  1. 필수 길잡이 · Tailwind CSS 실전 로드맵: 기본·반응형·상태·동적 클래스 학습 순서
  2. 필수 학습 · Tailwind CSS 기본 클래스 사용법: text font bg spacing 익히기
  3. 필수 학습 · Tailwind border flex 사용법: 레이아웃 기본 클래스 기준
  4. 필수 학습 · Tailwind CSS Preflight 기준: 기본 스타일 초기화가 생기는 이유
  5. 필수 학습 · Tailwind CSS Grid 실무 레이아웃 정리: 카드 목록부터 대시보드까지
  6. 필수 학습 · Tailwind CSS 반응형 클래스 사용법: sm md lg 기준 잡기
  7. 필수 학습 · Tailwind CSS 상태 variant 사용법: hover focus aria data 기준 잡기
  8. 필수 학습 · Tailwind CSS group peer has 차이: 상태 기준으로 고르는 방법
  9. 필수 학습 · Tailwind CSS dark 사용법: 다크 모드 구현 기준 잡기
  10. 필수 학습 · Tailwind CSS arbitrary value 차이: 대괄호 문법 사용 기준 잡기
  11. 선택 참고 · Tailwind CSS 임의 값 공백 처리: 대괄호 문법이 깨질 때 해결하기
  12. 필수 학습 · Tailwind CSS @theme 사용법: 변수와 디자인 토큰 구분하기
  13. 필수 학습 · Tailwind Container Query 사용법: 부모 너비 기준 반응형 처리
  14. 필수 학습 · React Tailwind className 정리: 조건부·variant·가독성 기준
  15. 필수 학습 · Tailwind CSS 동적 클래스 해결: 감지 원리와 @source 사용법
  16. 필수 학습 · Tailwind CSS @apply 사용 기준: @utility와 custom variant 구분하기
  17. 선택 참고 · Tailwind CSS transition, animation, motion-reduce 사용 기준 정리 현재 글
  18. 선택 참고 · Tailwind CSS v4.1 유틸리티 정리: text-shadow와 mask 사용하기
  19. 선택 참고 · Radix UI와 shadcn/ui 차이: 컴포넌트를 고를 때 헷갈리는 기준 정리
  20. 선택 참고 · shadcn/ui No import alias found 오류 해결: tsconfig 설정
  21. 선택 참고 · Tailwind CSS flex 레이아웃 깨질 때 확인할 것
  22. 선택 참고 · Tailwind CSS 클래스 적용 안 됨 해결 순서

새 글 받아보기

RSS 리더에서 BlogFlow의 새 글을 확인할 수 있습니다.

RSS 피드 구독하기

댓글 남기기