DEV Community

Cover image for How to build 5 clip-path hover effects
crayoncode
crayoncode

Posted on

How to build 5 clip-path hover effects

Today let's build 5 awesome hover effects with CSS clip-path!

Read the full article or watch me code this on Youtube:

Result

Basic Setup

Let's first setup a few basic things like:

  1. A custom font via google fonts
  2. A fiew css Variables for easy color customization in :root
  3. Basic font configuration on the html tag
  4. A CSS grid that will put all the links we're going to style in a nicely aligned fashion.
@import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@100;200;300;400;500;600;700;800;900&display=swap');

:root {
  --bg-color: #2b2b2b;
  --fg-color: rgba(255, 255, 255, 0.9);
  --bd-color: rgba(255, 255, 255, 0.2);

    --default-bg: 
    linear-gradient(322deg, #ba4aff, rgba(186, 74, 255, 0) 70%),
    linear-gradient(178deg, #008aff, rgba(0, 138, 255, 0) 70%), 
    linear-gradient(24deg, #00ffc6, rgba(0, 255, 198, 0) 35%),
    linear-gradient(0deg, rgba(255, 255, 255, 0.1), rgba(255, 255, 255, 0.1));
}

html {
  font-size: 34px;
  font-weight: 200;
}

body {
  min-height: 100vh;

  display: grid;
  place-content: center;
  grid-template-columns: repeat(3, max-content);
  gap: 1rem;

  background: var(--bg-color);

  font-family: "Montserrat", sans-serif;
}
Enter fullscreen mode Exit fullscreen mode

Basic Link Styles

Each link is going to have the same basic style, that means: No text decoration, a little border & padding as well as a small flex-box setup that makes the text centered horizontally and vertically.

The position: relative; setting on the link tag itself is important as it makes the position: absolute; on the ::before pseudo-element work.

a {
  position: relative;

  padding: 0.25rem 1rem;

  text-decoration: none;
  color: var(--fg-color);

  border: 1px solid var(--bd-color);

  display: flex;
  justify-content: center;
  align-items: center;
}
Enter fullscreen mode Exit fullscreen mode

The ::before pseudo-element is going to be spanned across the entire link (top, left, bottom & right all set to 0) and is put behind the link's text through setting -1 as its z-index. To make the clip-path effect well visible, the pseudo-element gets a few gradients as background.

a::before {
  content: '';

  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  right: 0;

  background: var(--default-bg);

  z-index: -1;

  transition: clip-path 400ms ease-out;
}
Enter fullscreen mode Exit fullscreen mode

In order to have all clip path configurations smoothly transition from one state to another, the clip-path property is made transitionable by putting it into the transition CSS property.

Style: Next

<a href="#"
   class="next">next</a>
Enter fullscreen mode Exit fullscreen mode

Now for the first style the clip path gets an arrowy shape on the left and right side.
So in the initial state both edges are positioned completely to the left.

a.next::before {
  clip-path: polygon(
    0% 0%,
    -0.5rem 0%,
    0% 50%,
    -0.5rem 100%,
    0% 100%,
    -0.5rem 50%
  );
}
Enter fullscreen mode Exit fullscreen mode

On hover the right edge of the arrow moves from left to right and upon mouse down the left edge of the arrow also moves from the left to right.

a.next:hover::before {
  clip-path: polygon(
    -0.5rem 0%,
    100% 0%,
    calc(100% + 0.5rem) 50%,
    100% 100%,
    -0.5rem 100%,
    0% 50%
  );
}
Enter fullscreen mode Exit fullscreen mode

Note the number of points in each clip path. In order make transition: clip-path work, the number of points in each polygon must be the same. If the number of points deviates from one state to another the browser will simply jump from one state to another without any smooth transition.

a.next:active::before {
  clip-path: polygon(
    100% 0%,
    100% 0%,
    calc(100% + 0.5rem) 50%,
    100% 100%,
    100% 100%,
    calc(100% + 0.5rem) 50%
  );
}
Enter fullscreen mode Exit fullscreen mode

Style: Dot

<a href="#"
   class="dot">dot</a>
Enter fullscreen mode Exit fullscreen mode

In this simple style, the ::before pseudo element is clipped down to a circle which is at the center of the link. Upon hover it smoothly expands and on mouse down spans the entire link.

The circle function takes the radius as the first argument before the at keyword and after that the position of the circle's center as x and y coordinate.

a.dot::before {
   clip-path: circle(
     0%
     at
     50% 50%
   );
}
Enter fullscreen mode Exit fullscreen mode
a.dot:hover::before {
  clip-path: circle(
    50%
    at
    50% 50%
  );
}
Enter fullscreen mode Exit fullscreen mode
a.dot:active::before {
  clip-path: circle(
    100%
    at
    50% 50%
  );
}
Enter fullscreen mode Exit fullscreen mode

Style: Sunrise

<a href="#"
   class="sunrise">sunrise</a>
Enter fullscreen mode Exit fullscreen mode

Let the sun rise 😎 The initial state is a large circle at the center of the link but shifted down outside the viewport.

a.sunrise::before {
  clip-path: circle(
    40%
    at
    50% 200%
  );
}
Enter fullscreen mode Exit fullscreen mode

In order to produce the effect of a rising sun it is shifted upwards upon hover.

a.sunrise:hover::before {
  clip-path: circle(
    50%
    at
    50% 100%
  );
}
Enter fullscreen mode Exit fullscreen mode

On mouse down the circle is moved to the center of the link and expanded completely.

a.sunrise:active::before {
  clip-path: circle(
    100%
    at
    50% 50%
  );
}
Enter fullscreen mode Exit fullscreen mode

Style: Slant

<a href="#"
   class="slant">slant</a>
Enter fullscreen mode Exit fullscreen mode

This time again both the left and the right edge are positioned to the very left. The lower point of each edge is shifted to the left by 1rem.

a.slant::before {
  clip-path: polygon(
    0% 0%,
    0% 0%,
    -1rem 100%,
    -1rem 100%
  );
}
Enter fullscreen mode Exit fullscreen mode

So upon hover, first the right edge is moved to the right, ...

a.slant:hover::before {
  clip-path: polygon(
    0% 0%,
    calc(100% + 1rem) 0%,
    100% 100%,
    -1rem 100%
  );
}
Enter fullscreen mode Exit fullscreen mode

... and on mouse down the left edge will also move to the right.

a.slant:active::before {
  clip-path: polygon(
    calc(100% + 1rem) 0%,
    calc(100% + 1rem) 0%,
    100% 100%,
    100% 100%
  );
}
Enter fullscreen mode Exit fullscreen mode

Style: Liquid

<a href="#"
   class="liquid">liquid</a>
Enter fullscreen mode Exit fullscreen mode

For the last style let's create a slight impression of a liquid rising/flowing. For that a larger ellipse is created. The ellipse function is similar to the circle except that it takes the horizontal and the vertical radius as the first two arguments before the at keyword.

a.liquid::before {
  clip-path: ellipse(
    100% 50%
    at
    50% 200%
  );
}
Enter fullscreen mode Exit fullscreen mode

On hover the ellipse is moved upwards and changes its vertical radius to produce a soft curve.

a.liquid:hover::before {
  clip-path: ellipse(
    100% 250%
    at
    50% 200%
  );
}
Enter fullscreen mode Exit fullscreen mode

And on mouse down the ellipse is vertically scaled down to zero as well as moved upwards to the upper edge.

a.liquid:active::before {
  clip-path: ellipse(
    100% 0%
    at
    50% 0%
  );
}
Enter fullscreen mode Exit fullscreen mode

Top comments (1)

Collapse
 
nikhilmwarrier profile image
nikhilmwarrier

Awesome!