If you have any questions or feedback, pleasefill out this form
This post is translated by ChatGPT and originally written in Mandarin, so there may be some inaccuracies or mistakes.
In recent years, CSS has introduced many pseudo-classes, and unlike in the past, where only a few browsers implemented them, they are now widely supported. Today, I want to introduce several CSS pseudo-classes and demonstrate how they can help with styling through practical examples.
:is
:where
:has
:lang
:focus-within
:first-child
and:last-child
:is
In the past, when we wanted to apply the same CSS rules to multiple selectors, we would write it like this:
.a-class,
.b-class {
font-size: 25px;
}
This approach works perfectly fine, but with the introduction of :is
, we can simplify our declarations:
:is(.a-class, .b-class) {
font-size: 25px;
}
When there are many selectors to apply styles to, using :is
makes the code much more readable. Although it's still in the Working Draft phase, according to Can I use, 98.1% of users can utilize it.
In addition to single classes, it can also be combined with other selectors:
:is(.a-class, .b-class) a:hover {
opacity: 0.8;
}
:where
The function of :where
is similar to that of :is
. However, there is a difference in terms of specificity. When using :where
, there is no weight, and its specificity is equivalent to *
. On the other hand, :is
calculates specificity based on the highest priority weight in the list.
Hello
In this example, because of #page
, the weight is greater than the class selector, resulting in the final color being red. If we use :where
, the selectors inside will not carry extra weight.
Hello
Thus, the final applied color will be yellow. From the results above, we can see two main use cases:
- Use
:where()
for global styles (like resets), making it easy to override later. - Use
:is()
for local styles across multiple selectors to ensure all selectors apply the same weight.
:has
:has
allows you to match a parent element when certain conditions for its child elements are met. This might sound a bit convoluted, so let's clarify with an example. A very practical scenario is when you want to change the style of a parent element when a child element receives focus.
Sometimes when implementing a search feature, we place the search icon next to the input field. When the input is focused, we want not just the input to apply the focus styles but the entire input area, including the icon. In this case, using input:focus
alone won't achieve the desired effect.
Previously, the solution involved modifying the DOM structure to make CSS selectors work, or using JavaScript to listen for focus events and adding a class to inform the parent element that the input was focused. Now, with :has
, we can achieve this directly in CSS without altering the structure.
Focus input!
This way, when the input is focused, .form:has(input:focus)
becomes active, applying styles to the parent element. Achieving the ability to "match the parent when the child meets certain conditions" has long been a wish for front-end developers, and we've now taken a significant step forward.
:has
also has other applications, especially for scenarios where you want to apply styles based on whether a child element meets certain conditions.
For example, when switching themes, you can use body:has(input[type="checkbox"]:checked)
to change color themes without JavaScript, while maintaining a sensible DOM structure.
For more use cases regarding :has
, you can refer to webkit. Since :has
is a relatively new pseudo-class, its browser support is still limited (82.92%), so if you plan to implement it, consider having a backup plan.
:focus-within
For some more experienced front-end engineers, you might have noticed that the previous example could actually be replaced with :focus-within
. This pseudo-class matches when a child element is focused, allowing you to modify the parent's styles.
For instance, if you want to add a box-shadow
to a form when focused, you could write it like this:
However, :focus-within
can only be applied to focus states, lacking the flexibility to match situations that are not focused, where :has
would be more suitable.
:lang
When implementing multilingual websites, we usually add a lang
attribute to the <html>
tag to indicate the current language of the site. Sometimes, we may need to display different fonts for different languages. Without using :lang
, we would have to write it like this:
html[lang="zh"] p { font-family: var(--font-zh); }
html[lang="en"] p { font-family: var(--font-en); }
This can be quite verbose; using :lang
can simplify the selector syntax.
p:lang(en) {
font-family: var(--font-en);
}
p:lang(zh) {
font-family: var(--font-zh);
}
:first-child and :last-child
Though these two pseudo-classes are quite common, I've noticed that many front-end developers are still unaware of them. These pseudo-classes can target the first or last child, often used to specify margin-right
without applying it to the last element, like this:
.tab {
margin-right: 8px;
}
.tab:last-child {
margin-right: 0;
}
You can also use them in conjunction with :not
for simplification:
.tag:not(:last-child) {
margin-right: 8px;
}
Why not just use JavaScript?
Some users may not have JavaScript enabled, especially on static websites like blogs that primarily feature written content. However, we don't want to compromise the reading experience too much, which is where the pseudo-classes introduced above can help us solve styling issues. If there’s a simple solution, who would want something complicated?
If you found this article helpful, please consider buying me a coffee ☕ It'll make my ordinary day shine ✨
☕Buy me a coffee