How to create a slick CSS animation from The Marvelous Mrs Maisel (TV series title sequence)
Tags: webdev, css, javascript, tutorial
Published: 2022-05-24 10:40:42


This time, I will tackle a title sequence from [The Marvelous Mrs. Maisel](https://en.wikipedia.org/wiki/The_Marvelous_Mrs._Maisel).

The Marvelous Mrs. Maisel is an American period comedy-drama television series. It is set in the late 1950s and early 1960s, it stars Rachel Brosnahan as Miriam "Midge" Maisel, a New York housewife who discovers she has a knack for stand-up comedy and pursues a career in it.

## The Marvelous Mrs Maisel opening title

I settled on making the title from Episode 2 of Season 4, which has a cool kind of curtain swipe affect. The colours and motion are quite fun.

Here is a clip of it:

{% youtube https://youtu.be/oimgFUAgI4E %}

I will make a SVG for the title. You could probably get away with regular HTML in this case. I don't know if I need to draw the sparkles, I must hunt the font down first! And in any case, SVG being scalable, it makes sizing things a bit easier, and `rect` is always more descriptive than `div`! 😉

## Preparing the SVG

Most sources suggest that the font used in the title is [Fontdinerdotcom](https://fontmeme.com/fonts/fontdinerdotcom-font/). It has 2 variants included: *Fontdotdinerdotcom* is the regular, and *Fontdotdinerdotcom Sparkly* has some sparkles I guess! I installed them both, and opened [LibreOffice Writer](https://www.libreoffice.org/discover/writer/) to test them out.

You can see sample text in the screenshot below.

<img src="https://roboleary.net/assets/img/blog/2022-05-23-how-to-make-a-slick-animation-the-marvelous-mrs-maisel-title-sequence/fontdinerdotcom.png" alt="fontdinerdotcom examples" width="808" height="611" loading="lazy"/>

It looks like the right match!

Let's create our SVG. I opened up Inkscape and created a drawing with landscape orientation, I picked a width of 1200 and height of 800.

I wanted to create a *`text`* element for each word in the title as they vary in size. On inspection, it looks like the words "The" and "Mrs." are the same size, the word "Marvelous" is about 30% bigger, then the word "Maisel" is about 20% bigger again. To arrive at the right result, I will need to play with font size and the spacing.

I settled at the following for the words:

- 80px for "The" and "Mrs."
- 120px for "Marvelous"
- 140px for "Maisel".

<img src="https://roboleary.net/assets/img/blog/2022-05-23-how-to-make-a-slick-animation-the-marvelous-mrs-maisel-title-sequence/svg-initial-text.png" alt="svg initial text" width="510" height="323" loading="lazy"/>

Next, I needed to add the 2 sparkles. I had started to draw the sparkle myself by hand, and was thinking what would be the quickest way to get the correct shape. I started playing with the star shape tool and produced one part of the shape. But then, I thought maybe the *Fontdotdinerdotcom Sparkly* has the sparkle as a character! So, I opened up the font in the Font Manager app to see the character set.

<img src="https://roboleary.net/assets/img/blog/2022-05-23-how-to-make-a-slick-animation-the-marvelous-mrs-maisel-title-sequence/font-manger-fontdinerdotcom-sparkly-characters.png" alt="fontdinerdotcom sparkly character set display in font manager appt" width="1271" height="412" loading="lazy"/>

And bingo, the asterisk character is a sparkle! That's a time-saver!

So, I added in 2 more `text` elements and added an asterisk as their text content. I tried out some font sizes to size them appropriately.

And the result looked like this:

<img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e1orzu12f8ow4jtp2ybl.png" alt="title text complete" width="668" height="455" loading="lazy"/>

And now, the SVG is short and sweet!

```html
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 800">
	<g id="title">
		<text x="530.745" y="248.517"  font-family="Fontdinerdotcom" font-size="80" >The</text>
		<text x="619.488" y="442.004"  font-family="Fontdinerdotcom" font-size="80" >Mrs.</text>
		<text x="400.183" y="352.084"  font-family="Fontdinerdotcom" font-size="140" >Marvelous</text>
		<text x="674.057" y="311.245"  font-family="Fontdinerdotcom Sparkly" font-size="120">*</text>
		<text x="465.468" y="548.199"  font-family="Fontdinerdotcom" font-size="190" >Maisel</text>
		<text x="409.372" y="542.419"  font-family="Fontdinerdotcom Sparkly" font-size="120">*</text>
	</g>
</svg>
```

Next, we need 6 rectangles to represent the curtains that swipe across. These are positioned behind the title text, so they position before our `text` elements in the SVG. We can make these the same `width` and `height` as the SVG.

We will set one `rect` element as the initial background. It has a `x` value of zero. I gave it an `id` of "bg1" to be easy to identify.

We will position the other 5 `rect` element just outside of the view, a  `x` value of 1200 does this. I gave them a class of "bg", so I can reference in the animation as a group later.

I grabbed the colors from the video by pausing the video when the background color changes, taking a screenshot , and uploading that screenshot to <https://imageresizer.com/color-picker> to extract the color. I assigned these to the `fill` attribute of each `rect`.

```html
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 800">
  <rect id="bg1" x="0" y="0" width="1200" height="800" fill="#100D18" />
  <rect id="bg2" class="bg" x="1200" y="0" width="1200" height="800" fill="#4EA5D5"/>
  <rect id="bg3" class="bg" x="1200" y="0" width="1200" height="800" fill="#C08250"/>
  <rect id="bg4" class="bg" x="1200" y="0" width="1200" height="800" fill="#BBC052"/>
  <rect id="bg5" class="bg" x="1200" y="0" width="1200" height="800" fill="#CC81B9"/>
  <rect id="bg6" class="bg" x="1200" y="0" width="1200" height="800" fill="#CD0000"/>
  <g id="title">
    <text x="530.745" y="248.517"  font-family="Fontdinerdotcom" font-size="80" >The</text>
	<text x="619.488" y="442.004"  font-family="Fontdinerdotcom" font-size="80" >Mrs.</text>
	<text x="400.183" y="352.084"  font-family="Fontdinerdotcom" font-size="140" >Marvelous</text>
	<text x="674.057" y="311.245"  font-family="Fontdinerdotcom Sparkly" font-size="120">*</text>
	<text x="465.468" y="548.199"  font-family="Fontdinerdotcom" font-size="190" >Maisel</text>
	<text x="409.372" y="542.419"  font-family="Fontdinerdotcom Sparkly" font-size="120">*</text>
  </g>
</svg>
```

At this stage, we are far enough along where we can start building the animation. The curtain swiping part of the animation is straightforward, we are just moving our rectangles by changing the `x` value. I am not sure how I will handle changing the color of the text yet!

I may need to return and make 6 copies of the `title` group to pull it off. If so, I can make the "title" group as a `symbol` and reuse it.

## Making the animation

I find it quicker to prototype with the [Greensock (GSAP) library](https://greensock.com/gsap/). If its a simple enough animation, I may convert it to a CSS animation when I have found the right values for the tweens.

I am going to break the animation into 3 parts:

1. I call this the "curtain swipe". This is the rectangles being shifted into view from right to left.
2. The text color transition is where the color of the text transitons from one colour to another as the curtain swipes across it.
3. Bringing the text forward.

### Part 1 - Curtain swipe

For the curtain swipe part of the animation, it last about 2 seconds. It appears that each swipe tween starts about 400 millseconds after the preceding one. So, we can stagger the beginning of each tween.

We need to play with the `duration`, `ease`, and `stagger` properties in the `vars` object we supply as an argument to the [`to()` function](https://greensock.com/docs/v3/GSAP/gsap.to()) to arrive at the right combination.  

I started with a `duration` of 0.4 (2 seconds divided by 5 elements as they overlap quite a bit).  The `x` value is the negative value of the `rect` width, so minus 1200.  Then, we get the `rect` moving across in a steady, linear way.

Next, I used the [GSAP Ease Visualizer](https://greensock.com/ease-visualizer) to try to find a suitable easing. I tried a few, but found that "power1.out" was the closest match. Adding and tweaking the `stagger` property was the final piece, and 0.3 was the sweet spot. This was the finished code.

```javascript
let tl = gsap.timeline();

tl.to(".bg", {
  duration: 0.4,
  x: -1200,
  ease: "power1.out",
  stagger: 0.3,
});
```

And the result is below.

{% codepen https://codepen.io/robatronbobby/pen/LYaLNBB %}

^ You can click the title to restart the animation if you missed it!

### Part 2 - Text color transition

I tried a few things out here and wasted a bit of time here frankly.

#### Round 1 - Try `clipPath`

Initially, I thought I will try the brute force approach, and then try to simplify. By brute force I mean making 6 copies of the "title" group stacked on top of one other with different colours, and then doing something with each.

I could have a rectangle in a `clipPath` that would hide the upper ones, and then I can move that rectangle across that "title" group to reveal the text. Then, I could remove the `clipPath` from the revealed group, reset the position of the rectangle, and then apply it to the next "title" group in the stack. And repeat this action 4 more times.

I tried out a minimal example and it worked, but it is tricky to coordinate the timing with the curtain swipe tweens to make it match up correctly. Plus, the code for the timeline is ugly.

I did take it close to the finishing line, even though I knew it was not the way I wanted to go. It works as intended, more or less.

{% codepen https://codepen.io/robatronbobby/pen/LYaLNBB %}

I picked this up close to my bedtime, which is generally a bad idea! I will return with a clear head tomorrow!

#### Round 2 - Would blending mode work?

How about using a blending mode for the text?

Could I used [`mix-blend-mode`](https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode) to change the color of the text as the background changes?

If so, I could use a single "title" group! The only downside would be that you have less control on how the colors turns out. I guess you can just pick different colors until you get a result you like! Let's try it out.

Initially I thought it didn't work as no text was visible when I added `mix-blend-mode: screen;` to the `text` selector, but when I changed the value to "difference", it changed the text color according to the background color.

After trying out some values for `fill` and `mix-blend-mode`, I found that a near-white color for `fill`, along with a  `mix-blend-mode` of "difference" or "exclusion" gave the best results for my background colors.

So, just by adding the CSS below to our code from part 1 was enough! No need to add more SVG elements or JavaScript! 🎉

```css
text {
  fill: papayawhip;
  mix-blend-mode: exclusion;
}
```

Here is the outcome:

{% codepen https://codepen.io/robatronbobby/pen/xxBrVaW %}

You can click the title to restart the animation if you missed it!

#### Round 3 - Convert to pure CSS animation

Since it is quite a simple animation in execution, let's convert it to a CSS-only animation.

We declare the animation on the "bg" class. To stagger the start of the animations, we need to add an increasingly longer delay to each instance using the `animation-delay` property. We can use a CSS variable for the base delay value called `--delay`. Each delay is a multiple of this value based on it's ordinal position e.g.  first instance is not delayed, second instance is delayed by the delay time, the third instance is delayed double the delay time, and so on.

```css
:root {
  --delay: 0.3s;
}

@keyframes slide-out {
  to {
    transform: translateX(-1200px);
  }
}

.bg {
  animation-duration: 0.4s;
  animation-fill-mode: forwards;
  animation-name: slide-out;
}

.bg:nth-of-type(2) {
  animation-delay: var(--delay);
}

.bg:nth-of-type(3) {
  animation-delay: calc(var(--delay) * 2);
}

.bg:nth-of-type(4) {
  animation-delay: calc(var(--delay) * 3);
}

.bg:nth-of-type(5) {
  animation-delay: calc(var(--delay) * 4);
}

.bg:nth-of-type(6) {
  animation-delay: calc(var(--delay) * 5);
}
```

### Part 3 - Bringing the text forward

To bring the text forward, we can do a translation along the Z-axis with with [translateZ() transformation function](https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/translateZ); or scale the text up with the [`scale()` transformation function](https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/scale). We will use the latter.

We will set its initial scale as 80% and increase it to its original scale.

Since we want to maintain the central position of the title text for the animation, we set the `transform-origin: center center;`.

As the animation starts quickly and then slows down, the "ease-out" timing function fits here.

We apply to the animation to the `text` selector. If we apply it to `#title`, it messes up the text color transition it seems.

```css
@keyframes scale-up {
  from {
    transform: scale(0.8);
  }

  to {
    transform: scale(1);
  }
}

text {
  animation-duration: 2s;
  animation-fill-mode: forwards;
  animation-name: scale-up;
  animation-timing-function: ease-out;

  transform-origin: center center;
}
```

## The final animation

Putting it all together. This is the result:

{% codepen https://codepen.io/robjoeol/pen/yLvopZe %}

Give it a ❤️ on Codepen if you like it! 😊

## Source code

The source code is available in [this github repo](https://github.com/robole/title-sequences). I will create more title sequences soon and them add to the repo also.

Also, you can check them all out in [this codepen collection](https://codepen.io/collection/nNmwgP).

## Last word

Part of why I decided to write down my progress when creating these animations is to remind myself that sometimes, you struggle. And sometimes, you make some interesting discoveries along the way that teach you new techniques. It can be frustrating and rewarding! As your experience grows, the frustration part shrinks!

I thought this was going to be an easy animation to create, the core elements are straightforward, but to make it work together was actually deceptively tricky! It took me some time to get to a more elegant solution for this, the missing ingredient was `mix-blend-mode`. It was an interesting journey and I am happy with the outcome, at last!

P.S. Actually, I just noticed as I was finishing up is that I missed animating the sparkles! They have a kind of twinkle animation. I think I got too caught up in making the rest of the animation and did not notice it! Oh well, I will leave as it!