WWDC Quick Look 💓 By SwiftGGTeam
What's new in Safari and WebKit

What's new in Safari and WebKit

元の動画を見る

ハイライト

これは Safari の年次アップデートのまとめであり、過去 7 つのバージョン (Safari 15 ~ 16) のすべての新機能をカバーしています。セッションは、HTML、CSS、Web Inspector、Web API、JavaScript/WebAssembly、セキュリティ、プライバシーのカテゴリ別に編成されています。


主要内容

過去 1 年間、Safari と WebKit は 7 つのバージョンを連続してリリースしました。 WebKit チームは冒頭で、今年は合計 162 の Web プラットフォーム機能と改善がもたらされたと述べました (01:18)。これらの更新は、開発者が長い間報告してきたいくつかの質問に答えることに重点を置いています。CSS コンポーネントの再利用が難しい、親状態を選択するのが難しい、レイアウトのデバッグが直感的ではない、Web アプリとネイティブ アプリの間には機能のギャップが依然として存在します。

このセッションでは、衣類交換 Web サイトを使用してすべてを結び付けます。このページには、アイテムをリクエストするためのポップアップ、リクエストを受信するためのカルーセル、さまざまな幅で再利用される製品カード、ウィンドウ間で同期される在庫ステータス、オフラインで保存する必要がある下書きが含まれています。各機能は、Safari 15 ~ 16 の新しい機能セットに対応しています。

HTML: カスタム ポップアップ ウィンドウとインタラクティブな状態の記述を減らす

以前は、ポップアップ ウィンドウを作成する場合、通常、フォーカス、背景マスク、キーボード ナビゲーション、アクセシビリティを自分で処理する必要がありました。 Safari がサポートされるようになりました<dialog>そして::backdrop、ポップアップとマスクをブラウザのネイティブ機能に戻すことができます (02:58)。ページ上にカルーセルまたは隠しパネルがある場合、inertまた、プロパティでは、クリック、キーボード ナビゲーション、支援技術へのアクセスから最新以外のコンテンツが除外されます (03:49)。

画像の読み込みには、HTML を直接書き込むこともできます。スクロールせずに見える範囲外の製品画像については、loading="lazy"、ユーザーが画像の近くをスクロールすると、ブラウザーはそれらを読み込みます (04:20)。このタイプの機能は、サイト上の最も一般的なカスタム コードを最初に置き換える場合に適しています。

CSS: コンポーネントはコンテナに従って動作します

CSS セクションの焦点はアーキテクチャです。 WebKit チームは、新しい Web テクノロジーに対する開発者の最大のリクエストがコンテナ クエリであることを明らかにし、それが Safari 16 でリリースされることを発表しました (05:10)。製品カードは、サイドバーに垂直に、メイン領域に水平に、または中程度の幅で 2 列に配置できます。判断の基準は、ビューポート全体からだけでなく、コンポーネントが配置されているコンテナからもたらされます。

@layer別の種類の長期的な問題を解決します。大規模なプロジェクトでは、設計システム、ツール クラス、およびビジネス カバレッジ スタイルが混在し、セレクターの優先順位を制御することがますます困難になります。カスケード レイヤーを使用すると、開発者は独自のレイヤーを定義できます。レイヤー全体の順序は、単一のセレクターの特異性よりも高くなります (07:14)。

:has()これにより、親要素は子要素の状態に基づいてスタイルを適用できるようになります (08:30)。 「緊急ですか?」フォーム内のチェックボックスが選択されている場合、状態を維持するために追加の JavaScript を必要とせずに、メッセージ フォーム全体の色を直接変更できます。

Web API と JavaScript: Web アプリは完全なアプリケーションに似ています

Web API 部分は Web Push から始まります。 Safari 16 は macOS Ventura で Web プッシュをサポートしており、iOS と iPadOS のサポートは来年提供される予定です (20:22)。これは標準ベースの実装であり、既存の Web プッシュ コードが他のブラウザで動作する場合、Safari で変更する必要はありません (20:41)。

同じソースからの複数のページを共同作業するためのより直接的な方法もあります。ブロードキャスト チャネルは、1 つのウィンドウ内の「アイテムを受信しました」メッセージを他の同じオリジン ウィンドウに送信できます (22:11)。 Origin プライベート ファイル システムは、下書きなどのデータを保存するためにオリジンによって分離されたプライベート ファイル ストレージをサイトに提供します (22:49)。

JavaScript の更新では、共有ワーカーにより、同じオリジンの複数のページがワーカーを共有できるようになり、各ウィンドウでバックグラウンド タスクを繰り返し開始することによって引き起こされるメモリ負荷が軽減されます (25:08)。配列も追加されましたfindLast()findLastIndex()そしてat()、末尾検索と負のインデックスへのアクセスがより簡単になります (25:56)。

詳細

ネイティブ ポップアップ ウィンドウ:<dialog>そして::backdrop

(02:58) 衣装交換 Web サイトには「アイテムのリクエスト」ポップアップ ウィンドウが必要です。<dialog>リクエストフォームのホストに適したネイティブオーバーレイコンテナを提供します。

<!-- <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>

キーポイント:

  • <dialog method="dialog">ネイティブ ダイアログ コンテナーを宣言します。 -<form id="dialogForm">送信コンテンツをダイアログ ボックス内に配置します。
  • 2つのグループ<label>そして<input>依頼者の名前を収集します。 ・チェックボックスは「対面取引が可能かどうか」という要望条件を表します。 -buttonフォームの送信を担当します。

(03:08) ポップアップ ウィンドウの背後にあるマスクには追加の DOM は必要ありません。::backdropダイアログの背景レイヤーを直接選択します。

/* ::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;
  }
}

キーポイント:

  • dialog::backdropモーダルの背後にあるブラウザを確認して、背景レイヤーを生成します。 -background背景レイヤーにグラデーションカラーを追加します。 -animationフェード アニメーションを背景レイヤーにバインドします。 -@keyframes fade-in透明から不透明への変化を定義します。

インタラクションが無効になりました:inert

(03:49) カルーセル内の現在のカードのみがインタラクティブである必要があります。inert要素とその子へのクリック、キーボード ナビゲーション、支援技術アクセスを無効にします。

// inert attribute

function switchToIndex(index) {
  this.items.forEach(item => item.inert = true);
  this.items[index].inert = false;
  this.currentIndex = index;
}

キーポイント:

  • switchToIndex(index)ターゲットのカルーセル項目インデックスを受け取ります。 -this.items.forEach(...)まず、すべてのカルーセル項目を不活性に設定します。 -this.items[index].inert = false現在の項目の対話性のみが復元されます。 -this.currentIndex = index以降の切り替えのために現在のインデックスを保存します。

画像の遅延読み込み:loading="lazy"

(04:20) 製品リストには通常、多くの写真が含まれています。フォールド外の画像は遅延して読み込むことができます。

<img src="images/shirt.jpg" loading="lazy"
     alt="a brown polo shirt"
     width="500" height="600">

キーポイント:

  • src実際の画像リソースを指します。 -loading="lazy"ブラウザにこの画像の読み込みを遅らせるように要求します。 -alt画像の代替テキストを提供します。 -widthそしてheightブラウザがレイアウト スペースを確保できるようにします。

コンテナクエリ: コンポーネントは独自のスペースに応じてレイアウトを切り替えます

(06:35) 同じ製品カードがサイドバー、メインコンテンツ領域、グリッドに表示されます。 Container Queriesを使用した後、コンポーネント内にレイアウト判定を記述します。

/* 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 */
}

キーポイント:

  • .container測定されるコンテナ要素です。 -container-type: inline-sizeクエリコンテナのインライン方向のサイズを表します。 -container-name: clothing-cardコンテナに名前を付けて使いやすくする@containerで引用。 -.contentまず、基本的な 1 列のグリッド レイアウトを定義します。 -@container clothing-card (width > 250px)コンテナの幅が 250px を超える場合に内部スタイルを適用します。 -grid-template-columns: 1fr 1frコンテンツを 2 列に切り替えます。

カスケード レイヤー: スタイルの優先順位をアーキテクチャに変える

(07:14) カスケード レイヤーを使用すると、チームはツール クラス、ビジネス カバレッジ、ユーザーのデフォルト スタイルを異なるレイヤーに配置できます。レベル間の順序は、個々のセレクターの特異性よりも優先されます。

/* 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;
  }
}

キーポイント:

  • @layer utilitiesツールスタイルレイヤーを定義します。 -@layer customizationsアイテムオーバーライドスタイルレイヤーを定義します。 -@layer userDefaults著者スタイルレイヤーの別のセットを定義します。
  • 各レイヤー内で同じものを使用しますdivセレクタ。
  • 最終的な優先順位は、ここでのセレクターの違いではなく、レイヤーが定義されている順序によって決まります。

:has(): 親要素は子要素の状態に応じてスタイルを変更します

(08:54) メッセージフォームでは、チェックボックスをオンにするとフォーム全体の色が変わります。以前は、JavaScript を使用してクラスを親要素に追加するのが一般的でした。:has()この状態を CSS に直接保持します。

<!-- :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>

キーポイント:

  • form:has(...)指定された要素の状態を含むフォームと一致します。 -input[type="checkbox"]:checked選択したチェックボックスを指します。 -background: #ff927a親フォームの背景をプロンプトの色に変更します。 -textareaメッセージのテキストです。
  • チェックボックスvalue="urgent"このメッセージの緊急性を表現してください。

Web アプリのアイコン: マニフェストとapple-touch-icon

(21:13) ユーザーが Web アプリをホーム画面に追加すると、アイコンは Web アプリのマニフェストから取得されます。

// Manifest file 

"icons": [
 {
   "src": "orange-icon.png",
    "sizes": "120x120",
    "type": "image/png"
  }
]

キーポイント:

  • iconsインストール ポータルで使用できるアイコン リソースをリストします。 -srcアイコン ファイルをポイントします。 -sizesアイコンのサイズをマークします。 -type画像の MIME タイプを指定します。

(21:28) HTML ヘッド内で宣言された場合apple-touch-icon、Apple デバイスのアイコンに使用されます。講演では、マニフェストアイコンを優先したい場合は、HTML ヘッドが定義されていないことを確認する必要があると説明されました。apple-touch-icon

<!-- HTML head -->

<link rel="apple-touch-icon" href="blue-icon.png" />

キーポイント:

  • rel="apple-touch-icon"Apple デバイスで使用されるホーム画面アイコンを宣言します。 -href="blue-icon.png"このデバイス固有のアイコンをポイントします。
  • iOS や iPadOS に他のプラットフォームとは異なるアイコンを提供するのに適しています。

マルチウィンドウ同期: ブロードキャスト チャネル

(22:20) 同じ服装交換 Web サイトが同時に 2 つのウィンドウで開かれる場合があります。 1 つのウィンドウは製品を受け取りましたが、もう 1 つのウィンドウはその製品がもう入手できないことを知る必要があります。

// State change
broadcastChannel.postMessage("Item is unavailable");

キーポイント:

  • broadcastChannel元の閲覧コンテキスト間の通信チャネルを表します。 -postMessage(...)ステータスの変更を他の元のページに送信します。
  • 「アイテムはもう入手できません」を表す文字列メッセージ。

オリジンプライベートファイルシステム: サイトプライベートファイル

(22:49) ファイル システム アクセス API のオリジン プライベート ファイル システムは、各オリジンに独立したプライベート ストレージ スペースを提供します。スピーチの草稿文書の例は、読み書きの入門を示しています。

// 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();

キーポイント:

  • navigator.storage.getDirectory()現在のオリジンのルート ディレクトリを取得します。 -root.getFileHandle("Draft.txt", { "create": true })ドラフト ファイルのハンドルを作成または取得します。 -root.getFileHandle("Draft.txt")既存のファイルへのハンドルを取得します。 -existingHandle.getFile()ファイルハンドルから読み取るFile物体。

共有ワーカー: バックグラウンド タスクを共有する同一オリジン ページ

(25:08) 複数の相同ページでバックグラウンド計算が必要な場合、ワーカーを共有して、ワーカーの重複によって引き起こされるメモリ負荷を軽減できます。

// 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");

キーポイント:

  • new SharedWorker("SharedWorker.js")共有ワーカーを作成します。 -worker.port.addEventListener("message", ...)ワーカーから返信されたメッセージをリッスンします。 -console.log(...)メッセージ受信時に内容を出力します。 -worker.port.postMessage(...)ポート経由でワーカーにメッセージを送信します。

配列末尾の操作:findLast()findLastIndex()そしてat()

(25:56) 配列の末尾から検索する場合、最初に検索する必要はありませんreverse()配列を変更します。

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

キーポイント:

  • list検索する製品名の配列です。 -hasShoeStringを含む一致ですshoeコールバック関数。 -findLast(...)配列の末尾から前方に向かって最初に一致するものを検索します。 -findLastIndex(...)配列の末尾から前方に向かって最初に一致する項目のインデックスを検索します。
  • コードスニペットでの呼び出し名の記述hasAppString;以前に宣言されたマッチング関数は、コンテキストに従って渡される必要があります。

26:17at()負のインデックスをサポートし、末尾近くの要素を読み取るのに適しています。

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));

キーポイント:

  • list[list.length - 2]その長さを使用して最後から 2 番目の要素を計算します。 -list.at(-2)最後から 2 番目の要素を直接読み取ります。
  • 負のインデックスを使用すると、末尾のアクセスが短くなり、手書きの長さの計算も短縮されます。

CSP Level 3:strict-dynamic

(29:04) コンテンツ セキュリティ ポリシー レベル 3strict-dynamicnonce を含むスクリプトを信頼し、このスクリプトによって読み込まれる後続のスクリプトに信頼を拡張して、明示的なドメイン名の許可リストを減らすことができます。

// 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";

キーポイント:

  • 最初のポリシーでは、ターゲット スクリプトと複数の依存スクリプト ドメイン名をリストする必要があります。
  • 許可リストが長いと、ロードが許可されるスクリプトの範囲を簡単に拡張できます。
  • 2 番目のポリシーは、ノンスを使用して信頼できるスクリプトをマークします。 -strict-dynamic信頼されたスクリプトによってロードされたスクリプトに信頼を渡します。

重要ポイント

  • アクセシビリティ優先の製品リクエスト ポップアップを作成します: を使用します<dialog>ポップアップ ウィンドウの構造を管理するには、次を使用します。::backdropマスクを管理するには、次を使用しますinert現在ではないカルーセル項目を除外します。エントリ ポイントは、サイト上で最も複雑なカスタム モーダルから始まります。

  • 再利用可能な製品カード コンポーネントを書き換える: コンポーネント コンテナを次のようにマークします。container-type: inline-size、使用@containerナローカラム、メインカラム、ヒーローカードのレイアウトを制御します。このようにして、コンポーネントが別の領域に移動されたときに、新しいページレベルのメディアクエリを追加する必要はありません。

  • フォーム状態スタイルを JavaScript から CSS に戻す: を使用します。form:has(input[type="checkbox"]:checked)緊急メッセージを処理し、契約に同意し、項目をフィルターしてこのタイプの親ステータスを選択します。まず、スタイルを変更するためだけに記述された JS クラスを選択し、それを置き換えるロジックを切り替えます。

  • マルチウィンドウ Web アプリにステータス ブロードキャストを追加: インベントリ、下書き、通知設定などのステータス変更ポイントで呼び出されますbroadcastChannel.postMessage(...)、同じ原点ウィンドウの一貫性を保つため。管理バックエンド、ショッピング カート、コラボレーション ツールに適しています。

  • Web アプリ用のプライベート ドラフト ストレージを追加: 使用しますnavigator.storage.getDirectory()元のプライベート ファイル システムのルート ディレクトリを取得して使用します。getFileHandle()未公開のコンテンツを保存します。記事の編集者、フォームの下書き、ローカル キャッシュに適しています。

関連セッション

コメント

GitHub Issues · utterances