Streaming is available in most browsers,
and in the Developer app.
-
What’s new in Safari and WebKit
Learn how the latest web technologies in Safari and WebKit can help you create incredible experiences. We'll highlight different CSS features and how they work, including scroll driven animation, cross document view transitions, and anchor positioning. We'll also explore new media support across audio, video, images, and icons.
Chapters
- 0:00 - Introduction
- 1:46 - Animation
- 19:01 - Layout
- 29:05 - Visual effects
- 38:22 - Media
Resources
- Can I use
- Safari Technology Preview
- Submit feedback
- Web Speech API - Web APIs | MDN
- WebKit Open Source Project
- WebKit.org – Bug tracking for WebKit open source project
Related Videos
WWDC25
-
Search this video…
Hi, I’m Saron Yitbarek, Web Technologies Evangelist on the Safari & WebKit team. Since last year’s WWDC, we’ve worked to bring you new web technologies, some that you’ve requested and others that will delight you. We care deeply about you and the web, and we want to empower you as web designers and developers, to make it easier to bring your ideas to life. Our goal is simple — we want to help you create a reliable, compatible and privacy-first experience for your users. And that’s why, today — I want to announce the new web technology coming in Safari this fall. There are a lot of great features in this release. We put a ton of time and attention into improving interoperability and filling the gaps in the latest features. In this session, I’ll teach you a few of my favorite features and show you some of the areas where we’ve filled in those gaps.
The features I’ll cover fall into four categories. In animation, I’ll show you how to use Scroll-driven Animations and Cross Document View Transitions.
Layout is all about the powerful world of anchor positioning.
In visual effects, we’ll cover background-clip, the new shape function, and text-wrap: pretty.
And I’ll follow all that CSS with media, where I’ll walk through SVG icon support, images, and improvements to existing media formats. Let’s start with animation.
Animation helps enhance the online experiences you create, adding some excitement and a touch of whimsy to the page. I’ll take you through an example to show you what I mean.
Let’s say I’m redesigning the website for my online education company, the A-School of Code. I want it to be practical with little moments of fun.
With the latest features, I can create exactly that using just CSS. Let me show you what my final site might look like.
Here, I’ve got the site for an online school that promises to teach you technical skills. I show the list of topics we cover in this nice grid, you can see the names of companies that hired my amazing students, read reviews from past students who loved the courses and curriculum, and finally, you see two options of what kind of program you want to learn more about. There are a few things that elevate this site.
One that probably caught your eye is the animation.
That’s brought to you by — Scroll-driven animations, now available in Safari 19.
CSS animations have been a part of the web since Apple first introduced it in WebKit in 2007. But linking that animation to user behavior has always required JavaScript. JavaScript is a powerful language that does so much for the web, but whenever you can get away with not using it, your users will get better performance and battery life. Scroll-driven animation allows you to skip JavaScript and link your animation to your scroll using just CSS. Let’s walk through how it all works. To understand scroll-driven animations, there’s a key concept you have to know first: Timelines.
The default timeline for the web is time based.
That means an animation plays as time goes by.
As the seconds increase, the animation of my progress bar moves along with it.
But with scroll driven animation, you get two new timelines. The first is the scroll timeline.
Now, in my website, I used the scroll() timeline to create the progress bar at the bottom of our page. This is a contrived example. In reality, you probably wouldn’t need a progress bar like this because you already have scroll bars. But this progress bar is an easy way of showing scroll-driven animation in action, so let’s look at how we built it. Before I start working on my bar, I need to consider something important — accessibility. Scroll-driven animation introduces motion to my website and some users may prefer less motion, which they can indicate in the accessibility settings. I want to respect that so that I don’t accidentally cause motion discomfort.
Experiencing motion discomfort is common and driven by a mix of factors, like experience, genetics, fatigue, and, in some cases, vestibular disorders. Symptoms can include nausea, dizziness, headaches, and other physical discomfort.
As developers, there are different types of motion that we add to our websites and web apps that might, unintentionally, trigger motion discomfort: Scaling large objects, zooming in or out, objects moving at different speeds like a parallax effect, techniques that simulate three-dimensional effects, or peripheral motion, like a constant movement outside the user’s intended area of focus.
This list doesn’t cover everything, but it’s a good starting point of things to consider. Does your site have these effects? In my case, my animation is a pretty simple progress bar, a kind of movement that’s fairly safe, small, and common on the web. So I don’t think I need to worry about triggering motion discomfort with this particular animation.
But anytime we add movement to the page, it’s a great opportunity to pause, and consider its impact on varying needs of people.
Let’s get to our scrollbar.
I’ll start with some CSS.
I’m going to add the scrollbar as a pseudo element in my footer.
I’ll give it some styling so it starts in my bottom left corner and stays at the bottom as I scroll.
Next I’m going to create the animation with keyframes using the CSS we already know and love.
This tells my browser to scale my progress bar so it increases on my X axis, creating the effect of a progress bar that grows from left to right.
So far, I’m laying the foundation for my scroll driven animation, and I need to do one more thing to complete that foundation.
Add the animation to my progress bar.
I can do that here. Now comes the scroll part.
I want the progress bar to start growing from left to right only as I scroll.
To do that, I need to give my progress bar a new animation timeline.
I’m going to add animation-timeline: scroll(); to my CSS, replacing my default timeline. Now keep in mind that to make this work, the animation-timeline has to come after the animation property.
And just like that, I connected my animation to my scroll timeline and created a progress bar, all with a few lines of CSS. No JavaScript needed. Let’s get to a more practical example, where I want an animation that’s not just based on scrolling, but based on when the elements appear on the page. Take this example. I see the blocks turning to create a grid.
It happens when I scroll, but there’s also another factor at play.
The animation only happens within that blue box. It starts at the bottom, only when the blocks have entered the viewport, and the blocks stop turning halfway across the screen, at the top of my blue box. Taking the viewport into account requires a different kind of timeline — The view timeline.
Let’s walk through some code to see how it works.
I’ll start with some html.
Here, I have my list of topics with some classes I’ll use in my CSS. Next, I’ll add some basic CSS styling to turn my list into yellow boxes that match my website’s theme. Now that I’ve got my foundation, I can start on my scroll-driven animation.
The first part is figuring out what animation I want to attach to a scroll. For my website, I have three animations happening at once, and I need to apply these animations to each block. The first makes the boxes come up and assemble from the left.
The second makes the boxes come up and assemble from the center.
And the third makes the blocks come up from the right.
The code so far isn’t anything new just yet. I’m still using classic CSS animation.
Now, I’m going to bring in the scroll-driven part of scroll-driven animations.
But first, I’m going to pause and think about the accessibility of this animation.
If I think back to the common triggers, spinning is one of them.
But that’s referring more to an environmental effect that might cause disorientation. Because this is just a few elements and it’s a relatively small animation, I don’t think this is at risk of being inaccessible, so I can get back to my code.
Next, I’m going to add my animation code to my CSS, applying the left animation to my left boxes, the center animation to my middle boxes, and the right animation to my right boxes. Because I want the elements to retain their animation styles before and after, I’ll also add animation-fill-mode: both.
And now, I finally get to the scroll-driven part of the code. There are two lines of code that I need to make this work. The first is my new view timeline. I’ll add that here. And the second is what’s called the animation range.
The animation range tells my browser where to start and end the animation based on its timeline. I’ve chosen the view timeline, so my range is going to reflect where my elements are in the viewport. The default value goes from 0 - 100%.
That sets the start of the animation at 0%, the moment where the element first enters the viewport, and ends the animation at 100%, which is when the element fully exits the viewport.
Let’s see what my animation looks like if I use these default values. That’s pretty close to what I want, but it’s not quite right. When I’m using scroll-driven animation, especially for a website like this where I’m displaying information, it’s important to remember the user experience. Animations are fun and can make my site stand out, but the information still has to be readable.
Animations shouldn’t get in the way of functionality.
In this case, what that means is being mindful of how long my animation lasts.
I don’t want it to last the full 100%, because that means the blocks are in motion the whole time, making them hard to read.
Instead, I want a short animation, something that catches the eyes, then I want the blocks to find their place so the user can read them. Halfway through the page sounds like a good place to try. That feels like it should give me some fun while still giving my users plenty of time to read. So I’m going to change my range to 0% and 50%.
That means the animation will be over when the elements are halfway through the page, limiting how long they’re in motion. And if I scroll back up, the animation is shown in reverse, with the boxes starting to fall apart at the 50% point and fully dissolving at 0%.
There’s more you can control with your range, like where exactly 0% is. To see what your options are and how they work, check out the different values, play around and explore. These new timelines are powerful, and combined with animation can create incredible experiences for your users. And that’s scroll-driven animation in WebKit for Safari.
The next animation that I’m excited to show you takes over the entire page.
They’re cross-document view transitions, shipped last December in Safari 18.2. They’re an extension of View transitions, introduced earlier in Safari 18.0.
Cross-document view transitions allow you to create smooth transitions between pages — going from something like this, to something more seamless like this cross fade, without needing JavaScript.
All you need for this effect is one line of CSS.
When you click on that nav item without a transition, the browser is repainting the page — wiping it away and loading a new one. What’s beautiful about transitions is that, because it’s taking snapshots before and after the page change and creating a transition between them, you get this nice, smooth experience. I love how that looks. Here’s how it works.
In your CSS file, just add the @view-transition at-rule and set the navigation property to auto.
And … that’s it! But before we ship, we need to do one more thing. Since this is adding movement to our page, we need to pause and consider if we have any accessibility concerns.
While cross fades are an animation, they’re pretty subtle and actually considered one of the safer animations for people who require reduced motion. So I’m good to go and free to ship.
Now, instead of the default cross-fade, let’s say I want to do a slide. When I click an item in my nav, I want my current page to slide out and my new page to slide in. I’m going to show you what I mean, but I want to warn you that if you’re sensitive to motion, this might give you some discomfort. I’ll let you know when it’s over.
Here I’m creating a slide effect and I like how that looks — it’s a step up from my cross fade. But first, let’s pause, and think about its impact on my users. The animation has concluded.
Is this slide effect the kind of animation that might potentially trigger motion discomfort? As far as animations go, it is a pretty big one. It’s not just moving a word or an element, it moves two whole pages. Since I’m not confident this animation is safe, and because it’s only an enhancement and it’s not core to the user experience, it’s best to put it in a reduced motion media query. Let’s do that now.
Here’s my media query that tells the browser only to run my sliding transition if no reduced motion is preferred. And now, I need to code my slide effect.
Here, I’m using CSS animations to create keyframes that describe what I want the two pages to do. I want one page to leave, sliding out, and the other page to enter, sliding in.
Next, to use these animations, I need something to target. By default, navigation: auto transitioned everything in the page. But I don’t actually want everything on my page to transition. I want that nav bar to stay put. And now that I think about it, I also want that footer to stay put too. So I’m going to wrap everything that I do want to transition in an id, one on each page.
I’m going to call it “school-info.” Now I need to declare a view-transition-name that I’ll use later.
When we implement view transitions, we also get a bunch of pseudo-elements. I’m going to use two of them.
View-transition-old and view-transition-new.
The view-transition-old represents a snapshot of our site just before the transition happens, and view-transition-new represents a snapshot after the transition happens. But to use them, I need to pass in an argument. This is where I use my “main-body” view-transition-name and pass it in to both since that encompasses the part of the page I want to transition.
Then, I’ll set my animation-name to the name of my keyframes. And that’s it. Let’s see how that looks. You might want to look away for a second if you’re sensitive to motion.
Great. Our nav stays still as the rest of the page transitions on the click.
Animation over.
The thing I really like about cross-document view transitions is that they’re an enhancement. They’re not required and they don’t change the functionality of my website. It’s a nice addition if it’s supported by the browser and wanted by your user, and totally fine if it isn’t.
When implementing cross document view transitions in your app or website, keep in mind that the pages you’re transitioning between need to be from the same origin. So going from example.com to example.com/cohorts will work just fine. But going from example.com to a different subdomain will not.
That helps ensure the safety and privacy of the user. You don’t want a potentially malicious page to manipulate animations targeting your website.
Next, let’s look at what’s new in layout, with — Anchor positioning, new to Safari, coming this fall.
Anchor positioning, especially when building off of the existing popover API, is a CSS module that makes it easier than ever to create tooltips and position menus exactly where you want them, and that respond appropriately to changes in the viewport.
Let’s take a look.
I want a smooth nav experience for my users. When they sign in, I want them to see their profile picture in the nav. When they click on their photo, a menu should appear, a feature commonly seen in web apps. I’ll start building this with some html.
Here’s my html for the nav bar with the profile picture. And here’s the menu that I want to show when I click on the picture. I’ll add some styling and see how that looks. Let’s get the rest of the page out of the way. Ok, we’re making progress— I’ve got a nice nav and a good looking menu. But, of course, I don’t want my menu to always be visible — I want the menu to pop up when I click the profile icon and then disappear when I click it again.
And when I click on it, I don’t want it to appear in that left corner, I want it to be anchored to my profile pic.
To solve this, before I can get into anchor positioning, I first need to use the popover API.
I’ll start by adding the popover attribute to the element that I want to popover, in this case, my profile menu. Then I’ll give the popover an ID.
Next, I need something I can click on, so I'm going to wrap my image with a button. When I do this, I need to make sure this button and its menu are accessible to those using assistive technologies.
So I’m going to add the aria-haspopup attribute to tell screen readers that, when clicked, this button will show the user a menu.
And finally, I need to add a `popovertarget` attribute set to the same value as the popover element’s ID. Great! Now, when I click on my profile picture, my popover appears, and when I click off of it, it disappears. But it’s showing up in the left corner of my page and I want it anchored to the profile picture, lined up just below. This is where I’ll bring in anchor positioning.
Anchor positioning allows you to anchor one element to another and position that element based on where that anchor is. Here’s how it works. I’ll start by deciding on my anchor and giving it a name through the anchor-name property. In this case, my anchor is my profile button, meaning I want to position the menu based on where the button is. So I’m going to name my anchor “profile-button.” The anchor-name is an arbitrary user-defined string, so it must start with two dashes.
Now, I need to go to my menu and connect it to the anchor I just named.
In the context of Anchor Positioning, my menu is known as my target and I need to give my target some information.
First I’m going to connect it to my anchor by setting the position-anchor` property to the name of my anchor, which is “—profile-button”. So I’ll write “—profile-button” here.
And finally, I have to tell the menu where it’s going to be positioned. There are two ways to do this.
The first is — Position area.
Let’s explore what this is.
My anchor sits in the middle of a grid with nine squares. I’ve got three columns: left, center, and right. And I’ve got three rows: top, center, and bottom. If I want to place my target in the top right corner of my anchor I can set our position-area to “top right,” giving me an intuitive way to describe where I want my menu in relation to my anchor. And if I want my menu below the profile picture— It’s at the bottom center of my grid, so that’s what we’ll write — bottom center.
That will look like this.
Hm, that’s pretty close, but it’s not exactly what I’m looking for. It’s at the bottom like I wanted, but because my menu is wider than my profile button, it doesn’t fit on my grid. What I actually want is to line up the left side of the menu with the left side of my profile picture. For that we’re going to use a different value for position-area: bottom span-right.
That’s going to tell my menu to start at exactly where that center grid starts but to spill over to the right.
And just like that, my menu and profile picture are nice and lined up.
This looks great at this screen size, but what happens on different devices, when the viewport is more narrow? To find out — I can go into Responsive Design Mode in Safari.
I can turn that on by going to my Settings, then Advanced, then checking “Show features for web developers.” That’ll get you a “Develop” option and here, you’ll find that we brought something back. It’s a requested feature that developers wanted — viewport presets. Now, you can select from a number of thoughtfully selected viewport sizes to speed up your testing and development.
I love that I can rotate the viewport preset with just a toggle, making it easy to test for portrait and landscape modes in just one click.
I want my menu to respond to changes in the viewport width without needing JavaScript. My menu right now is aligned to the left. When the width gets more narrow, I want it to move over and align to the right side of my profile button. The magic of anchor positioning is that it was designed to handle exactly this situation with ease. It uses a property called “position try,” and it does pretty much what it says. It lets you set positions to try if it doesn’t have space for the first one.
Because I want my menu to go from spanning right to, instead, spilling over to the left, I can set my position-try to “bottom span-left”.
But position try also allows for a different kind of value. Instead of explicitly stating where I want my menu to go, I can instead set a value that’s relative to my position-area.
One of those values is— “flip-inline”.
Flip inline says, whatever position the element was in originally, just flip it in the inline direction. You can also flip in the block direction with flip-block. It’s a more intuitive way to describe what you want to happen. Let’s see it in action. If I go back to my responsive mode, I can see that on the 13-inch iPad, my menu is aligned on the left.
But when I change orientation, position-try kicks in and the menu is aligned on the right, giving me a responsive menu, exactly like I want.
I’m proud that my colleagues here on the WebKit team proposed position area and worked with the standards bodies to bring you an intuitive way to work with anchor positioning. But there’s another approach to anchor positioning I’d like to show you, called — the anchor() function and it’s a pretty powerful tool.
With the anchor() function, instead of placing your element on a grid, you’re instead lining up the sides of your element to the sides of your anchor.
To start, first, I need to make sure I set the position to absolute.
Next, I’ll look at the top side of my menu. I want to line it up with the bottom of my anchor so I’ll set it to anchor(bottom).
Then, I’ll look at the left side of my menu.
I’m going to set that value to anchor(left) so it lines up with the left side of my anchor.
That was a pretty simple example, but what if I want to line up the menu with the photo instead, like this? I need to account for that padding. I could use position-area and add a margin-left, or I can use the anchor function and add the calc() function, like this.
Here, I’m combining my anchor function with the calc function and the em unit, and that’ll slide my menu on over.
In most use cases, position-area is a great, intuitive way to approach positioning, and when you’re doing something more complex, like animating from one position to another or using multiple anchors, reach for the anchor() function. No matter which you go with, anchor positioning makes it easy to create responsive, relative positioning with just CSS.
And finally for our CSS features, we’ll explore a few new ways to create great visual effects. The first takes borders to the next level. Almost 15 years ago— Apple shipped background-clip: text; This allows you to take text and, instead of filling it with a color, you can fill it with a gradient or background image.
Like this yellow to orange gradient in my logo. My logo’s pretty simple. It’s just words in a basic font, so instead of using an image, I’m just going to use text. That way I can style it with CSS.
My logo is an h1 element and I’ve styled my h1s as white, so I’m starting with white text. To add this gradient effect to my text, I first set my background-image to that subtle yellow to orange gradient that goes to the bottom right.
And I get this gradient behind my text.
Next is my background-clip property, which I’ll set to text. That gets me… back to white text. Hm, not what I want. There’s one more thing I need to do.
I need to override my h1 styling and make the color of the logo transparent, so it can get out of the way, and when I do, my gradient shines through.
But it’s not just gradients you can use for text — you can use images, too. Like this background image of autumn leaves. Background-clip: text introduced new visual capabilities to text and, now, we’re doing the same thing to borders. Let me show you how it works.
My buttons are styled with a thick white border, but for my primary button, I want to do something different.
I want to use my new background-clip value to add some brightness to it and give my border a gradient.
I’ll start by adding that subtle yellow to orange gradient I was working with as my background-image and I get a gradient in the background of my button.
Next, I’ll add my new background-clip with the value border-area and, I’m back to a black background and a white border. I’ve run into the same issue I did with the text — the color of the border did change, but it’s hidden behind the white border of my button’s styling.
I need to make my border color transparent so the gradient shines through.
I’ll add that here, and see what I get… Interesting. I got my gradient, but I have a new problem.
It looks like the gradient is repeating. It takes up the width of my button, but it only goes up to the inside of my borders, then restarts the gradient at the edges. To fix this, I need to extend the background all the way to the very outer edges of the border. I can do that by specifying background-origin and setting it to border-box. That looks great.
You can also use a short-hand that doesn’t require you to declare the background image. And there it is, a nice fun gradient using my new background clip, border area value. There’s a lot you can do with just a border. Between selecting images and picking exciting gradients, you can make things like this progress circle, this warning sign, And this beautiful double border photograph. And that’s background-clip: border-area, more options to help you beautify your content and make your websites and web apps stand out.
For demos of those examples and more details on background-clip, check out our blog post at webkit.org.
Our improvements to visual effects don’t just stop at border, they include the ever so versatile shapes we can create in CSS. Next up, I’m excited to tell you all about the shape() function, now supported in Safari 18.4.
Shapes are used a number of different ways in our apps and websites, and I use them for my A School of Code website.
In my testimonials section, I like having these curvy arrows as the background for all of my reviews. They’re CSS shapes set to my clip-path as a fun aesthetic element to my page.
Before the shape() function, I would use path to make this happen. The path function is a powerful tool, it’s versatile, allowing you to use all kinds of points and curves to draw a wide range of shapes. But what happens to my shape when my viewport width changes? To illustrate, I’m going to isolate the three arrows and show you how they respond.
As I resize, you’ll notice that both my arrow tip and my curve get cut off. They’re not resizing with my viewport. I want something more responsive. But here’s the thing, I don’t want every line and every curve in my shape to change with my viewport. I just want some parts to change. I want the curve to keep its shape, I want the arrow tip to maintain its angle, but I want length and height to scale based on viewport.
If I instead use my shape() function, I’ll see that as the window of my demo gets smaller, the width of my shapes also get smaller, which is what I want.
But you’ll notice that the angle of the point and shape of the curve hold steady, remaining unchanged as things get more narrow. This is what I love about the shape() function it gives you this kind of granular control, allowing you to pick and choose what stays static and what responds to changes.
Let me show you what that code looks like.
You’ll notice in my code that I’m using different units, like container query height and percentages. That’s because shape() can take all CSS units. I’m even using the calc function. Being able to create responsive values and use a wide range of units allows me to selectively control different parts of my shape, giving me the exact responsiveness I’m looking for.
With support of the shape() function, we can create flexible, more responsive shapes. But it’s not just shapes that we’re making easier to work with. We’ve got some great improvements to text too, with new typography features like — text-wrap: pretty, available on Safari 19.
If you look at this body of text, it’s ok but there are a few things about it that make it a little harder to read and less pleasant to look at.
The first is those short lines with just one word at the end of their paragraphs. It’s visually distracting and makes the space between the paragraphs look bigger than they are.
The second is the hyphenation.
There’s nothing wrong with hyphenation per se, but we want to use it sparingly. We don’t want three hyphenated sentences in a row.
The third is what we call rag, the overall shape that the lines of the paragraph make. Good rag is made up of lines that are roughly the same length. Bad rag, like this one, is jagged and visually stands out.
The effects of text-wrap: pretty are subtle but intentional.
Here, it got rid of the one-word lines, the hyphenations — and the bad rag.
Here’s how it works.
Without text-wrap: pretty, the browser’s job is to maximize every line, using up all the space until the very end.
But when you add text wrap pretty, you’re telling the browser to aim for a different area, somewhere near that green line — that’s our target, our ideal line length.
But we have some wiggle room, represented by the area between my purple and red lines. Anything in there is fair game.
It will also move around words to ensure the last line is not extra short and tries not to hyphenate, choosing to move the word to the next line instead. The code is simple.
Apply text-wrap pretty to any element you want: paragraphs, headlines, and more, especially if you see some short last lines, a bunch of hyphenation or some bad rag, and see how it impacts your text.
The great thing about text-wrap pretty is that it’s also an enhancement. Add it to your text, and if the browser supports it, it’ll make your text more pleasing. And if your user’s browser doesn’t support it, or if it decides to only adjust the last few lines and nothing else, the user still has a good experience. No harm, no foul.
We went through quite a few CSS features, mostly to do with styling. Now, let’s look at media.
The first bit of media I’ll share with you is small but mighty. We’ve heard your feedback, and we’re excited to bring you, SVG icons, coming to Safari this fall.
SVG icons do more than just serve as favicons. They can be seen in bookmarklets, everywhere on the Safari start page, when the user adds to dock and more.
Using SVG as a favicon for modern browsers allows Safari to generate icons best suited for the context of that icon. The file size is also usually smaller than the pngs commonly used in favicons.
There’s another type of static media that I’m excited to announce.
Coming today to WebKit and Safari is — HDR images. HDR, or high dynamic range, makes your media richer and more vibrant than the typical SDR photos and videos we’re used to seeing on the web.
We’ve had support for HDR videos for the last five years, since Safari 14.0, and now we’re adding on images. To illustrate the differences between HDR and SDR, let me show you a simulation of the two.
My image on the left is an SDR, or standard dynamic range image and it can come in many different file formats. I cannot show you what HDR looks like through this video, so I created this image on the right that simulates some of the differences. You’ll notice the deeper hues, the wider range, and the brighter colors, of the simulated HDR.
Seeing these differences on a true HDR image is stunning.
There a few technical reasons for those differences, one of them being the amount of data held by each image. SDR is 8-bit while HDR is 10 to 16. Those extra bits give HDR more image data to display. SDR also generally lives in the sRGB colorspace while HDR uses wider and richer colorspaces like P3. And the file formats are different as well. SDR is available in a range of formats including JPEG, while HDR is available in those same formats in addition to HEIC and AVIF.
But displaying HDR in the real world requires some thought and intention. After all, most media online probably won’t be HDR for awhile. So we need to consider how to display HDR and SDR images side by side, and what that will look like.
Because of the dynamic range, HDR images tend to look much brighter than their SDR counterparts. You see how that bluebird stands out? In the context of search result images or a gallery, this can be distracting to the user and create a poor experience. As the developer, we want to give you more control over how to manage this discrepancy. To do that, there’s a CSS property, called: dynamic-range-limit.
The default value for dynamic-range-limit is `no-limit`. It lets HDR images and videos look just as they are, even when they stand out far brighter than the other images. Or you can use CSS dynamic-range-limit: standard to tell the browser to render any HDR images or videos as if they were SDR. A third option is `dynamic-range-limit: constrained.’ It asks the browser to use the extra dynamic range of the HDR image to make it look fantastic, but to do so in a fashion that it doesn’t stand out. This allows a mix of SDR and HDR content to be comfortably viewed together.
The `constrained` value isn’t supported yet in the first beta of Safari 19, but stay tuned.
And if you use an HDR image and the browser doesn’t support it, that's ok. The browser will simply map the HDR contents to the SDR range, so no fallbacks are needed. You can use the best image available and the browser will take care of the rest.
That means that, now, you can bring more dynamic photos and videos into your websites and web apps, creating richer, more beautiful visuals for your users. Other kinds of media, like audio and video, can help bring your websites and web apps to life, and we’ve made progress on supporting even more media formats.
Over the last few years, we’ve filled in a lot of the pieces to more fully support a wide range of codecs and containers. We were the first to support JPEG XL and HEIC on the web, and in Safari 19, we’ve added Ogg Opus and Ogg Vorbis to the list of media we support.
With support of 15 formats, we give you even more options on what you can put on your website or web app.
Part of our work has been to ensure more of the complex combinations of codecs and containers work across API on the web. In Safari 18.4, we closed the gap by shipping— support for WebM in the MediaRecorder API.
This API for recording media allows you to integrate features like real time podcast and video recording into your web apps. Now such apps can create WebM files using the Opus audio codec and either VP8 and VP9 for video in Safari and WKWebView.
We’ve also worked to bring more media support to the spatial web. We have added support to render 3D models stereoscopically, inline with your other web content, while allowing people to interact with the model.
You can also include immersive videos in your web page that Safari understands and can render correctly without needing any additional tools.
To learn more, check out our session on the spatial web. We’ll cover embedding 3D models, presenting spatial media to your users, and previewing a new feature that will allow you to add a three dimensional environment to your website. Those are the highlights of the media support work we’ve done in the last year. In this session, we focused on CSS and media. But there are more sessions that go in depth on other features we’ve shipped. We have a session on WebGPU that introduces the concepts of WebGPU, gives an overview of the Wig Sil shading language, and talks about how to get optimal device performance.
And we also have a session on Declarative Web Push that’ll cover how to use it without needing a service worker, how it’s more efficient and transparent, and how you can retain backward compatibility with the original Web Push.
And there’s so much more. Lots of new features have been shipped in multiple versions of Safari since last fall.
Including some of the most-requested features… CSS to help you polish your typography and support all of the languages of the world… And features that help protect user privacy.
Check out the release notes on webkit.org. You can also keep up with the latest web technology with articles about what’s new in Safari.
You can file bug reports and feature requests for web technology at bugs.webkit.org‚ the issue tracker for WebKit.
For issues about the interface of Safari; or anything about iOS, iPadOS, and macOS — file a report at feedbackassistant.apple.com.
Make sure you have the latest information about what is supported in Safari. Caniuse.com is a great resource for this.
Download Safari Technology Preview to keep up with what’s coming in the future. It’s updated about every two weeks, so it’s got the very latest additions to WebKit.
We’ve been hard at work to bring you a range of features to help you make incredible experiences on the web. We hope these releases will make building easier and more exciting than ever before. As you use these features, please tell us what you think. Happy coding.
-
-
6:18 - Progress bar code scroll() example
footer::after { content: ""; height: 1em; width: 100%; background: var(--yellow); left: 0; bottom: 0; position: fixed; transform-origin: top left; animation: progress-scale linear; animation-timeline: scroll(); } @keyframes progress-scale { from { transform: scaleX(0); } to { transform: scaleX(1); } }
-
8:36 - html an css of text blocks showcasing different code topics
<section class="topics"> <h3>What you can learn:</h3> <ul class="topics"> <li class="topic-item">Web Development</li> <li class="topic-item">Computer Science</li> <li class="topic-item">Data Science</li> <!-- additional HTML... --> </ul> </section> .topic-item { background: var(--yellow); border: 1px solid var(--gray); /* additional CSS... */ }
-
9:12 - text blocks twisting from the left - animation
@keyframes in-from-left { from { opacity: 0; transform: scale(.8) rotate(-90deg) translateY(15vh); } }
-
9:18 - text blocks twisting from the middle - animation
@keyframes in-from-middle { from { opacity: 0; transform: scale(.8) translateY(15vh); }
-
9:24 - text blocks twisting from the right - animation
@keyframes in-from-right { from { opacity: 0; transform: scale(.8) rotate(90deg) translateY(15vh); } }
-
10:07 - view() timeline example with timeline and range
.topic-item { animation-fill-mode: both; animation-timeline: view(); animation-range: &:nth-child(3n + 1) { animation-name: in-from-left; } &:nth-child(3n + 2) { animation-name: in-from-middle; } &:nth-child(3n + 3) { animation-name: in-from-right; } }
-
12:20 - animation range 50%
.topic-item { animation-fill-mode: both; animation-timeline: view(); animation-range: 0% 50%; &:nth-child(3n + 1) { animation-name: in-from-left; } &:nth-child(3n + 2) { animation-name: in-from-middle; } &:nth-child(3n + 3) { animation-name: in-from-right; } }
-
14:20 - simple cross document view transition code
@view-transition { navigation: auto; }
-
16:00 - adding media query for reduced motion
@view-transition { navigation: auto; } @media not (prefers-reduced-motion) { @keyframes slide-in { from { translate: 100vw 0; } } @keyframes slide-out { to { translate: -100vw 0; } } }
-
16:22 - adding ids to html for cross document view transition
<body> <nav> <!-- additional HTML... --> </nav> <section class="hero"> <div class="hero-image"> <!-- additional HTML... --> </main> <footer> <!-- additional HTML... --> </footer> <body>
-
16:58 - slide effect for cross document view transition
@view-transition { navigation: auto; } @media not (prefers-reduced-motion) { #school-info { view-transition-name: main-body; } ::view-transition-old(main-body) { } ::view-transition-new(main-body) { } @keyframes slide-in { from { translate:e100vw 0; } } }
-
19:48 - nav bar and profile menu
<nav> <h1 class="logo">A-School of Code</h1> <ul> <li>Courses</li> <li>Cohorts</li> <li class="profile"> <img src="https://example.com/saron.jpeg" alt="woman speaking"/> </li> </ul> </nav> <ul class="profile-menu"> <li>Account</li> <li>Settings</li> <li>Profile</li> <li>Billing</li> </ul>
-
20:37 - adding popover attributes
<ul class="profile-menu" id="profile-menu" popover> <li>Account</li> <li>Settings</li> <li>Profile</li> <li>Billing</li> </ul>
-
20:51 - adding aria to popover target
<nav> <div class="wrapper"> <h1 class="logo">A-School of Code</h1> <ul> <li>Courses</li> <li>Cohorts</li> <li class="profile"> <button class="profile-button" aria-haspopup="true" popovertarget="profile-menu"> > <img src="https://example.com/saron.jpg" alt="woman speaking"/> </button> </li> </ul> </div> </nav>
-
21:58 - establishing the anchor
.profile-button { anchor-name: --profile-button; } .profile-menu { position-anchor: --profile-button; }
-
23:25 - setting the target to top right
.profile-menu { position-anchor: --profile-button; position-area: top right; }
-
23:39 - setting the target to bottom center
.profile-menu { position-anchor: --profile-button; position-area: bottom center; }
-
24:16 - setting the target to span right
.profile-menu { position-anchor: --profile-button; position-area: span-right; }
-
24:17 - setting the target to span left
.profile-menu { position-anchor: --profile-button; position-area: span-left; }
-
27:30 - intro to the anchor() function
.profile-button { anchor-name: --profile-button; } .profile-menu { position-anchor: --profile-button; position: absolute; top: anchor(bottom); left: anchor(left); }
-
28:26 - using calc and units in anchor() function
.profile-button { anchor-name: --profile-button; } .profile-menu { position-anchor: --profile-button; position: absolute; top: anchor(bottom); left: calc(anchor(left) + 1.5em); }
-
29:43 - adding a text gradient
.logo { background-image: linear-gradient(to bottom right in hsl, yellow, orange); background-clip: text; color: transparent; }
-
31:05 - adding a gradient to border
.primary-btn { background-image: linear-gradient(to bottom right in hsl, yellow, orange); background-clip: border-area; border-color: transparent; background-origin: border-box; }
-
32:15 - shorthand for adding gradient to border
.primary-btn { background: border-area linear-gradient(to bottom right in hsl, yellow, orange); border-color: transparent; }
-
33:33 - arrow shape using path
.review-shape { clip-path: path("M0 0 L 500 0 L 600 100 L 500 200 L 0 200 Q 100 100 0 0 z"); }
-
35:01 - arrow shape using shape()
.review-shape { clip-path: shape(from top left, line to calc(100% - 50cqh) 0%, line to 100% 50cqh, line to calc(100% - 50cqh) 100%, line to bottom left, curve to top left with 50cqh 50cqh, close); }
-
41:42 - dynamic range limit: no limit
img { dynamic-range-limit: no-limit; }
-
41:57 - dynamic range limit: standard
img { dynamic-range-limit: standard; }
-
-
- 0:00 - Introduction
The Safari and WebKit teams have been hard at work since last WWDC, enhancing web technologies to improve interoperability and user experience. New features in animation, layout, visual effects, and media — including scroll-driven animations, anchor positioning, SVG icon support, and improved media formats — will be available in Safari this fall.
- 1:46 - Animation
Animation is a powerful tool to enhance online experiences, making websites more engaging and enjoyable. Using the latest features in CSS, specifically scroll-driven animations now available in Safari 19, you can elevate a website without the need for JavaScript. This capability is significant because it improves user performance and battery life. Scroll-driven animations introduce two new timelines: the scroll timeline and the view timeline. The scroll timeline allows animations to be linked to the user's scrolling behavior, creating a more interactive experience. For example, a progress bar at the bottom of the page can grow as someone scrolls down, providing a visual cue of their progress. Considering accessibility when implementing animations is important. Some people might prefer less motion due to motion discomfort, which can be triggered by various factors, such as scaling large objects, zooming, or objects moving at different speeds. By being mindful of these triggers, you can create animations that are inclusive and enjoyable for everyone.
- 19:01 - Layout
This fall, Safari will introduce a new CSS module called anchor positioning, which will revolutionize how web developers create tooltips and position menus. Anchor positioning allows you to easily anchor one element to another, such as a menu to a profile picture. This capability enables precise positioning of the menu relative to the anchor. Safari also re-introduced viewport presets in Responsive Design Mode, making it easier for you to test your websites on various devices and screen sizes.
- 29:05 - Visual effects
There are several new CSS features to enhance visual effects on websites and web apps. One notable feature is the expansion of the 'background-clip' property. Originally used to fill text with gradients or images, you can now apply this property to borders. However, some additional adjustments, such as making the border color transparent and extending the background origin, are necessary to achieve the desired effect. You can use this technique to make various elements, like buttons and progress circles, more visually appealing. Another exciting development is support for the 'shape()' function, which allows designers to create responsive shapes more easily than before. It gives you granular control over which parts of a shape scale with the viewport and which remain static, ensuring that shapes maintain their appearance and proportions across different screen sizes. This is particularly useful for creating decorative elements, like arrows or backgrounds. In addition, CSS introduces new typography features, such as 'text-wrap: pretty', available in Safari 19. This feature aims to improve the readability of text by addressing issues like short lines, excessive hyphenation, and uneven line lengths. It adjusts the word spacing and line breaks to create a more pleasing and balanced paragraph layout. This is a subtle enhancement that can make a significant difference in the overall user experience, and it gracefully degrades in browsers that don't support it.
- 38:22 - Media
Safari is set to introduce several significant media enhancements this fall. Among them is the implementation of SVG icons, which will improve user experience across various Safari features, such as favicons, bookmarklets, and the start page. SVG icons offer better scalability and smaller file sizes compared to traditional PNGs. Additionally, Safari brings support for high dynamic range (HDR) images to WebKit and Safari today. HDR images make web content more vibrant and visually appealing by displaying deeper hues, wider ranges, and brighter colors. Safari is also expanding its support for various media formats, including Ogg Opus and Ogg Vorbis for audio, and has made progress in ensuring that complex combinations of codecs and containers work across different APIs. These upgrades enable you to integrate real-time podcast and video-recording features into your web apps. Further, Safari is enhancing its support for the spatial web, allowing you to render 3D models stereoscopically and include immersive videos in web pages. These improvements aim to make the web more interactive and engaging for everyone.