How to Code a CSS Glitch Effect in Email [+ Code]

Code CSS Glitch Header

The Halloween edition of newsletter took a seasonally spooky turn. Our email team created a fabulously freaky email using glitching animations, all created using CSS.

 

Read to find out how our Email Marketing Specialist Carin Slater created this CSS glitch effect.

Subscribe for more email tricks and treats

Keep your know-how fresh on email marketing, design, and development with our newsletters—including “surprise and delight” moments like our October newsletter.

Sign me up!

Tutorial: CSS glitch effect in email

This piece originally appeared on Carin’s personal blog.

Slater: The glitching is all CSS animation using keyframes, so it’s very much something that works only in the browser window or in Apple or iOS mail. Great for progressive enhancements, not so good if your subscribers open mostly on Outlook or Gmail. 

The animation is the same for the image and for the text. In both instances, I created duplicate versions of the image/text using :before and :after pseudo classes and then animated those versions. Llets start with the image, I added the image as a background in a div so I could play around with it. Then I added the image again in a <img> tag so that it would show up anywhere that the animation wasn’t supported. I also added a size and shape to the div. The CSS:

.glitch-image {
 	max-width: 560px;
 	min-height: 250px;
 	width: 100%;
 	height: auto;
}
.image {
 	background: url('https://campaigns.litmus.com/_email/2022/October/2022-10-Newlsetter/202210_mod2.png') no-repeat center center;
 	background-size: 100% 100%;
}

And the html for the image:

<div class="glitch-image image">
 	<img src="https://campaigns.litmus.com/_email/2022/October/2022-10-Newlsetter/202210_mod2.png" width="560" height="250" alt="A spooky hotel being watched over by the Litmus Live logo in the moon." style="width: 100%; max-width: 560px; height: auto;" /></a>
</div>

I added another wrapper div around that glitch image. I added an overflow:hidden to the wrapper so any glitch that went outside the div didn’t show up. I wanted a TV screen type of glitch for the images, where it all happens in a designated space.

.glitch-wrapper {
 	max-width: 560px;
 	min-height: 250px;
 	width: 100%;
 	height: auto;
 	position: relative;
 	overflow: hidden;
}

And the html for the image:

<div class="glitch-wrapper">
 	<div class="glitch-image image">
      	<img src="https://campaigns.litmus.com/_email/2022/October/2022-10-Newlsetter/202210_mod2.png" width="560" height="250" alt="A spooky hotel being watched over by the Litmus Live logo in the moon." style="width: 100%; max-width: 560px; height: auto;" /></a>
 	</div>
</div>

For the text I added a data-text attribute on a span around the text in the html:

<h2><span class="glitch-text" data-text="How to Create Clickable Phone Numbers with HTML in Emails">How to Create Clickable Phone Numbers with HTML in Emails</span></h2>

I had to add some aria around it to make it more accessible, otherwise I’d end up with screen readers reading out the :before and :after content as well. So the final version had an aria-label on the h2 to tell the screen readers what to read and an aria-hidden on the span to hide the actual copy:

Related Post  How to Create a SaaS Content Marketing Strategy That Converts

<h2 aria-label="How to Create Clickable Phone Numbers with HTML in Emails"><span class="glitch-text" data-text="How to Create Clickable Phone Numbers with HTML in Emails" aria-hidden="true"><a rel="noopener" target="_blank" href="https://www.litmus.com/blog/html-clickable-phone-number-in-email/?utm_content=headline">How to Create Clickable Phone Numbers with HTML in Emails</a></span></h2>

I used the screen reader with Litmus Email Testing to test to see what it would sound like and it worked perfectly.

Ok, so now that we’ve got the base set up, we start building the pseudo classes. For the image we’re going to have those have nothing in the content, but they’ll inherit the background image:

.glitch-image:after, .glitch-image:before {
 	content: "";
 	background: inherit;
}

For the text, I had the data-text attribute display as the content and I made sure it was the same color as the text:

.glitch-text:before, .glitch-text:after {
 	color: #262524;
 	content: attr(data-text);
}

At this point we have three versions of the image and three versions of the text, we need to stack them on top of each other so that we can eventually hide, show, and move around the before and after content to create the glitch effect. So we’ll use absolute positioning to put the pseudo class content on top of the original.

.glitch-image:after, .glitch-image:before {
 	content: "";
 	background: inherit;
 	position: absolute;
  	top: 0;
  	left: 0;
  	right: 0;
  	bottom: 0;
}

For the text, I added the absolute positioning, but I also had to add some positioning to the text to make sure everything stacked correctly. And some alignment and widths to the pseudo class content to make sure it lined up with the actual text. This was the CSS that let me get that all aligned:

.glitch-text:before, .glitch-text:after {
 	color: #262524;
 	content: attr(data-text);
 	position: absolute;
 	top: 0;
 	left: 0;
}
.glitch-text {
  	position: relative;
  	display: grid;
 	grid-template-columns: 1fr;
}

Fun fact: When the text was shorter and didn’t span the whole width I found I had to add text-align:center and width:100% to make sure it actually ended up on top of the content. Since I only had one headline that was like that I made a special class just for that headline.

Related Post  The 16 Best Ways to Get More Followers on Instagram in 2022

.text-glitch-a:before, .text-glitch-a:after { text-align: center; width: 100%; }

If you find that your pseudo class content isn’t getting positioned correctly, you can try adding that in. So that gives us the two elements we’ll be animating. Now to add the animations.

Essentially what we’re going to do is create a rectangle clip-path of the additional content that moves up and down and gets hidden/shown at different points during the animation. And since the rectangles are directly on top of the content, I also moved the content to the left a bit during the animation. I used two variations of the same animation, one for the before content and one for the after content, so the content would be doing two different things when the glitch happened.

How did I land on these specific points? Well I wanted the rectangle to move pretty fast to create a flicker, so I know I wanted the animation points to be close together, but I also didn’t want to overwhelm anyone so I wanted there to be a good space between glitches. So the animation only goes to 25%, then stays static after that. And the rectangles I had at several different intervals. The animation CSS ended up looking like this:

@keyframes glitch-anim {
  	0%, 25.1% { clip-path: polygon(0 0, 0 0, 0 0, 0 0); left: 2px; }
  	5% { clip-path: polygon(0 80%, 100% 80%, 100% 70%, 0 70%); left: 4px; }
  	9% { clip-path: polygon(0 20%, 100% 20%, 100% 30%, 0 30%); left: 7px; }
  	10% { clip-path: polygon(0 20%, 100% 20%, 100% 20%, 0 20%); left: 6px; }
  	15% { clip-path: polygon(0 40%, 100% 40%, 100% 30%; 0 30%); left: 5px; }
  	19% { clip-path: polygon(0 60%, 100% 60%, 100% 70%, 0 70%); left: 7px; }
  	20% { clip-path: polygon(0 60%, 100% 60%, 100% 50%, 0 50%); left: 4px; }
  	25% { clip-path: polygon(0 0, 100% 0, 100% 0, 0 0); left: 5px; }
}
@keyframes glitch-anim-2 {
  	0%, 25.1% { clip-path: polygon(0 0, 0 0, 0 0, 0 0); left: -2px; }
  	3% { clip-path: polygon(0 80%, 100% 80%, 100% 90%, 0 90%); left: -7px; }
  	7% { clip-path: polygon(0 20%, 100% 20%, 100% 30%, 0 30%); left: -3px; }
  	8% { clip-path: polygon(0 20%, 100% 20%, 100% 30%, 0 30%); left: -7px; }
  	12% { clip-path: polygon(0 40%, 100% 40%, 100% 50%; 0 50%); left: -4px; }
  	16% { clip-path: polygon(0 60%, 100% 60%, 100% 70%, 0 70%); left: -6px; }
  	17% { clip-path: polygon(0 60%, 100% 60%, 100% 60%, 0 60%); left: -5px; }
  	25% { clip-path: polygon(0 0, 100% 0, 100% 0, 0 0); left: -7px; }
}

.glitch-text:before, .glitch-image:before {
 	animation-name: glitch-anim;
}
.glitch-text:after, .glitch-image:after {
 	animation-name: glitch-anim-2;
}

Then we applied the animation settings. In this case, I’m ok with them being the same for the before and after content so I group it all together:

.glitch-image:after, .glitch-image:before, .glitch-text:before, .glitch-text:after {
 	animation-timing-function: linear;
 	animation-fill-mode: forwards;
 	animation-iteration-count: infinite;
}

I made the text duration longer than the image duration, as the longer duration looked better on the text, so here is the last bit of CSS for the animation:

.glitch-text:before, .glitch-text:after {
 	animation-duration: 3s;
 	animation-delay: 0s;
}
.glitch-image:before, .glitch-image:after {
 	animation-duration: 7s;
}
.image:after, .image:before {
 	animation-delay: 0s;
}

When I did this in the newsletter, I had the images set up with different delays so that the glitches didn’t happen all at the same time. I wanted to make sure that if someone was scrolling and missed one animation, they’d still see the glitch on one of the images.

The last little bits that I added were to optimize for mobile, dark mode, and reduced motion:

@media screen and (max-width:600px) {
 	.glitch-wrapper, .glitch-image {
      	min-height: 140px;
  	}
}
@media (prefers-reduced-motion) {
 	.glitch:before, .glitch:after, .glitch-text:before, .glitch-text:after {
      	animation-name: none;
 	}
}
@media (prefers-color-scheme: dark) {
 	.glitch-text:before, .glitch-text:after {
      	color: #fdfdfd;
 	}
}
[data-ogsc] .glitch-text:before, [data-ogsc] .glitch-text:after {
 	color: #fdfdfd;
}

I put everything in a CSS file and hosted it on our servers so that it would only load on Apple mail and iOS. That helped with it only showing where it was supported. I ran into some of the glitches not showing up correctly in some email clients in Litmus, so I added some targeting to take it out of those specific clients, but that’s something you’ll have to do on a case by case basis when you start testing it in your email.

Ensure your designs come across right

Broken emails lead to less conversions. Preview your emails across 100+ email clients, apps, and devices to ensure an on-brand, error-free subscriber experience. Every time.

Disclaimer: This post was shared via an RSS Feed

Liked this content? Please show some love to the author in this link.

Jerry Gordon

About Jerry Gordon

Webmaster, nature and tech lover. Jerry manages the day-to-day operations at DigiToolsadvisor. He loves enjoying his free time, but most of all, trying new tools to master.