Ananya Neogi A frontend developer focusing on creating digital products with a great user experience.

CSS pseudo-classes you might need

3 min read 1008

CSS pseudo-classes you might need

A CSS pseudo-class is a keyword added to a selector that specifies a special state of the selected element.

You might already be familiar with the ones that are commonly used, such as :hover.

In this post, we are going to explore some lesser-known and infrequently-used pseudo-classes and their practical use cases.

:is()

This function takes a list of selectors as its argument and selects any element that matches one of the selectors in that list.

Now, what we write like this:

article > h1, article > h2, article > h3 {
  color: #555;
}

button:focus, button:hover {
  border: 1px solid orangered;
}

will become this:

article > :is(h1,h2,h3) {
  color: #555;
}

button:is(:focus, :hover) {
  border: 1px solid orangered;
}

As you can see, this is useful for compactly writing large selectors.

Certain older versions of browsers support this functionality as :matches() because:is was previously called:matches().

We can also use the older, vendor-prefixed pseudo-class — :any() — which acts in a similar way as :is().

article > :-webkit-any(h1,h2,h3) {
  color: #555;
}

article > :-moz-any(h1,h2,h3) {
  color: #555;
}

article > :matches(h1,h2,h3) {
  color: #555;
}

:focus-within

This represents an element that has received focus or contains an element that has received focus.

<form>
  <label for="name">Name:</label>
  <input type="text" id="name">
  <label for="email">Email</label>
  <input type="email" id="email">
</form>

We can modify the form styles when any of its input elements are focused.

form:focus-within {
 background: coral;
}

Here’s a CSS-only dropdown made possible with :focus-within
(https://codepen.io/ananyaneogi/pen/KKwbyQX)

:focus-visible

It applies while an element is in the focused state.

Most browsers show a focus ring by default in this case.

This selector is useful for providing a different focus indicator based on how the user is interacting with the element (i.e with a mouse, a keyboard, etc.)

In this example, when we navigate the links with a keyboard (i.e. by tabbing), only then will the focus styles be applied.



This helps appy prominent styles to guide users who are navigating with a keyboard without altering focus states when using a mouse.

:focus-visible

body { background-color: #f7f7f7; display: flex; height: 100vh; justify-content: center; align-items: center; font-family: sans-serif; } section { border: 2px dashed #ccc; padding: 2em; } h1 { margin-bottom: 1.8em; } ul { list-style-type: none; padding: 0; } li { margin-bottom: 1.6em; font-size: 1.2em; } li:last-child { margin-bottom: 0; } a { color: cadetblue; text-decoration: none; font-weight: bold; } a:focus, a:hover, a:active { color: #9f64a9; } /* trick to avoid focus line on click, the focus line will only be applied when navigating with keyboard */ a:focus:not(:focus-visible) { outline: 2px solid transparent; } a:focus { outline: 2px solid #9f64a9; }

 

:only-child and :only-of-type

:only-child selects an element that is the ONLY child of a parent. That means only one element exists within that parent. Even if it’s a different element type, it won’t be considered an only child. One element, no exceptions!

p:only-child {
      color: magenta;
    }


    <article>
      <p>A simple paragraph</p> <!-- p will be of magenta colour -->
    </article>
    
    <!-- ❌ no element selected because p is not the only child -->
    <article>
      <h1>Heading</h1>
      <p>A simple paragraph</p>
    </article>

:only-of-type selects an element that is the ONLY child of a particular type within a parent. Having other siblings of different types is fine.

p:only-of-type {
color: magenta;
}
<!-- p will be of magenta colour, because even though article contains more than one child but still <p> is the only one of it's own type -->
<article>
  <h1>Heading</h1>
  <p>A simple paragraph</p>
</article>

Here’s a practical example using :only-child, adding a “Hurry, last item remaining” type of message to a product card if it’s the only product card remaining.

:only-child

li.product-card:only-child:after { content: ” Hurry! Last item remaining.

:not()

This represents elements that do not match a list of selectors it takes as the argument.

<article>
  <p>A simple paragraph</p>
  <span>Span text</span>
</article>


/* Everything except the <p> inside the <article> will be of magenta colour! */
article :not(p) {
  color: magenta;
  }

This is not a very practical example but we can use :not() along with other pseudo-classes to achieve interesting results. We’re going to see that next.

Note: nesting of :not i.e :not(:not(...)) is invalid.

:empty

This represents any element that has no children. Children can be either element nodes or text including whitespace.

/* Selects any <span> that contains no content */
span:empty {
   background: magenta;
}

Here’s an example of eliminating empty tags that contain no content using :empty along with :not().

Target empty elements with :empty

Add External Stylesheets/Pens Any URLs added here will be added as s in order, and before the CSS in the editor. You can use the CSS from another Pen by using its URL and the proper URL extension. JavaScript Preprocessor Babel includes JSX processing.


More great articles from LogRocket:


 

:placeholder-shown

This represents any input element that is currently displaying the placeholder text.

In this example, we are highlighting the required fields in the form that are yet not filled by the user.

We are signaling that the fields are not yet filled in by simply targeting the inputs whose placeholder is still showing.

:required + :placeholder-shown – Highlight required fields

{ box-sizing: border-box; } .container { font-family: sans-serif; border: 1px dashed #ccc; padding: 30px; font-size: 14px; margin: 30px; } .container div { margin-bottom: 20px; } .container div:last-child { margin: 0; } .container label { display: block; font-size: 13px; margin-bottom: 5px; } .container input { font-size: inherit; font-family: inherit; border: 2px solid #999; display: block; width: 100%; padding: 10px; } .container input:required:placeholder-shown { border: 2px solid magenta; }

If you look closely in the example above, you’ll notice that another pseudo-class, :required, is used along with  :placeholder-shown.

:required is one of the many pseudo-classes related to forms that we have at our disposal. Next, we are going to look at some form-related pseudo-classes that make our form’s user experience better.

:required

This represents any input element that has the required attribute set on it. It is useful for highlighting fields that must be entered by the user before the form can be submitted.

:read-only

This represents an element that is not editable by the user.

:invalid</code

This represents any input form element whose contents fail to validate.

:valid

This represents any input form element whose contents validate successfully.

Here’s an example using the previously mentioned form-related pseudo-classes:

HTML Forms + CSS pseudo class

{ box-sizing: border-box; } .container { font-family: sans-serif; border: 1px dashed #ccc; padding: 30px; font-size: 14px; margin: 30px; div { margin-bottom: 20px; position: relative; &:last-child { margin: 0; } } label { display: block; font-size: 13px; margin-bottom: 5px; } button { border: 1px solid rgba(0, 0, 0, 0.9); color: #fff;

Conclusion

Remember to always refer to the browser compatibility before using them in production.

However, don’t let the browser inconsistency stop you from experimenting with new things: just keep a fallback handy and you’re good to go.

Find more documentation related to these pseudo-classes on MDN.

Is your frontend hogging your users' CPU?

As web frontends get increasingly complex, resource-greedy features demand more and more from the browser. If you’re interested in monitoring and tracking client-side CPU usage, memory usage, and more for all of your users in production, try LogRocket.https://logrocket.com/signup/

LogRocket is like a DVR for web and mobile apps, recording everything that happens in your web app, mobile app, or website. Instead of guessing why problems happen, you can aggregate and report on key frontend performance metrics, replay user sessions along with application state, log network requests, and automatically surface all errors.

Modernize how you debug web and mobile apps — .

Ananya Neogi A frontend developer focusing on creating digital products with a great user experience.

Leave a Reply