Shape Hover Effect with SVG

In this tutorial we'll recreate the hover effect as seen on The Christmas Experiments website. We'll be using SVG for the shape and Snap.svg for animating it on hover.

ShapeHoverEffect

View demo Download source

If you have visited the fantastic new edition of The Christmas Experiments then you might have noticed the really cool hover effect in the Christmas calendar that uses a triangular shape. The shape is made up of a triangle using borders and today I would like to show you how to achieve the same effect using SVG and Snap.svg. The idea is to create a SVG with one path that represents the shape background for some caption and to morph that path into another one on hover. There are many creative possibilities and today we’ll create three different examples. The nice thing of utilizing SVG is that we can adjust the shape size to their parent container’s size and make everything fluid.

The illustrations used in the demos are by talented Isaac Montemayor. See his original artwork on his website or Dribbble.

So, let’s get started!

The Markup

What we’ll do first, is to draw two shapes in a vector graphics editor like Adobe Illustrator or Inkscape. Each shape will consist of one path and when we are done, we’ll copy the points of our paths to be used in our markup. Note that we’ve transformed a polygon into a path. If you are using Inkscape you can do that by selecting the object and choosing Path > Object to Path. The points for the path can be obtained from Edit > XML Editor… which will open a view as seen in the following screenshot:

InkscapeShape

The “d” (path data) value is what you are looking for.

For the markup we’ll have a section with the class “grid” which contains figures wrapped in anchors. You could as well use a list here which would need some extra markup.
The figure will contain the image, the initially visible shape and a figcaption:

<section id="grid" class="grid clearfix">
	<a href="#" data-path-hover="m 180,34.57627 -180,0 L 0,0 180,0 z">
		<figure>
			<img src="img/1.png" />
			<svg viewBox="0 0 180 320" preserveAspectRatio="none"><path d="M 180,160 0,218 0,0 180,0 z"/></svg>
			<figcaption>
				<h2>Crystalline</h2>
				<p>Soko radicchio bunya nuts gram dulse.</p>
				<button>View</button>
			</figcaption>
		</figure>
	</a>
	<!-- ... -->
</section>

The SVG will have the respective viewBox values for the graphic and a preserveAspectRatio of “none”. This will allow us to resize and stretch the shape to the dimensions we want, which is 100% in this case. We’ll define the width and height in our style sheet. The information of the hover path will be stored in the data-attribute data-path-hover of the wrapping anchor.

Let’s style all this.

The CSS

Note that the CSS will not contain any vendor prefixes, but you will find them in the files.
The style that we’ll be going through is for all three examples. First, we’ll take a look at the common style and then we’ll set the individual styles for all the demos.

We’ll start with the grid. Let’s center it and give it a max-width and a percentage width to make it fluid:

.grid {
	margin: 40px auto 120px;
	max-width: 1000px;
	width: 90%;
}

The anchors should float left and we’ll give them a max-width of 250px and a width of 25% since we’d like them to be fluid and show four items in a row. We’ll take care of smaller screen sizes in our media queries later on:

.grid a {
	float: left;
	max-width: 250px;
	width: 25%;
	color: #333;
}

To create some offset for the odd items, we’ll set a top margin of 30px and a bottom margin of -30px. This will create a nice look for the grid, just like in the grid on The Christmas Experiments website:

.grid a:nth-child(odd) {
	margin: 30px 0 -30px 0;
}

The figure should be positioned relatively because we’ll need some of the children to be absolute. Since our hover effect might cause some overflow, and we don’t want that to be visible, we’ll set the overflow to “hidden”:

.grid figure {
	position: relative;
	overflow: hidden;
	margin: 5px;
	background: #333;
}

The image will occupy all the width of its parent and the opacity is set to 0.7. On hover we want to animate the opacity, so we’ll add a transition:

.grid figure img {
	position: relative;
	display: block;
	width: 100%;
	opacity: 0.7;
	transition: opacity 0.3s;
}

The figcaption needs to be absolutely positioned and we’ll stretch it over the whole item:

.grid figcaption {
	position: absolute;
	top: 0;
	z-index: 11;
	padding: 10px;
	width: 100%;
	height: 100%;
	text-align: center;
}

The headline and the paragraph will both be animated on hover, so let’s give them the respective transitions and move their initial position a bit:

.grid figcaption h2 {
	margin: 0 0 20px 0;
	color: #3498db;
	text-transform: uppercase;
	letter-spacing: 1px;
	font-weight: 300;
	font-size: 130%;
	transition: transform 0.3s;
}

.grid figcaption p {
	padding: 0 20px;
	color: #aaa;
	font-weight: 300;
	transition: opacity 0.3s, transform 0.3s;
}

.grid figcaption h2,
.grid figcaption p {
	transform: translateY(50px);
}

The common button style for all three examples is the following. The button will also animate, so we’ll add a transition for the opacity and for the transform:

.grid figure button {
	position: absolute;
	padding: 4px 20px;
	border: none;
	text-transform: uppercase;
	letter-spacing: 1px;
	font-weight: bold;
	transition: opacity 0.3s, transform 0.3s;
}

To avoid some flickering and glitches, we’ll need to give the animating elements and their parent a hidden backface-visibility:

.grid figcaption,
.grid figcaption h2,
.grid figcaption p,
.grid figure button {
	backface-visibility: hidden;
}

The SVG will also be positioned absolutely and we’ll stretch it over the item by setting the width and height to 100%. Giving it a top value of -1px instead of 0 will ensure that there is not strange line in Firefox (26.0 / Mac).

.grid svg {
	position: absolute;
	top: -1px; /* fixes rendering issue in FF */
	z-index: 10;
	width: 100%;
	height: 100%;
}

The fill color of the path will be white:

.grid svg path {
	fill: #fff;
}

At this point you could also try to add some transitions to the path and e.g. change the fill color on hover.

The common hover effects for the anchors will be the following:

.grid a:hover figure img {
	opacity: 1;
}

.grid a:hover figcaption h2,
.grid a:hover figcaption p {
	transform: translateY(0);
}

.grid a:hover figcaption p {
	opacity: 0;
}

The opacity of the image will be set to 1 while the paragraph of the captions slides up and disappears. The headline will move to up, too.

Let’s style the individual demos. Some of the styles will be common to more than one demo.

In the first and third example, we want the button to have a white border and be centered on the item. It will be hidden initially and scaled down. The other transforms are used to “pull” it into position and center it. On hover, we’ll make the button scale up and fade in:

.demo-1 body {
	background: #3498db;
}

.demo-1 .grid figure button,
.demo-3 .grid figure button {
	top: 50%;
	left: 50%;
	border: 3px solid #fff;
	background: transparent;
	color: #fff;
	opacity: 0;
	transform: translateY(-50%) translateX(-50%) scale(0.25);
}

.demo-1 .grid a:hover figure button,
.demo-3 .grid a:hover figure button {
	opacity: 1;
	transform: translateY(-50%) translateX(-50%) scale(1);
}

For the second demo, we’ll redefine some colors and set the button to be hidden at the bottom of the figure. For that we set the bottom to 0 and translate it 100% (of its own height). On hover we’ll make it slide up with a different easing function “ease-out” (the deafult is “ease”):

.demo-2 body {
	background: #e74c3c;
}

.demo-2 .grid figcaption h2 {
	color: #e74c3c;
}

.demo-2 .grid figcaption p {
	transition-delay: 0.05s;
}

.demo-2 .grid figure button {
	bottom: 0;
	left: 0;
	padding: 15px;
	width: 100%;
	background: #fff;
	color: #333;
	font-weight: 300;
	transform: translateY(100%);
}

.demo-2 .grid a:hover figure button {
	transition-timing-function: ease-out;
	transform: translateY(0);
}

The headline and paragraph of the second and third example will have a cubic-bezier easing function that will help emulate an elastic transition. We’ll also set a different duration with no delay for the paragraph when hovering. This will ensure that the paragraph fades out quickly before our SVG shape reaches the top:

.demo-2 .grid figcaption h2, 
.demo-2 .grid figcaption p,
.demo-3 .grid figcaption h2,
.demo-3 .grid figcaption p {
	timing-function: cubic-bezier(0.250, 0.250, 0.115, 1.445);
}

.demo-2 .grid a:hover figcaption p,
.demo-3 .grid a:hover figcaption p {
	transition-delay: 0s;
	transition-duration: 0.1s;
}

For the third demo we’ll change some colors and leave the headline translated a bit on hover instead of making it go to 0:

.demo-3 body {
	background: #52be7f;
}

.demo-3 .grid figcaption h2 {
	color: #52be7f;
}

.demo-3 .grid a:hover figcaption h2 {
	transform: translateY(5px);
}

For smaller screens, we want to change the number of items in a row, so we’ll reset the width and also the margin of the odd children. The anchors that should have a margin are the 2nd, 5th, 8th, 11th and so on, which is described by the sequence 3n-1. Tip: If you want to find the sequence to a row of numbers quickly, you can for example go to WolframAlpha and type your numbers. A possible sequence identification with the closed form can be found in one last boxes.

For even smaller sizes, we’ll adjust the max-width of the grid and redefine some margins for the caption elements:

@media screen and (max-width: 58em) {
	.grid a {
		width: 33.333%;
	}

	.grid a:nth-child(odd) {
		margin: 0;
	}

	.grid a:nth-child(3n-1) {
		margin: 30px 0 -30px 0;
	}
}

@media screen and (max-width: 45em) {
	.grid {
		max-width: 500px;
	}

	.grid a {
		width: 50%;
	}

	.grid a:nth-child(3n-1) {
		margin: 0;
	}

	.grid a:nth-child(even) {
		margin: 30px 0 -30px 0;
	}


	.grid figcaption h2 {
		margin-bottom: 0px;
		transform: translateY(30px);
	}

	.grid figcaption p {
		margin: 0;
		padding: 0 10px;
	}
}

@media screen and (max-width: 27em) {
	.grid {
		max-width: 250px;
	}

	.grid a {
		width: 100%;
	}

	.grid a:nth-child(even) {
		margin: 0;
	}
}

And that’s all the style! Let’s move over to the JavaScript.

The JavaScript

We’ll be using Snap.svg, a great library for working with SVGs. Please take a look at the documentation and the interactive Getting Started tutorial for a better understanding on how it can be used.

Let’s start by defining the speed and the easing variable. We’ll also define a variable that will contain the info on the path and the hover path. When hovering over an anchor, we’ll animate the path to morph into the “to” path. When leaving the anchor, we’ll animate back to the “from” path:

(function() {
	
	function init() {
		var speed = 250,
			easing = mina.easeinout;

		[].slice.call ( document.querySelectorAll( '#grid > a' ) ).forEach( function( el ) {
			var s = Snap( el.querySelector( 'svg' ) ), path = s.select( 'path' ),
				pathConfig = {
					from : path.attr( 'd' ),
					to : el.getAttribute( 'data-path-hover' )
				};

			el.addEventListener( 'mouseenter', function() {
				path.animate( { 'path' : pathConfig.to }, speed, easing );
			} );

			el.addEventListener( 'mouseleave', function() {
				path.animate( { 'path' : pathConfig.from }, speed, easing );
			} );
		} );
	}

	init();

})();

And that’s it! We’ve just shown you three examples, but there are so many possibilities for cool shape morphing or color effects.

I hope you enjoyed this tutorial and find it inspiring!

View demo Download source

Previous:
Next:

Tagged with:

Mary Lou (Manoela Ilic) 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.

View all contributions by

Website: http://tympanus.net/

Related Articles

Feedback 43

  1. 1

    Hi,
    Firefox giving a black line of one Pixel on the Right Side of Image
    would you help in This Please ?
    Thanks…

    • 3

      Hey, Yousuff, what did you do? I’m noticing the same problem as well and haven’t pinned down the source of the issue yet.

  2. 11

    I have a problem with the “button”.
    Takes a strange style and shows all the time, not just on hover.
    The code is perfect, help me!
    It’s happened with someone else?

  3. 13

    Hello,

    Does someone know how to make this work in wordpress?
    Where can i find a tutorial on how to put js code in wordpress?

    Thanks,

    Will

  4. 14

    Hey, nice work!

    I have a question for you. cause i’m new to svg. could u please tell me how you get the value for ‘data-path-hover’? and also the d attribute for

    tag path in svg.

    Waiting for the reply, thank you!

    • 15

      He, indeed nice work!
      But I also would like to know how to get the value for “data-path-hover”. I’ve created my own SVG in Illustrator and copied the path to the “d” value. But I can’t figure out how to get the “data-path-hover”. Hope someone knows, thanks! (:

  5. 18

    Hey Mary,

    I am new to SVGs. I made an SVG that is a grouped image that features a black circle with a white accent in the bottom right corner. Is it possible to make the grouped image transition into the simple square image that you have for the hover state in the first demo? Currently my code makes the white accent turn into the white box but the black circle remains. How can I make the black circle disappear for the hover state?

  6. 20

    Replacing <button> with <a> tag issue.
    When i trying to change the button tag with the a tag I got an error
    “Uncaught TypeError: Cannot read property ‘attr’ of null ”
    in js line
    from : path.attr( ‘d’ ),
    How can i fix it?

Follow this discussion

Leave a Comment

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>