Get up to date on some of the exciting features in modern CSS.
There are a ton of exciting things happening in CSS right now—and many of them are already supported in today's browsers! Our talk at CDS 2019, which you can watch below, covers several new and upcoming features we thought should get some attention.
This post focuses on the features you can use today, so be sure to watch the talk for a deeper discussion of upcoming features like Houdini. You can also find demos for all the features we discuss on our CSS@CDS page.
Contents
- Scroll Snap
:focus-within
- Media Queries Level 5
- Logical properties
position: sticky
backdrop-filter
:is()
gap
- CSS Houdini
- Overflow
Scroll Snap
Scroll Snap lets you define snap points as the user scrolls your content vertically, horizontally, or both. It offers built-in scroll inertia and deceleration, and it's touch enabled.
This sample code sets up horizontal scrolling in a <section>
element with snap points aligned to the left sides of child <picture>
elements:
section {
overflow-x: auto;
overscroll-behavior-x: contain;
scroll-snap-type: x mandatory;
}
section > picture {
scroll-snap-align: start;
}
Here's how it works:
- On the parent
<section>
element,overflow-x
is set toauto
to allow horizontal scrolling.overscroll-behavior-x
is set tocontain
to prevent any parent elements from scrolling when the user reaches the boundaries of the<section>
element's scroll area. (This isn't strictly necessary for snapping, but it's usually a good idea.)scroll-snap-type
is set tox
—for horizontal snapping—andmandatory
—to ensure that the viewport always snaps to the closest snap point.
- On the child
<picture>
elements,scroll-snap-align
is set to start, which sets the snap points on the left side of each picture (assumingdirection
is set toltr
).
And here's a live demo:
You can also take a look at demos for vertical scroll snap and matrix scroll snap.
:focus-within
:focus-within
addresses a long-standing accessibility issue: there are many cases when focusing a child element should affect the presentation of a parent element so that the UI is accessible to users of assistive technologies.
For example, if you have a dropdown menu with several items, the menu should remain visible while any of the items has focus. Otherwise, the menu disappears for keyboard users.
:focus-within
tells the browser to apply a style when focus is on any child element of a specified element. Returning to the menu example, by setting :focus-within
on the menu element, you can make sure it stays visible when a menu item has focus:
.menu:focus-within {
display: block;
opacity: 1;
visibility: visible;
}
Try tabbing through the focusable elements in the demo below. You'll notice that the menus remain visible as you focus on the menu items:
Media Queries Level 5
New media queries give us powerful ways to adjust the user experience of our apps based on a user's device preferences. Basically, the browser serves as a proxy for system-level preferences that we can respond to in our CSS using the prefers-*
group of media queries:
Here are the new queries we think developers will be most excited about:
- prefers-reduced-motion
- prefers-color-scheme
- prefers-contrast
- prefers-reduced-transparency
- forced-colors
- inverted-colors
These queries are a huge win for accessibility. Previously we had no way to know, for example, that a user had set their OS to high-contrast mode. If you wanted to provide a high-contrast mode for a web app that remained true to your brand, you had to ask users to choose it from UI within your app. Now you can detect the high-contrast setting from the OS using prefers-contrast
.
One exciting implication of these media queries is that we can design for multiple combinations of system-level user preferences to accommodate the wide range of user preferences and accessibility needs. If a user wants high-contrast dark mode when in dimly lit environments, you can do that!
It's important to Adam that "prefers reduced motion" doesn't get implemented as "no motion." The user is saying they prefer less motion, not that they don't want any animation. He asserts reduced motion is not no motion. Here's an example that uses a crossfade animation when the user prefers reduced motion:
Logical properties
Logical properties solve a problem that has gained visibility as more developers tackle internationalization. Many layout properties like margin
and padding
assume a language that is read top-to-bottom and left-to-right.
When designing pages for multiple languages with varying writing modes, developers have had to adjust all those properties individually across multiple elements, which quickly becomes a maintainability nightmare.
Logical properties let you maintain layout integrity across translations and writing modes. They dynamically update based on the semantic ordering of content rather than its spatial arrangement. With logical properties, each element has two dimensions:
- The block dimension is perpendicular to the flow of text in a line. (In English,
block-size
is the same asheight
.) - The inline dimension is parallel to the flow of text in a line. (In English,
inline-size
is the same aswidth
.)
These dimension names apply to all logical layout properties. So, for example, in English, block-start
is the same as top
, and inline-end
is the same as right
.
With logical properties, you can automatically update your layout for other languages by simply changing the writing-mode
and direction
properties for your page rather than updating dozens of layout properties on individual elements.
You can see how logical properties work in the demo below by setting the writing-mode
property on the <body>
element to different values:
position: sticky
An element with position: sticky
remains in block flow until it starts to go offscreen,
at which point it stops scrolling with the rest of the page
and sticks to the position specified by the element's top
value.
The space allocated for that element remains in the flow,
and the element returns to it when the user scrolls back up.
Sticky positioning lets you create many useful effects that previously required JavaScript. To show some of the possibilities, we've created several demos. Each demo uses largely the same CSS and only slightly adjusts the HTML markup to create each effect.
Sticky Stack
In this demo, all sticky elements share the same container. That means that each sticky element slides over the previous one as the user scrolls down. The sticky elements share the same stuck position.
Sticky Slide
Here, the sticky elements are cousins. (That is, their parents are siblings.) When a sticky element hits the lower boundary of its container, it moves up with the container, creating the impression that lower sticky elements are pushing up higher ones. In other words, they appear to compete for the stuck position.
Sticky Desperado
Like Sticky Slide, the sticky elements in this demo are cousins. However, they've been placed in containers set to a two-column grid layout.
backdrop-filter
The backdrop-filter
property lets you apply graphical effects to the area behind an element rather than to the element itself. This makes lots of cool effects that were previously only achievable using complicated CSS and JavaScript hacks doable with one line of CSS.
For example, this demo uses backdrop-filter
to achieve OS-style blurring:
We already have a great post about backdrop-filter
, so head there for more info.
:is()
While the :is()
pseudo-class is actually over ten years old, it still doesn't see as much use as we think it deserves. It takes a comma-separated list of selectors as its argument and matches any selectors in that list. That flexibility makes it incredibly handy and can significantly reduce the amount of CSS you ship.
Here's a quick example:
button.focus,
button:focus {
…
}
article > h1,
article > h2,
article > h3,
article > h4,
article > h5,
article > h6 {
…
}
/* selects the same elements as the code above */
button:is(.focus, :focus) {
…
}
article > :is(h1,h2,h3,h4,h5,h6) {
…
}
gap
CSS grid layout has had gap
(previously grid-gap
) for some time. By specifying the internal spacing of a containing element rather than the spacing around child elements, gap
solves many common layout issues. For example, with gap, you don't have to worry about margins on child elements causing unwanted whitespace around the edges of a containing element:
Even better news: gap
is coming to flexbox, bringing all the same spacing perks that grid has:
- There's one spacing declaration rather than many.
- There's no need to establish conventions for your project about which child elements should own spacing—the containing element owns the spacing instead.
- The code is more easily understandable than older strategies like the lobotomized owl.
The following video shows the benefits of using a single gap
property for two elements, one with a grid layout and one with a flex layout:
Right now, only FireFox supports gap
in flex layouts, but play around with this demo to see how it works:
CSS Houdini
Houdini is a set of low-level APIs for the browser's rendering engine that lets you tell the browser how to interpret custom CSS. In other words, it gives you access to the CSS Object Model, letting you extend CSS via JavaScript. This has several benefits:
- It gives you much more power to create custom CSS features.
- It's easier to separate rendering concerns from application logic.
- It's more performant than the CSS polyfilling we currently do with JavaScript since the browser will no longer have to parse scripts and do a second rendering cycle; Houdini code is parsed in the first rendering cycle.
Houdini is an umbrella name for several APIs. If you want more information about them and their current status, take a look at Is Houdini Ready Yet? In our talk, we covered the Properties and Values API, the Paint API, and the Animation Worklet because they're currently the most supported. We could easily dedicate a full post to each of these exciting APIs, but, for now, check out our talk for an overview and some cool demos that start to give a sense of what you can do with the APIs.
Overflow
There are a few more things on the horizon that we wanted to discuss but didn't have time to cover in depth, so we ran through them in a speed round.⚡ If you haven't heard of some of these features yet, be sure to watch the last part of the talk!
size
: a property that will allow you to set height and width at the same timeaspect-ratio
: a property that sets an aspect ratio for elements that don't have one intrinsicallymin()
,max()
, andclamp()
: functions that will let you set numeric constraints on any CSS property, not just width and heightlist-style-type
an existing property, but it will soon support a wider range of values, including emoji and SVGsdisplay: outer inner
: Thedisplay
property will soon accept two parameters, which will let you explicitly specify its outer and inner layouts rather than using compound keywords likeinline-flex
.- CSS regions: will let you fill a specified, non-rectangular area that content can flow into and out of
- CSS modules: JavaScript will be able to request a CSS module and get a rich object back that's easy to perform operations on