Motion
Motion presets are available for both CSS transitions and animations and
JavaScript-driven animations in the form of
Framer Motion spring presets. Motion can be
used simply for delight or to add semantic value to an interface, though
ideally, it adds a bit of both. Substantial motion should always respect
useIsMotionReduced
.
Framer
Framer Motion is a JavaScript animation library. It's ideal for more substantial interface motion as its physics-based movement creates much more realistic, fluid effects. It can also be applied in cases that are difficult or impossible to reproduce effectively with CSS (i.e: when a component is unmounted).
Usage
A modified version of
Framer's motion()
export
is available from @hover/blueprint that integrates motion and system
functionality. See below and the Framer Motion
documentation for more details.
import { motion, Center, Loader, Motion } from '@hover/blueprint';import { AnimatePresence } from 'framer-motion';// The system `motion()` provides the correct props when wrapping system// components and custom components that compose system componentsconst MotionLoader = motion(Loader);export const MyComponent = ({ isVisible }) => ( // Elements created with `motion.[element]` support system // style props in addition to the Framer Motion APIs <motion.div animate={{ x: isVisible ? '0%' : '100%' }} background="neutral.100" borderRadius="400" > {/* System motion presets are available on the `Motion` component */} <Motion.Firm> <AnimatePresence> {isVisible && ( <MotionLoader initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} /> )} </AnimatePresence> </Motion.Firm> </motion.div>);
Motion Factory
Using the modified motion()
factory from @hover/blueprint instead of that
from framer-motion ensures that system components wrapped with motion()
have the correct prop typings for both
style props and Framer's animation
props.
It also automatically augments JSX elements created using the <motion.div />
shorthand with system style props. See the
system factory page for more information.
import { motion as framerMotion } from 'framer-motion';import { motion as systemMotion, system } from '@hover/blueprint';// Thisconst MotionDiv = framerMotion(system.div);<MotionDiv animate={{ x: 100 }} bg="primary.100" />// Is equivalent to this<systemMotion.div animate={{ x: 100 }} bg="primary.100" />
Presets
Framer Motion presets are applied via React context with the Motion
helper.
Simply wrap any any Framer-driven animations with a Motion
preset.
<Motion.Snappy>
</Motion.Snappy>
<Motion.Tight>
</Motion.Tight>
<Motion.Firm>
</Motion.Firm>
<Motion.Soft>
</Motion.Soft>
Gestures
Swipe
Use the onSwipe
helper handler for invoking actions in response to swipe
gestures.
Given a swipe axis and actions to invoke when swiping towards the start or end of said axis, it determines whether to invoke one of the provided actions depending on whether the swipe in the respective direction passes the provided threshold.
Property | Type | Description |
---|---|---|
axis | 'x' | 'y' | Direction of swipe |
start? | SwipeAction | Action to invoke when swiping towards start of axis |
end? | SwipeAction | Action to invoke when swiping towards end of axis |
none? | SwipeAction | Action to invoke when no swipe threshold is reached |
end? | SwipeAction | Action to invoke when swiping towards end of axis |
none? | SwipeAction | Action to invoke when no swipe threshold is reached (defaults to 10000 ) |
distance? | number | Optionally, a distance threshold that will trigger a swipe. If defined and distance is reached, it will trigger a swipe regardless of the provided force threshold. |
import { motion, Image, onSwipe } from '@hover/blueprint';const MotionImage = motion(Image);<MotionImage drag="x" onDragEnd={onSwipe({ axis: 'x', start: () => console.log('swiped left'), end: () => console.log('swiped right'), })}/>;
CSS
CSS transitions are ideal for short state changes, such as the :hover
state of
a button and the :focus
state of an input. For more substantial movement, you
might consider Framer Motion for a more realistic effect.
Usage
The easiest way to apply CSS presets is via the transition
helper. Simply
provide the property or properties and spread the return value into a
component's props or a style object.
They are also available as strings from the motion
tokens export for
interpolating into more complex transitions.
import { Button } from '@hover/blueprint';import { motion } from '@hover/blueprint/foundation';export const PartyButton = () => ( <Button _hover={{ opacity: 1, filter: 'grayscale(0%)' }} filter="grayscale(100%)" label="party" opacity="0.5" shape="circle" transition={`opacity ${motion.easings[150]}, filter ${motion.easings[500]}`} > 🥳 </Button>);
Presets
CSS transition presets are available on the transition
helper as functions
(e.g: transition.easing150(...properties)
) and from the motion.easings
tokens as strings including the duration and easing function (e.g:
motion.easings[150]
).
transition.easing150(...)
transition.easing250(...)
transition.easing500(...)