Highlight
Safari 13.1 and Safari 14 give web developers the ability to add Web Animations, ResizeObserver, Async Clipboard, CSS Shadow Parts, WebP, HDR media, modern JavaScript syntax, and App Clips website banners, bringing websites, home screen Web Apps, and embedded WebKit content closer to the native platform experience.
Core Content
Web developers’ daily troubles often come from inconsistent performance of the same set of pages in browsers, home screen Web Apps, and in-App WebViews. The 2020 session focused on interoperability from the beginning: Safari passed 140,000 new Web Platform Tests this year, covering service workers, XHR + Fetch, pointer events, CSS, SVG, WebAssembly and other areas.
Performance is also the other side of the same coin. Safari 14 opens unvisited websites 13% faster, opens recently visited webpages 42% faster, and the address bar opens recently visited webpages 52% faster. Instant back can cache more pages, scrolling CPU usage is reduced to one-third, and IndexedDB operations are up to 10 times faster. The page becomes faster first, and then the subsequent API has room to handle more interactions.
Apple then broke the update into several lines: Web API allows pages to control animations, monitor element sizes, access the clipboard and encapsulate components; CSS updates bring typography, fonts and selectors closer to the needs of large sites; media updates complete WebP, HDR, remote playback, picture-in-picture and HLS metadata; JavaScript updates bring BigInt, null value merging, optional chaining, logical assignment, public class fields andreplaceAll。
This is not a launch event for a single framework. It is more like a migration checklist for Safari and WebKit: first find the parts of the page that are supported by polyfills, libraries, or platform-specific capabilities, and then replace them with standard Web APIs one by one. For existing websites, the most practical benefit is to write less compatibility code and at the same time bring the experience on Safari, iOS, iPadOS and macOS together.
Detailed Content
1. Web API moves from page-level response to component-level response
(04:22) The Web Animations API is available in Safari 13.1. It allows JavaScript to directly create and control CSS animations and transitions without manually changing a set of element properties. An example of session is to make the compass pointer rotate again when the WebKit logo is clicked.
// Web Animations API Code Example
let needle = document.getElementById("needle");
let logo = document.getElementById("logo");
logo.addEventListener("click", () => {
needle.animate({
transform: [
"rotateX(35deg) rotateZ(13deg)",
"rotateX(35deg) rotateZ(733deg)",
],
easing: ["ease-out"],
}, 800);
});
Key points:
getElementById("needle")andgetElementById("logo")Get the elements controlled by animation and the elements that trigger clicks respectively. -addEventListener("click", ...)Tie animations to real user interactions. -needle.animate(...)Receives a keyframe object and duration, described directly from atransformto anothertransformchanges. -easing: ["ease-out"]Make the spin start fast and end soft. -800Is the animation duration, in milliseconds.
(06:43) ResizeObserver solves another common pain point: components not only become narrower due to changes in the viewport, but may also change size due to changes in the editor, sidebar, or child elements. After Safari 13.1 supports it, the page can monitor the width of a container and switch CSS classes according to the container state.
// Resize Observer Example
let formatPanelObserver = new ResizeObserver((entries) => {
entries.forEach((entry) => {
let container = entry.target;
container.classList.toggle("small", entry.contentRect.width < 175);
}
});
formatPanelObserver.observe(document.getElementById("format-panel"));
Key points:
new ResizeObserver(...)Creates a size observer whose callback receives change entries. -entries.forEach(...)Process elements that change size one by one. -entry.targetIs the container being observed. -entry.contentRect.width < 175Use the width of the container itself to determine whether it has entered the small size state. -classList.toggle("small", ...)Synchronize width judgment results to CSS. -observe(document.getElementById("format-panel"))Specifies the formatting toolbar that is actually to be observed.
(08:22) The Async Clipboard API is available in Safari 13.1. It can read and write the system clipboard asynchronously, preventing the page from blocking during clipboard access. There are shortcut methods for plain text, and the call also requires security context and user interaction.
// Programmatic copy
copyButtonElement.addEventListener("click", (event) => {
navigator.clipboard.writeText("Plain text to copy.").then(() => {
// Successful copy
}, () => {
// Copy failed
});
});
// Programmatic paste
pasteButtonElement.addEventListener("click", (event) => {
navigator.clipboard.readText().then((clipText) => {
document.querySelector(".editor").innerText += clipText);
});
});
Key points:
- Copy and paste are placed on the button
clickhandler, meeting user interaction requirements. -navigator.clipboard.writeText(...)Write plain text. -thenThe two callbacks handle copy success and failure respectively. -navigator.clipboard.readText()Read the plain text clipboard contents. -document.querySelector(".editor").innerText += clipTextAppend the read text to the editor.
2. Web Components gain controllable external style entry
(10:36) Safari has long supported Web Components. The focus of the 2020 update is to make the division of labor between component authors and page authors clearer. The component author is responsible for the internal structure, and the page author only styles the parts that are explicitly exposed.
let template = document.getElementById("format-button");
window.customElements.define(template.id, class extends HTMLElement {
constructor() {
super();
this.attachShadow({mode: "open"});
let newButtonElement = template.content.cloneNode(true);
let parts = newButtonElement.querySelectorAll("span");
parts[0].textContent = this.getAttribute("data-icon");
parts[1].textContent = this.textContent;
this.shadowRoot.appendChild(newButtonElement);
this.addEventListener("click", this.handleClick.bind(this));
}
});
Key points:
template.idUsed as the registered name of the custom element.- Component class inheritance
HTMLElement, first call in the constructorsuper()。 attachShadow({mode: "open"})Create shadow root. -template.content.cloneNode(true)Copy the template content. -querySelectorAll("span")Find two fillable positions for icons and labels. -getAttribute("data-icon")andthis.textContentPut the data in the page markup into the component. -appendChildPut the processed DOM fragment into the shadow root.- The last line binds the click event to the component itself
handleClick。
(12:30) CSS Shadow Parts allows component authors to use styleable internal nodespartMark it out. The page does not need to know the complete shadow DOM structure and only relies on these named entries.
<template id="format-button">
<button class="format">
<span part="icon" class="icon"></span>
<span part="label" class="label"></span>
</button>
</template>
Key points:
<template id="format-button">Still a component template. -part="icon"Exposes the icon area so that the page author can style it individually. -part="label"Expose text area.- The internal button structure is still preserved by the component, the page author only touches the style points exposed by the component.
(12:38) The author of the page then used::part(...)Select these public widgets to add visual cues to the different formatting buttons.
#bold::part(icon) {
color: var(--formatting-button-icon-color);
font-weight: bold;
}
#italic::part(icon) {
color: var(--formatting-button-icon-color);
font-style: italic;
}
#underline::part(icon) {
color: var(--formatting-button-icon-color);
text-decoration: underline;
}
Key points:
#bold::part(icon)Only style the exposed icon widget of the bold button. -var(--formatting-button-icon-color)Let the colors continue to be controlled by the page theme. -font-weight: bold、font-style: italic、text-decoration: underlineMap button meanings separately.- These rules do not require page authors to penetrate the full DOM inside the component.
3. CSS updates focus on fonts, line breaks and selector complexity
(14:32) WebKit adds system font families.system-uiMaps to platform-appropriate UI fonts,ui-serifCorresponds to New York,ui-monospaceCorresponding to SF Mono,ui-roundedCorresponds to SF Rounded. Web App can be more natural and close to the system interface.
font-family: system-ui;
font-family: ui-sans-serif;
font-family: ui-serif;
font-family: ui-monospace;
font-family: ui-rounded;
Key points:
system-uiIt is the universal system UI font entry. -ui-sans-serifUse interface fonts like San Francisco on Apple platforms. -ui-serifSystem serif font suitable for use in content areas. -ui-monospaceGood for code and monowidth content. -ui-roundedSuitable for interfaces that require rounded font style.
(16:43)line-break: anywhereUsed to handle long technical strings in narrow containers. It allows line breaks at character positions before overflow, preventing API names, commands, or long identifiers from breaking the layout.
code {
line-break: anywhere;
}
Key points:
- Rules act directly on
codeon the elements. -line-break: anywhereAllows more aggressive searching for line breaks. - The transcript scenario is that the long syntax in the Web Inspector command line API document causes empty lines and horizontal overflow of list items.
(18:02):is()Make repeat selectors shorter. session uses the example of removing the top margin when the titles are adjacent. Originally, a large number ofh1 + h2、h1 + h3Combinations can now be merged.
h1, h2, h3, h4, h5, h6 {
margin-top: 3em;
}
:is(h1, h2, h3, h4, h5, h6) + :is(h1, h2, h3, h4, h5, h6) {
margin-top: 0;
}
Key points:
- The first rule adds default top spacing to all headings.
-
:is(h1, h2, h3, h4, h5, h6)Match any level title. - middle
+Indicates that the next title immediately follows the previous title. - The second rule zeroes out the top spacing between consecutive titles.
(19:07):where()The matching method is similar, but the specificity of the matching result is always zero. It is suitable for writing default styles and making subsequent rules easy to override.
:where(.intro, .pullquote, #hero) + p {
text-transform: uppercase;
}
h2 + p,
h3 + p,
h4 + p,
h5 + p,
h6 + p {
text-transform: none;
}
Key points:
:where(.intro, .pullquote, #hero) + pMatches the paragraph following these elements. -:where()Return its own specificity to zero.- Follow-up
h2 + parriveh6 + pRules can override the previous default uppercase. - this solved
:is()Situations that are difficult to cover because the highest specificity is involved in the calculation.
4. Media updates cover image format, layout stability and video control
(19:53) Safari 14 and iOS 14 support WebP. WebP can provide lossy compression close to JPEG, lossless compression similar to PNG, and also supports transparency and animation. Can be used when landingpictureDo a gradual fallback.
<picture>
<source srcset="example.webp" type="image/webp">
<img src="example.jpg" alt="Example Image">
</picture>
Key points:
<picture>Let the browser choose an available format among multiple resource candidates. -<source type="image/webp">WebP version available. -<img src="example.jpg">This is a fallback when WebP is not supported.- In the examples of the same quality given by transcript, WebP lossy images save 41% compared to JPEG, and lossless images save 33% compared to PNG.
(21:19) WebKit in Safari 13.1 and iOS 13.4 will remove the image tag fromwidthandheightProperty calculation default aspect ratio. After the developer adds the size, the browser can reserve space before the image is loaded, reducing layout jitter.
<img src="MexicoCity.png" width="560" height="747">
Key points:
srcPoints to the image resource. -width="560"andheight="747"Available in original size.- WebKit uses these two properties to calculate the default aspect ratio.
- The page can retain the correct space even when the image has not been downloaded.
(22:42) Safari 14 on macOS supports HDR video in web pages. Developers can use CSS media queries or JavaScript to detect high dynamic range display capabilities and then deliver enhanced content.
<style>
@media only screen (dynamic-range: high) {
/* HDR-only CSS rules */
}
</style>
<script>
if (window.matchMedia("dynamic-range: high")) {
// HDR-specific JavaScript
}
</script>
Key points:
@media only screen (dynamic-range: high)Limit the style to HDR display scenes.- JavaScript side pass
window.matchMedia("dynamic-range: high")Do similar tests. - Transcript’s suggestion is progressive enhancement: provide corresponding content when HDR capability is available, and maintain a normal experience when HDR capability is not available.
(23:19) The Remote Playback API provides a standard remote playback entrance for custom web media players. It can send audio or video to connected TVs, speakers, and AirPlay devices.
<video id="videoElement" src="https://site.example/video.mp4"></video>
<button id="deviceButton">Send video to a remote device</button>
<script>
let videoElement = document.getElementById("videoElement");
let deviceButton = document.getElementById("deviceButton");
deviceButton.addEventListener("click", (event) => {
videoElement.remote.prompt().then(updateRemotePlaybackState);
});
</script>
Key points:
<video>Is the media element to be played remotely. -<button>It is the device selection button in the custom player. -getElementByIdGet the video and button respectively.- Called after clicking the button
videoElement.remote.prompt()Open the device selection process. -then(updateRemotePlaybackState)Update playback status after selection results are returned.
5. New JavaScript syntax reduces defensive boilerplate code
(27:11) Safari 14 supports BigInt. It is used in integer scenarios that exceed JavaScript’s maximum safe integer, commonly seen in cryptography. Special reminder: session operations cannot be mixed with ordinary numbers, nor can they be used directly.JSON.stringifyDo serialization.
let bigInt = BigInt(Number.MAX_SAFE_INTEGER);
// 9007199254740991n
console.log(8n / 2n);
// 4n
console.log(9n / 2n);
// 4n
Key points:
BigInt(Number.MAX_SAFE_INTEGER)Create a BigInt value.- in the output
nRepresents a BigInt literal. -8n / 2nget4n。 9n / 2nalso get4n, because BigInt division discards the fractional part.
(28:02) The null value coalescing operator checks fornullandundefined. it retainsfalse, empty string and0This type of valid value is suitable for processing forms, profiles and configuration objects.
class Person {
constructor(firstName, lastName, age) {
this.firstName = firstName ?? "Unknown";
this.lastName = lastName ?? "Unknown";
this.age = age ?? NaN;
}
}
console.log(new Person());
// { firstName: "Unknown", lastName: "Unknown", age: NaN }
console.log(new Person(false, false, true));
// { firstName: false, lastName: false, age: true }
console.log(new Person("John", "", 0));
// { firstName: "John", lastName: "", age: 0 }
console.log(new Person("John", "Appleseed", 42));
// { firstName: "John", lastName: "Appleseed", age: 42 }
Key points:
- Constructor receives
firstName、lastNameandage。 ??On the left isnullorundefinedOnly use the default value on the right. -new Person()will get the default name andNaNage. -false, empty string,0will be retained and will not be replaced by mistake.
(29:41) Optional chaining operators let property, index, and method access write less explicit guards. The following registration function directly accesses something that may not existperson。
class Person {
constructor(firstName, lastName, age) {
this.firstName = firstName ?? "Unknown";
this.lastName = lastName ?? "Unknown";
this.age = age ?? NaN;
this.name = { firstName: this.firstName, lastName: this.lastName };
}
}
function register(person) {
// With optional chaining
console.log(person?.name.firstName);
}
register(new Person());
undefined
register(new Person("John", "Appleseed"));
"John"
Key points:
PersonThe constructor still provides default fields using null merging. -this.nameSave nested monikers. -register(person)Used insideperson?.name.firstNameAccess nested properties. -register(new Person())Can be exported safelyundefined.- When the complete name is passed in, output
"John"。
(30:52) The logical assignment operator combines checking and assignment. For session??=Shown only on the left asnullorundefinedDefault content is written.
a &&= b // and assignment operator
a ||= b // or assignment operator
a ??= b // nullish assignment operator
// Nullish coalescing approach
element.innerHTML = element.innerHTML ?? "Hello World!"
// Logical assignment operator
element.innerHTML ??= "Hello World!"
Key points:
&&=、||=、??=Corresponds to logical AND, logical OR, and null value merge assignment respectively.- Ordinary null value merging writing method will be re-written
element.innerHTMLAssignment. -element.innerHTML ??= "Hello World!"The default string is only written if the current value is null. - transcript Emphasize that the expression on the left will only be evaluated once.
Core Takeaways
-
Make a component-adaptive comment editor: Let the toolbar automatically collapse text labels according to the width of the editor container. Why it’s worth doing: ResizeObserver monitors the size of the element itself and does not rely on the global window width. How to start: Observe the editor toolbar container and toggle it when it falls below a threshold
smallclass, and use CSS to hide the label. -
Add secure rich text paste button to content site: Add user-triggered paste entry to CMS, forum or knowledge base editor. Why it’s worth doing: Async Clipboard API can read the clipboard asynchronously, and the transcript also shows that it supports multiple item types such as images and rich text. How to get started: Use it first
readText()Make a plain text path, then paste the rich HTML into an explicit button interaction. -
Open the design system components into themable Web Components: the internal structure is maintained by the components, and the business pages are only adjusted
icon、labeland other public parts. Why it’s worth doing: CSS Shadow Parts let component authors declare styleable boundaries without page authors relying on shadow DOM details. How to start: Add themeable nodes inside the templatepart, and then provide the page example::part(...)rule. -
Reduce loading bounce and volume for image-intensive pages: suitable for photo albums, product lists, and article cover flows. Why it’s worth doing: WebP supports reducing image size,
widthandheightProperty tells WebKit to reserve space for images. How to start: Generate two sets of WebP and JPEG resources for images, usepictureOutput format fallback, and inimgPreserve size attributes on. -
Complete the system media entry for custom video players: Add remote playback and picture-in-picture buttons to teaching, conference playback or live broadcast sites. Why it’s worth doing: Both the Remote Playback API and the Picture in Picture API require user interaction and are suitable for putting into player controls. How to start: Give first
<video>Create a custom button and call it separatelyremote.prompt()andrequestPictureInPicture()。
Related Sessions
- Meet Safari Web Extensions — 10663 At the end, it is recommended to continue viewing Safari Web Extensions. This session talks about the extension creation, migration and release process.
- What’s new in Web Inspector — 10663 The animation and debugging capabilities of Web Inspector have been mentioned many times, 10646 expand on the 2020 tool updates.
- Discover WKWebView enhancements — 10663 For websites and WebKit content, 10188 continue to talk about the scripting, exporting and privacy capabilities of WKWebView in the App.
- Meet Face ID and Touch ID for the web — 10663 Click to the Web Authentication API to support Face ID and Touch ID, 10670 talk about the complete login process.
- Shop online with AR Quick Look — The platform integration section of 10663 mentions the AR Quick Look shopping banner, and 10604 talks about this e-commerce experience in depth.
Comments
GitHub Issues · utterances