Skip to main content

Animations

A good UX lives from good and reasonable animations. With animations users for example spot newly added components easier. Animations can also improve the connection between two elements or a user action (e.g. a menu that slides in after clicking on a button).

But animations are also hard to create, especially enter and exit animations, as you need to keep your elements available until the exit animation is finished. Existing tooling tries to make this kind of animations easier, but it's still a lot of boilerplate code or you need to write your own abstractions (e.g. to make it work with emotion).

Pablo tries to abstract animations, so you don't need to worry about them too much, all you need to do is wrap the component that you want to show/hide with an animation in an animation component (e.g. SlideAnimation or FadeAnimation).

Currently Pablo has three build in animations, StackAnimation, SlideAnimation and FadeAnimation. You can easily write your own animations as well.

Some animations like StackAnimation and SlideAnimation need additional props (like the side where the animation should happen).

In the example below you can declare the animation duration and show and hide the wrapped component. In the code you can change AnimationComponent to be either SlideAnimation or FadeAnimation.

Live Editor
() => {
  const AnimationComponent = SlideAnimation; // or use FadeAnimation
  const animationComponentProps = {
    side: 'left',
    reverse: false,
  };
  const [duration, setDuration] = React.useState(300);
  const [visible, setVisible] = React.useState(false);
  return (
    <Flex
      alignItems="stretch"
      height={400}
    >
      <Flex justifyContent="center" alignItems="center" flexGrow={1} flexBasis={0}>
        <Box mb={4}>
          <Checkbox
            mb={1}
            checked={visible}
            onChange={() => setVisible((v) => !v)}
            label="visible"
          />
          <Input
            label="Duration"
            mb={1}
            onChange={(v) => setDuration(parseInt(v, 10) || 0)}
            value={duration}
          />
        </Box>
      </Flex>
      <Box
        display="flex"
        justifyContent="center"
        alignItems="center"
        flexGrow={1}
        flexShrink={0}
        flexBasis={0}
        bgColor="gray.50"
      >
        <Box maxWidth={400} height={200}>
          <AnimationComponent duration={duration} visible={visible} {...animationComponentProps}>
            <Card>
              <Title>I am animated</Title>
            </Card>
          </AnimationComponent>
        </Box>
      </Box>
    </Flex>
  );
}
Result
Loading...

Common animation props

All animation components share the following props

duration

Duration of the animation in milliseconds.

visible

Boolean value that defines if the wrapped component should be shown or not. If this value changes from false to true the enter animation is played, if it changes from true to false the exit animation is played.

Animation components

SlideAnimation

The SlideAnimation is helpful for popups and toasts. It has the following props to affect the animation:

side

You can specify the side where the animation happens (e.g. top means ot slides in from the top). Can be either top, right, bottom or left.

reverse

You can also revert the direction, which makes it slide in from the opposite site. This is helpful for things like tooltips, where you want to share the side prop but want to slide to the other direction (e.g. slide away from the wrapped component instead of sliding to it).

// reverse prop is false by default
<SlideAnimation duration={300} visible={true} side="top">
<div>hi</div>
</SlideAnimation>

// With reverse being true
<SlideAnimation duration={300} visible={true} side="top" reverse>
<div>ho</div>
</SlideAnimation>

FadeAnimation

The FadeAnimation is really simple, it fades the component in and out. It does not have any props in addition to the common animation props.

<FadeAnimation duration={300} visible={true}>
<div>hi</div>
</FadeAnimation>

StackAnimation

The StackAnimation is similar to the slide animation. Difference is that the animation side is fixed, entering from the bottom and leaving to the top. It also pushes the surrounding content during the animation. So during the enter animation it pushes the content below down and pulls it back up when the exit animation is played.

The StackAnimation is used as the default animation for toasts.

<StackAnimation duration={300} visible={true}>
<div>hi</div>
</StackAnimation>

Usage with bojagi components

Some bojagi components allow to replace the default animation. To do this you simply pass the animation component as with the animation prop. If animation customization is possible, the component will also have an animationProps prop for any custom animation prop.

Among others the following components allow animation replacement:

  • Menu
  • Popover (more internal component that is used by Menu and Tooltip)

This is an example with the Menu component:

<Menu
// ... all the other menu props
animation={SlideAnimation}
animationProps={{
duration: 150,
side: 'bottom',
}}
>
// ...the wrapped component
</Menu>

Custom animations

It's really easy to write custom animations, here's the source code for our fade animation:

import { css } from '@emotion/react';
import { createInOutAnimation } from '@bojagi/pablo/animation';

const fadeAnimationBase = css`
transition: ${(props) => css`opacity ${props.duration}ms`};
opacity: 0;
`;

const fadeAnimationEnter = css`
opacity: 1;
transform: translateY(0) translateX(0);
`;

const fadeAnimationExit = css`
opacity: 0;
`;

export const FadeAnimation = createInOutAnimation({
baseStyles: fadeAnimationBase,
enterStyles: fadeAnimationEnter,
exitStyles: fadeAnimationExit,
});

To create a custom animation, you need to use createInOutAnimation. This creates an animation component. It takes three styles (just use styled-component's css tag literal) for different steps.

  • baseStyles for shared styles (hold pre init styles and default transitions)
  • enterStyles for styles when the wrapped component should be visible
  • exitstyles for styles when the wrapped component should be hidden

You can use custom props for animations by just accessing it like usual in emotion (like with the duration prop in the example above).