How to create a slick CSS animation from The Marvelous Mrs Maisel

14 min read
How to create a slick CSS animation from The Marvelous Mrs Maisel cover image

This time, I will tackle a title sequence from 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:

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! 😉

TLDR

Here is the codepen with the final result.

See the Pen The Marvelous Mrs. Maisel by Rob (@robjoeol) on CodePen.

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

Preparing the SVG

Most sources suggest that the font used in the title is Fontdinerdotcom. 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 to test them out.

You can see sample text in the screenshot below.

fontdinerdotcom examples

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”.

svg initial text

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.

fontdinerdotcom sparkly character set display in font manager appt

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:

title text complete

And now, the SVG is short and sweet!

<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.

<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. 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 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 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.

let tl = gsap.timeline();

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

And the result is below.

^ 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.

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

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 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! 🎉

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

Here is the outcome:

^ 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.

: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; or scale the text up with the scale() transformation function. 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.

@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:

See the Pen The Marvelous Mrs. Maisel by Rob (@robjoeol) on CodePen.

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

Source code

The source code is available in this github repo. I will create more title sequences soon and add them to the repo also.

Also, you can check them all out in this codepen collection.

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!