Dunfey · Hotel WWDC as data, est. 1983
Front desk everything
Years
Topics

2022 Safari & Web

WWDC22 · 32 min · Safari & Web

What’s new in Safari and WebKit

Explore the latest features in Safari and WebKit and learn how you can make better and more powerful websites. We’ll take you on a tour through the latest updates to HTML, CSS enhancements, Web Inspector tooling, Web APIs, and more.

Watch at developer.apple.com ↗

Transcript all transcripts

Code shown on screen · 23 snippets

Dialog element xml · at 2:59 ↗
<!-- <dialog> element -->

<dialog method="dialog">
  <form id="dialogForm">
    <label for="givenName">Given name:</label>
    <input class="focus" type="text" name="givenName">
    <label for="familyName">Family name:</label>
    <input class="focus" type="text" name="familyName">
    <label>
      <input type="checkbox"> Can trade in person
   </label>
   <button>Send</button>
  </form>
</dialog>
Backdrop pseudo-class javascript · at 3:09 ↗
/* ::backdrop pseudo-element */

dialog::backdrop {
  background: linear-gradient(rgba(233, 182, 76, 0.7), rgba(103, 12, 0, 0.6));
  animation: fade-in 0.5s;
}

@keyframes fade-in {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}
inert attribute javascript · at 3:53 ↗
// inert attribute

function switchToIndex(index) {
  this.items.forEach(item => item.inert = true);
  this.items[index].inert = false;
  this.currentIndex = index;
}
Lazy image loading xml · at 4:22 ↗
<img src="images/shirt.jpg" loading="lazy"
     alt="a brown polo shirt"
     width="500" height="600">
Container Queries javascript · at 6:46 ↗
/* Container queries */

.container {
  container-type: inline-size;
  container-name: clothing-card;
}
.content {
  display: grid;
  grid-template-rows: 1fr;
  gap: 1rem;
}
@container clothing-card (width > 250px) {
  .content {
    grid-template-columns: 1fr 1fr;
  }
  /* additional layout code */
}
Cascade layers javascript · at 8:05 ↗
/* Author Styles - Layer A */
@layer utilities {
  div {
    background-color: red;
  }
}

/* Author Styles - Layer B */
@layer customizations {
  div {
    background-color: teal;
  }
}

/* Author Styles - Layer C */
@layer userDefaults {
  div {
    background-color: yellow;
  }
}
:has() pseudo-class xml · at 8:54 ↗
<!-- :has() pseudo-class -->

<style>
  form:has(input[type="checkbox"]:checked) {
    background: #ff927a;
  }
</style>



<form class="message">
  <textarea rows="5" cols="60" name="text" 
    placeholder="Enter text"></textarea>
  <div class="checkbox">
    <input type="checkbox" value="urgent"> 
    <label>Urgent?</label>
  </div>
  <button>Send Message</button>
</form>
Offset Path javascript · at 11:08 ↗
/* offset-path */

:is(.blue, .teal, .yellow, .red)  {
  offset-path: circle(9vw at 5vw 50%);
}

@keyframes move {
  100% { 
    offset-distance: 100%;
  }
}

/* Animation */
.clothing-header.clicked :is(.blue, .teal, .red, .yellow) {
  animation: move 1100ms ease-in-out;
}
scroll-behavior: auto javascript · at 11:43 ↗
html {
  scroll-behavior: auto;
}
scroll-behavior: smooth javascript · at 12:09 ↗
html {
  scroll-behavior: smooth;
}
:focus-visible & accent-color javascript · at 13:10 ↗
/* :focus-visible & accent-color */

:focus-visible {
  outline: 4px solid var(--green);
  box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.3);
}

:root {
  accent-color: var(--green);
}
Font palette dark mode & light mode javascript · at 14:50 ↗
/* Dark mode */
font-palette: dark;

/* Light mode */
font-palette: light;
Font palette custom colors javascript · at 15:01 ↗
/* Dark mode */
font-palette: dark;

/* Light mode */
font-palette: light;

/* Custom colors */
@font-palette-values --MyPalette {
  override-colors: 1 yellow;
}

#logo {
  font-palette: --MyPalette;
}
CSS Grid javascript · at 15:55 ↗
/* Grid to layout cards */
main {
  display: grid;
  grid-template-columns: 
    repeat(auto-fit, minmax(225px, 1fr));
  gap: 1rem;
}

/* Grid to layout each card’s content */
article {
  display: grid;
  grid-row: span 5;
}
Adding sub grid javascript · at 16:35 ↗
/* Grid to layout cards */
main {
  display: grid;
  grid-template-columns: 
    repeat(auto-fit, minmax(225px, 1fr));
  gap: 1rem;
}

/* Grid to layout each card’s content */
article {
  display: grid;
  grid-row: span 5;
/* Adding subgrid, tying them together */
  grid-template-rows: subgrid; 
}
Web App Manifest file icons json · at 21:15 ↗
// Manifest file 

"icons": [
 {
   "src": "orange-icon.png",
    "sizes": "120x120",
    "type": "image/png"
  }
]
apple-touch-icon xml · at 21:29 ↗
<!-- HTML head -->

<link rel="apple-touch-icon" href="blue-icon.png" />
Broadcast Channel javascript · at 22:36 ↗
// State change
broadcastChannel.postMessage("Item is unavailable");
Origin private file system javascript · at 23:14 ↗
// Accessing the origin private file system
const root = await navigator.storage.getDirectory();

// Create a file named Draft.txt under root directory
const draftHandle = await root.getFileHandle("Draft.txt", { "create": true });

// Access and read an existing file
const existingHandle = await root.getFileHandle("Draft.txt");
const existingFile = await existingHandle.getFile();
Shared Worker javascript · at 25:32 ↗
// Create Shared Worker
let worker = new SharedWorker("SharedWorker.js");

// Listen for messages from Shared Worker
worker.port.addEventListener("message", function(event) {
  console.log("Message received from worker: " + event);
});

// Send messages to Shared Worker
worker.port.postMessage("Send message to worker");
findLast() and findLastIndex() javascript · at 25:56 ↗
const list = ["shirt","pants","shoes","hat","shoestring","dress"];
const hasShoeString = (string) => string.includes("shoe");

console.log(list.findLast(hasAppString));
// shoestring

console.log(list.findLastIndex(hasAppString));
// 4
at() javascript · at 26:17 ↗
const list = ["shirt","pants","shoes","hat","shoestring","dress"];

// Instead of this:
console.log(list[list.length - 2]);

// It's as easy as:
console.log(list.at(-2));
strict-dynamic source expression javascript · at 29:12 ↗
// strict-dynamic source expression

// Without strict-dynamic
Content-Security-Policy: script-src desired-script.com dependent-script-1.com
  dependent-script-2.com dependent-script-3.com; default-src "self";

// With strict-dynamic
Content-Security-Policy: default-src "self"; script-src "nonce-desired" "strict-dynamic";

Resources