Skip to content
BrandCloud

Motion

Motion that explains something: feedback, orientation, continuity and the occasional earned moment. Nothing here moves until you ask it to, and the switch below turns all of it down to short fades.

Reduce motion for this site, remembered on this device. Your system setting: checking.

Timing presets

Durations in milliseconds and easing curves from @brandcloud/motion. Press a track, or Play every dot, to move the dots with each token. Under reduced motion every distance is zero, so the dots stay put: that is the point.

CSS

@import "@brandcloud/motion/motion.css";
.panel { transition: transform var(--brand-motion-duration-normal) var(--brand-motion-ease-enter); }

duration.instant 80ms

duration.fast 140ms

duration.normal 220ms

duration.slow 320ms

duration.slower 480ms

duration.count is 900ms and moves digits, not layout, so it has its own demo under React presets.

easing.standard0.2, 0, 0, 1
easing.enter0.22, 1, 0.36, 1
easing.exit0.4, 0, 1, 1
easing.move0.65, 0, 0.35, 1
easing.linear0, 0, 1, 1

Springs

Physical stiffness, damping and mass, so the web and React Native run the same physics. CSS gets each spring as a linear() curve and a duration.

spring.press700 / 32 / 1, settles in394ms
spring.snappy500 / 40 / 1, settles in289ms
spring.gentle260 / 30 / 1, settles in441ms
spring.bouncy380 / 18 / 1, settles in660ms
Distance, scale, stagger and budget tokens
GroupValues
distance (px)nudge 1, lift 4, reveal 12, dialog 8
scalepress 0.95, pressLarge 0.98, dialog 0.98
staggertight 30, normal 50, maxItems 6
budgetmaxUiDuration 500, maxStaggerTotal 300, maxConcurrent 3

React presets

Spread a preset on a Motion element. Wrap the app in BrandMotionConfig so the OS setting and this page's switch both apply.

TSX

import { LazyMotion, domAnimation, m, AnimatePresence } from "motion/react";
import { BrandMotionConfig, depthButton, dialog } from "@brandcloud/motion/react";

<BrandMotionConfig>
  <LazyMotion features={domAnimation}>
    <m.button {...depthButton}>Book a visit</m.button>
    <AnimatePresence>{open && <m.div variants={dialog} initial="hidden" animate="visible" exit="exit" />}</AnimatePresence>
  </LazyMotion>
</BrandMotionConfig>

Reduced motion is off for these demos.

press

Down fast, back on the press spring with a small overshoot.

depthButton

1px lift and 4% brighter on hover, press to 0.95.

tierTile

An offer tile lifts 4px on hover or focus, with a light press.

dialog and overlay

The scrim fades while the dialog settles in.

drawer

Slides from the inline end.

sheet

Rises from the bottom, for phone menus and pickers.

list and listItem

Items arrive after a user action, with the stagger capped at 300ms in total.

    reveal

    One reveal on first view. Use it for one or two moments a page, never every section.

    Revealed once, as it came into view

    useCountUp

    One hero figure at most. Prices never count up.

    0

    transitions

    Named transitions from the tokens, for your own components.

    • instant 80ms
    • fast 140ms
    • normal 220ms
    • slow 320ms
    • slower 480ms
    • press spring, stiffness 700
    • snappy spring, stiffness 500
    • gentle spring, stiffness 260

    CSS utilities

    For Astro and static pages: classes in the brand-motion layer, so your own rules win. Only transform, opacity and filter animate.

    .m-press and .m-press-lg

    .m-depth

    .m-focus

    The ring settles in from further out.

    dialog.m-dialog

    dialog.m-drawer

    .m-popover[popover]

    No JavaScript: @starting-style does the entrance.

    .m-spinner

    A slow pulse under reduced motion.

    .m-enter

    [data-reveal] with reveal()

    One section per page at most. The reveal script only hides what is below the fold, so nothing already on screen fades out. This page uses it on the box at the bottom of the drivers section.

    .m-reveal-scroll

    Tied to scroll position with no script. Browsers without scroll-driven animation just show it.

    A native dialog

    Enters and leaves with @starting-style. No framework.

    A native drawer

    Slides in from the inline end, so from the left on right-to-left pages.

    Character and surface presets

    From @brandcloud/motion/character.css: weight for things that move, such as a guide, a badge or a card. Use a class (m-settle) or setdata-motion="settle" from script. All of them stop under reduced motion.

    One-shot presets

    Surface presets on the guide

    m-phrase

    Hello.

    This section explains the prices.

    Ask me anything.

    m-splash-art, m-splash-rise, m-splash-fade

    Welcome back

    Your saved brand is ready.

    m-card-enter

    m-ripple

    m-typing (loop)

    m-progress (loop)

    m-squash

    What each preset is for
    • settle: A landing squash after a glide or a drop.
    • pop-in: Appearing at a new place: a small pop with one squash.
    • hop: A short hop on the spot.
    • celebrate: Anticipation, hop and landing. Once per earned moment.
    • settle-in: First arrival on the page: drops in and settles.
    • hop-out: Leaving one place to reappear at another.
    • stretch: Held while gliding: a slight stretch along the move.
    • hold: Squashed and still while the page scrolls fast.
    • breathe: Idle: a very slow breath. Loops.
    • float: Idle: drifts up and down. Loops.
    • blink: Blinks four times, then stops.
    • nod: A small nod. Loops.
    • wave: Waves twice.
    • phrase: Phrases arrive in order, each with --i.
    • splash-art: A splash or panel opening: the art rises first.
    • splash-rise: Then the text rises, with --i for order.
    • splash-fade: Then the rest fades in.
    • card-enter: A card arriving in a list or conversation.
    • ripple: A touch ripple from the press point (--x, --y).
    • typing: Typing dots, each with --i. Tie it to real progress.
    • progress: The moving part of an indeterminate progress bar. Loops.
    • squash: Squash on press, spring back on release. Works under reduced motion too.

    Spring drivers

    From @brandcloud/motion/character. Each runs one animation frame loop only while something moves, and jumps under reduced motion.

    TS

    import { flyTo, hopTo, springFollower, turntable, playPreset } from "@brandcloud/motion/character";

    flyTo: along an arc, then settles

    Basket

    Click an item to send it to the basket.

    springFollower: a guide that keeps level

    Click a row: the guide glides there, stretches on the way and settles.

    hopTo: out, moved, popped in

    Saved

    turntable: drag, release, turn

    Drag the dial, or use the arrow keys. It springs to the nearest quarter turn.

    The one reveal on this page. This box faded in once as it came into view, using reveal(). With reduced motion it was simply there.

    The rules that keep motion honest

    • Every animation has a job: feedback, orientation, continuity or an earned moment of delight.
    • Interface movement stays under 500ms. Exits are shorter than entrances.
    • Nothing loops unless it shows progress, and loops stop off screen.
    • Under reduced motion there is no travel, scale, overshoot or parallax. Short fades stay so things still visibly appear.
    • Content is never hidden waiting for an animation.