Kalan's Blog

Kalan 頭像照片,在淡水拍攝,淺藍背景

四零二曜日電子報上線啦!訂閱訂起來

Software Engineer / Taiwanese / Life in Fukuoka
This blog supports RSS feed (all content), you can click RSS icon or setup through third-party service. If there are special styles such as code syntax in the technical article, it is still recommended to browse to the original website for the best experience.

Current Theme light

我會把一些不成文的筆記或是最近的生活雜感放在短筆記,如果有興趣的話可以來看看唷!

Please notice that currenly most of posts are translated by AI automatically and might contain lots of confusion. I'll gradually translate the post ASAP

Pseudo-classes used for styling

In recent years, CSS has added many pseudo-classes, and it is no longer limited to a few browsers as in the past. Today, I want to introduce several CSS pseudo-classes and demonstrate their usage through practical examples.

  • :is
  • :where
  • :has
  • :lang
  • :focus-within
  • :first-child and :last-child

:is

Previously, if we wanted to apply the same CSS rules to multiple selectors, we would write it like this:

.a-class,
.b-class {
  font-size: 25px;
}

There is no problem with this approach, but with :is, we can simplify the declaration:

:is(.a-class, .b-class) {
  font-size: 25px;
}

Using :is makes it easier to read when there are many selectors to apply. Although it is still in the Working Draft, according to the data from Can I use, 98.1% of users can use it.

In addition to single classes, it can also be used with other selectors:

:is(.a-class, .b-class) a:hover {
  opacity: 0.8;
}

:where

:where has the same purpose as :is. However, there is a difference in specificity. When using :where, it has no specificity, and its specificity is the same as *. However, :is is different and will have the specificity of the selector with the highest specificity in the list.

In this example, because of #page, its specificity is higher than the class selector, so the final color will be red. If we use :where, the selectors inside will not have additional specificity.

Therefore, the final applied color will be yellow. From the results above, the usage scenarios can be divided into:

  • Use :where() for global styles (e.g., reset) to make it easier to override when needed.
  • Use :is() for local styles on multiple selectors to ensure consistent specificity for all selectors.

:has

:has can match the parent element when the specified condition for the child element is met. It may sound a bit complicated, but it will be clearer with an example. One useful scenario is when we want to change the style of the parent element when the child element is focused.

Sometimes, when implementing a search function, we place the search icon next to the input box. When the input box is focused, we want not only the input box to have the focus style but also the entire part of the input box including the icon. Using input:focus alone cannot achieve the desired effect.

In the past, the solution was to modify the DOM structure to make the CSS selector work or to use JavaScript to listen for focus events and add a class to let the parent element know that the input has been focused. With :has, we can achieve this directly in CSS without changing the structure.

This way, when the input is focused, .form:has(input:focus) will take effect and apply the styles to the parent element. Being able to match the parent element when the child element meets a condition has been a long-standing wish for front-end developers, and now we have taken a big step forward.

:has has other use cases, especially when we want to apply a selector when the child element meets a certain condition.

For example, when switching themes, using body:has(input[type="checkbox"]:checked) can switch color themes without JavaScript while maintaining a reasonable DOM structure.

For more use cases of :has, you can refer to the webkit blog. :has is a relatively newer pseudo-class, and its browser support is not as high yet (82.92%), so it may require some fallback plans if you want to use it.

:focus-within

For some experienced front-end developers, you may have noticed that the previous example can actually be replaced with :focus-within. :focus-within matches when a child element is focused, allowing us to modify the style of the parent element.

For example, if we want to add a box-shadow to a form when it is focused, we can write it like this:

.container {
  display: flex;
  justify-content: center;
  align-items: center;
}
label {
  display: block;
}
label:not(:last-child) {
  margin-bottom: 1em;
}
form {
  width: fit-content;
  background-color: #fff;
  border: 2px solid #000;
  padding: 20px;
  border-radius: 8px;
}
form:focus-within {
  box-shadow: 0px 3px 8px 3px rgba(0, 0, 0, 0.3);
}

However, :focus-within can only be applied to focus and cannot provide the versatility for matching non-focus situations. If you need to match non-focus situations, you can use the more flexible :has.

:lang

When implementing multilingual websites, we add a lang attribute to the <html> tag to indicate the current language of the website. Sometimes, we may need to display different fonts for different languages. Without using :lang, we might need to write it like this:

html[lang="zh"] p { font-family: var(--font-zh); }
html[lang="en"] p { font-family: var(--font-en); }

It becomes a bit lengthy. With :lang, we 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

Although these two pseudo-classes are very common, I have noticed that many front-end developers are not aware of them. These two pseudo-classes can target the first or last child. The most common scenario is when specifying margin-right and not wanting to apply it to the last element. In this case, we can write it like this:

.tab {
  margin-right: 8px;
}

.tab:last-child {
  margin-right: 0;
}

It can also be simplified using :not:

.tag:not(:last-child) {
  margin-right: 8px;
}

Why not just use JavaScript?

Some users may not have JavaScript enabled, especially for websites that mainly consist of static articles like blogs. However, we still want to provide a good reading experience without relying too much on JavaScript. The aforementioned pseudo-classes can help us solve layout issues. Why make things complicated when they can be simple?

Prev

Rewrite the entire blog using Next.js

Next

HTML and CSS can solve many problems, but JavaScript is also very important.

If you found this article helpful, please consider buy me a drink ☕️ It'll make my ordinary day shine✨

Buy me a coffee