1import{cd as c,ce as e}from"./index-C84RuTgJ.js";function h(s){const n={a:"a",code:"code",em:"em",h2:"h2",p:"p",pre:"pre",strong:"strong",table:"table",tbody:"tbody",td:"td",th:"th",thead:"thead",tr:"tr",...c(),...s.components},{Divider:t,MotionMatrix:a,PopoverPattern:o,Principle:i,PrincipleList:d}=n;return t||r("Divider"),a||r("MotionMatrix"),o||r("PopoverPattern"),i||r("Principle"),d||r("PrincipleList"),e.jsxs(e.Fragment,{children:[e.jsx(n.p,{children:`Motion gives the interface a sense of cause and effect. Things appear, move, 2and disappear in ways that mirror how objects behave in the physical world â 3and when used carefully, animation reinforces hierarchy, communicates state 4changes, and helps the product feel alive. Used carelessly, it gets in the 5way.`}),` 6`,e.jsx(n.p,{children:`This page covers when to animate, the duration and easing tokens we ship, and 7the patterns that combine them.`}),` 8`,e.jsx(t,{}),` 9`,e.jsx(n.h2,{id:"when-to-animate",children:e.jsx(n.a,{href:"#when-to-animate",children:"When to animate"})}),` 10`,e.jsxs(n.p,{children:["Reach for animation when motion ",e.jsx(n.strong,{children:`answers a question the user is already 11asking`})," â ",e.jsx(n.em,{children:"Where did this come from? Where did it go? What just changed?"}),` 12Static interfaces force the eye to re-orient on every change; a short 13transition stitches the before and after into a single, understandable moment.`]}),` 14`,e.jsxs(d,{children:[e.jsx(i,{title:"Show cause and effect",children:e.jsx(n.p,{children:`Animate the result of an action so the user can connect what they did with 15what just happened. A panel sliding in from the right is recognisably "a 16thing that opened from this side."`})}),e.jsx(i,{title:"Preserve continuity",children:e.jsx(n.p,{children:`When a single object moves between two states (open â closed, collapsed â 17expanded), animate the transition. Never let the object teleport.`})}),e.jsx(i,{title:"Stay out of the way",children:e.jsx(n.p,{children:`Animation should never delay the user's next action. If a transition makes 18the UI feel slow, shorten it or remove it.`})}),e.jsx(i,{title:"Skip pure decoration",children:e.jsx(n.p,{children:`If a motion answers no question and serves no state change, don't ship it. 19Subtle is the system's default â flair belongs to marketing surfaces, not 20the product.`})})]}),` 21`,e.jsx(t,{}),` 22`,e.jsx(n.h2,{id:"duration",children:e.jsx(n.a,{href:"#duration",children:"Duration"})}),` 23`,e.jsx(n.p,{children:`Compass exposes two duration tokens. Together they cover almost every motion 24in the product.`}),` 25`,e.jsxs(n.table,{children:[e.jsx(n.thead,{children:e.jsxs(n.tr,{children:[e.jsx(n.th,{children:"Token"}),e.jsx(n.th,{children:"Value"}),e.jsx(n.th,{children:"Use for"})]})}),e.jsxs(n.tbody,{children:[e.jsxs(n.tr,{children:[e.jsx(n.td,{children:e.jsx(n.code,{children:"--duration-quick"})}),e.jsx(n.td,{children:"150ms"}),e.jsx(n.td,{children:"On-screen micro-transitions â hover states, color changes, small moves"})]}),e.jsxs(n.tr,{children:[e.jsx(n.td,{children:e.jsx(n.code,{children:"--duration-moderate"})}),e.jsx(n.td,{children:"300ms"}),e.jsx(n.td,{children:"Entrances, exits, and larger movements â panels, drawers, popovers"})]})]})]}),` 26`,e.jsx(n.p,{children:`Quick is the default for elements already on screen. Reach for moderate 27whenever an element is entering or leaving the viewport, or travelling a 28significant distance â long enough that 150ms would feel rushed.`}),` 29`,e.jsx(t,{}),` 30`,e.jsx(n.h2,{id:"easing",children:e.jsx(n.a,{href:"#easing",children:"Easing"})}),` 31`,e.jsxs(n.p,{children:["Easing controls the ",e.jsx(n.strong,{children:"shape"}),` of a motion â whether it accelerates, 32decelerates, or moves at a constant rate. Compass uses three easing tokens, 33each tuned to a specific kind of moment.`]}),` 34`,e.jsxs(n.table,{children:[e.jsx(n.thead,{children:e.jsxs(n.tr,{children:[e.jsx(n.th,{children:"Token"}),e.jsx(n.th,{children:"Curve"}),e.jsx(n.th,{children:"Use for"})]})}),e.jsxs(n.tbody,{children:[e.jsxs(n.tr,{children:[e.jsx(n.td,{children:e.jsx(n.code,{children:"--ease-transition"})}),e.jsx(n.td,{children:"ease-in-out"}),e.jsx(n.td,{children:"Element already on screen â moving, expanding, changing color"})]}
34),e.jsxs(n.tr,{children:[e.jsx(n.td,{children:e.jsx(n.code,{children:"--ease-entrance"})}),e.jsx(n.td,{children:"ease-out"}),e.jsx(n.td,{children:"Element entering the screen â settles softly into place"})]}),e.jsxs(n.tr,{children:[e.jsx(n.td,{children:e.jsx(n.code,{children:"--ease-exit"})}),e.jsx(n.td,{children:"ease-in"}),e.jsx(n.td,{children:"Element leaving the screen â accelerates away"})]})]})]}),` 35`,e.jsx(n.p,{children:`The asymmetry between entrance and exit matters: things should arrive gently 36and leave decisively. Inverting that â sharp entrances, sluggish exits â makes 37the interface feel jittery, even when the timing is right.`}),` 38`,e.jsx(t,{}),` 39`,e.jsx(n.h2,{id:"choosing-a-duration--easing-pair",children:e.jsx(n.a,{href:"#choosing-a-duration--easing-pair",children:"Choosing a duration + easing pair"})}),` 40`,e.jsxs(n.p,{children:["Pick a token pair by asking two questions. ",e.jsx(n.em,{children:`Is the element already on screen, 41or is it entering or leaving?`})," And ",e.jsx(n.em,{children:"how far is it travelling?"})]}),` 42`,e.jsx(a,{}),` 43`,e.jsxs(n.p,{children:[`These four pairings cover the bulk of UI motion. When in doubt, default to 44`,e.jsx(n.code,{children:"--duration-quick"})," paired with ",e.jsx(n.code,{children:"--ease-transition"}),"."]}),` 45`,e.jsx(t,{}),` 46`,e.jsx(n.h2,{id:"popover-panels",children:e.jsx(n.a,{href:"#popover-panels",children:"Popover panels"})}),` 47`,e.jsxs(n.p,{children:[`Popovers â menus, info popovers, dropdowns â animate on open and close with a 48combined `,e.jsx(n.strong,{children:"scale + fade"}),`. The scale grows the panel out from its anchor; the 49fade prevents the appearance from feeling abrupt.`]}),` 50`,e.jsxs(n.table,{children:[e.jsx(n.thead,{children:e.jsxs(n.tr,{children:[e.jsx(n.th,{children:"Phase"}),e.jsx(n.th,{children:"Scale"}),e.jsx(n.th,{children:"Opacity"}),e.jsx(n.th,{children:"Duration"}),e.jsx(n.th,{children:"Easing"})]})}),e.jsxs(n.tbody,{children:[e.jsxs(n.tr,{children:[e.jsx(n.td,{children:"Opening"}),e.jsx(n.td,{children:"90% â 100%"}),e.jsx(n.td,{children:"0 â 1"}),e.jsx(n.td,{children:e.jsx(n.code,{children:"--duration-quick"})}),e.jsx(n.td,{children:e.jsx(n.code,{children:"--ease-entrance"})})]}),e.jsxs(n.tr,{children:[e.jsx(n.td,{children:"Closing"}),e.jsx(n.td,{children:"100% â 90%"}),e.jsx(n.td,{children:"1 â 0"}),e.jsx(n.td,{children:e.jsx(n.code,{children:"--duration-quick"})}),e.jsx(n.td,{children:e.jsx(n.code,{children:"--ease-exit"})})]})]})]}),` 51`,e.jsxs(n.p,{children:["Popovers are a short-distance entrance/exit, so they use ",e.jsx(n.code,{children:"--duration-quick"}),` 52rather than the moderate timing reserved for full-size panels and drawers.`]}),` 53`,e.jsxs(n.p,{children:["Set ",e.jsx(n.code,{children:"transform-origin"}
53),` so the scale grows from the anchor â for a popover that 54opens below-and-right of its trigger, use `,e.jsx(n.code,{children:"transform-origin: top left"}),`. For 55unmount-on-close panels, the `,e.jsx(n.code,{children:"useExitAnimation"})," hook in ",e.jsx(n.code,{children:"src/hooks/"}),` keeps the 56element rendered long enough for the exit animation to finish.`]}),` 57`,e.jsx(o,{}),` 58`,e.jsx(t,{}),` 59`,e.jsx(n.h2,{id:"always-use-the-tokens",children:e.jsx(n.a,{href:"#always-use-the-tokens",children:"Always use the tokens"})}),` 60`,e.jsx(n.p,{children:`Never hard-code durations or easing keywords directly. Bind to the CSS 61variables so timing stays consistent across components, and so changes to the 62system propagate without a search-and-replace.`}),` 63`,e.jsx(n.pre,{children:e.jsx(n.code,{className:"language-scss",children:`// Small on-screen transition (e.g. hover, short expand) 64transition: opacity var(--duration-quick) var(--ease-transition); 65 66// Large on-screen transition (e.g. panel sliding across the view) 67transition: transform var(--duration-moderate) var(--ease-transition); 68 69// Entrance 70transition: opacity var(--duration-moderate) var(--ease-entrance); 71 72// Exit 73transition: opacity var(--duration-moderate) var(--ease-exit); 74`})}),` 75`,e.jsx(t,{}),` 76`,e.jsx(n.h2,{id:"reduced-motion",children:e.jsx(n.a,{href:"#reduced-motion",children:"Reduced motion"})}),` 77`,e.jsxs(n.p,{children:["Respect the user's system preference. When ",e.jsx(n.code,{children:"prefers-reduced-motion: reduce"}),` is 78set, suppress non-essential animation so the interface stays usable for users 79who get motion-sick or are easily distracted by movement.`]}),` 80`,e.jsx(n.pre,{children:e.jsx(n.code,{className:"language-scss",children:`@media (prefers-reduced-motion: reduce) { 81 *, 82 *::before, 83 *::after { 84 animation-duration: 0.01ms !important; 85 transition-duration: 0.01ms !important; 86 } 87} 88`})}),` 89`,e.jsxs(n.p,{children:["Animation that ",e.jsx(n.strong,{children:"conveys meaning"}),` â a loading spinner, a progress indicator, 90a typing dot â can keep moving, but should be reduced to its minimum legible 91form. Animation that's purely a flourish should disappear.`]}),` 92`,e.jsx(t,{}),` 93`,e.jsx(n.h2,{id:"tokens-and-implementation",children:e.jsx(n.a,{href:"#tokens-and-implementation",children:"Tokens and implementation"})}),` 94`,e.jsxs(n.p,{children:[`Each duration and easing is exposed as a CSS custom property. For the full 95token list, copy-ready values, and interactive playback of each token, see the 96`,e.jsx(n.a,{href:"/foundations/animation/specimen",children:"Animation specimen page"}),"."]})]})}function x(s={}){const{wrapper:n}={...c(),...s.components};return n?e.jsx(n,{...s,children:e.jsx(h,{...s})}):h(s)}function r(s,n){throw new Error("Expected component `"+s+"` to be defined: you likely forgot to import, pass, or provide it.")}export{x as default};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.