How to use hover in CSS with transition and transform for image effects

- Andrés Cruz - ES En español

How to use hover in CSS with transition and transform for image effects

In the following sections, we will see some experiments with the :hover selector combined with CSS Transitions and/or CSS Animations to achieve curious and visually attractive css hover effects that you can implement directly on your website.

For this first experiment, besides the :hover selector, we will use the ::before and ::after pseudo-elements, which together allow us to create additional layers over the element without needing to add extra HTML. The result is the following effect:

Hover your cursor over it

The idea is...

Let's start with the simplest: we use a single div whose only child will be an image. The necessary HTML is as follows:

<div class="cuerpo leon derecha">
    <img src="leon.jpg" />
</div>

To that div we add a little CSS to give the desired shape to the container and the image:

.cuerpo {
   width: 200px;
   height: 200px;
   margin: 30px auto 0 auto;
}
.cuerpo img {
    width: inherit;
    height: inherit;
    border-radius: 50%;
}

With this we get the following result:

CSS hover experiment layers

Analyzing the previous CSS:

  • We define a fixed and proportional size, centered with margin.
  • The image size is inherited from the parent using the inherit value, avoiding duplicating values and maintaining layout consistency.
  • We apply border-radius: 50% to crop the image into a circular shape, and combine it with the object-fit property to control the aspect ratio without distorting the image, just as we saw in a previous installment.

The ::before and ::after pseudo-elements

With the ::before and ::after pseudo-elements it is possible to create two virtual "layers" on top of the element's content, without modifying the HTML. You can think of them as two additional divs that CSS automatically inserts before and after the content:

Layers of the experiment with before and after

The ::before and ::after pseudo-elements are very versatile: they can receive dimensions, absolute positioning, background colors, shadows, and transitions, just like any div.

In this experiment, the layer generated by ::before will be permanently displayed on top of the image as an interior translucent border; while the ::after layer will remain invisible by default and will only appear when the cursor is positioned over the div, activating the CSS transition.

CSS of the ::before pseudo-element

To create the rounded and translucent border that is always visible, we use the following CSS on .cuerpo::before:

.cuerpo:before {
    display: block;
    border-radius: 50%;
    content: '';
    box-shadow: inset 0 0 0 10px rgba(255, 255, 255, 0.5);
    width: inherit;
    height: inherit;
    position: absolute;
}

Analyzing the previous CSS:

  • We use box-shadow with the inset parameter to create a translucent inner border, instead of using border. This gives us more control over the color and blur of the outline.
  • The size of the pseudo-element is inherited from the parent div with the inherit value, ensuring it covers the image exactly.
With the inherit value, the pseudo-element automatically inherits the width and height of the parent element.

CSS of the ::after pseudo-element

Now we define the CSS of ::after, the layer that will appear when hovering over the div through a CSS transition:

.cuerpo:after {
    display: table-cell;
    border-radius: 50%;
    box-shadow: inset 0 0 0 10px rgba(255, 255, 255, 0.5);
    width: inherit;
    height: inherit;
    position: absolute;
    background: rgba(0, 0, 0, 0.8);
    transition: all 1s;
    opacity: 0;
    content: "";
    color: #FFF;
    text-align: center;
    font-family: Arial;
    font-size: 12px;
    font-weight: bold;
    line-height: 50px;
}

Analyzing the previous CSS:

  • opacity: 0 makes the layer completely invisible by default; it is the initial state before any interaction.
  • The transition: all 1s property indicates that any CSS change on this element will animate smoothly for 1 second. This is what turns the abrupt change of opacity into a fluid transition when hovering.

We already have almost everything ready. The last step is to define the behavior when hovering: what happens when we position the cursor over the div and the CSS transition starts. For this we combine the directional class (.derecha, .izquierda, etc.) with the :after pseudo-element to establish the initial position outside the image —from where the layer will "enter"— and the final state when hovering:

.derecha:after {
    /* exit position: outside the div, to the right */
}

For example, we define the entry and exit transformations for each direction using transform: translate():

/* Initial state: the layer is outside the div according to the direction */
.derecha:after {
    transform: translate(300px, -200px);
}
.izquierda:after {
    transform: translate(-300px, -200px);
}
.arriba:after {
    transform: translate(0, -500px);
}
.abajo:after {
    transform: translate(0, 300px);
}
.lateral1:after {
    transform: translate(-300px, -300px);
}
.lateral2:after {
    transform: translate(300px, -300px);
}
.lateral3:after {
    transform: translate(300px, 300px);
}
.lateral4:after {
    transform: translate(-300px, 300px);
}

/* Hover state: the layer moves to its final position and becomes visible */
.derecha:hover:after,
.izquierda:hover:after,
.arriba:hover:after,
.abajo:hover:after,
.lateral1:hover:after,
.lateral2:hover:after,
.lateral3:hover:after,
.lateral4:hover:after {
    transform: translate(0px, -200px);
    opacity: 1;
}

/* Text content of the ::after pseudo-element */
.leon:after {
    content: "Lion";
}
.chita:after {
    content: "Cheetah";
}

The key to the effect is combining transform: translate() with transition: the pseudo-element starts from an offset position (outside the container) and, upon hovering, slides to its final position while its opacity goes from 0 to 1. You can view the complete code and download the resources at the following links:

Effects with hover and transition in CSS - CSS Filters

In the previous experiment we saw how to create an animated overlay on images using ::before, ::after, and :hover. In this second part we will see how CSS3 Filters —combined with CSS transitions— allow creating visually striking hover effects on images, taking advantage of the filters that modern browsers natively support.

Creating the effect with CSS filters and hover

As you can see in the previous iframe, the images have a CSS filter applied by default. By positioning the cursor over each one, the filter is progressively removed thanks to CSS Transitions, resulting in a very attractive reveal effect. The interesting thing is that this effect is purely CSS: you don't need JavaScript or external libraries.

Images CSS: filters and transitions

First we define the base CSS of the container and the images, including the CSS filters for each effect:

.cuerpo {
    width: 30%;
    height: 200px;
    margin: 30px 7px;
    float: left;
    transition: 1s all;
}
.cuerpo img {
    width: 100%;
    height: inherit;
    object-fit: cover;
    box-shadow: 0 0 20px #999;
    border: 2px solid #333;
}

/* CSS filters applied by default */
.grayscale {
    -webkit-filter: grayscale(100%);
    filter: grayscale(100%);
}
.hue-rotate {
    -webkit-filter: hue-rotate(220deg);
    filter: hue-rotate(220deg);
}
.sepia {
    -webkit-filter: sepia(100%);
    filter: sepia(100%);
}
.contrast {
    -webkit-filter: contrast(190%);
    filter: contrast(190%);
}
.saturate {
    -webkit-filter: saturate(50%);
    filter: saturate(50%);
}
.blur {
    -webkit-filter: blur(10px);
    filter: blur(10px);
}
.invert {
    -webkit-filter: invert(100%);
    filter: invert(100%);
}
.brightness {
    -webkit-filter: brightness(0.25);
    filter: brightness(0.25);
}
.opacity {
    -webkit-filter: opacity(34%);
    filter: opacity(34%);
}

Once again we make use of the object-fit property with the cover value so that the images cover the container without distortion, regardless of their original aspect ratio. Note also that we add the standard filter property along with -webkit-filter to guarantee compatibility with all modern browsers; -webkit-filter was necessary for older versions of Chrome and Safari, but nowadays the standard filter property has universal support.

Images CSS: the :hover effect

The CSS for the :hover selector is surprisingly simple: all it does is reset each filter to its neutral value. The transition property defined on the container takes care of the rest, smoothly interpolating between the filtered state and the unfiltered state. This is what the complementary CSS to the previous section looks like:

.hue-rotate:hover {
    -webkit-filter: hue-rotate(0deg);
    filter: hue-rotate(0deg);
}
.brightness:hover {
    -webkit-filter: brightness(1);
    filter: brightness(1);
}
.sepia:hover {
    -webkit-filter: sepia(0);
    filter: sepia(0);
}
.saturate:hover {
    -webkit-filter: saturate(1);
    filter: saturate(1);
}
.opacity:hover {
    -webkit-filter: opacity(1);
    filter: opacity(1);
}
.invert:hover {
    -webkit-filter: invert(0);
    filter: invert(0);
}
.grayscale:hover {
    -webkit-filter: grayscale(0);
    filter: grayscale(0);
}
.contrast:hover {
    -webkit-filter: contrast(100%);
    filter: contrast(100%);
}
.blur:hover {
    -webkit-filter: blur(0px);
    filter: blur(0px);
}

You can download the complete experiment at the following links:

Hover effects on images with CSS and transitions

Hover effects on images with CSS and JavaScript

In this third experiment we will see how to create a hover effect on a background image using CSS, HTML, and a bit of JavaScript (jQuery). The result is that characteristic grid effect that lights up when passing the cursor.

The technique consists of placing the image as the background of the html element and superimposing a series of small consecutive divs on top, each with a fraction of the container's size. By default these squares are transparent (invisible); upon positioning the cursor over them, a CSS Transition makes each square progressively acquire a semi-transparent color. To facilitate the dynamic creation of all those divs we use a small jQuery script:

View example

Hover the cursor from one end of the image to the other.

The CSS for the background uses the image directly on the html element, which makes it easier to occupy 100% of the viewport responsively, something more complex to achieve with an img tag:

html {
    background: url(tigre.jpg) no-repeat center center fixed;
    background-size: cover;
}

The CSS of the squares is as follows:

.block {
    width: 6.25%;
    float: left;
    position: relative;
    background-color: transparent;
    transition: all 3s linear;
}
.block:before {
    content: "";
    display: block;
    padding-top: 100%; /* trick to keep squares square */
}
.block:hover {
    background-color: rgba(255, 255, 255, 0.25);
    transition: all 100ms linear; /* fast entry, slow exit */
}

Note the intentional difference between the transition values in .block (3s) and in .block:hover (100ms): the square lights up quickly when the cursor enters and fades out slowly when leaving. This trick —using asymmetrical transition durations— noticeably improves the feel of the effect without adding a single line of JavaScript.

This is the JavaScript with jQuery that dynamically generates the squares over the image:

var i = 0,
    newBlock = '<div class="block"></div>',
    $blockContainer = $(".blocks");

for (i = 0; i < 150; i++) {
    $blockContainer.append(newBlock);
}

With this we obtain the grid effect shown above.

We can also vary the effect: instead of changing the background opacity, we make inner borders appear on each square using box-shadow with the inset parameter:

View example

Hover the cursor from one end of the image to the other.

The CSS is as follows:

.block {
    width: 6%;
    float: left;
    position: relative;
    box-shadow: inset 0 0 0 0 rgba(255, 255, 255, 0.25);
    transition: all 1s ease;
}
.block:before {
    content: "";
    display: block;
    padding-top: 100%;
}
.block:hover {
    box-shadow: inset 0 0 0 100px rgba(255, 255, 255, 0.25);
}

The trick here is to animate the fourth value of box-shadow —the spread radius— from 0 to 100px: the inner border "grows" from the inside out thanks to the transition, producing a completely different effect from the previous one with very little additional code.

The next experiment on the list is some interesting effects that can be achieved with lists in CSS.

Discover how to implement CSS hover effects on images—from transitions using ::before and ::after to CSS hover filters like grayscale, blur, and sepia. Ready-to-use experiments.


Únete a la comunidad de desarrolladores que han decidido dejar de picar código y empezar a construir productos reales. Recibe mis mejores trucos de arquitectura cada semana:

I agree to receive announcements of interest about this Blog.