5 New Features That Will Change the Way You Write CSS
The past year has seen an explosion of new CSS features that have revolutionized the way we write CSS. No matter where you are in your web development journey, there will be new things to learn. For a simple styling language, it does change pretty fast!
1. :has() selector
The new selector works in every browser except Firefox, but it’s supported when the flag is turned on, so we know it’s coming.
:has() selectors allow us to style parent elements based on their child elements. For example, we can do this:
figure { background: white; }
figure:has(img) { background: grey; }Changes its background to gray if figure within the element . img of course, there are more practical uses for this, such as:
form:has(input:invalid) { /*there an invalid input*/
background: red;
}
form:not(:has(input:invalid)) { /*all inputs valid*/
background: green
}That’s :not(:has(input:invalid)) cool, but kind of confusing. Basically, if the form has no invalid ones input, it only contains valid ones inputs, so it's a valid form 👍.
2. :focus-within pseudo-class
You’ve probably been using :focus for a while, but :focus only works on the current element. What if you want to know if the user is focused on a child element? iframe this is useful if you have a sublink on the page or in a menu. You can use it like this:
div:focus-within { background: red; }
Turns red if the user follows div anything in the . div it's very convenient! You can even do the same in our previous example!
form:has(input:invalid):focus-within {
background: red;
}
form:not(:has(input:invalid)):focus-within {
background: green
}That way, the form won’t turn red or green unless the user interacts with it. An empty form is technically an invalid form 😱
3. Cascade layer
This one is a bit unique, and while I’ve never seen it used in practice, there is definitely one. Start with this HTML:
<div class="box">
<p>Hello, world!</p>
</div>Select <p> the tab:
p { font-size: 18px }But what if we want to add more styles? Well, CSS cascades down, so we just need to add some styles after it.
p {
font-size: 18px
}
p {
font-weight: bold;
font-size: 20px;
color: red;
}So far, it’s just basic CSS, nothing revolutionary. But what if we wanted to add styles to font-weight and color, but keep font-size the ? The obvious solution is to remove font-size: 20px this line, but now there is a new way, using "Layers":
p {
font-size: 18px;
}
@layer type {
p {
font-weight: bold;
font-size: 20px;
color: red;
}
}The result of this CSS will display paragraphs in bold, red, and with a font size of 18 pixels . Read it again. Although the second p selector is more specific (since it's at a deeper position), since it's “type” inside the layer, the 20px fontsize won't override the 18px fontsize
Think of it that way, @layer everything inside is written at the top of the stylesheet, like this:
/*How it's written*/
p {
font-size: 18px;
}
@layer type {
p {
font-weight: bold;
font-size: 20px;
color: red;
}
}
/*How it's rendered*/
p {
font-weight: bold;
font-size: 20px;
color: red;
}
p {
font-size: 18px;
}We can also organize the rendering order of layers in this way:
@layer template, unique;
p { font-size: 18px; }
@layer unique {
p {
font-size: 12px;
}
}
@layer template {
p {
font-size: 20px;
}
}This renders because at the top of the style sheet, template before unique the .
p {
font-size: 12px;
}
p {
font-size: 20px;
}
p {
font-size: 18px;
}It is also possible to import specific layers in the stylesheet, just like importing JS modules or Python libraries:
@import "style.css" layer(template);4. Goodbye Transforms
I remember when transforms first came out, they were awesome. You can scale elements, rotate them, distort them, and even turn them into 3D shapes.
But they always have a problem. If you have CSS like this:
div {
transform: translate(-50%, -50%) rotate(10deg)
}Then you want to add a hover effect to zoom, you’ll have to write it again:
div:where(:hover, :focus) {
transform: translate(-50%, -50%) rotate(10deg) scale(1.1)
}This coding is such a headache 😔
Thankfully, now we have a new option. We can ditch them entirely transforms, and style our elements without them.
div {
translate: -50%, -50%;
rotate: 10deg;
}
div:where(:hover, :focus) {
scale: 1.1;
}5. code from user agent
CSS custom properties allow us to save styles in code and reuse them later, like this:
:root {
--color: red;
}
p { var(--color); }
img { border: 2px solid var(--red); }However, while these are defined by us programmers, there are other styles defined by user agents. We can env() access them through styles. Currently, only eight such styles are available:
/*the safe-area-inset-* styles */
env(safe-area-inset-top)
env(safe-area-inset-right)
env(safe-area-inset-bottom)
env(safe-area-inset-left)
/*the titlebar-are-* styles */
env(titlebar-area-x)
env(titlebar-area-y)
env(titlebar-area-width)
env(titlebar-area-height)The first four define the padding for the top, right, bottom and left of the browser window. This is handy on non-square screens, like smartwatches or some phones whose screens curve to the edges. You can use these directly safe-area-inset-*, but their value is 0 pixels.
These titlebar-area-* styles are only available for Progressive Web Apps, and only when using window-controls-overlay display_override the value. They can be used to prevent the PWA from covering the minimize, maximize and close buttons.
Well, I can’t think of any other instances where a user agent could communicate with a stylesheet to determine how it should be displayed. Yes, you can @media (prefers-color-scheme: dark) check for dark mode using the , or you can force the printer to print something with a background image.
@media print {
* {
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
}
}But these are not the same as actually getting the value from the user agent and putting it in the stylesheet. Although the current application is limited, I can imagine future applications may be to obtain the default operating system font or obtain the browser’s zoom level through something like this.
body { font-size: calc(100% * env(browser-zoom-level)); }We can’t do that at the moment, and it’s a problem for developers.
While env() there aren't many use cases for , it does have the potential to change the way we write CSS, and it's something we should be aware of.
In Plain English 🚀
Thank you for being a part of the In Plain English community! Before you go:
- Be sure to clap and follow the writer ️👏️️
- Follow us: X | LinkedIn | YouTube | Discord | Newsletter
- Visit our other platforms: Stackademic | CoFeed | Venture | Cubed
- More content at PlainEnglish.io






