4 stunning UI design-to-code animations

4 stunning UI design-to-code animations


Lorenzo Doremi

2 years ago | 5 min read

Ever wondered how programmers transform our complex UI animation mockups into functional, fast, and beautiful code? Well, if they use React.js, probably Framer Motion is their favorite solution.

Framer Motion is an outstanding library that allows to easily animate complex layouts inside React, and in my opinion, is a must when developing cool looking websites. But since we’re designers or trying React for the first time, and developing isn’t indeed our strength, I created this simple website to teach the basics of animation with FM.

Let’s see: it’s a portfolio gallery.

Sadly GIF compression doesn’t show the full beauty.

There are a lot of different animations going on, but let’s split them into main components:

  1. The white “curtains” moving in alternate directions
  2. The trendy bottom-appearing title
  3. The moving images
  4. The details component appearing from the bottom

Let’s start.

1: The intro curtains

In this case, we’ll use variants, since we have two different alternate animations: curtains going up and going down.

the alternate constant is a variant, which holds multiple animations we can later use. We have also defined a nice transition, with ease written using the Bezier’s curve standard notation.

export default function Curtain({ number }) {const alternate = { even: { y: “100vh”, transition: transition, }, odd: { top: 0, y: “-100vh”, transition: transition, },};const transition= {duration: 1,ease: [1,.01,.49,1.05],delay: 0.4}... other code ...

We have now the needed animations: even curtains will go down, and odd curtains will go up. But how to assign these animations? thanks to math.

return ( <motion.div id="curtains"> {[…Array(number)].map((x, i) => ( <motion.div animate = { i%2 == 0 ? “odd”: “even” } className=”curtain” variants={alternate} ></motion.div> ))} </motion.div> ); // the return closing
} // the full function closing

Everything starts with a cryptic array. Just copy and paste it. The number variable is a value we pass in our function like

function Curtains({number}) {
return(“what we need”);

We are going to use it to define how many curtains we want. Not mandatory.

Using the map index i, we can calculate it’s division-by-two remainder, knowing so if we’re in an even or odd index and assign the correct variant.

animate = { i%2 == 0 ? “odd”: “even” }

Now we just need the style: styling can be applied both in CSS or directly inside the motion.div using initial:

initial = {{ background: “white”, height: "100vw" }} // for example

(which allows you to define special rules based on indexes and math).

Anyway, CSS version:

#curtains { /* this is the container of all curtains */ width: 100vw; position: absolute; overflow: hidden; top: 0; left: 0;}.curtain { /* a single curtain */ background: white ; borderRight: 1px rgb(247,247,247) solid; height: 100vh; width: 25vw; /* change if you want more columns */ float: left; display: inline-block;}

The fundamental part is that the container must have no overflow, to prevent curtains from moving around and changing the full website layout.

2: The title

Learning this type of animation is extremely useful because it opens our creative coding perspective, and it’s widely used in modern web design.

We basically want the title to appear from the bottom, but like if it’s being cut from an invisible block. In fact, we can obtain this by using the invisible overflow principle.

<span id=”overflower”> <motion.h1 initial={{ opacity: 0, y: “100%”, }} animate={{ opacity: 1, y: 0, transition: {

duration: 0.45, delay: 1.4, ease: “easeInOut”},}}>photographer </motion.h1></span>

We basically defined a simple animation for the title, which goes from down to up. The cutting is obtained using CSS:

#overflower { display: block; overflow: hidden;}#overflower h1 {font-family: “Volux”; color: white; font-size: 80px; position: relative; width: 50%; padding: 30px;}

3: The images

Now that we know how to use variants, we can apply the same principles to the images. We can also notice that there are two animations going on: the hover effect, and the intro.

function Gallery() { const hover = { scaleX: 1.1, scaleY: 1.1, filter: “brightness(1)”, transition: { duration: 0.3, ease: “linear”, },};const zoom = {initial: { scale: 1, x: -80, filter: “brightness(0.78)” },animate: { x: 0, scale: 1.2, transition: { duration: 1.8 } },};

To bring some life, we make the images scale a bit, and move from left (-80) to right (0px). Then, when we hover, we scale again and bring brightness to the default 1, because we darkened them a bit in initial.

We used scaleX and scaleY instead of scale to prevent Motion-framer to use the 1.8s second animate duration when hovering out. It’s a sort of trick to an unwanted feature.

Let’s see part of the return method.

return (<> { /** this is a placeholder **/ <div id=”gallery”> <div className="item"> <motion.img variants={zoom} initial=”initial” animate=”animate” whileHover={hover} src=”assets/pic4.jpg”></motion.img> <Detail title=”Araki Toshida” role=”photographer” /></div>... other "items"...

Nothing strange: we have an “item” that holds both our image and the <Detail /> block we’ll see later. Using a parent container (item) for the image is vital because we need to hide the scaling overflow of the image.

The styling part is pretty simple:

#gallery { overflow: hidden; position: absolute; height: 100vh;}#gallery .item { width: 25%; height: 100%; float: left; overflow: hidden; position: relative;}#gallery .item img { width: 100%; height: 100%; object-fit: cover;}

As said, we need again to hide overflowing, and simply enlarge the image to cover the entire container.

4: The details

This is the simplest block since we basically need just some CSS.

function Detail({ title, role }) {return (<div className=”details”><h3>{title}</h3><p>{role}</p><span> > </span></div>);}

Let’s define a simple component function that receives, for example, the image title and job role as parameters, and uses them as text inside. In this way, we can call it as:

<Detail title={“something”} role={“something else”} />

The CSS is a bit longer, but nothing strange.

#gallery .item .details { background: white; width: 100%; position: absolute; bottom: -300px; height: 300px; transition: bottom 0.5s; transition-timing-function: cubic-bezier(0.785, 0.135, 0.15, 0.86); padding: 20px;}#gallery .item:hover .details { bottom: 0px;}.details h3 { font-size: 40px; font-family: “Bambi”;}.details span { width: 80px; height: 80px; border-radius: 50%; display: flex; align-items: center; justify-content: center; background: black; color: white; position: absolute; bottom: 30px; right: 30px; transition: background 0.5s, color 0.5s; transition-timing-function: cubic-bezier(0.785, 0.135, 0.15, 0.86);}.details span:hover { background: rgba(0,0,0,0); border: 2px black solid; color: black; cursor: pointer;}

We basically defined 5 parts:

  1. The block itself (which is out of sight thanks to absolute positioning and bottom: -300px )
  2. Making it appear when we hover the .item
  3. some text styling
  4. the button style and positioning with absolute.
  5. the button hover.


We defined simple components, passed a few values, and animated both by using variants and direct animation. Thanks to JS, this kind of animation workflow is a lot more performant than CSS and allows smooth rendering.

Some tips for motion coding:

  1. Think in terms of single components.
  2. When animating, remember to hide overflow most of the time.
  3. Often, beziers are more classy than linear and standard easings.

If you want the full code of these components:


Created by

Lorenzo Doremi

A Jack of all trades UX guy. Mainly interested in human-computer interaction, contemporary sociology and art.







Related Articles