A few days in the past, the Apple crew launched Safari 26.0! Is it an enormous deal? I imply, browsers launch new variations on a regular basis, the place they sprinkle in a pair or few new options. They’re, after all, all helpful, however there aren’t often a whole lot of “huge leaps” between variations. Safari 26 is completely different, although. It introduces a lot of recent stuff. To be exact, it provides: 75 new options, 3 deprecations, and 171 different enhancements.
I’d formally name {that a} huge deal.
The WebKit weblog submit does an incredible job breaking down every of the brand new (not solely CSS) options. However once more, there are such a lot of that the brand new stuff coming to CSS deserves its personal highlight. So, at the moment I wish to examine (and in addition strive) what I feel are essentially the most fascinating options coming to Safari.
In case you are like me and don‘t have macOS to check Safari, you may use Playwright as a substitute.
What’s new (to Safari)?
Safari 26 introduces a number of options chances are you’ll already know from prior Chrome releases. And… I can’t blame Safari for seemingly lagging behind as a result of Chrome is delivery new CSS at a scarily quick tempo. I admire that browsers stagger releases to allow them to refine issues in opposition to one another. Keep in mind when Chrome initially shipped position-area
as inset-area
? We obtained higher naming between the 2 implementations.
I feel what you’ll discover (as I did) that many of those overlapping options are a part of the larger effort in direction of Interop 2025, one thing WebKit is dedicated to. So, let’s look particularly at what’s new in Safari 26… at the least that’s new to Safari.
Anchor positioning
Anchor positioning is one in all my favourite options (I wrote the information on it!), so I’m so glad it’s arrived in Safari. We are actually one step nearer to extensively accessible assist which suggests we’re that a lot nearer to utilizing anchor positioning in our manufacturing work.
With CSS Anchor Positioning, we will connect an absolutely-positioned ingredient (that we might name a “goal”) to a different ingredient (that we might name an “anchor”). This makes creating issues like tooltips, modals, and pop-ups trivial in CSS, though it may be used for a number of layouts.
Utilizing anchor positioning, we will connect any two components, like these, collectively. It doesn’t even matter the place they’re within the markup.
anchor
goal
Heads up: Regardless that the supply order doesn’t matter for positioning, it does for accessibility, so it’s a good suggestion to determine a relationship between the anchor and goal utilizing ARIA attributes for higher experiences that depend on assistive tech.
We register the .anchor
ingredient utilizing the anchor-name
property, which takes a dashed ident. We then use that ident to connect the .goal
to the .anchor
utilizing the position-anchor
property.
.anchor {
anchor-name: --my-anchor; /* the ident */
}
.goal {
place: absolute;
position-anchor: --my-anchor; /* connected! */
}
This positions the .goal
on the middle of the .anchor
— once more, irrespective of the supply order! If we wish to place it someplace else, the best manner is utilizing the position-area
property.
With position-area
, we will outline a area round the .anchor
and place the .goal
in it. Consider it like drawing a grid of squares which might be mapped to the .anchor
‘s middle
, high
, proper
, backside
and left
.

For instance, if we want to place the goal on the anchor’s top-right nook, we will write…
.goal {
/* ... */
position-area: high proper;
}
That is only a style since anchor positioning is a world unto itself. I’d encourage you to learn our full information on it.
Scroll-driven animations
Scroll-driven animations hyperlink CSS animations (created from @keyframes
) to a component’s scroll place. So as a substitute of operating an animation for a given time, the animation will rely on the place the consumer scrolls.
We will hyperlink an animation to 2 varieties of scroll-driven occasions:
- Linking the animation to a scrollable container utilizing the
scroll()
operate. - Linking the animation to a component’s place on the viewport utilizing the
view()
operate.
Each of those capabilities are used contained in the animation-timeline
, which hyperlinks the animation progress to the kind of timeline we’re utilizing, be it scroll or view. What’s the distinction?
With scroll()
, the animation runs because the consumer scrolls the web page. The best instance is a type of studying bars that you just would possibly see develop as you learn down the web page. First, we outline our on a regular basis animation and add it to the bar ingredient:
@keyframes develop {
from {
rework: scaleX(0);
}
to {
rework: scaleX(1);
}
}
.progress {
transform-origin: left middle;
animation: develop linear;
}
Notice: I’m setting transform-origin
to left
so it the animation progresses from the left as a substitute of increasing from the middle.
Then, as a substitute of giving the animation a period, we will plug it into the scroll place like this:
.progress {
/* ... */
animation-timeline: scroll();
}
Assuming you’re utilizing Safari 26 or the most recent model of Chrome, the bar grows in width from left to proper as you scroll down the viewport.
The view()
operate is comparable, however it bases the animation on the ingredient’s place when it’s in view of the viewport. That manner, an animation can begin or cease at particular factors on the web page. Right here’s an instance making photos “pop” up as they enter view.
@keyframes popup {
from {
opacity: 0;
rework: translateY(100px);
}
to {
opacity: 1;
rework: translateY(0px);
}
}
img {
animation: popup linear;
}
Then, to make the animation progress because the ingredient enters the viewport, we plug the animation-timeline
to view()
.
img {
animation: popup linear;
animation-timeline: view();
}
If we go away like this, although, the animation ends simply because the ingredient leaves the display. The consumer doesn’t see the entire thing! What we would like is for the animation to finish when the consumer is in the midst of the viewport so the total timeline runs in view.
That is the place we will attain for the animation-range
property. It lets us set the animation’s begin and finish factors relative to the viewport. On this particular instance, let’s say I need the animation to begin when the ingredient enters the display (i.e., the 0%
mark) and finishes a little bit bit earlier than it reaches the direct middle of the viewport (we’ll say 40%
):
img {
animation: popup linear;
animation-timeline: view();
animation-range: 0% 40%;
}
As soon as once more, scroll-driven animations go manner past these two fundamental examples. For a fast intro to all there may be to them, I like to recommend Geoff’s notes.
I really feel safer utilizing scroll-drive animations in my manufacturing work as a result of it’s extra of a progressive enhancement that received’t break an expertise even when it’s not supported by the browser. Even so, somebody might choose decreased (or no) animation in any respect, which means we’d higher progressively improve it anyway with prefers-reduced-motion
.
progress()
operate
The That is one other characteristic we obtained in Chrome that has made its method to Safari 26. Humorous sufficient, I missed it in Chrome when it launched just a few months in the past, so it makes me twice as glad to see such a useful characteristic baked into two main browsers.
The progress()
operate tells you ways a lot a worth has progressed in a spread between a place to begin and an ending level:
progress(, , )
If the
is lower than the
, the result’s 0
. If the
reaches the
, the result’s 1
. Something in between returns a decimal between 0
and 1
.
Technically, that is one thing we will already do in a calc()
-ulation:
calc((worth - begin) / (finish - begin))
However there’s a key distinction! With progress()
, we will calculate values from combined information sorts (like including px
to rem
), which isn’t presently potential with calc()
. For instance, we will get the progress worth formatted in viewport models from a numeric vary formatted in pixels:
progress(100vw, 400px, 1000px);
…and it’ll return 0
when the viewport is 400px
, and because the display grows to 1000px
, it progresses to 1
. This implies it might probably typecast completely different models right into a quantity, and as a consequence, we will transition properties like opacity
(which takes a quantity or share) primarily based on the viewport (which is a distance size).
There’s one other workaround that accomplishes this utilizing tan()
and atan2()
capabilities. I’ve used that strategy earlier than to create easy viewport transitions. However progress()
enormously simplifies the work, making it rather more maintainable.
Working example: We will orchestrate a number of animations because the display measurement adjustments. This subsequent demo takes one of many demos I made for the article about tan()
and atan2()
, however swaps that out with progress()
. Works like a attraction!
That’s a fairly wild instance. One thing extra sensible is likely to be decreasing a picture’s opacity because the display shrinks:
img {
opacity: clamp(0.25, progress(100vw, 400px, 1000px), 1);
}
Go forward and resize the demo to replace the picture’s opacity, assuming you’re it in Safari 26 or the most recent model of Chrome.
I’ve clamp()
-ed the progress()
between 0.25
and 1
. However, by default, progress()
already clamps the
between 0
and 1
. In line with the WebKit launch notes, the present implementation isn’t clamped by default, however upon testing, it does appear to be. So, in the event you’re questioning why I’m clamping one thing that’s supposedly clamped already, that’s why.
An unclamped model might come sooner or later, although.
Self-alignment in absolute positioning
And, hey, examine this out! We will align-self
and justify-self
content material inside absolutely-positioned components. This isn’t as huge a deal as the opposite options we’ve checked out, however it does have a useful use case.
For instance, I generally wish to place an absolutely-positioned ingredient straight within the middle of the viewport, however inset
-related properties (i.e., high
, proper
, backside
, left
, and so on.) are relative to the ingredient’s top-left nook. Which means we don’t get completely centered with one thing like this as we’d anticipate:
.absolutely-positioned {
place: absolute;
high: 50%;
left: 50%;
}
From right here, we may translate the ingredient by half to get issues completely centered. However now we’ve got the middle
key phrase supported by align-self
and justify-self
, which means fewer transferring items within the code:
.absolutely-positioned {
place: absolute;
justify-self: middle;
}
Weirdly sufficient, I observed that align-self: middle
doesn’t appear to middle the ingredient relative to the viewport, however as a substitute relative to itself. I discovered that may use the anchor-center
worth to middle the ingredient relative to its default anchor, which is the viewport on this particular instance:
.absolutely-positioned {
place: absolute;
align-self: anchor-center;
justify-self: middle;
}
And, after all, place-self
is a shorthand for the align-self
and justify-self
properties, so we may mix these for brevity:
.absolutely-positioned {
place: absolute;
place-self: anchor-center middle;
}
What’s new (for the online)?
Safari 26 isn’t nearly maintaining with Chrome. There’s a whole lot of thrilling new stuff in right here that we’re getting our palms on for the primary time, or that’s refined from different browser implementations. Let’s take a look at these options.
constrast-color()
operate
The The constrast-color()
isn’t new by any means. It’s truly been in Safari Know-how Preview since 2021 the place it was initially referred to as color-contrast()
. In Safari 26, we get the up to date naming in addition to some polish.
Given a sure shade worth, contrast-color()
returns both white
or black
, whichever produces a sharper distinction with that shade. So, if we have been to offer coral
as the colour worth for a background, we will let the browser determine whether or not the textual content shade is extra contrasted with the background as both white
or black
:
h1 {
--bg-color: coral;
background-color: var(--bg-color);
shade: contrast-color(var(--bg-color));
}
Our personal Daniel Schwarz just lately explored the contrast-color()
operate and located it’s truly not that nice at figuring out the most effective distinction between colours:
Undoubtedly, the primary shortcoming is that
contrast-color()
solely resolves to both black or white. In the event you don’t need black or white, nicely… that sucks.
It sucks as a result of there are circumstances the place neither white
nor black
produces sufficient distinction with the offered shade to fulfill WCAG shade distinction tips. There’s an intent to increase contrast-color()
so it might probably return extra shade values, however there nonetheless can be issues about how precisely contrast-color()
arrives on the “finest” shade, since we’d nonetheless must consider the font’s width, measurement, and even household. At all times examine the precise distinction!
So, whereas it’s nice to lastly have constrat-color()
, I do hope we see enhancements added sooner or later.
Fairly textual content wrapping
Safari 26 additionally introduces text-wrap: fairly
, which is fairly (get it?) easy: it makes paragraphs wrap in a prettier manner.
You could do not forget that Chrome shipped this again in 2023. However take discover that there’s a fairly (OK, that’s the final time) huge distinction between the implementations. Chrome solely avoids typographic orphans (brief final strains). Safari does extra to prettify the best way textual content wraps:
- Prevents brief strains. Avoids single phrases on the finish of the paragraph.
- Improves rag. Retains every line comparatively the identical size.
- Reduces hyphenation. When enabled, hyphenation improves rag but in addition breaks phrases aside. On the whole, hyphenation ought to be saved to a minimal.
The WebKit weblog will get into a lot better element in the event you’re interested in what concerns they put into it.

That is just the start!
I feel these are all of the CSS options coming to Safari that it’s best to look out for, however I don’t need you to assume they’re the one options within the launch. As I discussed on the high, we’re speaking about 75 new Net Platform options, together with HDR Pictures, assist for SVG favicons, logical property assist for overflow
properties, margin trimming, and far, rather more. It’s value perusing the full launch notes.