Tailwind CSS v4의 애니메이션 시스템, @keyframes 정의, @starting-style 활용, transition-behavior, 그리고 성능 최적화된 애니메이션 패턴을 다룹니다.
6장에서 새로운 유틸리티와 변형을 배웠습니다. 이 장에서는 Tailwind CSS v4의 애니메이션 시스템을 깊이 있게 다룹니다. @theme에서 커스텀 애니메이션을 정의하는 방법, @starting-style을 활용한 진입 애니메이션, 그리고 성능을 고려한 애니메이션 설계 전략을 살펴봅니다.
Tailwind v4는 몇 가지 기본 애니메이션을 제공합니다.
<!-- 회전 -->
<div class="animate-spin">로딩 스피너</div>
<!-- 바운스 -->
<div class="animate-bounce">알림 뱃지</div>
<!-- 맥박 -->
<div class="animate-pulse">스켈레톤 로더</div>
<!-- 핑 -->
<div class="animate-ping">알림 표시기</div>2장에서 배운 @theme의 --animate-* 네임스페이스를 활용하면 커스텀 애니메이션을 정의할 수 있습니다.
@import "tailwindcss";
@theme {
--animate-fade-in: fade-in 0.3s ease-out;
--animate-fade-out: fade-out 0.3s ease-in;
--animate-slide-up: slide-up 0.4s ease-out;
--animate-slide-down: slide-down 0.4s ease-out;
--animate-scale-in: scale-in 0.2s ease-out;
--animate-scale-out: scale-out 0.2s ease-in;
--animate-shake: shake 0.5s ease-in-out;
}
@keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes fade-out {
from { opacity: 1; }
to { opacity: 0; }
}
@keyframes slide-up {
from { opacity: 0; transform: translateY(1rem); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes slide-down {
from { opacity: 0; transform: translateY(-1rem); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes scale-in {
from { opacity: 0; transform: scale(0.95); }
to { opacity: 1; transform: scale(1); }
}
@keyframes scale-out {
from { opacity: 1; transform: scale(1); }
to { opacity: 0; transform: scale(0.95); }
}
@keyframes shake {
0%, 100% { transform: translateX(0); }
10%, 30%, 50%, 70%, 90% { transform: translateX(-4px); }
20%, 40%, 60%, 80% { transform: translateX(4px); }
}<div class="animate-fade-in">페이드 인 효과</div>
<div class="animate-slide-up">슬라이드 업 효과</div>
<div class="animate-shake">에러 흔들림 효과</div><!-- 기본 트랜지션 -->
<div class="transition">모든 속성 트랜지션</div>
<div class="transition-colors">색상만 트랜지션</div>
<div class="transition-opacity">투명도만 트랜지션</div>
<div class="transition-transform">변환만 트랜지션</div>
<div class="transition-shadow">그림자만 트랜지션</div>
<!-- 지속 시간 -->
<div class="transition duration-150">150ms</div>
<div class="transition duration-300">300ms</div>
<div class="transition duration-500">500ms</div>
<!-- 이징 -->
<div class="transition ease-in">가속</div>
<div class="transition ease-out">감속</div>
<div class="transition ease-in-out">가속 후 감속</div>
<!-- 지연 -->
<div class="transition delay-100">100ms 지연</div>
<div class="transition delay-300">300ms 지연</div>v4에서 transition-behavior: allow-discrete를 지원하여, 이전에는 트랜지션이 불가능했던 이산 속성(discrete properties) 의 전환이 가능합니다. display와 overlay 속성이 대표적입니다.
<!-- display: none에서 block으로 전환 시 트랜지션 적용 -->
<div class="
transition-all transition-discrete
hidden open:flex
opacity-0 open:opacity-100
">
display: none → flex 전환에도 트랜지션이 동작합니다
</div>6장에서 소개한 starting: 변형을 활용한 진입 애니메이션을 더 깊이 다룹니다.
<dialog class="
fixed inset-0 m-auto
w-full max-w-md
rounded-xl shadow-xl
p-6
/* 기본 상태 (열린 상태) */
opacity-100 scale-100 translate-y-0
/* 진입 시작점 */
starting:open:opacity-0
starting:open:scale-95
starting:open:translate-y-4
/* 트랜지션 */
transition-all duration-300 ease-out
transition-discrete
/* 백드롭 */
backdrop:bg-black/50
backdrop:starting:open:opacity-0
backdrop:transition-opacity backdrop:duration-300
">
<h2 class="text-xl font-bold mb-4">다이얼로그 제목</h2>
<p class="text-muted mb-6">다이얼로그 내용이 여기에 들어갑니다.</p>
<button onclick="this.closest('dialog').close()" class="btn btn-primary">
닫기
</button>
</dialog><div class="
fixed bottom-4 right-4
flex items-center gap-3
bg-surface border rounded-lg shadow-lg
px-4 py-3
/* 기본 상태 */
opacity-100 translate-x-0
/* 진입 시작점 */
starting:opacity-0 starting:translate-x-full
/* 트랜지션 */
transition-all duration-500 ease-out
">
<span class="text-success font-medium">저장되었습니다</span>
</div><div class="relative">
<button popovertarget="menu" class="btn">메뉴</button>
<div id="menu" popover class="
absolute top-full mt-1
w-48 rounded-lg shadow-lg border
bg-surface p-1
/* 기본 상태 */
opacity-100 scale-100 translate-y-0
/* 진입 시작점 */
starting:opacity-0 starting:scale-95 starting:translate-y-(-1)
/* 트랜지션 */
transition-all duration-200 ease-out
transition-discrete
">
<a href="#" class="block px-3 py-2 rounded-md hover:bg-surface-alt">프로필</a>
<a href="#" class="block px-3 py-2 rounded-md hover:bg-surface-alt">설정</a>
<a href="#" class="block px-3 py-2 rounded-md hover:bg-surface-alt">로그아웃</a>
</div>
</div>CSS animation-timeline과 view()를 활용한 스크롤 연동 애니메이션도 유틸리티로 표현할 수 있습니다.
@theme {
--animate-scroll-fade-in: scroll-fade-in linear forwards;
}
@keyframes scroll-fade-in {
from {
opacity: 0;
transform: translateY(2rem);
}
to {
opacity: 1;
transform: translateY(0);
}
}<div class="animate-scroll-fade-in" style="animation-timeline: view(); animation-range: entry 0% entry 100%;">
스크롤하면 나타나는 요소
</div>스크롤 기반 애니메이션은 아직 모든 브라우저에서 완전히 지원되지 않을 수 있습니다. @supports (animation-timeline: view()) 쿼리로 지원 여부를 확인하고, 폴백을 제공하는 것이 좋습니다.
애니메이션 성능을 위해 컴포지터 전용 속성만 애니메이션하는 것이 중요합니다.
<!-- 권장: transform, opacity (GPU 가속) -->
<div class="transition-transform hover:scale-105">확대</div>
<div class="transition-opacity hover:opacity-80">투명도 변화</div>
<!-- 지양: width, height, padding (레이아웃 재계산 유발) -->
<!-- <div class="transition-all hover:w-64"> 느림 </div> -->| 속성 | 성능 | 이유 |
|---|---|---|
transform | 최상 | 컴포지터 레이어에서 처리 |
opacity | 최상 | 컴포지터 레이어에서 처리 |
filter | 양호 | GPU 가속 가능 |
background-color | 보통 | 리페인트 발생 |
width, height | 주의 | 레이아웃 재계산 |
box-shadow | 주의 | 리페인트 + 비용 높음 |
<div class="will-change-transform hover:scale-105 transition">
브라우저에 변환 예정 힌트
</div>will-change는 성능 힌트이지 강제 최적화가 아닙니다. 남용하면 오히려 메모리 사용량이 증가합니다. 실제로 애니메이션이 발생하는 요소에만, 그리고 가능하면 hover 등 직전에만 적용하세요.
접근성을 위해 사용자가 애니메이션 감소를 요청한 경우에 대응해야 합니다.
<!-- motion-safe: 모션 허용 시에만 적용 -->
<div class="motion-safe:animate-fade-in motion-safe:transition-all">
모션 허용 시에만 애니메이션
</div>
<!-- motion-reduce: 모션 감소 요청 시 적용 -->
<div class="transition-all motion-reduce:transition-none">
모션 감소 시 트랜지션 제거
</div>@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}<div class="space-y-4">
<!-- 제목 스켈레톤 -->
<div class="h-8 w-3/4 rounded-md bg-neutral-200 animate-pulse"></div>
<!-- 본문 스켈레톤 -->
<div class="space-y-2">
<div class="h-4 w-full rounded-md bg-neutral-200 animate-pulse"></div>
<div class="h-4 w-full rounded-md bg-neutral-200 animate-pulse delay-75"></div>
<div class="h-4 w-2/3 rounded-md bg-neutral-200 animate-pulse delay-150"></div>
</div>
<!-- 이미지 스켈레톤 -->
<div class="aspect-video w-full rounded-lg bg-neutral-200 animate-pulse delay-200"></div>
</div>Tailwind CSS v4의 애니메이션 시스템은 @theme의 --animate-* 네임스페이스, @starting-style 기반 진입 애니메이션, transition-discrete를 통한 이산 속성 전환을 조합하여 강력한 인터랙션을 구현합니다. GPU 가속 속성 우선 사용, will-change 적절한 활용, prefers-reduced-motion 대응은 성능과 접근성을 모두 확보하는 핵심 전략입니다.
다음 장에서는 Tailwind v4의 다크 모드 전략과 테마 시스템을 다룹니다. CSS 커스텀 프로퍼티 기반의 런타임 테마 전환, 다중 테마 패턴, 그리고 next-themes 같은 프레임워크와의 통합을 배웁니다.
이 글이 도움이 되셨나요?
관련 주제 더 보기
Tailwind CSS v4의 다크 모드 전략, CSS 커스텀 프로퍼티 기반 런타임 테마 전환, 다중 테마 패턴, 그리고 next-themes와의 통합을 실전 중심으로 다룹니다.
Tailwind CSS v4에서 추가, 변경, 제거된 유틸리티 클래스와 변형(Variants)을 종합 정리하고, 실전 활용 패턴을 다룹니다.
Tailwind CSS v4의 @custom-variant, @plugin 디렉티브, CSS 기반 확장 시스템, 그리고 기존 JS 플러그인에서의 마이그레이션을 실전 중심으로 다룹니다.