모션Motion

globals.css에 있는 이징, 애니메이션 토큰, 모션 유틸리티와 동작 줄이기 처리입니다.The easings, animation tokens, motion utilities and reduced-motion handling in globals.css.

이징과 길이Easings and durations

이징은 세 개이고 CSS 변수로 정의됩니다. 컴포넌트와 유틸리티는 var(--ease-*)로 읽습니다.There are three easings, defined as CSS variables. Components and utilities read them as var(--ease-*).

  • --ease-out-quint: cubic-bezier(0.22, 1, 0.36, 1). 대부분의 진입과 이동에 씁니다.--ease-out-quint: cubic-bezier(0.22, 1, 0.36, 1). Used for most entrances and moves.
  • --ease-emphasized: cubic-bezier(0.2, 0, 0, 1). 항목 사이를 이동하는 표시에 씁니다.--ease-emphasized: cubic-bezier(0.2, 0, 0, 1). Used for indicators that travel between items.
  • --ease-spring: cubic-bezier(0.34, 1.3, 0.64, 1). 살짝 튀는 등장에 씁니다.--ease-spring: cubic-bezier(0.34, 1.3, 0.64, 1). Used for entrances with a slight overshoot.

길이는 색과 누름 피드백이 120~150ms, 팝업과 패널이 180~240ms, 항목 사이를 이동하는 표시가 약 280ms입니다.Durations: 120-150ms for color and press feedback, 180-240ms for popups and panels, about 280ms for indicators that travel between items.

애니메이션 토큰Animation tokens

@theme에 --animate-*로 등록되어 animate-shimmer 같은 Tailwind 클래스로 바로 씁니다. 길이와 이징은 토큰에 들어 있습니다.They are registered in @theme as --animate-*, so the Tailwind class animates directly, such as animate-shimmer. Duration and easing live in the token.

  • 등장: animate-pop-in 220ms ease-spring, animate-fade-in 160ms ease-out-quint, animate-scale-out 120ms ease-in(퇴장).Entrances: animate-pop-in at 220ms ease-spring, animate-fade-in at 160ms ease-out-quint, animate-scale-out at 120ms ease-in (exit).
  • 퇴장: animate-pop-out 130ms ease-in.Exits: animate-pop-out at 130ms ease-in.
  • 이동: animate-slide-next와 animate-slide-prev 200ms, animate-slide-shift-next와 animate-slide-shift-prev 200ms, animate-swap-move 200ms. 모두 ease-out-quint입니다.Moves: animate-slide-next and animate-slide-prev at 200ms, animate-slide-shift-next and animate-slide-shift-prev at 200ms, animate-swap-move at 200ms. All ease-out-quint.
  • 반복: animate-shimmer 1.6s linear, animate-live-pulse 2s ease-out-quint, animate-sweep 1.4s ease-out-quint.Loops: animate-shimmer at 1.6s linear, animate-live-pulse at 2s ease-out-quint, animate-sweep at 1.4s ease-out-quint.
  • 짧은 반복: animate-caret 1s steps(1, end), animate-thinking 1.2s ease-in-out, animate-wave 900ms ease-in-out.Short loops: animate-caret at 1s steps(1, end), animate-thinking at 1.2s ease-in-out, animate-wave at 900ms ease-in-out.

모션 유틸리티Motion utilities

컴포넌트가 조합하는 @utility입니다. 열고 닫는 상태는 Base UI가 붙이는 data-starting-style, data-ending-style, data-collapsed, data-hidden 속성으로 읽습니다.These are @utility rules the components compose. Open and close states come from the data-starting-style, data-ending-style, data-collapsed and data-hidden attributes Base UI sets.

  • motion-rise: tab-panel-in을 220ms ease-out-quint로 재생합니다. 목록 지연은 animation-delay: calc(var(--i) * 30ms)로 줍니다.motion-rise: plays tab-panel-in at 220ms ease-out-quint. Stagger a list with animation-delay: calc(var(--i) * 30ms).
  • motion-popup: 진입 200ms, 이탈 130ms. opacity, scale, translate를 함께 움직이고 Base UI가 정한 --transform-origin에서 자랍니다.motion-popup: 200ms in, 130ms out. Moves opacity, scale and translate, growing from the --transform-origin Base UI sets.
  • motion-fade: opacity만 200ms ease-out으로 바꿉니다. 배경 딤처럼 단순한 페이드에 씁니다.motion-fade: opacity only, 200ms ease-out. For plain fades such as a backdrop dim.
  • motion-collapse: 240ms. Base UI Collapsible이 재는 --collapsible-panel-height로 높이를 잽니다.motion-collapse: 240ms. It animates to the --collapsible-panel-height Base UI Collapsible measures.
  • motion-details: details 본문을 240ms로 열고 닫습니다. Chromium 131+와 Safari 18.4+에서 동작하고, 그 밖의 브라우저는 즉시 열고 닫습니다.motion-details: opens and closes a details body over 240ms. Works in Chromium 131+ and Safari 18.4+; other browsers toggle at once.
  • motion-reveal: 200ms에 opacity 150ms. 자식 하나를 감싸고 data-collapsed로 닫습니다. 가로 방향 짝은 motion-reveal-x입니다.motion-reveal: 200ms, opacity at 150ms. Wrap one child and close it with data-collapsed. motion-reveal-x is the horizontal counterpart.
  • motion-swap: 150ms. 두 자식을 같은 칸에 겹치고 data-hidden으로 비활성 쪽을 숨깁니다.motion-swap: 150ms. Stacks two children in one cell and hides the inactive one with data-hidden.
  • motion-indicator: 280ms ease-emphasized. 탭과 세그먼트 컨트롤의 표시가 항목 사이를 이동합니다.motion-indicator: 280ms ease-emphasized. The indicator travels between tab and segmented control items.
  • motion-colors는 150ms ease-out으로 색을 바꾸고, motion-press는 150ms에 :active에서 scale 0.97을 더합니다. 비활성 상태는 제외합니다.motion-colors changes color over 150ms ease-out, and motion-press adds scale 0.97 on :active over 150ms. Disabled states are excluded.
example.tsx
<button className="motion-press motion-colors rounded-md bg-bg-surface px-3 py-1.5 text-label-sm">
  Save
</button>

<div className="motion-rise" style={{ animationDelay: "calc(var(--i) * 30ms)" }}>
  ...
</div>

동작 줄이기Reduced motion

prefers-reduced-motion: reduce에서는 globals.css가 모든 애니메이션과 전이 길이를 0.01ms로 줄이고 반복 횟수를 1로 고정합니다. scroll-behavior도 auto가 됩니다.Under prefers-reduced-motion: reduce, globals.css cuts every animation and transition to 0.01ms, fixes the iteration count at 1 and sets scroll-behavior to auto.

스크립트로 직접 그리는 모션은 useReducedMotion으로 같은 설정을 읽습니다. 이 훅은 서버 렌더와 첫 패스에서 false를 돌려줍니다. 자세한 계약은 훅 페이지에 있습니다.Motion drawn from script reads the same setting through useReducedMotion, which returns false during a server render and the first pass. Its hook page has the full contract.