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:

Analyzing the previous CSS:
- We define a fixed and proportional size, centered with
margin. - The image size is inherited from the parent using the
inheritvalue, avoiding duplicating values and maintaining layout consistency. - We apply
border-radius: 50%to crop the image into a circular shape, and combine it with theobject-fitproperty 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:

::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-shadowwith theinsetparameter to create a translucent inner border, instead of usingborder. This gives us more control over the color and blur of the outline. - The size of the pseudo-element is inherited from the parent
divwith theinheritvalue, ensuring it covers the image exactly.
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: 0makes the layer completely invisible by default; it is the initial state before any interaction.- The
transition: all 1sproperty indicates that any CSS change on this element will animate smoothly for 1 second. This is what turns the abrupt change ofopacityinto 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:

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 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:
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:
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.