• 15 hours
  • Medium

Free online content available in this course.

course.header.alt.is_video

course.header.alt.is_certifying

Got it!

Last updated on 6/7/21

Use pseudo-selectors to trigger CSS transitions

Lighting the fuse: triggering transitions

One of the key ingredients to creating CSS transitions is incorporating a pseudo-class to allow the user to change an element's state and trigger a transition. We've just used a :hover pseudo-selector to trigger the growth of a button. And, while :hover is probably the most common pseudo-class that you'll use, there are others that can trigger transitions as well.

In fact, there are more than 50 pseudo-classes to choose from, but not all of them are well suited to transitions. To understand which will, or won't, work well with transitions, let's take a look at what a pseudo-class is and how it works.

A pseudo-class is a lot like an if-statement for the browser: If the condition of the pseudo-class is true, then apply its styling to the object. In the case of our growing button, if it's being hovered over, use the styling contained within the :hover pseudo-selector, such as transforming it by increasing its scale.

Hovering over the button changes its state from "not-hovered" to "hovered." It is that change that makes it perfect for triggering transitions. The best pseudo-class candidates are those whose states you can anticipate being changed by the user as they interact with the site.

Just like you can anticipate a user hovering over a button, you could also anticipate them making a text-input :active, or entering text that is :invalid and style them as you see fit.

The following are pseudo-classes commonly used in conjunction with transitions. That isn't to say they are the only ones, but rather a list of likely candidates. For a complete list of pseudo-classes, check out the MDN documentation. Even those that don't work well with transitions can make styling elements a lot easier.

  • :hover

  • :active

  • :focus

  • :valid

  • :invalid

  • :not()

  • :checked

  • :enabled

  • :disabled

Constructive criticism: validation

Let's create a form that gives the user feedback as they enter their email address. The HTML for our form could look something like this:

<body>
    <div class="container">
         <div class="form">
            <div class="form__group">
                <label for="">email</label>
                <input type="email" name="" id="">
            </div>
         </div>   
    </div>
</body>

We have .form block element, containing a .form__group, which is comprised of an email input and it's label, which renders in the browser as:

Now, let's use the  :active  pseudo selector to provide the user with a bit of feedback, letting them know that they have made the input field active by highlighting it with a border:

$cd-txt: #6300a0;

.form {
    &__group {
        & input {
            border: 2px solid $cd-box;
            border-radius: 100rem;
            color: $cd-txt;
            font-family: 'Montserrat', sans-serif;
            font-size: 2.5rem;
            outline: none;
            padding: .5rem 1.5rem;
            width: 100%;
            &:focus {
                border: 2px solid $cd-txt;
            }
        }
    }
}

Now, when we change the state of the input to :focus, it should add a purple border to the element:

 

Perfect! Now, let's use HTML5's built in ability to validate fields, and combine it with the  :invalid  pseudo-class, so that they receive feedback that they haven't entered a valid email address. Let's start by adding an :invalid pseudo-selector to the input selector and add properties to change its text to white on a red background:

$cd-txt: #6300a0;
$cd-txt--invalid: #fff;
$cd-danger: #b20a37;

.form {
    &__group {
        & input {
            border: 2px solid $cd-box;
            border-radius: 100rem;
            color: $cd-txt;
            font-family: 'Montserrat', sans-serif;
            font-size: 2.5rem;
            outline: none;
            padding: .5rem 1.5rem;
            width: 100%;
            &:focus {
                border: 2px solid $cd-txt;
            }
            &:invalid {
                background: $cd-danger;
                border: 2px solid $cd-danger;
                color: $cd-txt--invalid;
            }
        }
    }
}

Now let's take our form for a spin!

It's validating the email address as it's being entered, but flickering between the :focus and  :invalid is a bit obnoxious. Let's make use of the :not() pseudo-selector, combined with the  :focus pseudo-selector to allow the user to finish entering their email address before telling them whether it's valid or not:

$cd-txt: #6300a0;
$cd-txt--invalid: #fff;
$cd-danger: #b20a37;

.form {
    &__group {
        & input {
            border: 2px solid $cd-box;
            border-radius: 100rem;
            color: $cd-txt;
            font-family: 'Montserrat', sans-serif;
            font-size: 2.5rem;
            outline: none;
            padding: .5rem 1.5rem;
            width: 100%;
            &:focus {
                border: 2px solid $cd-txt;
            }
            &:not(:focus):invalid {
                background: $cd-danger;
                border: 2px solid $cd-danger;
                color: $cd-txt--invalid;
            }
        }
    }
}

 The :not() pseudo-selector registers as true when it's containing selector registers as false. In this case, :not() will return true if the input is not in the :focus state. You can chain pseudo-classes together, just like with standard classes. In this case, we've created a pseudo-selector that will apply its rule set if the element isn't in the :focus state and the email address is :invalid.  This way we don't see the red warning signs of an invalid email until they've finished typing their address:

 There. That's much nicer, don't you think? But we can make it nicer yet! This is an animation class, after all, so let's add animation to the changes in state by adding a transition for the  background-color that takes half of a second to complete:

$cd-txt: #6300a0;
$cd-txt--invalid: #fff;
$cd-danger: #b20a37;

.form {
    &__group {
        & input {
            border: 2px solid $cd-box;
            border-radius: 100rem;
            color: $cd-txt;
            font-family: 'Montserrat', sans-serif;
            font-size: 2.5rem;
            outline: none;
            padding: .5rem 1.5rem;
            width: 100%;
            transition: background-color 500ms;
            &:focus {
                border: 2px solid $cd-txt;
            }
            &:not(:focus):invalid {
                background-color: $cd-danger;
                border: 2px solid $cd-danger;
                color: $cd-txt--invalid;
            }
        }
    }
}

Now we'll get a quick fade between when the user enters their email and when it displays as invalid:

Perfect! 👌 By chaining together pseudo-classes into a more specific pseudo-selector, we've been able to improve the user's experience!

Picking on your little brother: triggering siblings

Now, as you've just seen, you can use pseudo-classes to trigger transitions on their assigned element, such as changing the size of a hovered button, or invalid text field. But you can also use pseudo-classes to trigger transitions on other elements as well.

Hey Jim, yeah... can we get an asterisk for that last sentence? Perfect... thanks!

That's better. So, why the asterisk? What's the disclaimer?

For a pseudo-selector to trigger a transition on another element, that element must be the next sibling in the HTML document. In other words, you must use the adjacent sibling combinator:  +  when setting up the transition.

Say whuuh?

Use a :hover pseudo-selector and add styles to it like so:

.btn {
    background: $cd-primary;
    font-size: 3rem;
    cursor: pointer;
    padding: 1.85rem 3rem;
    border-radius: 10rem;
    &:hover{
        transform: scale(1.15);
    }
}

In the event of a hover, those styles are applied to the element that :hover is attached to:

 

But let's say the button has a sibling with the class .ball assigned that you would like to affect instead.

<body>
    <div class="container">
        <div class="btn">
            Grow!
        </div>
        <div class="ball"></div>
    </div>
</body>

You can use the adjacent sibling  combinator to couple .ball with the  :hover the pseudo-selector instead:

.btn {
    background: $cd-primary;
    font-size: 3rem;
    cursor: pointer;
    padding: 1.85rem 3rem;
    border-radius: 10rem;
    &:hover + .ball{
        transform: scale(1.15);
    }
}

.ball {
    width: $ball-size;
    height: $ball-size;
    background: $cd-secondary;
    margin-bottom: 1rem;
    border-radius: $ball-size * 0.5;
}

Now when you hover over the button, .ball will scale up instead:

Let's change things up a bit, and, rather than starting huge and getting huger, let's have it start small by setting the initial scale() to .1, and add a transition. But rather than taking a fraction of a second, let's have it take a full four seconds to complete the animation so it creates the perception of continuously growing:

.btn {
    background: $cd-primary;
    font-size: 3rem;
    cursor: pointer;
    padding: 1.85rem 3rem;
    border-radius: 10rem;
    &:hover + .ball{
        transform: scale(1.15);
    }
}

.ball {
    width: $ball-size;
    height: $ball-size;
    background: $cd-secondary;
    margin-bottom: 1rem;
    border-radius: $ball-size * 0.5;
    transform: scale(0.1);
    transition: transform 4000ms;
}

And, finally, let's change the :hover selector to :active, so the scale increases when the button is pressed, rather than simply hovered over. Then, change the scale of the :active state to be  1.0 rather than 1.15 and we should be all set!

.btn {
    background: $cd-primary;
    font-size: 3rem;
    cursor: pointer;
    padding: 1.85rem 3rem;
    border-radius: 10rem;
    &:active + .ball{
        transform: scale(1.0);
    }
}

.ball {
    width: $ball-size;
    height: $ball-size;
    background: $cd-secondary;
    margin-bottom: 1rem;
    border-radius: $ball-size * 0.5;
    transform: scale(0.1);
    transition: transform 4000ms;
}

When we view it in the browser now, we should have a button that, when pressed, triggers a transition to the scale of its sibling, from 10% to 100%, over the course of four seconds. Let's check it out:

Pseudo-classes are extremely helpful in styling elements and triggering transition based on their states, but can also be used to manipulate other elements on the page, allowing you to create more interactive experiences for your visitors.

We've covered the basics of transitions, so, coming up next, you'll learn methods for adding complexity and life to animations from some of the masters!

Let's recap!

  • Pseudo-classes are used to trigger CSS transitions.

  • The best pseudo-classes to use with transitions are those that the user will interact with as they browse the site.

  • You can chain pseudo-classes together to make more specific pseudo-selectors.

  • Pseudo-selectors can be used to manipulate its element's next sibling as well as itself.

Example of certificate of achievement
Example of certificate of achievement