https://frontendmasters.com/blog/what-you-need-to-know-about-modern-css-spring-2024-edition/
Search Frontend Masters -- Boost
Courses Learn Become a Member
- Back to FrontendMasters.com
What You Need to Know about Modern CSS (Spring 2024 Edition)
March 26, 2024
My goal with this bookmarkable guide is to provide a list of
(frankly: incredible) new additions to CSS lately. There is no
hardline criteria for this list other than that these things are all
fairly new and my sense is that many people aren't aware of these
things. Or even if they are, they don't have a great understanding of
them and could use a plain language explanation of what it is, why
they should care, and a bit of reference code. Maybe that's you.
I'd like to work on our collective muscle memory on these features.
Like I said, "even if you know about this stuff, it takes time to
build the muscle memory around it."
There is quite a bit more syntax, detail, and nuance to these things
than I am presenting here. I want you to know what's possible,
reference the most basic usage and syntax, then dig deeper when you
need to.
Container Queries (Size)
What are Size Container Queries?
Container Queries allow you to write styles that apply to the
children of a container element when that container matches certain
media conditions, typically a width measurement.
Code language: HTML, XML (xml)
.element-wrap {
container: element / inline-size;
}
@container element (min-inline-size: 300px) {
.element {
display: flex;
gap: 1rem;
}
}Code language: CSS (css)
When should you care?
If you've ever thought: I wish I could make styling decisions based
on the size of this element, not the entire page like @media queries
force me to do. Then using @container queries are for you! People who
work on design systems or heavily component-based websites will
probably mostly use Container Queries to style things based on size,
because the size of the entire page is a poor proxy for the size of a
component.
Support
Browser Support Full
Progressive Potentially -- if it's not critical what you are
Enhancement? styling, then yes.
Polyfillable Yes
Basic Demo of Usage
Use the resizer in the middle to see the calendar change layout
depending on how much space it has. It has three breakpoints of its
own.
CodePen Embed Fallback
Container Queries (Style)
What are Style Container Queries?
Container Style Queries allow you to apply styles when a given Custom
Property has a given value.
.container {
--variant: 1;
&.variant2 {
--variant: 2;
}
}
@container style(--variant: 1) {
button { } /* You can't style .container, but can select inside it */
.other-things { }
}
@container style(--variant: 2) {
button { }
.whatever { }
}Code language: CSS (css)
When should you care?
Have you ever wanted a mixin in CSS? As in, you set one property but
get multiple properties. Sass made mixins fairly popular. You can do
that with a Style Container Query. But just like how Sass had
variables then CSS variables turned out to be more powerful and
useful, Style Container Queries are likely to be more powerful and
useful, because they respect the cascade and can be calculated and
such.
Support
Chrome 'n' Friends
Browser Support Safari
Firefox
Progressive Potentially -- It depends on what you're doing with
Enhancement? the styles, but let's say not really.
Polyfillable No
Basic Demo of Usage
CodePen Embed Fallback
Container Units
What are Container Units?
Container Units (literally units, like px, rem, or vw) allow you to
set the size of things based on the current size of a container
element. Similar to how with viewport units 1vw is 1% of the browser
window width, 1cqw is 1% of the width of the container (although I'd
recommend you use cqi instead, the "logical equivalent", meaning the
"inline direction").
The units are cqw ("container query width"), cqh ("container query
height"), cqi ("container query inline"), cqb ("container query
block"), cqmin (smaller of cqi and cqb), and cqmax (larger of cqi and
cqb).
When should you care?
If the sizing of anything in an element feels as if it should be
based on the current size of the container, container units are
essentially the only way. An example of this is typography. A typical
Card element may deserve a larger header text when it happens to be
rendered larger, without something like a class name needing to be
added to control that. (I'm a fan of this demo.) Even a container
query is clunky comparatively.
Support
Browser Full
Support
Progressive Yes -- you could list a declaration using fallback units
Enhancement? right before the declaration using container query
units.
Polyfillable Yes
Basic Demo of Usage
This element uses container query units for not only the font-size,
but the padding, border, and margin as well.
CodePen Embed Fallback
The :has() Pseudo Selector
What is the :has() selector?
The :has() selector allows you to conditionally select an element
when elements deeper in the DOM tree of the original element match
the selector you put inside :has().
figure:has(figcaption) {
border: 1px solid black;
padding: 0.5rem;
}Code language: CSS (css)
When should you care?
If you've ever wanted a "parent" selector in CSS, :has() can do that,
but it's more powerful than that, as once you've selected the parent
you want, you can again drill back down. Jhey Tompkins once called it
a "family selector" which a nice way to think about it. You can also
combine it with :not() to build a selector when an element doesn't
"have" a matching element inside.
Support
Browser Support Full
Progressive Depends on what you're doing with the styles, but
Enhancement? let's say not really.
Polyfillable For the JavaScript side only
Basic Demo of Usage
CodePen Embed Fallback
View Transitions
What are View Transitions?
There are two types of View Transitions:
1. Same-Page Transitions (Require JavaScript)
2. Multi-Page Transitions (CSS Only)
They are both useful. A same-page transition involves and animation
when the DOM is changed without the page changing, like a list being
sorted. A multi-page transition is for animating elements between
page loads, like a video thumbnail transitioning into a video
element. This is the basic syntax for a same-page transition:
if (!document.startViewTransition) {
updateTheDOM();
} else {
document.startViewTransition(() => updateTheDOM());
}Code language: JavaScript (javascript)
For multi-page transitions: you need this meta tag:
Code language: HTML, XML (xml)
Then any element you want to transition between pages you make sure
has a totally unique view-transition-name applied in the styles, on
both the outgoing page and incoming page.
When should you care?
Users can understand an interface better if an element moves to a new
position rather than instantly being there. There is an animation
concept called tweening where the animation is automatically created
based on a starting and ending state. View Transitions are
essentially tweening. You can control aspects of the animation, but
for the most part the animation is automatically created based on the
starting and ending state of the DOM, rather than you having to be
really specific about the animation details.
Support
Chrome 'n' Friends
Browser Support Safari
Firefox
Progressive Yes -- the transitions can just not run, or you could
Enhancement? provide a fallback animation.
Polyfillable No
Basic Demo of Usage
This is an example of a same-page view transition:
CodePen Embed Fallback
Nesting
What is nesting?
Nesting is a way of writing CSS that allow you to write additional
selectors within an existing ruleset.
.card {
padding: 1rem;
> h2:first-child {
margin-block-start: 0;
}
footer {
border-block-start: 1px solid black;
}
}
Code language: CSS (css)
When should you care?
Nesting is mostly a CSS authoring convenience, but the fact that it
can group related CSS nicely together and prevent you from having to
repeat writing a selector can mean avoiding mistakes and making the
CSS easier to read. Nested CSS can also be something of a footgun in
that may encourage writing CSS that matches the nesting of HTML in an
unnecessary way, increasing the specificity and decreasing the
reusability of some CSS.
.card {
container: card / inline-size;
display: grid;
gap: 1rem;
@container (min-inline-size: 250px) {
gap: 2rem;
}
}
Code language: CSS (css)
The only major difference from Sass-style nesting is that you can't
combine the & directly.
.card {
body.home & { /* totally fine */ }
& .footer { /* totally fine, don't even need the & */
&__big { /* nope, can't do this */ }
}Code language: CSS (css)
Support
Browser Support Full
Progressive No
Enhancement?
Polyfillable You could use a processor like LightningCSS,
Sass, Less, etc.
Scroll-Driven Animations
What are Scroll-Driven Animations?
Any animation that is tied to the scrolling of an element (often the
page itself) can now be done in CSS rather than needing to bind DOM
scrolling events in JavaScript. They come in two varieties:
* The scroll progress of the element. (animation-timeline: scroll
())
* An element's current viewable position within the element.
(animation-timeline: view())
When should you care?
Imagine a reading progress indicator bar that fills from 0% to 100%
as the user scrolls down the page. That can be done with an animation
moving the background-position of an element tried to the overall
scroll position of the page. Doing this in CSS instead of JavaScript
is good for performance.
The other major use case covered by scroll-driven animations is to
run an animation as an element enters (or leaves!) the viewport. You
have lots of control over the details, like when the animation starts
and ends based on how visible the element is.
Support
Chrome 'n' Friends
Browser Support Safari
Firefox
Progressive Yes -- these effects tend to be visual flair, not
Enhancement? required functionality.
Polyfillable Yes
Basic Example of Usage
This is the demo from when we looked at image zooming and page
scrolling.
CodePen Embed Fallback
Anchor Positioning
What is Anchor Positioning?
Anchor positioning allows you to place items relative to where
another element is. Seems pretty obvious when put like that, but
that's what it is. You declare an element an anchor and give it a
name, then can position elements to the top/right/bottom/left (or
center, or the logical equivalents) of the anchor.
When should you care?
Once you can use this freely, you'll have to care less about exact
DOM positioning of elements (aside from accessibility concerns). The
way it is now, the element you want to position relative to another
has to be a child element and for there to be a positioning context
to work within. This can dictate where elements go in the DOM,
whether or not that makes sense.
The big use cases are going to be tooltips and custom context menus.
Support
Chrome 'n' Friends
Browser Support Safari
Firefox
Progressive Possibly -- if you can tolerate a totally different
Enhancement? position for elements.
Polyfillable Yes
Basic Example of Usage
At the time I'm publishing this, this only works in Chrome Canary
with the "Experimental Web Platform Features" flag enabled.
CodePen Embed Fallback
Scoping
What is Scoped CSS?
Scoping in CSS is in the form of an @scope at-rule that declares a
block of CSS to only apply to the given selector. And optionally,
stop applying at another given selector.
When should you care?
You can also scope CSS by applying a class and nesting within that
class. But @scope has a few tricks up it's sleeve that can make it
interesting. The "donut scope" option is a unique ability it has:
@scope (.card) to (.markdown-output) {
h2 {
background: tan; /* stop doing this when we get to the Markdown */
}
}Code language: CSS (css)
More logical proximity styling is another useful feature. This is a
bit tricky to explain but once you see it you can't unsee it.
Consider theming. You have a .dark selector and a .light selector. If
you only ever use one on the entire page, that's fine, but if you end
up nesting them at all, because they have the same specificity,
whichever one you define later is technically a bit more powerful,
and can win out even if it doesn't make sense to. Minimal example:
.purple-paragraphs p { color: purple; }
.red-paragraphs p { color: red; }Code language: CSS (css)
Code language: HTML, XML (xml)
You might think the paragraph element in there would have the color
purple, but it will actually be red. That's just awkward, but it can
be fixed with @scope. When scoped selectors match, as Bramus says,
"it weighs both selectors by proximity to their scoping root", and
since "light" is closer here, it would win.
My favorite though is the ability to drop in a
Code language: HTML, XML (xml)
Support
Chrome
Browser Support Safari
Firefox
Progressive Enhancement? No
Polyfillable No
Basic Example of Usage
CodePen Embed Fallback
Cascade Layers
What are Layers?
Cascade Layers in CSS are an extremely powerful syntax that affects
the styling strength of a chunk of styles. You can optionally name
and order layers (if you don't explicitly order them, they order in
source order). Styles in higher layers automatically beat styles in
lower layers, regardless of selector strength. Styles not within
layers are the most powerful.
Code language: HTML, XML (xml)
@layer base {
body#home {
margin: 0;
background: #eee;
}
}
body {
background: white;
}
Code language: CSS (css)
We're used to thinking that body#home is a much more powerful
selector, thus the background will be #eee. But because there are
unlayered styles here, that will win, making the background white.
You may have as many layers as you like and can order them upfront. I
think layering is likely to become a best practice on new greenfield
projects, and take shape something like:
@layer reset, default, themes, patterns, layouts, components, utilities;Code language: CSS (css)
One gotcha is that !important rules on lower layers are actually more
powerful.
When should you care?
One clear way you get a lot of value out of CSS layers if you work on
a project that uses a third-party styling library. You can put that
library on a lower layer than the styles that your team writes, and
you won't have to worry about fighting the third-party library in
terms of selector strength. Your styles on a higher layer will always
win, which is likely to create cleaner and more maintainable CSS.
For example, put all of Bootstrap on a lower layer just using the
layer keyword and then any styles you write after that will win, even
if Bootstrap itself uses a higher power selector.
@import url("https://cdn.com/bootstrap.css") layer;
h5 {
margin-bottom: 2rem;
}Code language: CSS (css)
Support
Browser Support Full
Progressive Enhancement? No
Polyfillable Yes
Basic Example of Usage
CodePen Embed Fallback
Logical Properties
What are Logical Properties?
Logical properties are alternatives to properties that specify a
direction. For example, in a left-to-right language like English, the
inline direction is horizontal and the block direction is vertical,
so margin-right is equivalent to margin-inline-end and margin-top is
equivelant to margin-block-start. In a right-to-left language like
Arabic, margin-inline-end changes to the equivalent of margin-left,
because that is the end side of the inline flow of elements. There
are a lot of CSS properties and values that have a directional
component like this (border, padding, offset, set), so the trick is
understanding inline and block flow and using the correct start or
end value.
When should you care?
Often when you are declaring directional information in CSS, what you
mean is "in the inline direction of text". That might sound strange,
but imagine a button and the space between an icon and the text. If
you apply margin-right to the icon to space it away from the text,
but then the page is translated to a right-to-left language, that
spacing is now on the wrong side of the icon. What you meant was
margin-inline-end on the icon. If you code your side using logical
properties in this way, it will automatically translate better
without writing any additional conditional code.
Support
Browser Full
Support
Progressive You'd have to use @supports and unset to remove the
Enhancement? fallback value and reset using a logical property, but
it's possible.
Polyfillable I can't vouch for it, but there is a processing option.
Basic Example of Usage
CodePen Embed Fallback
P3 Colors
What is the Display P3 Color Space?
We're largely used to the sRGB color space on the web. That's what
hex colors use, and the rgb(), hsl(), and hsb() functions. Many
displays these days are capable of display a much wider range of
colors than sRGB is capable of describing, so being limited to that
color space sucks. The Display P3 color space is about 50% wider than
sRGB, expanding in the direction of more rich and vibrant colors. New
CSS functions, which can even use different color models that have
their own useful properties, allow us to declare colors in this
space.
When should you care?
If you want to use colors that are quite vibrant, you'll need to tap
into colors in the P3 Color Space. Using newer color models (and
functions) can do this, and are very useful for a variety of other
things.
For example, the oklch() function (and thus OKLCH color model) can
display any color any other method can (plus P3), has a similar human
readability in common with hsl(), and has "uniform perceived
brightness", so that the first number (lightness) behaves way more
predictably than it does in hsl(). That's awfully nice for color on
the web. But it's not the only new color model and function! I find
the oklab color model generally best for gradients.
Support
Browser Full (e.g. oklab)
Support
Progressive Yes -- you can declare fallback colors and displays that
Enhancement? can't display the color you declare will come back down
into range.
Polyfillable Yes
Basic Example of Usage
You can edit these