Lines to Content Layout Animation

Today I’d like to share a little layout animation with you. The idea is to switch from a line view to a larger content view while animating a tiny image. This time, I used GreenSock’s Flip plugin which made things a lot easier as it does most of the work for transitioning an element to a new state and new layout.

The effect is inspired by part of this cool video by Holographik:

Typography elements disappear while images get animated to larger views.

The initial view of our layout is the following:

When clicking on one of the images, we transition to a new layout which looks as follows:

The whole flow looks like this:

The Flip plugin is a very useful tool that you can use for many complex cases. It also factors in nested transforms which is something quite special as it allows for powerful view switching without compromising your structures.

Hope you enjoy this demo and find it useful! Thanks for checking by!

The post Lines to Content Layout Animation appeared first on Codrops.

Hover Preview Effect with Mini Map

I find mini maps really exciting. These tiny UI components can give visual feedback on where we are on a page, e.g. what we are hovering or viewing. Sometimes this helps create a representation of content and ease navigation. But we can also go crazy and try some experimental stuff with it! And that’s exactly what I would like to share with you today.

The main idea is to use a hover effect in order to preview some content. Like a giant tooltip. Since this large preview will cover the whole page (including the thing we are hovering) we use a mini map to guide us. This map shows where we are with the mouse.

The design and animation for this demo is based on the beautiful website of La Culture des Lieux. Have a look at it, it’s a wonderful scroll-based design with lots of creative animations. The epic photography used in this demo are by Frankie Cordoba.

The initial view of our demo is a set of three thumbnails:

When we hover over any of the thumbs, we animate some content in:

We also show a mini map that maps the movements of the pointing device to a small representation of the three thumbnails. This indicates where we are at the moment. As soon as we move the mouse away from the thumbnail, the content pops back in and we see the initial thumbnails view.

Have a look at the entire interaction flow:

There are two demos. The first one hides the cursor on hover. The second one shows a custom cursor with a larger appearance while hovering the thumbnails.

Note that this is a highly experimental concept! Obviously this should be some kind of add-on to a design. For mobile, some other interaction and solution would have to be used.

If you are in love with mini maps like me, you might enjoy the Grid Zoom Layout that contains a tiny grid version when opening the content.

The post Hover Preview Effect with Mini Map appeared first on Codrops.

Repetition Image Hover Effects

The other day, I encountered a very interesting animation. It’s kind of a repetition effect on an image where the same gets scaled, layer by layer. This is Eva Habermann’s website where this element has that exact effect:

While this is a scroll based animation, there was also a hover effect somewhere, I just can’t recall where! If you’ve seen it, please let me know so that I can add it here.

There are some interesting parameters that we can play with in order to create different touches, all with a unique look and feel, so this is what I came up with, hope you enjoy it!

The way that we can define the parameters is as follows:

<div 
	class="image image--style-3" 
	data-repetition data-repetition-elems="6" 
	data-repetition-origin="150% 100%" 
	data-repetition-animate="scaleY" 
	data-repetition-stagger="-0.12" 
	data-repetition-initial-scale="1.3" 
	data-repetition-duration="0.5" 
	data-repetition-ease="power1.inOut" 
	style="background-image:url(img/11.jpg);">
</div>

We make the following structure out of this:

<div class="image image--style-3" style="background-image: none; transform-origin: 150% 100%; transform: translate(0px, 0px);">
    <div class="image__wrap">
        <div class="image__element" style="background-image: url(11.jpg); transform-origin: 150% 100%; transform: translate(0px, 0px);"></div>
    </div>
    <div class="image__element" style="background-image: url(11.jpg); transform: translate(0px, 0px);"></div>
    <div class="image__element" style="background-image: url(11.jpg); transform: translate(0px, 0px);"></div>
    <div class="image__element" style="background-image: url(11.jpg); transform: translate(0px, 0px);"></div>
    <div class="image__element" style="background-image: url(11.jpg); transform: translate(0px, 0px);"></div>
    <div class="image__element" style="background-image: url(11.jpg); transform: translate(0px, 0px);"></div>
</div>

Then we animate the “image_element” divs according to the parameters set.

Here’s a short explanation of the parameters:

data-repetition 					
// this is so that we know we have to apply the effect

data-repetition-elems="4" 			
// number of inner elements/images

data-repetition-animate="scale" 	
// property to animate: scale, scaleX, scaleY 

data-repetition-origin="50% 50%" 	
// transform origin

data-repetition-stagger="-0.1" 		
// GSAP animation stagger value between each inner image

data-repetition-initial-scale="2" 	
// this is the initial scale that is applied to the first inner child

data-repetition-duration="0.8" 		
// animation duration

data-repetition-ease="power2.inOut" 
// animation ease

There’s lots of things to experiment with here, so I hope you can use this to have some fun with it!

Here’s our first example:

The main idea is to have repeated layers of the same image and do something to them, like scale them up and down, like in this case. There’s a little twist added here, which is that the first and last layer also have a zoom effect on the image. Changing the transform origin, can also add a nice touch to it:

I really hope you enjoy this and find it useful!

Thanks for checking by and if you want to support our work, please share and give us a follow @codrops!

The post Repetition Image Hover Effects appeared first on Codrops.

6 Creative Ideas for CSS Link Hover Effects

Creating CSS link hover effects can add a bit of flair to an otherwise bland webpage. If you’ve ever found yourself stumped trying to make a slick hover effect, then I have six CSS effects for you to take and use for your next project.

A default link hover effect above a styled link hover effect with a rainbow underline.

Let’s get right to it!

I know we’re talking about :hover and all, but it can sometimes (but maybe not always) be a good idea lump :focus in as well, as not all interactions are directly from a mouse, but perhaps a tap or keystroke.

This effect applies a box shadow to the inline link, altering the color of the link text in the process. We start with padding all around the link, then add a negative margin of the same value to prevent the padding from disrupting the text flow.

We will use box-shadow instead of the background property since it allows us to transition.

a {
  box-shadow: inset 0 0 0 0 #54b3d6;
  color: #54b3d6;
  margin: 0 -.25rem;
  padding: 0 .25rem;
  transition: color .3s ease-in-out, box-shadow .3s ease-in-out;
}
a:hover {
  box-shadow: inset 100px 0 0 0 #54b3d6;
  color: white;
}

Here’s a fun one where we swap the text of the link with some other text on hover. Hover over the text and the linked text slides out as new text slides in.

Easier to show than tell.

There’s quite a bit of trickery happening in this link hover effect. But the magic sauce is using a data-attribute to define the text that slides in and call it with the content property of the link’s ::after pseudo-element.

First off, the HTML markup:

<p>Hover <a href="#" data-replace="get a link"><span>get a link</span></a></p>

That’s a lot of inline markup, but you’re looking at a paragraph tag that contains a link and a span.

Let’s give link some base styles. We need to give it relative positioning to hold the pseudo-elements — which will be absolutely positioned — in place, make sure it’s displayed as inline-block to get box element styling affordances, and hide any overflow the pseudo-elements might cause.

a {
  overflow: hidden;
  position: relative;
  display: inline-block;
}

The ::before and ::after pseudo-elements should have some absolute positioning so they stack with the actual link. We’ll make sure they are set to the link’s full width with a zero offset in the left position, setting them up for some sliding action.

a::before,
a::after {
 content: '';
  position: absolute;
  width: 100%;
  left: 0;
}

The ::after pseudo-element gets the content from the link’s data-attribute that’s in the HTML markup:

a::after {
  content: attr(data-replace);
}

Now we can transform: translate3d() the ::after pseudo-element element to the right by 200%. We move it back into position on :hover. While we’re at it, we can give this a zero offset n the top direction. This’ll be important later when we use the ::before pseudo-element like an underline below the text.

a::after {
  content: attr(data-replace);
  top: 0;
  transform-origin: 100% 50%;
  transform: translate3d(200%, 0, 0);
}

a:hover::after,
a:focus::after {
  transform: translate3d(0, 0, 0);
}

We’re also going to transform: scale() the ::before pseudo-element so it’s hidden by default, then scale it back up on :hover. We’ll make it small, like 2px in height, and pin it to the bottom so it looks like an underline on the text that swaps in with ::after.

a::before {
  background-color: #54b3d6;
  height: 2px;
  bottom: 0;
  transform-origin: 100% 50%;
  transform: scaleX(0);
}

a:hover::before,
a:focus::before {
  transform-origin: 0% 50%;
  transform: scaleX(1);
}

The rest is all preference! We drop in a transition on the transform effects, some colors, and whatnot to get the full effect. Those values are totally up to you.

View full CSS
a {
  overflow: hidden;
  position: relative;
  display: inline-block;
}

a::before,
a::after {
 content: '';
  position: absolute;
  width: 100%;
  left: 0;
}
a::before {
  background-color: #54b3d6;
  height: 2px;
  bottom: 0;
  transform-origin: 100% 50%;
  transform: scaleX(0);
  transition: transform .3s cubic-bezier(0.76, 0, 0.24, 1);
}
a::after {
  content: attr(data-replace);
  height: 100%;
  top: 0;
  transform-origin: 100% 50%;
  transform: translate3d(200%, 0, 0);
  transition: transform .3s cubic-bezier(0.76, 0, 0.24, 1);
  color: #54b3d6;
}

a:hover::before {
  transform-origin: 0% 50%;
  transform: scaleX(1);
}
a:hover::after {
  transform: translate3d(0, 0, 0);
}

a span {
  display: inline-block;
  transition: transform .3s cubic-bezier(0.76, 0, 0.24, 1);
}

a:hover span {
  transform: translate3d(-200%, 0, 0);
}

This is a pretty popular effect I’ve seen used in quite a few places. The idea is that you use the link’s ::before pseudo-element as a thick underline that sits slightly behind the actual text of the link. Then, on hover, the pseudo-element expands to cover the whole thing.

OK, some base styles for the link. We want no text-decoration since ::before will act like one, then some relative positioning to hold ::before in place when we give that absolute positioning.

a {
  text-decoration: none;
  position: relative;
}

Now let’s set up ::before by making it something like 8px tall so it looks like a thick underline. We’ll also give it absolute positioning so we have control to make it the full width of the actual link while offsetting it so it’s at the left and is just a smidge off the bottom so it looks like it’s subtly highlighting the link. May as well give it z-index: -1 so we’re assured it sits behind the link.

a::before {
  content: '';
  background-color: hsla(196, 61%, 58%, .75);
  position: absolute;
  left: 0;
  bottom: 3px;
  width: 100%;
  height: 8px;
  z-index: -1;
}

Nice, nice. Let’s make it appear as though ::before is growing when the link is hovered. All we need is to change the height from 3px to 100%. Notice that I’m also dropping the bottom offset back to zero so the background covers more space when it grows.

a:hover::before {
  bottom: 0;
  height: 100%;
}

Now for slight transition on those changes:

a::before {
  content: '';
  background-color: hsla(196, 61%, 58%, .75);
  position: absolute;
  left: 0;
  bottom: 3px;
  width: 100%;
  height: 8px;
  z-index: -1;
  transition: all .3s ease-in-out;
}
View full CSS
a {
  text-decoration: none;
  color: #18272F;
  font-weight: 700;
  position: relative;
}

a::before {
  content: '';
  background-color: hsla(196, 61%, 58%, .75);
  position: absolute;
  left: 0;
  bottom: 3px;
  width: 100%;
  height: 8px;
  z-index: -1;
  transition: all .3s ease-in-out;
}

a:hover::before {
  bottom: 0;
  height: 100%;
}

I personally like using this effect for links in a navigation. The link starts in one color without an underline. Then, on hover, a new color slides in from the right while an underline slides in from the left.

Neat, right? There’s a lot of motion happening in there, so you might consider the accessibility implications and wrap it all in a prefers-reduced-motion query to replace it with something more subtle for those with motion sensitivities.

Here’s how it works. We give the link a linear background gradient with a hard stop between two colors at the halfway mark.

a {
  background-image: linear-gradient(
    to right,
    #54b3d6,
    #54b3d6 50%,
    #000 50%
  );
}

We make the background double the link’s width, or 200%, and position it all the way over to the left. That way, it’s like only one of the gradients two colors is showing.

a {
  background-image: linear-gradient(
    to right,
    #54b3d6,
    #54b3d6 50%,
    #000 50%
  );
  background-size: 200% 100%;
  background-position: -100%;
}

The magic happens when we reach for a couple of non-standard -webkit-prefixed properties. One strips the color out of the text to make it transparent. The other clips the background gradient to the text so it appears the text is actually the color of the background.

a {
  background-image: linear-gradient(
    to right,
    #54b3d6,
    #54b3d6 50%,
    #000 50%
  );
  background-size: 200% 100%;
  background-position: -100%;
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
}

Still with me? Now let’s make the link’s faux underline by putting ::before to use. We’ll give it the same color we gave the on the hidden portion of the link’s background gradient and position it under the actual link so it looks like a proper text-decoration: underline.

a:before {
  content: '';
  background: #54b3d6;
  display: block;
  position: absolute;
  bottom: -3px;
  left: 0;
  width: 0;
  height: 3px;
}

On hover, we slide ::before into place, coming in from the left:

a:hover {
 background-position: 0;
}

Now, this is a little tricky. On hover, we make the link’s ::before pseudo-element 100% of the link’s width. If we were to apply this directly to the link’s hover, we’d make the link itself full-width, which moves it around the screen. Yikes!

a:hover::before {
  width: 100%;
}

Add a little transition to smooth things out:

a {
  background-image: linear-gradient(
    to right,
    #54b3d6,
    #54b3d6 50%,
    #000 50%
  );
  background-size: 200% 100%;
  background-position: -100%;
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  transition: all 0.3s ease-in-out;
}
View full CSS
a {
  background-image: linear-gradient(
    to right,
    #54b3d6,
    #54b3d6 50%,
    #000 50%
  );
  background-size: 200% 100%;
  background-position: -100%;
  display: inline-block;
  padding: 5px 0;
  position: relative;
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  transition: all 0.3s ease-in-out;
}

a:before {
  content: '';
  background: #54b3d6;
  display: block;
  position: absolute;
  bottom: -3px;
  left: 0;
  width: 0;
  height: 3px;
  transition: all 0.3s ease-in-out;
}

a:hover {
 background-position: 0;
}

a:hover::before {
  width:100%;
}

We can’t do text-decoration-color: rainbow, but we can fake it with a little background magic mixed with linear gradients.

First, we remove the link’s text-decoration:

a {
  text-decoration: none;
}

Now for those gradients. We chain two linear gradients together on the same background property. One gradient is the initial color before hover. The second is the rainbow on hover.

a {
  background:
    linear-gradient(
      to right,
      rgba(100, 200, 200, 1),
      rgba(100, 200, 200, 1)
    ),
    linear-gradient(
      to right,
      rgba(255, 0, 0, 1),
      rgba(255, 0, 180, 1),
      rgba(0, 100, 200, 1)
  );
}

Let’s make the background size a mere 3px tall so it looks like, you know, an underline. We can size both gradients together on the background-size property so that the initial gradient is full width and 3px tall, and the rainbow is zero width.

a {
  background:
    linear-gradient(
      to right,
      rgba(100, 200, 200, 1),
      rgba(100, 200, 200, 1)
    ),
    linear-gradient(
      to right,
      rgba(255, 0, 0, 1),
      rgba(255, 0, 180, 1),
      rgba(0, 100, 200, 1)
  );
  background-size: 100% 3px, 0 3px;
}

Now we can position the background gradients — at the same time on the background-position property — so that the first gradient is fully in view and the rainbow is pushed out of view. Oh, and let’s make sure the background isn’t repeating while we’re at it.

a {
  background:
    linear-gradient(
      to right,
      rgba(100, 200, 200, 1),
      rgba(100, 200, 200, 1)
    ),
    linear-gradient(
      to right,
      rgba(255, 0, 0, 1),
      rgba(255, 0, 180, 1),
      rgba(0, 100, 200, 1)
  );
  background-size: 100% 3px, 0 3px;
  background-position: 100% 100%, 0 100%;
  background-repeat: no-repeat;
}

Let’s update the background-size on hover so that the gradients swap values:

a:hover {
  background-size: 0 3px, 100% 3px;
}

And, finally, a little transition when the hover takes place:

a {
  background:
    linear-gradient(
      to right,
      rgba(100, 200, 200, 1),
      rgba(100, 200, 200, 1)
    ),
    linear-gradient(
      to right,
      rgba(255, 0, 0, 1),
      rgba(255, 0, 180, 1),
      rgba(0, 100, 200, 1)
  );
  background-size: 100% 3px, 0 3px;
  background-position: 100% 100%, 0 100%;
  background-repeat: no-repeat;
  transition: background-size 400ms;
}

Voilà!

Geoff Graham actually covered this same one recently when he dissected Adam Argyle’s slick hover effect. In his demo, a background color enters from the left behind the link, then exits to the right on mouse out.

My version pares down the background so it’s more of an underline.

a {
  position: relative;
}

a::before {
    content: '';
    position: absolute;
    width: 100%;
    height: 4px;
    border-radius: 4px;
    background-color: #18272F;
    bottom: 0;
    left: 0;
    transform-origin: right;
    transform: scaleX(0);
    transition: transform .3s ease-in-out;
  }

a:hover::before {
  transform-origin: left;
  transform: scaleX(1);
}

That’s not the only way to accomplish this! Here’s another one by Justin Wong using background instead:

Geoff also has a roundup of CSS link hover effects, ranging from neat to downright absurd. Worth checking out!

Have a blast linking!

There are a lot of options when it comes to creating your own hover effect for in-line links with CSS. You can even play with these effects and create something new. I hope you liked the article. Keep experimenting!


6 Creative Ideas for CSS Link Hover Effects originally published on CSS-Tricks. You should get the newsletter.

Adam Argyle’s Sick Mouse-Out CSS Hover Effect

I was killing some time browsing my CodePen feed for some eye candy and didn’t need to go past the first page before spotting a neat CSS hover effect by Adam Argyle.

I must’ve spent 10 minutes just staring at the demo in awe. There’s something about this that feels so app-like. I think it might be how contextually accurate it is in that the background color slides in from the left, then exits out through the right. It’s exactly the sort of behavior I’d expect from a mouse-in, mouse-out sort of interaction.

Whatever the case, I fired up a fresh pen and went to work recreating it. And it’s not super complex or anything, but rather a clever use of transitions and transforms paired with proper offsets. Quite elegant! I’m actually a little embarrassed how long it took me to realize how the mouse-out part works.

Here’s how I tackled it, warts and all.

“I bet that’s using a transition on a background.”

That was my first thought. Define the background-color, set the background-size and background-position, then transition the background-position. That’s how I’ve seen that “growing” background color thing done in the past. I’ve done that myself on some projects, like this:

If I could do the same thing, only from left-to-right, then all that’s left is the mouse-out, right? Nope. The problem is there’s nothing that can really make the background-position transition from left-to-right to left-to-right. I could make it do one or the other, but not both.

“Maybe it’s a transform instead.”

My next attempt was jump into transforms. The transform property provides a bunch of functions that can transition together for slightly more complex movement. For example, the background can “grow” or “shrink” by changing the element’s scale(). Or, in this case, just along the x-axis with scaleX().

But like I mentioned, there isn’t a way to isolate the element’s background to do that. Going from scaleX(0) to scaleX(1) scales the entire element, so that basically squishes the link — content and all — down to nothing, then stretches it back out to its natural size which is a totally different effect. Plus, it means starting with scaleX(0) which hides the whole dang thing by default making it unusable.

But a pseudo-element could work! It doesn’t matter if that gets squished or hidden because it isn’t part of the actual content. Gotta put the background on that instead and position it directly under the link.

a {
  /* Keeps the pseudo-element contained to the element */
  position: relative;
}

a::before {
  background: #ff9800;
  content: "";
  inset: 0; /* Logical equivalent to physical offsets */
  position: absolute;
  transform: scaleX(0); /* Hide by default */
  z-index: -1; /* Ensures the link is stacked on top */
}

“Now I need ::before to change on hover.”

I knew I could make ::before scale from 0 to 1 by chaining it to the link element’s :hover state.

a:hover::before {
  transform: scaleX(1)
}

Nice! I was onto something.

Sprinkle a little transition fairy dust on it and things start to come to life.

a::before {
  background: #ff9800;
  content: "";
  inset: 0;
  position: absolute;
  transform: scaleX(0);
  transition: transform .5s ease-in-out;
  z-index: -1;
}

“Hmm, the transition moves in both directions.”

Again, this is where I sorta got stuck. Something in my head just wasn’t clicking for some reason. As per usual, I ran over to the CSS-Tricks Almanac to see what property might’ve slipped my mind.

Ah, yes. That would be transform-origin. That allows me to set where the transform starts, which is not totally dissimilar from setting the background-position like I tried earlier. The transform could start from the left instead of its default 50% 50% position.

a::before {
  background: #ff9800;
  content: "";
  inset: 0;
  position: absolute;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform .5s ease-in-out;
  z-index: -1;
}

Yeah, like this:

I was already transitioning ::before to scaleX(1) on link hover. If I reversed the transform-origin from left to right at the same time, then mayyyybe the highlight goes out the opposite of how it came in when the mouse exits?

a:hover::before {
  transform: scaleX(1);
  transform-origin: right;
}

🤞

Whoops, backwards! Let’s swap the left and right values. 🙃

Gorgeous. Thank you, Adam, for the inspiration!


Adam Argyle’s Sick Mouse-Out CSS Hover Effect originally published on CSS-Tricks. You should get the newsletter and become a supporter.

Ripple Effect on a Texture with Three.js

In this ALL YOUR HTML coding session you will learn how to recreate the interesting ripple effect seen on the homunculus.jp website with Three.js. We’ll have a look at render targets and use a little bit of math.

This coding session was streamed live on November 21, 2021.

Support: https://www.patreon.com/allyourhtml

Setup: https://gist.github.com/akella/a19954…

The post Ripple Effect on a Texture with Three.js appeared first on Codrops.

How to Code the K72 Marquee Hover Animation

A while back, the folks of K72 released their amazing new website made by the award winning agency Locomotive that boasts with coolness and great design. It has many engaging details but the one I love the most is the fun menu hover effect that involves a marquee:

In this tutorial I’ll show how to create this direction-aware marquee hover effect. We’ll not code the opening animation of the menu itself, but instead focus on animations involved when hovering a menu item.

We won’t be looking under the hood and how the great folks of Locomotive did it but instead, do our version from the visual effect.

Let’s get started!

The Markup

We’ll need a couple of elements to be able to pull off the “reveal” effect. This animation consists of translating one element (where the overflow is hidden) in one direction while moving its child in the opposite direction. The illusion created is that the element reduces its height and gets cut off.

Another structure we need to take care of, is the the one for the marquee. We cover the CSS-only marquee animation in this tutorial. So the structure will be similar but we’ll simplify things a bit style-wise (we won’t have an offset for the items) so less “calculations” will be needed.

So this is the markup we’ll set up for the menu and a menu item:

<nav class="menu">
	<div class="menu__item">
		<a class="menu__item-link">Guayaquil</a>
		<div class="marquee">
			<div class="marquee__inner-wrap">
				<div class="marquee__inner" aria-hidden="true">
					<span>Frank Tower</span>
					<div class="marquee__img" style="background-image:url(img/1.jpg);"></div>
					<span>Dom Dom</span>
					<div class="marquee__img" style="background-image:url(img/2.jpg);"></div>
					<span>Santa Maria</span>
					<div class="marquee__img" style="background-image:url(img/3.jpg);"></div>
					<span>Big Molly</span>
					<div class="marquee__img" style="background-image:url(img/4.jpg);"></div>
					<span>Frank Tower</span>
					<div class="marquee__img" style="background-image:url(img/1.jpg);"></div>
					<span>Dom Dom</span>
					<div class="marquee__img" style="background-image:url(img/2.jpg);"></div>
					<span>Santa Maria</span>
					<div class="marquee__img" style="background-image:url(img/3.jpg);"></div>
					<span>Big Molly</span>
					<div class="marquee__img" style="background-image:url(img/4.jpg);"></div>
				</div><!--/marquee__inner-->
			</div><!--/marquee__inner-wrap-->
		</div><!--/marquee-->
	</div><!--/menu__item-->
	<!-- ... -->
</nav><!--/menu-->

For the looping marquee animation, we duplicate our content. Let’s look into the details for that in a moment.

The marquee element and its child, marquee__inner-wrap, will be used for the cut off reveal effect.

Let’s now take care of the styling.

The CSS

We’ll start by styling the menu item:

.menu__item {
	cursor: default;
	position: relative;
	overflow: hidden;
	text-align: center;
	box-shadow: 0 -1px var(--color-border);
}

.menu__item:last-child {
	box-shadow: 0 1px var(--color-border), 0 -1px var(--color-border);
}

Since we want the little border to be visible when moving over to another item, we’ll use a box shadow. The variables are defined in the body styles.

What is important here is that the element’s overflow is set to “hidden” because we’ll be sliding the inner elements up and down and we don’t want to see them.

The link is styled simply and we also take care of focus styles:

.menu__item-link {
	display: block;
	position: relative;
	cursor: pointer;
	text-decoration: none;
}

.menu__item-link:focus,
.menu__item-link:focus-visible {
	color: var(--menu-focus);
}

.menu__item-link:focus:not(:focus-visible) {
	color: var(--color-link);
}

The marquee will be positioned absolutely and we’ll translate it down by default, while the child will be translated up. When hovering, we’ll reset these translations dynamically using JavaScript depending on where we come from with the mouse:

.marquee {
	position: absolute;
	top: 0;
	left: 0;
	overflow: hidden;
	width: 100%;
	height: 100%;
	pointer-events: none;
	background: var(--marquee-bg);
	transform: translate3d(0,101%,0);
}

.marquee__inner-wrap {
	height: 100%;
	width: 100%;
	transform: translate3d(0,-101%,0);
}

The inner marquee element will be as large as its content and we’ll have an animation running:

.marquee__inner {
	height: 100%;
	width: fit-content;
	align-items: center;
	display: flex;
	position: relative;
	animation: marquee 15s linear infinite;
	will-change: transform;
}

@keyframes marquee {
	100% {
		transform: translate3d(-50%, 0, 0);
	}
}

Since we doubled the content, we know exactly when we have to “restart” the animation. At half of the element’s width, we’ll go back to the beginning, creating the illusion of an endless flow.

And finally, some styling for our text elements and the images:

.menu__item-link,
.marquee span {
	white-space: nowrap;
	font-size: 6vw;
	line-height: 1.2;
	font-weight: 600;
	padding: 1vh 1vw 0;
	text-transform: uppercase;
}

.marquee span {
	text-align: center;
	color: var(--marquee-text);
	font-weight: 400;
}

.marquee__img {
	width: 15vw;
	height: 70%;
	margin: 0 2vw;
	border-radius: 5vw;
	background-size: cover;
	background-position: 50% 50%;
}

And that’s all the styling! Let’s now take care of the dynamic part.

The JavaScript

The core of our script is the changing of the transforms based on the direction we are coming from with the mouse. John Stewart coded an elegant solution for this and we’ll integrate his code into our script. We’ll use GSAP.

Let’s first create our entry file (index.js) and initialize our Menu:

import { Menu } from './menu';
// initialize the menu
new Menu(document.querySelector('.menu'));

The Menu has a set of items:

import { MenuItem } from './menuItem';

export class Menu {
    constructor(el) {
        // .menu element
        this.DOM = {el: el};
        // the menu items
        this.DOM.menuItems = this.DOM.el.querySelectorAll('.menu__item');
        // array of MenuItem
        this.menuItems = [];
        this.DOM.menuItems.forEach(menuItem => this.menuItems.push(new MenuItem(menuItem)));
    }
}

We initialize a MenuItem instance for each of the menu’s items.

Now let’s create a class MenuItem where we add the mouse enter/leave logic. We want to animate both the .marquee and .marquee__inner-wrap elements when hovering over the .menu__item-link element. These two elements need to be translated in different directions so that we achieve the cut-off reveal effect.

Let’s start by initializing some elements and events:

import { gsap } from 'gsap';
import { closestEdge } from './utils';

export class MenuItem {
    constructor(el) {
        // .menu__item element
        this.DOM = {el: el};
        // .menu__item-link element
        this.DOM.link = this.DOM.el.querySelector('a.menu__item-link');
        // .marquee element
        this.DOM.marquee = this.DOM.el.querySelector('.marquee');
        // .marquee__inner-wrap element
        this.DOM.marqueeInner = this.DOM.marquee.querySelector('.marquee__inner-wrap');
        // some default options for the animation's speed and easing
        this.animationDefaults = {duration: 0.6, ease: 'expo'};
        // events initialization
        this.initEvents();
    }
    initEvents() {
        this.onMouseEnterFn = ev => this.mouseEnter(ev);
        this.DOM.link.addEventListener('mouseenter', this.onMouseEnterFn);
        this.onMouseLeaveFn = ev => this.mouseLeave(ev);
        this.DOM.link.addEventListener('mouseleave', this.onMouseLeaveFn);
    }
    // ...
}

When hovering in or out, we want the marquee content to be revealed by sliding out both, the marquee and marqueeInner elements. This animation should follow the mouse movement, meaning that if we enter the element from the top then the sliding effect will be from top to bottom and vice-versa. To achieve this, we need to set up the correct initial positions for both elements:

export class MenuItem {
    // ...
    mouseEnter(ev) {
        // find closest side to the mouse
        const edge = this.findClosestEdge(ev);
        
        // set the initial y position for both the marquee and marqueeInner elements
        // for the reveal effect to happen, both start at opposite positions
        // the directions are different depending on the direction the cursor enters the element (bottom or top)
        gsap.timeline({defaults: this.animationDefaults})
        .set(this.DOM.marquee, {y: edge === 'top' ? '-101%' : '101%'}, 0)
        .set(this.DOM.marqueeInner, {y: edge === 'top' ? '101%' : '-101%'}, 0)
        .to([this.DOM.marquee, this.DOM.marqueeInner], {y: '0%'}, 0);
    }
    mouseLeave(ev) {
        // find closest side to the mouse
        const edge = this.findClosestEdge(ev);
        
        gsap.timeline({defaults: this.animationDefaults})
        .to(this.DOM.marquee, {y: edge === 'top' ? '-101%' : '101%'}, 0)
        .to(this.DOM.marqueeInner, {y: edge === 'top' ? '101%' : '-101%'}, 0);
    }
    // find closest side to the mouse when entering/leaving
    findClosestEdge(ev) {
        const x = ev.pageX - this.DOM.el.offsetLeft;
        const y = ev.pageY - this.DOM.el.offsetTop;
        return closestEdge(x,y,this.DOM.el.clientWidth, this.DOM.el.clientHeight);
    }
    // ...
}

And that’s all! Our direction-aware marquee hover effect is done!

Check out the final demo.

Now, if you’d like another challenge, try to implement the opening/closing of the menu. The Locomotive team used a really great 3D effect here, so go on and try that or experiment with other cool “openings”.

I really hope you enjoyed this tutorial and found it useful!

The post How to Code the K72 Marquee Hover Animation appeared first on Codrops.

Thumbnail Hover Effect with SVG Filters

Already a while back, we explored applying SVG filters to images that appear when hovering a menu item. Today I thought that it would be interesting to play with a similar effect on thumbnails.

So the idea is to hover a small image and apply an SVG filter to it while sliding in another element, a caption that covers the image.

This kind of animation adds that little special extra to a design component like this. I really hope you like it and that it inspires you for new ideas.

I’ve used a different filter on each one of the images, so you can get an idea on the different possibilities here.

Please download it and use it as you wish, and thanks for stopping by!

Let me know what you think of it @crnacura or @codrops.

The post Thumbnail Hover Effect with SVG Filters appeared first on Codrops.

How to Code a Crosshair Mouse Cursor with Distortion Hover Effect

Today I’d like to show you how to code a special cursor effect. Custom cursors have been very popular and there’s so many creative possibilities that can enhance a certain design and an interaction logic.

Let’s have a look how to create a fullscreen crosshair cursor in SVG and how to distort the cursors’s lines with an SVG filter when hovering over links. We’ll also add a nice hover animation for some menu items using Splitting.js.

The Markup

For the SVG cursor we want a singe SVG for each line that has enough of space to get the distortion effect applied to it:

<div class="cursor">
	<svg class="cursor__line cursor__line--horizontal" viewBox="0 0 200 20" preserveAspectRatio="none">
		<line class="cursor__line-element" x1="0" y1="10" x2="200" y2="10" shape-rendering="crispEdges" vector-effect="non-scaling-stroke" />
	</svg>
	<svg class="cursor__line cursor__line--vertical" viewBox="0 0 20 200" preserveAspectRatio="none">
		<line class="cursor__line-element" x1="10" y1="0" x2="10" y2="200" shape-rendering="crispEdges" vector-effect="non-scaling-stroke" />
	</svg>
</div>

We’ll add the filters like this:

<div class="cursor">
	<svg class="cursor__line cursor__line--horizontal" viewBox="0 0 200 20" preserveAspectRatio="none">
		<defs>
			<filter id="filter-noise-x" x="-50%" y="-50%" width="200%" height="200%" 
			filterUnits="objectBoundingBox">
				<feTurbulence type="fractalNoise" baseFrequency="0" numOctaves="1" result="warp" />
				<feOffset dx="-30" result="warpOffset" />
				<feDisplacementMap xChannelSelector="R" yChannelSelector="G" scale="30" in="SourceGraphic" in2="warpOffset" />
			</filter>
		</defs>
		<line class="cursor__line-element" x1="0" y1="10" x2="200" y2="10" shape-rendering="crispEdges" vector-effect="non-scaling-stroke" />
	</svg>
	<svg class="cursor__line cursor__line--vertical" viewBox="0 0 20 200" preserveAspectRatio="none">
		<defs>
			<filter id="filter-noise-y" x="-50%" y="-50%" width="200%" height="200%" 
			filterUnits="objectBoundingBox">
				<feTurbulence type="fractalNoise" baseFrequency="0" numOctaves="1" result="warp" />
				<feOffset dy="-30" result="warpOffset" />
				<feDisplacementMap xChannelSelector="R" yChannelSelector="G" scale="30" in="SourceGraphic" in2="warpOffset" />
			</filter>
		</defs>
		<line class="cursor__line-element" x1="10" y1="0" x2="10" y2="200" shape-rendering="crispEdges" vector-effect="non-scaling-stroke" />
	</svg>
</div>

Let’s set up our menu with some data-splitting attributes:

<nav class="menu">
	<a href="#content-1" class="menu__item">
		<span data-splitting class="menu__item-title">Maputo</span>
		<span data-splitting class="menu__item-sub">Nights</span>
	</a>
	<a href="#content-1" class="menu__item">
		<span data-splitting class="menu__item-title">Gaborone</span>
		<span data-splitting class="menu__item-sub">Vibes</span>
	</a>
	<a href="#content-1" class="menu__item">
		<span data-splitting class="menu__item-title">Kinshasa</span>
		<span data-splitting class="menu__item-sub">Walks</span>
	</a>
</nav>

The CSS

First, we need to hide our cursor by default, and we just want to show it if the user has a pointing device, like a mouse. So we add a media query with the any-pointer media feature:

.cursor {
	display: block;
}

@media (any-pointer:fine) {
	.cursor {
		position: fixed;
		top: 0;
		left: 0;
		display: block;
		pointer-events: none;
		z-index: 1001;
	}

	.no-js .cursor {
		display: none;
	}

	.cursor__line {
		position: fixed;
		display: block;
		will-change: transform, opacity;
	}
	
	.cursor__line--horizontal {
		top: -10px;
		left: -10%;
		width: 120%;
		height: 20px;
	}
	
	.cursor__line--vertical {
		left: -10px;
		top: -10%;
		height: 120%;
		width: 20px;
	}
	
	.cursor__line-element {
		fill: none;
		stroke: var(--cursor-stroke);
		stroke-width: var(--cursor-stroke-width);
	}

}

The variables are define in the body.

For the menu, we’ll use some flexbox magic to lay out the menu items beneath each other:

.menu {
	display: flex;
	flex-direction: column;
	width: 100vw;
	height: calc(100vh - 13rem);
	position: relative;
	justify-content: flex-start;
	align-items: center;
}

.menu {
	text-align: center;
	padding-top: 10vh;
}

.menu__item {
	display: inline-block;
	margin-bottom: 3rem;
	text-decoration: none;
	color: var(--color-menu);
	font-family: vortice-concept, sans-serif;
}

.menu__item-title {
	line-height: 1;
	font-size: 7.5vw;
}

.menu__item-sub {
	font-size: 1.5vw;
	display: block;
}

@media screen and (min-width: 53em) {
	.menu {
		height: 100vh;
		justify-content: center;
	}
}

The JavaScript

Let’s create our custom cursor. So we have two SVGs, one for each line. As we saw in the markup earlier, each one of the SVGs will include a filter that we’ll apply to the respective line when hovering over a menu link.

Let’s start coding the entry JavaScript file (index.js):


import { Cursor } from './cursor';

// initialize custom cursor
const cursor = new Cursor(document.querySelector('.cursor'));

// mouse effects on all links
[...document.querySelectorAll('a')].forEach(link => {
    link.addEventListener('mouseenter', () => cursor.enter());
    link.addEventListener('mouseleave', () => cursor.leave());
});

Now, let’s create a class for the cursor (in cursor.js):


import { gsap } from 'gsap';
import { getMousePos } from './utils';

// Track the mouse position and update it on mouse move
let mouse = {x: 0, y: 0};
window.addEventListener('mousemove', ev => mouse = getMousePos(ev));

export class Cursor {
    constructor(el) {
    }
    // hovering over a link
    enter() {
    }
    // hovering out a link
    leave() {
    }
    // create the turbulence animation timeline on the cursor line elements
    createNoiseTimeline() {
    }
    // render styles and loop
    render() {
        // ...
        requestAnimationFrame(() => this.render());
    }
}

What we do here is to update the mouse position as we move the mouse around. For that, we use the getMousePos function (in utils.js).

Let’s move on to the next interesting part:


...
constructor(el) {
    // main DOM element which includes the 2 svgs, each for each line
    this.DOM = {el: el};
    // both horizontal and vertical lines
    this.DOM.lines = this.DOM.el.children;
    [this.DOM.lineHorizontal, this.DOM.lineVertical] = this.DOM.lines;
    // hide initially
    gsap.set(this.DOM.lines, {opacity: 0});
    ...
}
...

We initialize the line DOM elements and hide them initially.

We want to update the lines transform (translation values) as we move the mouse. For that, let’s create an object that stores the translation state:


...
constructor(el) {
    ...
    // style properties that will change as we move the mouse (translation)
    this.renderedStyles = {
        tx: {previous: 0, current: 0, amt: 0.15},
        ty: {previous: 0, current: 0, amt: 0.15}
    };
    ...
}
...

With interpolation, we can achieve a smooth animation effect when moving the mouse. The “previous” and “current” values are the values we’ll be interpolating. The current value of one of these “animatable” properties will be one between these two values at a specific increment. The value of “amt” is the amount to interpolate. As an example, the following formula calculates our current translationX value:

this.renderedStyles.tx.previous = lerp(this.renderedStyles.tx.previous, this.renderedStyles.tx.current, this.renderedStyles.tx.amt);

...
constructor(el) {
    ...
    // svg filters (ids)
    this.filterId = {
        x: '#filter-noise-x',
        y: '#filter-noise-y'
    };
    // the feTurbulence elements per filter
    this.DOM.feTurbulence = {
        x: document.querySelector(`${this.filterId.x} > feTurbulence`),
        y: document.querySelector(`${this.filterId.y} > feTurbulence`)
    }
    // turbulence current value
    this.primitiveValues = {turbulence: 0};
    // create the gsap timeline that will animate the turbulence value
    this.createNoiseTimeline();
}
...

Next, we initialize the filter ids, the feTurbulence elements for each SVG filter (one per line) and also the current turbulence value. Then we create the GSAP timeline that will take care of updating the baseFrequency of each filter with the current turbulence value. Here’s how that timeline and the methods that start/stop it look like:


...
createNoiseTimeline() {
    // turbulence value animation timeline:
    this.tl = gsap.timeline({
        paused: true,
        onStart: () => {
            // apply the filters for each line element
            this.DOM.lineHorizontal.style.filter = `url(${this.filterId.x}`;
            this.DOM.lineVertical.style.filter = `url(${this.filterId.y}`;
        },
        onUpdate: () => {
            // set the baseFrequency attribute for each line with the current turbulence value
            this.DOM.feTurbulence.x.setAttribute('baseFrequency', this.primitiveValues.turbulence);
            this.DOM.feTurbulence.y.setAttribute('baseFrequency', this.primitiveValues.turbulence);
        },
        onComplete: () => {
            // remove the filters once the animation completes
            this.DOM.lineHorizontal.style.filter = this.DOM.lineVertical.style.filter = 'none';
        }
    })
    .to(this.primitiveValues, { 
        duration: 0.5,
        ease: 'power1',
        // turbulence start value
        startAt: {turbulence: 1},
        // animate to 0
        turbulence: 0
    });
}
enter() {
    // start the turbulence timeline
    this.tl.restart();
}
leave() {
    // stop the turbulence timeline
    this.tl.progress(1).kill();
}
...

The animation will change the turbulence value (which starts at 1 and ends at 0) and apply it to each feTurbulence’s baseFrequency attribute. The filters get applied to the lines in the beginning and removed once the animation is completed.

To finalize the constructor method we fade in the lines and start updating the translation values the first time we move the mouse:


...
constructor(el) {
    ...
    import { lerp, getMousePos } from './utils';
    ...
    // on first mousemove fade in the lines and start the requestAnimationFrame rendering function
    this.onMouseMoveEv = () => {
        this.renderedStyles.tx.previous = this.renderedStyles.tx.current = mouse.x;
        this.renderedStyles.ty.previous = this.renderedStyles.ty.previous = mouse.y;
        gsap.to(this.DOM.lines, {duration: 0.9, ease: 'Power3.easeOut', opacity: 1});
        requestAnimationFrame(() => this.render());
        window.removeEventListener('mousemove', this.onMouseMoveEv);
    };
    window.addEventListener('mousemove', this.onMouseMoveEv);
}
...

Now we’re only missing the actual method that updates the lines’ translation values as we move the mouse:



...
render() {
    // update the current translation values
    this.renderedStyles['tx'].current = mouse.x;
    this.renderedStyles['ty'].current = mouse.y;
    // use linear interpolation to delay the translation animation
    for (const key in this.renderedStyles ) {
        this.renderedStyles[key].previous = lerp(this.renderedStyles[key].previous, this.renderedStyles[key].current, this.renderedStyles[key].amt);
    }
    // set the new values
    gsap.set(this.DOM.lineVertical, {x: this.renderedStyles['tx'].previous});
    gsap.set(this.DOM.lineHorizontal, {y: this.renderedStyles['ty'].previous});
    // loop this until the end of time
    requestAnimationFrame(() => this.render());
}

As an extra, let’s add a little glitch effect to the menu items texts when we hover over them. We’ll use the Splitting library to split the menu texts into spans/chars so we can animate each one individually. We want to change the translation values of each character and also it’s color. Let’s update our index.js file:


import { Cursor } from './cursor';
import { MenuItem } from './menuItem';
// Splitting (used to split the menu item texts to spans/characters)
import 'splitting/dist/splitting.css';
import 'splitting/dist/splitting-cells.css';
import Splitting from 'splitting';

// initialize Splitting
const splitting = Splitting();

// initialize custom cursor
const cursor = new Cursor(document.querySelector('.cursor'));

// Menu Items
[...document.querySelectorAll('.menu > a')].forEach(el => new MenuItem(el));

// mouse effects on all links
[...document.querySelectorAll('a')].forEach(link => {
    link.addEventListener('mouseenter', () => cursor.enter());
    link.addEventListener('mouseleave', () => cursor.leave());
});

Assuming we have data-splitting set in all elements we want to split into chars, then we only need to call Splitting() and we then have each text split into a bunch of spans, for every letter of the text.

Let’s now have a look at our MenuItem class:


import { gsap } from 'gsap';

export class MenuItem {
    constructor(el) {
        this.DOM = {el};
        // all text chars (Splittingjs) 
        this.DOM.titleChars = this.DOM.el.querySelectorAll('span.char');
        // initial and final colors for each span char (before and after hovering)
        const bodyComputedStyle = getComputedStyle(document.body);
        this.colors = {
            initial: bodyComputedStyle.getPropertyValue('--color-menu'), 
            final: bodyComputedStyle.getPropertyValue('--color-link')
        };
        this.initEvents();
    }
    ...
}

We get a reference to all the characters of the menu item text and also it’s initial and final colors for the hover animation.


...
initEvents() {
    this.onMouseEnterEv = () => this.onMouseEnter();
    this.DOM.el.addEventListener('mouseenter', this.onMouseEnterEv);

    this.onMouseLeaveEv = () => this.onMouseLeave();
    this.DOM.el.addEventListener('mouseleave', this.onMouseLeaveEv);
}
...

We initialize the mouseenter/mouseleave events which will triggger the animation on the characters.

When hovering over a menu item we will randomly change its characters position and color, and when hovering out we reset the original color:

onMouseEnter() {
    if ( this.leaveTimeline ) {
        this.leaveTimeline.kill();
    }

    // let's try to do an animation that resembles a glitch effect on the characters
    // we randomly set new positions for the translation and rotation values of each char and also set a new color
    // and repeat this for 3 times
    this.enterTimeline = gsap.timeline({
        defaults: {
            duration: 0.05,
            ease: 'power3',
            x: () => gsap.utils.random(-15, 15),
            y: () => gsap.utils.random(-20, 10),
            rotation: () => gsap.utils.random(-5, 5),
            color: () => gsap.utils.random(0, 3) < 0.5 ? this.colors.final : this.colors.initial
        }
    })
    // repeat 3 times (repeatRefresh option will make sure the translation/rotation values will be different for each iteration)
    .to(this.DOM.titleChars, {
        repeat: 3,
        repeatRefresh: true
    }, 0)
    // reset translation/rotation and set final color
    .to(this.DOM.titleChars, {
        x: 0, 
        y: 0, 
        rotation: 0,
        color: this.colors.final
    }, '+=0.05');
}
onMouseLeave() {
    // set back the initial color for each char
    this.leaveTimeline = gsap.timeline()
    .to(this.DOM.titleChars, {
        duration: 0.4,
        ease: 'power3',
        color: this.colors.initial
    });
}

And that is all!

I hope this has not been too difficult to follow and that you have gained some insight into constructing this fancy effect.

Please let me know if you have any question @codrops or @crnacura.

Thank you for reading!

The post How to Code a Crosshair Mouse Cursor with Distortion Hover Effect appeared first on Codrops.

Rotated 3D Letters and Image Hover Effect

Some months ago, I wrote about how to achieve a hover effect for a menu where an image would appear for each item. Then we saw how to apply this in Exploring Animations for Menu Hover Effects. When I discovered the fantastic site of One up Studio I wanted to combine their cool 3D-ish menu hover effect with that previous animation, so this is a little fusion between the two.

The 3D motion is slightly different and an image is shown in an angle, creating a playful look.

I really hope you like this little experiment and find it useful. Thanks for visiting!

The post Rotated 3D Letters and Image Hover Effect appeared first on Codrops.

Magnetic 3D Grid Interaction with Content Preview

Today I’d like to share another thumbnail to full view animation with you. This time we have an initial grid with thumbnails that move with the mouse in 3D and have a magnetic effect including a little tooltip on hover. Once clicked, all items animate out, giving way to a content preview with more details.

The grid interaction looks as follows:

Here’s how the grid to content animation looks like:

I really hope you find this useful! Thanks for visiting!

The post Magnetic 3D Grid Interaction with Content Preview appeared first on Codrops.

Let’s Create an Image Pop-Out Effect With SVG Clip Path

Few weeks ago, I stumbled upon this cool pop-out effect by Mikael Ainalem. It showcases the clip-path: path() in CSS, which just got proper support in most modern browsers. I wanted to dig into it myself to get a better feel for how it works. But in the process, I found some issues with clip-path: path(); and wound up finding an alternative approach that I wanted to walk through with you in this article.

If you haven’t used clip-path or you are unfamiliar with it, it basically allows us to specify a display region for an element based on a clipping path and hide portions of the element that fall outside the clip path.

A rectangle with a pastel pattern, plus an unfilled star shape with a black border, equals a star shape with the pastel background pattern.
You can kind of think of it as though the star is a cookie cutter, the element is the cookie dough, and the result is a star-shaped cookie.

Possible values for clip-path include circle , ellipse and polygon which limit the use-case to just those specific shapes. This is where the new path value comes in — it allows us to use a more flexible SVG path to create various clipping paths that go beyond basic shapes.

Let’s take what we know about clip-path and start working on the hover effect. The basic idea of the is to make the foreground image of a person appear to pop-out from the colorful background and scale up in size when the element is hovered. An important detail is how the foreground image animation (scale up and move up) appears to be independent from the background image animation (scale up only).

This effect looks cool, but there are some issues with the path value. For starters, while we mentioned that support is generally good, it’s not great and hovers around 82% coverage at the time of writing. So, keep in mind that mobile support is currently limited to Chrome and Safari.

Besides support, the bigger and more bizarre issue with path is that it currently only works with pixel values, meaning that it is not responsive. For example, let’s say we zoom into the page. Right off the bat, the path shape starts to cut things off.

This severely limits the number of use cases for clip-path: path(), as it can only be used on fixed-sized elements. Responsive web design has been a widely-accepted standard for many years now, so it’s weird to see a new CSS property that doesn’t follow the principle and exclusively uses pixel units.

What we’re going to do is re-create this effect using standard, widely-supported CSS techniques so that it not only works, but is truly responsive as well.

The tricky part

We want anything that overflows the clip-path to be visible only on the top part of the image. We cannot use a standard CSS overflow property since it affects both the top and bottom.

Photo of a young woman against a pastel floral pattern cropped to the shape of a circle.
Using overflow-y: hidden, the bottom part looks good, but the image is cut-off at the top where the overflow should be visible.

So, what are our options besides overflow and clip-path? Well, let’s just use <clipPath> in the SVG itself. <clipPath> is an SVG property, which is different than the newly-released and non-responsive clip-path: path.

SVG <clipPath> element

SVG <clipPath> and <path> elements adapt to the coordinate system of the SVG element, so they are responsive out of the box. As the SVG element is being scaled, its coordinate system is also being scaled, and it maintains its proportions based on the various properties that cover a wide range of possible use cases. As an added benefit, using clip-path in CSS on SVG has 95% browser support, which is a 13% increase compared to clip-path: path.

Let’s start by setting up our SVG element. I’ve used Inkscape to create the basic SVG markup and clipping paths, just to make it easy for myself. Once I did that, I updated the markup by adding my own class attributes.

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -10 100 120" class="image">
  <defs>
    <clipPath id="maskImage" clipPathUnits="userSpaceOnUse">
      <path d="..." />
    </clipPath>
    <clipPath id="maskBackground" clipPathUnits="userSpaceOnUse">
      <path d="..." />
    </clipPath>
  </defs>
  <g clip-path="url(#maskImage)" transform="translate(0 -7)">
    <!-- Background image -->
    <image clip-path="url(#maskBackground)" width="120" height="120" x="70" y="38" href="..." transform="translate(-90 -31)" />
    <!-- Foreground image -->
    <image width="120" height="144" x="-15" y="0" fill="none" class="image__foreground" href="..." />
  </g>
</svg>
A bright green circle with a bright red shape coming out from the top of it, as if another shape is behind the green circle.
SVG <clipPath> elements created in Inkscape. The green element represents a clipping path that will be applied to the background image. The red is a clipping path that will be applied to both the background and foreground image.

This markup can be easily reused for other background and foreground images. We just need to replace the URL in the href attribute inside image elements.

Now we can work on the hover animation in CSS. We can get by with transforms and transitions, making sure the foreground is nicely centered, then scaling and moving things when the hover takes place.

.image {
  transform: scale(0.9, 0.9);
  transition: transform 0.2s ease-in;
}

.image__foreground {
  transform-origin: 50% 50%;
  transform: translateY(4px) scale(1, 1);
  transition: transform 0.2s ease-in;
}

.image:hover {
  transform: scale(1, 1);
}

.image:hover .image__foreground {
  transform: translateY(-7px) scale(1.05, 1.05);
}

Here is the result of the above HTML and CSS code. Try resizing the screen and changing the dimensions of the SVG element to see how the effect scales with the screen size.

This looks great! However, we’re not done. We still need to address some issues that we get now that we’ve changed the markup from an HTML image element to an SVG element.

SEO and accessibility

Inline SVG elements won’t get indexed by search crawlers. If the SVG elements are an important part of the content, your page SEO might take a hit because those images probably won’t get picked up.

We’ll need additional markup that uses a regular <img> element that’s hidden with CSS. Images declared this way are automatically picked up by crawlers and we can provide links to those images in an image sitemap to make sure that the crawlers manage to find them. We’re using loading="lazy" which allows the browser to decide if loading the image should be deferred.

We’ll wrap both elements in a <figure> element so that we markup reflects the relationship between those two images and groups them together:

<figure>
  <!-- SVG element -->
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -10 100 120" class="image">
     <!-- ... -->
  </svg>
  <!-- Fallback image -->
  <img src="..." alt="..." loading="lazy" class="fallback-image" />
</figure>

We also need to address some accessibility concerns for this effect. More specifically, we need to make improvements for users who prefer browsing the web without animations and users who browse the web using screen readers.

Making SVG elements accessible takes a lot of additional markup. Additionally, if we want to remove transitions, we would have to override quite a few CSS properties which can cause issues if our selector specificities aren’t consistent. Luckily, our newly added regular image has great accessibility features baked right in and can easily serve as a replacement for users who browse the web without animations.

<figure>
  <!-- Animated SVG element -->
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -10 100 120" class="image" aria-hidden="true">
    <!-- ... -->
  </svg>

  <!-- Fallback SEO & a11y image -->
  <img src="..." alt="..." loading="lazy" class="fallback-image" />
</figure>

We need to hide the SVG element from assistive devices, by adding aria-hidden="true", and we need to update our CSS to include the prefers-reduced-motion media query. We are inclusively hiding the fallback image for users without the reduced motion preference meanwhile keeping it available for assistive devices like screen readers.

@media (prefers-reduced-motion: no-preference) {
.fallback-image {
  clip: rect(0 0 0 0); 
  clip-path: inset(50%);
  height: 1px;
  overflow: hidden;
  position: absolute;
  white-space: nowrap; 
  width: 1px;
  } 
}

@media (prefers-reduced-motion) {
  .image {
    display: none;
  }
}

Here is the result after the improvements:

Please note that these improvements won’t change how the effect looks and behaves for users who don’t have the prefers-reduced-motion preference set or who aren’t using screen readers.

That’s a wrap

Developers were excited about path option for clip-path CSS attribute and new styling possibilities, but many were displeased to find out that these values only support pixel values. Not only does that mean the feature is not responsive, but it severely limits the number of use cases where we’d want to use it.

We converted an interesting image pop-out hover effect that uses clip-path: path into an SVG element that utilizes the responsiveness of the <clipPath> SVG element to achieve the same thing. But in doing so, we introduced some SEO and accessibility issues, that we managed to work around with a bit of extra markup and a fallback image.

Thank you for taking the time to read this article! Let me know if this approach gave you an idea on how to implement your own effects and if you have any suggestions on how to approach this effect in a different way.


The post Let’s Create an Image Pop-Out Effect With SVG Clip Path appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

Want to Write a Hover Effect With Inline CSS? Use CSS Variables.

The other day I was working on a blog where each post has a custom color attached to it for a little dose of personality. The author gets to pick that color in the CMS when they’re writing the post. Just a super-light layer of art direction.

To make that color show up on the front end, I wrote the value right into an inline style attribute on the <article> element. My templates happened to be in Liquid, but this would look similar in other templating languages:

{% for post in posts %}
<article style="background: {{post.custom_color}}">
  <h1>{{post.title}}</h1>
  {{content}}
</article>
{% endfor %}

No problem there. But then I thought, “Wouldn’t it be nice if the custom color only showed up when when hovering over the article card?” But you can’t write hover styles in a style attribute, right?

My first idea was to leave the style attribute in place and write CSS like this:

article {
  background: lightgray !important;
}
article:hover {
  /* Doesn't work! */
  background: inherit;
}

I can override the inline style by using !important, but there’s no way to undo that on hover.

Eventually, I decided I could use a style attribute to get the color value from the CMS, but instead of applying it right away, store it as a CSS variable:

<article style="--custom_color: {{post.custom_color}}">
  <h1>{{post.title}}</h1>
  {{content}}
</article>

Then, that variable can be used to define the hover style in regular CSS:

article {
  background: lightgray;
}
article:hover {
  /* Works! */
  background: var(--custom_color);
}

Now that the color value is saved as a CSS variable, there are all kinds of other things we can do with it. For instance, we could make all links in the post appear in the custom color:

article a {
  color: var(--custom_color);
}

And because the variable is scoped to the <article> element, it won’t affect anything else on the page. We can even display multiple posts on the same page where each one renders in its own custom color.

Browser support for CSS variables is pretty deep, with the exception of Internet Explorer. Anyway, just a neat little trick that might come in handy if you find yourself working with light art direction in a CMS, as well as a reminder of just how awesome CSS variables can be.


The post Want to Write a Hover Effect With Inline CSS? Use CSS Variables. appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

Distributed Letters Animation Layout

A while back I stumbled upon this great Dribbble shot by Seth Eckert. I love that letter effect and so I was thinking about a layout where I could incorporate this kind of animation.

After playing around a bit, I thought that a layout that allows for opening a panel would be interesting. So here it is; a little layout with a distributed letter animation on hover. When clicking, the letters will move to their respective place in the panel and the other elements will animate in.

I really hope you like this and that it comes in handy.

Thank you for checking by!

The post Distributed Letters Animation Layout appeared first on Codrops.

Long Hover

I had a very embarrassing CSS moment the other day.

I was working on the front-end code of a design that had a narrow sidebar of icons. There isn’t enough room there to show text of what the icons are, so the idea is that we’ll use accessible (but visually hidden, by default) text that is in there already as a tooltip on a “long hover.” That is, a device with a cursor, and the cursor hovering over the element for a while, like three seconds.

So, my mind went like this…

  1. I can use state: the tooltip is either visible or not visible. I’ll manage the state, which will manifest in the DOM as a class name on an HTML element.
  2. Then I’ll deal with the logic for changing that state.
  3. The default state will be not visible, but if the mouse is inside the element for over three seconds, I’ll switch the state to visible.
  4. If the mouse ever leaves the element, the state will remain (or become) not visible.

This was a React project, so state was just on the mind. That ended up like this:

Not that bad, right? Eh. Having state managed in JavaScript does potentially open some doors, but in this case, it was total overkill. Aside from the fact that I find mouseenter and mouseleave a little finicky, CSS could have done the entire thing, and with less code.

That’s the embarrassing part… why would I reach up the chain to a JavaScript library to do this when the CSS that I’m already writing can handle it?

I’ll leave the UI in React, but rip out all the state management stuff. All I’ll do is add a transition-delay: 3s when the .icon is :hover so that it’s zero seconds when not hovered, then goes away immediately when the mouse cursor leaves).

A long hover is basically a one-liner in CSS:

.thing {
  transition: 0.2s;
}
.thing:hover {
  transition-delay: 3s; /* delay hover animation only ON, not OFF */
}

Works great.

One problem that isn’t addressed here is the touch screen problem. You could argue screen readers are OK with the accessible text and desktop browsers are OK because of the custom tooltips, but users with touch-only screens might be unable to discover the icon labels. In my case, I was building for a large screen scenario that assumes cursors, but I don’t think all-is-lost for touch screens. If the element is a link, the :hover might fire on first-tap anyway. If the link takes you somewhere with a clear title, that might be enough context. And you can always go back to more JavaScript and handle touch events.


The post Long Hover appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

Circular SVG Text Animation

The other day I looked at some nice animations that I always wanted to code up somehow. One of them was this gem by Twotwentytwo and Nathan Riley from 2019:

Furthermore, there’s another beautiful animation that I have stumbled upon recently called Spiral Scheduler by Michael Crawford. Go check it out on, it’s such a smooth animation.

The idea of using circular typography is really awesome and therefore I wanted to explore some animations with it. To do this, I used an SVG with texts on circular paths. There’s a really great article by Amelia Bellamy-Royds that explains how to make them, it’s a great read: Perfecting Paths for <textPath>

I thought it would be nice to make some kind of intro animation and play with different scale effects. There are a lot of possibilities, for the look and for the feel of the animation. So here are three demos for you to explore and hopefully they give you some inspiration for some circular motion design.

Let me know what you think and thank you for checking by!

The post Circular SVG Text Animation appeared first on Codrops.

Ideas for CSS Button Hover Animations

After exploring some hover animations for links using CSS only, I also wanted to make some button animation using some interesting CSS techniques like animating a clip path or applying a mix-blend-mode.

The following button uses a mix of a fancy border radius and a clip path animation. Note that I’m using SVG path data for the clip path and animating to another path seems to work already, so this is such a cool thing and allows for so many great effects!

.button {
	pointer-events: auto;
	cursor: pointer;
	background: #e7e7e7;
	border: none;
	padding: 1.5rem 3rem;
	margin: 0;
	font-family: inherit;
	font-size: inherit;
	position: relative;
	display: inline-block;
}

.button::before,
.button::after {
	position: absolute;
	top: 0;
	left: 0;
	width: 100%;
	height: 100%;
}

.button--janus {
	font-family: freight-display-pro, serif;
	font-weight: 900;
	width: 175px;
	height: 120px;
	color: #fff;
	background: none;
}

.button--janus::before {
	content: '';
	background: #e6e6e6;
	-webkit-clip-path: path("M154.5,88.5 C131,113.5 62.5,110 30,89.5 C-2.5,69 -3.5,42 4.5,25.5 C12.5,9 33.5,-6 85,3.5 C136.5,13 178,63.5 154.5,88.5 Z");
	clip-path: path("M154.5,88.5 C131,113.5 62.5,110 30,89.5 C-2.5,69 -3.5,42 4.5,25.5 C12.5,9 33.5,-6 85,3.5 C136.5,13 178,63.5 154.5,88.5 Z");
	transition: clip-path 0.5s cubic-bezier(0.585, 2.5, 0.645, 0.55), -webkit-clip-path 0.5s cubic-bezier(0.585, 2.5, 0.645, 0.55), background 0.5s ease;
}

.button--janus:hover::before {
	background: #000;
	-webkit-clip-path: path("M143,77 C117,96 74,100.5 45.5,91.5 C17,82.5 -10.5,57 5.5,31.5 C21.5,6 79,-5.5 130.5,4 C182,13.5 169,58 143,77 Z");
	clip-path: path("M143,77 C117,96 74,100.5 45.5,91.5 C17,82.5 -10.5,57 5.5,31.5 C21.5,6 79,-5.5 130.5,4 C182,13.5 169,58 143,77 Z");
}

.button--janus::after {
	content: '';
	height: 86%;
	width: 97%;
	top: 5%;
	border-radius: 58% 42% 55% 45% / 56% 45% 55% 44%;
	border: 1px solid #000;
	transform: rotate(-20deg);
	z-index: -1;
	transition: transform 0.5s cubic-bezier(0.585, 2.5, 0.645, 0.55);
}

.button--janus:hover::after {
	transform: translate3d(0,-5px,0);
}

.button--janus span {
	display: block;
	transition: transform 0.3s ease;
	mix-blend-mode: difference;
}

.button--janus:hover span {
	transform: translate3d(0,-10px,0);
}

There are some initial button styles for all our buttons (you can simplify that if you’re just using one of them, of course). So on hover, we morph from one path to another using a bouncy timing function.

The other shape uses a fun border radius that makes it look like a blob. This button is a great example for how different techniques can create a similar look. For me, that’s the beauty of CSS — there are just so many different possibilities and never just one way to do things.

In this example, I wanted to have a border around the shape, and with a clip path that’s not possible. So, I used some goofy border radius values to create the look.

These great tools helped me do this:

One word of caution when using a clip path directly on a button element: if you have some kind of focus styles around the button, they will be cut off. So it’s probably better if you use clipping on another (pseudo) element.

Another button style that I was itching to experiment with, was that rotating badge-like thing that can be seen on many sites these days. Here I used some text on a circle path in SVG with the help of the great article by Amelia Bellamy-Royds, Perfecting Paths for <textPath>.

Check out the result:

I really hope these buttons bring you some joy! Let me know if you have any questions or if you want to show me what you do with these by sending me a tweet @crnacura or @codrops!

The post Ideas for CSS Button Hover Animations appeared first on Codrops.

Simple CSS Line Hover Animations for Links

Those little line animations are a perfect way to enhance a design and add subtle micro-interactions to a website. Today I’d love to share some super-simple ideas that are based on CSS only, no JavaScript involved.

Most effects use a pseudo-element as line and some have a little SVG line animation, like this one:

<a href="#" class="link link--herse">
	<span>Sign up</span>
	<svg class="link__graphic link__graphic--stroke link__graphic--arc" width="100%" height="18" viewBox="0 0 59 18">
		<path d="M.945.149C12.3 16.142 43.573 22.572 58.785 10.842" pathLength="1"/>
	</svg>
</a>

The effect works by animating the stroke-dashoffset and we can use a super cool trick to “normalize” the path length so that we don’t have to bother with the real length that we would need to do the SVG line animation.

.link--herse {
	font-family: freight-display-pro, serif;
	font-size: 1.375rem;
	font-weight: bold;
}

.link__graphic--arc {
	top: 73%;
	left: -23%;
}

.link__graphic--arc path {
	stroke-dasharray: 1;
	stroke-dashoffset: 1;
	transition: stroke-dashoffset 0.4s cubic-bezier(0.7, 0, 0.3, 1);
}

.link:hover .link__graphic--arc path {
	stroke-dashoffset: 0;
	transition-timing-function: cubic-bezier(0.8, 1, 0.7, 1);
	transition-duration: 0.3s;
}

I hope you find these little hover effects useful!

The post Simple CSS Line Hover Animations for Links appeared first on Codrops.