Text Opening Sequence with CSS Animations

A simple and fun text opening sequence effect with blurry letters using CSS animations.
Opening Sequence

From our sponsor: Experience a seamless creation process from concept to production with advanced design features on Editor X.

Today I want to show you how to create a fun little typography effect with CSS animations and text shadows. Maybe you know those eerie opening sequences of movie trailers where some text is being faded in on a dark background. After seeing Introducting Briefs (which is not a terror movie trailer but a preview for an interesting app) I got inspired for recreating the effect using CSS.

The idea is to blur text and make it appear with a rotation while animating the space between the letters. This can be achieved in a variety of ways using properties like the new CSS filters, animating padding or margin and more. Here we’ll simply use text shadows and letter-spacing to achieve the desired effect. Since we will need to control each letter, we’ll use Dave Rupert’s Lettering.js to wrap the words and letters into multiple spans.

So, let’s do it.

Please note: this is highly experimental and only works as intended in browsers that support the respective CSS properties.

The idea is to have a couple of sentences fade in with the aforementioned effect, so we’ll want to wrap every appearing bit into an h2:

<div class="os-phrases">
	<h2>Sometimes it's better</h2>
	<h2>to hide</h2>
	<h2>in order to</h2>
	<h2>survive evil</h2>
	<h2>This fall</h2>
	<h2>Refresh to replay</h2>

Remember, this is just a experimental effect; let’s not be too strict with markup best practices πŸ™‚

Now that we have the pieces of the sentences in place, let’s apply lettering to wrap every word and letter into a span. We will actually need a letter to be wrapped into two spans because we want to use perspective on each single letter. So let’s go nuts and do this:

$("#os-phrases > h2").lettering('words').children("span").lettering().children("span").lettering();

This will created the following insane structure:

<div class="os-phrases" id="os-phrases">
		<span class="word1">
			<span class="char1">
				<span class="char1">
			<span class="char2">
				<span class="char1">
			<span class="char3">
				<span class="char1">
			<!-- ... -->
		</span> <!-- /word1 -->
		<!-- ... -->
		<!-- ... -->
	<!-- ... -->

We don’t really have to go that nuts but we want to be persnickety when it comes to the slight rotation: we want it to be in perspective. Rotating it without perspective will not differ too much but we want the real deal. If we are already using lettering.js we might as well go all the way.

Now that we’ve created a span wrapping hell, let’s make use of this mess and style the nested letters πŸ™‚

Note that the CSS will not contain any vendor prefixes, but you will find them in the files.

Our heading will be placed blatantly on the screen, absolutely, occupying all space:

.os-phrases h2 {
	font-family: 'Dosis', 'Lato', sans-serif;
	font-size: 70px;
	font-weight: 200;
	height: 100%;
	width: 100%;
	overflow: hidden;
	text-transform: uppercase;
	padding: 0;
	margin: 0;
	position: absolute;
	top: 0;
	left: 0;
	letter-spacing: 14px;
	text-align: center;

We want our sentences not to wrap but we want them to be centered across the screen, vertically and horizontally. How do we do that? Yes, right, we’ll abuse flexbox for that:

.os-phrases h2,
.os-phrases h2 > span {
	height: 100%;
	/* Centering with flexbox */
    display: flex;
    flex-direction: row;
    justify-content: center;
    align-items: center;

Note that the default value for flex-wrap is nowrap which is exactly what we want, so no need to add it here.
This will center our sentence parts nicely.

Let’s add some space between words:

.os-phrases h2 > span {
	margin: 0 15px;

The first wrapper for a letter will have perspective:

.os-phrases h2 > span > span {
	display: inline-block;
	perspective: 1000px;
	transform-origin: 50% 50%;

The letters itself will be transparent and we’ll run an animation on them. The animation will take 5.2 seconds.

.os-phrases h2 > span > span > span {
	display: inline-block;
	color: hsla(0,0%,0%,0);
	transform-style: preserve-3d;
	transform: translate3d(0,0,0);
	animation: OpeningSequence 5.2s linear forwards;

Before we get into the animation, let’s already define the delays for the sentence parts. The 5th sentence part, the one that has our imaginary movie title “Thanatos”, will be a special one and we’ll make it take longer and appear a bit later. The 6th and 7th will follow each other more quickly.


.os-phrases h2:nth-child(2) > span > span > span {
	animation-delay: 5s;

.os-phrases h2:nth-child(3) > span > span > span {
	animation-delay: 10s;

.os-phrases h2:nth-child(4) > span > span > span {
	animation-delay: 15s;

.os-phrases h2:nth-child(5) > span > span > span {
	font-size: 150px;
	animation-delay: 21s;
	animation-duration: 8s;

.os-phrases h2:nth-child(6) > span > span > span {
	animation-delay: 30s;

.os-phrases h2:nth-child(7) > span > span > span {
	animation-delay: 34s;

And the last one? We’ll give that one a special animation, but let’s take a look at the main one first.

In this animation we first want the text-shadow to make the letters look very blurry. The letter spacing will be very large and the letters will be rotated on the Y-axis, so that we only see it as a slice. The opacity is 0.2.

Half-way through the animation, we’ll sharpen up the letters and reduce the letter spacing to 14px, increase opacity and rotate them to 0 so that we are looking right at them.


In the last step, we’ll fade the letters out and scale them up a bit, making them slightly blurry again:

@keyframes OpeningSequence {
	0% {
		text-shadow: 0 0 50px #fff;
		letter-spacing: 80px;
		opacity: 0.2;
		transform: rotateY(-90deg);
	50% {
		text-shadow: 0 0 1px #fff;
		letter-spacing: 14px;
		opacity: 0.8;
		transform: rotateY(0deg);
	85% {
		text-shadow: 0 0 1px #fff;
		opacity: 0.8;
		transform: rotateY(0deg) translateZ(100px);
	100% {
		text-shadow: 0 0 10px #fff;
		opacity: 0;
		transform: translateZ(130px);
		pointer-events: none;

Our last sentence part will have another animation, one that will simply fade it in and leave it standing there:

.os-phrases h2:nth-child(8) > span > span > span {
	font-size: 30px;
	animation: FadeIn 4s linear 40s forwards;

@keyframes FadeIn { 
	0% {
		opacity: 0;
		text-shadow: 0 0 50px #fff;
	100% {
		opacity: 0.8;
		text-shadow: 0 0 1px #fff;

Last, but not least, we’ll add some emphasis on specific words by making them bold:

/* Bold words */
.os-phrases h2:first-child .word3,
.os-phrases h2:nth-child(2) .word2,
.os-phrases h2:nth-child(4) .word2 {
	font-weight: 600;

And that’s it! I hope this was fun and inspirational!

Tagged with:

Mary Lou

ML is a freelance web designer and developer with a passion for interaction design. She studied Cognitive Science and Computational Logic and has a weakness for the smell of freshly ground peppercorns.


Stay up to date with the latest web design and development news and relevant updates from Codrops.

CSS Reference

Learn about all important CSS properties from the basics with our extensive and easy-to-read CSS Reference.

It doesn't matter if you are a beginner or intermediate, start learning CSS now.

Feedback 68

Comments are closed.
  1. Dear Mary;
    I’m a single, semi-attractive male. I cook, I clean and am bare-footed in the kitchen for the most part when I’m not coding sites for clients. I could go on and on about traits that make me best suited for you but I’ll sum it to…WILL YOU MARRY ME?

  2. You should probably use user-select: none; to prevent users from selecting the hidden text πŸ˜‰

    • Good point. I noticed that too when watching the demo.

      other than that: great stuff!

  3. How to implement this effect in first load and then to switch to normal site look?

  4. Do you know what “Thanatos” means? Thanatos is a greek word and it means “death”. And I am proud of you πŸ™‚ Who needs FLASH anymore?

  5. WoW πŸ™‚
    Awesome effect but only in Chome and Opera looks perfect. In Safari I only see “Refresh to replay” animation and in FF blur effect in far from perfection. BUT STILL IS F**KIN’ GREAT PIECE OF CODING. Hail for our Queen Mary of St.Code πŸ™‚

  6. Mary, goddess of CSS! I know I should be leaving a constructive comment but I can’t think of any at the moment. I just thought this was awesome. I’ve always been in the learning process and never knew something like this, which is usually done in flash or video, is doable using CSS animation.

    Mad props!

  7. That’s really cool, but I’m curious as to why you use ? I’ve noticed it a in a few things you do, was just wondering! Ta

    • Opps! Seems my comment filterted out the HTML.

      I meant to say why do you use the blank HTML comments

  8. Can you explain to me why it works when I look at the Demo in Safari, but when I upload the demo on my server/or look at it my computer in Safari it no longer works? Thank.

    • Same problem here and couldn’t figure out any effective solution yet. Any working suggestion anyone? Thanks a lot, in advance…


    • Replace the script in the index file with:

      $(document).ready(function() { $("#os-phrases > h2").css('opacity',1).lettering('words').children("span").lettering().children("span").lettering(); })

  9. Mmmm,
    very interesting code !
    Thanks for sharing !


    Top icon of IE is on “question mark”,
    last version of Safari in Windows work.