Exploring New CSS Features in Safari 26

A few days ago, Apple launched Safari 26.0! Is it significant? Browsers often update with a few new features, which are useful but rarely groundbreaking. However, Safari 26 is an exception. It introduces a substantial amount of new content: 75 new features, 3 deprecations, and 171 other improvements.

I’d call that a significant update.

The WebKit blog post does an excellent job of detailing each new feature. However, the new CSS features deserve special attention. Today, I want to explore what I find to be the most interesting features coming to Safari.

If you’re like me and don’t have macOS to test Safari, you can use Playwright instead.

What’s new in Safari?

Safari 26 introduces features you might recognize from previous Chrome updates. I don’t blame Safari for seemingly lagging since Chrome releases new CSS rapidly. I appreciate staggered browser releases for refinement. Remember when Chrome launched position-area as inset-area? We got better naming between implementations.

Many overlapping features are part of the bigger Interop 2025 effort, which WebKit is committed to. Let’s see what’s new in Safari 26… at least what’s new to Safari.

Anchor positioning

Anchor positioning is one of my favorite features (I wrote the guide on it!), so I’m thrilled it’s in Safari. We’re closer to widespread support, meaning we’re closer to using anchor positioning in production.

CSS Anchor Positioning allows us to attach an absolutely-positioned element (a “target”) to another element (an “anchor”). This simplifies creating tooltips, modals, and pop-ups in CSS, though it can be used for various layouts.

With anchor positioning, we can attach any two elements together. It doesn’t even matter where they are in the markup.

Heads up: While source order doesn’t matter for positioning, it does for accessibility, so it’s wise to establish a relationship between the anchor and target using ARIA attributes for better experiences with assistive tech.

We register the .anchor element using the anchor-name property, which takes a dashed ident. We then use that ident to attach the .target to the .anchor using the position-anchor property.

This positions the .target at the center of the .anchor — regardless of source order! To position it elsewhere, use the position-area property.

With position-area, we define a region around the .anchor and place the .target in it. Think of it like drawing a grid of squares mapped to the .anchor’s center, top, right, bottom, and left.

For example, to place the target at the anchor’s top-right corner, we write…

This is just a taste; anchor positioning is a world unto itself. I recommend reading our full guide on it.

Scroll-driven animations

Scroll-driven animations link CSS animations (from @keyframes) to an element’s scroll position. Instead of running for a set time, the animation depends on where the user scrolls.

We can link an animation to two types of scroll-driven events:

– Linking the animation to a scrollable container using the scroll() function.
– Linking the animation to an element’s position in the viewport using the view() function.

Both functions are used inside the animation-timeline, linking animation progress to the timeline type, whether scroll or view. What’s the difference?

With scroll(), the animation runs as the user scrolls the page. A simple example is a reading bar that grows as you read down the page. First, we define our animation and add it to the bar element:

Note: I set transform-origin to left so the animation progresses from the left instead of expanding from the center.

Then, instead of giving the animation a duration, we plug it into the scroll position:

Assuming you’re using Safari 26 or the latest Chrome, the bar grows in width from left to right as you scroll.

The view() function is similar but bases the animation on the element’s position when it is in the viewport. This allows an animation to start or stop at specific points on the page. Here’s an example making images “pop” up as they enter view.

Then, to make the animation progress as the element enters the viewport, we plug the animation-timeline to view().

If we leave it like this, though, the animation ends as the element leaves the screen. The user doesn’t see the whole thing! We want the animation to end when the user is in the middle of the viewport so the full timeline runs in view.

This is where we use the animation-range property. It lets us set the animation’s start and end points relative to the viewport. In this example, let’s say I want the animation to start when the element enters the screen (0%) and finishes a little before it reaches the viewport’s center (40%):

Once again, scroll-driven animations go beyond these two examples. For a quick intro to all there is to them,


Discover more from WIREDGORILLA

Subscribe to get the latest posts sent to your email.

Similar Posts