PWA のタイトルバーのウィンドウ コントロール オーバーレイをカスタマイズする

ウィンドウ コントロールの横にあるタイトルバー領域を使用して、PWA をアプリのように見せることができます。

PWA をアプリのように感じさせるという記事を覚えていらっしゃる方は、アプリのようなエクスペリエンスを実現するための戦略として、アプリのタイトルバーをカスタマイズする方法について説明したことを思い出されるかもしれません。macOS の Podcasts アプリで表示される例を次に示します。

macOS の Podcasts アプリのタイトルバー。メディア コントロール ボタンと、現在再生中のポッドキャストに関するメタデータが表示されています。
カスタム タイトルバーを使用すると、PWA がプラットフォーム固有のアプリのように感じられます。

ここで、ポッドキャストはブラウザで実行されないプラットフォーム固有の macOS アプリであるため、ブラウザのルールに従う必要がなく、自由に動作できると反論したくなるかもしれません。確かにそうですが、このたび、この記事のテーマである Window Controls Overlay 機能が導入され、PWA で同様のユーザー インターフェースを作成できるようになります。

ウィンドウ コントロール オーバーレイのコンポーネント

ウィンドウ コントロール オーバーレイは、次の 4 つのサブ機能で構成されています。

  1. ウェブアプリ マニフェストの "display_override" フィールドの "window-controls-overlay" 値。
  2. CSS 環境変数 titlebar-area-xtitlebar-area-ytitlebar-area-widthtitlebar-area-height
  3. 以前は独自仕様だった CSS プロパティ -webkit-app-region を、ウェブ コンテンツ内のドラッグ可能な領域を定義する app-region プロパティとして標準化しました。
  4. window.navigatorwindowControlsOverlay メンバーを介してウィンドウ コントロール領域をクエリし、回避するメカニズム。

ウィンドウ コントロール オーバーレイとは

タイトルバー領域とは、ウィンドウ コントロール(最小化、最大化、閉じるなどのボタン)の左側または右側のスペースを指し、多くの場合、アプリケーションのタイトルが含まれています。ウィンドウ コントロール オーバーレイを使用すると、プログレッシブ ウェブ アプリケーション(PWA)で、既存の全幅のタイトルバーをウィンドウ コントロールを含む小さなオーバーレイに置き換えることで、アプリのような操作感を実現できます。これにより、デベロッパーは、以前はブラウザが制御していたタイトルバー領域にカスタム コンテンツを配置できます。

現在のステータス

Step ステータス
1. 説明を作成する 完了
2. 仕様の最初のドラフトを作成する 完了
3. フィードバックを収集してデザインを反復 作成中
4. オリジン トライアル 完了
5. リリース 完了(Chromium 104)

ウィンドウ コントロール オーバーレイの使用方法

ウェブアプリ マニフェストに window-controls-overlay を追加する

プログレッシブ ウェブアプリでウィンドウ コントロール オーバーレイを有効にするには、ウェブアプリ マニフェストで "window-controls-overlay" をプライマリ "display_override" メンバーとして追加します。

{
  "display_override": ["window-controls-overlay"]
}

ウィンドウ コントロール オーバーレイは、次のすべての条件が満たされた場合にのみ表示されます。

  1. アプリはブラウザではなく、別の PWA ウィンドウで開きます。
  2. マニフェストには "display_override": ["window-controls-overlay"] が含まれています。(その後は他の値も許可されます)。
  3. PWA がデスクトップ オペレーティング システムで実行されている。
  4. 現在のオリジンが、PWA がインストールされたオリジンと一致している。

その結果、タイトルバー領域が空になり、オペレーティング システムに応じて、左または右に通常のウィンドウ コントロールが表示されます。

タイトルバーが空で、ウィンドウ コントロールが左側に表示されているアプリ ウィンドウ。
カスタム コンテンツ用の空のタイトルバー。

タイトルバーへのコンテンツの移動

タイトルバーにスペースができたので、そこに何かを移動できます。この記事では、Wikimedia のおすすめコンテンツの PWA を作成しました。このアプリに役立つ機能としては、記事のタイトルに含まれる単語の検索などが考えられます。検索機能の HTML は次のようになります。

<div class="search">
  <img src="logo.svg" alt="Wikimedia logo." width="32" height="32" />
  <label>
    <input type="search" />
    Search for words in articles
  </label>
</div>

この div をタイトルバーに移動するには、次の CSS が必要です。

.search {
  /* Make sure the `div` stays there, even when scrolling. */
  position: fixed;
  /**
   * Gradient, because why not. Endless opportunities.
   * The gradient ends in `#36c`, which happens to be the app's
   * `<meta name="theme-color" content="#36c">`.
   */
  background-image: linear-gradient(90deg, #36c, #131313, 33%, #36c);
  /* Use the environment variable for the left anchoring with a fallback. */
  left: env(titlebar-area-x, 0);
  /* Use the environment variable for the top anchoring with a fallback. */
  top: env(titlebar-area-y, 0);
  /* Use the environment variable for setting the width with a fallback. */
  width: env(titlebar-area-width, 100%);
  /* Use the environment variable for setting the height with a fallback. */
  height: env(titlebar-area-height, 33px);
}

このコードの効果は、下のスクリーンショットで確認できます。タイトルバーは完全にレスポンシブです。PWA ウィンドウのサイズを変更すると、タイトルバーは通常の HTML コンテンツで構成されているかのように反応します。実際、タイトルバーは HTML コンテンツで構成されています。

タイトルバーに検索バーが表示されたアプリ ウィンドウ。
新しいタイトルバーがアクティブで、応答性があります。

タイトルバーのどの部分をドラッグ可能にするかを決定する

上記のスクリーンショットでは完了したように見えますが、まだ完了していません。ウィンドウ コントロール ボタンはドラッグ領域ではなく、タイトルバーの残りの部分は検索ウィジェットで構成されているため、PWA ウィンドウは(ごく一部を除いて)ドラッグできなくなります。この問題を解決するには、drag の値を持つ app-region CSS プロパティを使用します。具体的なケースでは、input 要素以外のすべてをドラッグ可能にしてもかまいません。

/* The entire search `div` is draggable… */
.search {
  -webkit-app-region: drag;
  app-region: drag;
}

/* …except for the `input`. */
input {
  -webkit-app-region: no-drag;
  app-region: no-drag;
}

この CSS を配置すると、ユーザーは通常どおり divimglabel をドラッグしてアプリ ウィンドウをドラッグできます。input 要素のみがインタラクティブなので、検索クエリを入力できます。

特徴検出

Window Controls Overlay のサポートは、windowControlsOverlay の存在をテストすることで検出できます。

if ('windowControlsOverlay' in navigator) {
  // Window Controls Overlay is supported.
}

windowControlsOverlay を使用してウィンドウ コントロール領域をクエリする

これまでのコードには 1 つ問題があります。一部のプラットフォームではウィンドウ コントロールが右側にあり、他のプラットフォームでは左側にあります。さらに、プラットフォームに応じて Chrome のその他メニューの位置も変わります。つまり、<meta name="theme-color" content="maroon"> によって決定されるタイトルバーの maroon 背景色に溶け込むように、線形グラデーションの背景画像を #131313maroon または maroon#131313maroon に動的に適応させる必要があります。これは、navigator.windowControlsOverlay プロパティで getTitlebarAreaRect() API をクエリすることで実現できます。

if ('windowControlsOverlay' in navigator) {
  const { x } = navigator.windowControlsOverlay.getTitlebarAreaRect();
  // Window controls are on the right (like on Windows).
  // Chrome menu is left of the window controls.
  // [ windowControlsOverlay___________________ […] [_] [■] [X] ]
  if (x === 0) {
    div.classList.add('search-controls-right');
  }
  // Window controls are on the left (like on macOS).
  // Chrome menu is right of the window controls overlay.
  // [ [X] [_] [■] ___________________windowControlsOverlay [⋮] ]
  else {
    div.classList.add('search-controls-left');
  }
} else {
  // When running in a non-supporting browser tab.
  div.classList.add('search-controls-right');
}

変更後のコードでは、以前のように .search クラスの CSS ルールに背景画像を直接指定するのではなく、上記のコードで動的に設定される 2 つのクラスを使用しています。

/* For macOS: */
.search-controls-left {
  background-image: linear-gradient(90deg, #36c, 45%, #131313, 90%, #36c);
}

/* For Windows: */
.search-controls-right {
  background-image: linear-gradient(90deg, #36c, #131313, 33%, #36c);
}

ウィンドウ コントロールのオーバーレイが表示されているかどうかを判断する

ウィンドウ コントロール オーバーレイは、状況によってはタイトルバー領域に表示されません。ウィンドウ コントロール オーバーレイ機能をサポートしていないブラウザでは当然表示されませんが、問題の PWA がタブで実行されている場合も表示されません。この状況を検出するには、windowControlsOverlayvisible プロパティをクエリします。

if (navigator.windowControlsOverlay.visible) {
  // The window controls overlay is visible in the title bar area.
}

または、JavaScript や CSS で display-mode メディアクエリを使用することもできます。

// Create the query list.
const mediaQueryList = window.matchMedia('(display-mode: window-controls-overlay)');

// Define a callback function for the event listener.
function handleDisplayModeChange(mql) {
  // React on display mode changes.
}

// Run the display mode change handler once.
handleDisplayChange(mediaQueryList);

// Add the callback function as a listener to the query list.
mediaQueryList.addEventListener('change', handleDisplayModeChange);
@media (display-mode: window-controls-overlay) { 
  /* React on display mode changes. */ 
}

ジオメトリの変更の通知

getTitlebarAreaRect() でウィンドウ コントロールのオーバーレイ領域をクエリすることは、ウィンドウ コントロールの位置に基づいて正しい背景画像を設定するなどの 1 回限りの処理には十分ですが、他のケースではより細かい制御が必要になります。たとえば、利用可能なスペースに基づいてウィンドウ コントロールのオーバーレイを調整し、十分なスペースがある場合はウィンドウ コントロールのオーバーレイにジョークを追加する、といったユースケースが考えられます。

テキストが短縮された狭いウィンドウで、ウィンドウ コントロールがオーバーレイ領域に表示されている。
狭いウィンドウに合わせて調整されたタイトルバーのコントロール。

navigator.windowControlsOverlay.ongeometrychange をサブスクライブするか、geometrychange イベントのイベント リスナーを設定することで、ジオメトリの変更を通知できます。このイベントは、ウィンドウ コントロール オーバーレイが表示されている場合、つまり navigator.windowControlsOverlay.visibletrue の場合にのみ発生します。

const debounce = (func, wait) => {
  let timeout;
  return function executedFunction(...args) {
    const later = () => {
      clearTimeout(timeout);
      func(...args);
    };
    clearTimeout(timeout);
    timeout = setTimeout(later, wait);
  };
};

if ('windowControlsOverlay' in navigator) {
  navigator.windowControlsOverlay.ongeometrychange = debounce((e) => {
    span.hidden = e.titlebarAreaRect.width < 800;
  }, 250);
}

ongeometrychange に関数を割り当てる代わりに、次のように windowControlsOverlay にイベント リスナーを追加することもできます。この 2 つの違いについては、MDN をご覧ください。

navigator.windowControlsOverlay.addEventListener(
  'geometrychange',
  debounce((e) => {
    span.hidden = e.titlebarAreaRect.width < 800;
  }, 250),
);

タブで実行する場合やサポートされていないブラウザで実行する場合の互換性

考慮すべきケースは次の 2 つです。

  • Window Controls Overlay をサポートしているブラウザでアプリが実行されているが、アプリがブラウザタブで使用されている場合。
  • Window Controls Overlay をサポートしていないブラウザでアプリが実行されている場合。

どちらの場合も、デフォルトでは、ウィンドウ コントロール オーバーレイ用に構築された HTML は通常の HTML コンテンツのようにインラインで表示され、env() 変数のフォールバック値が位置決めに使用されます。対応ブラウザでは、オーバーレイの visible プロパティをチェックし、false が返された場合は、ウィンドウ コントロール オーバーレイ用に指定された HTML を表示しないようにすることもできます。

ブラウザタブで実行されている PWA。ウィンドウ コントロール オーバーレイが本文に表示されています。
タイトルバー用のコントロールを古いブラウザの本文に簡単に表示できます。

サポートしていないブラウザでは、"display_override" ウェブアプリ マニフェスト プロパティがまったく考慮されないか、"window-controls-overlay" が認識されないため、フォールバック チェーンに従って次の可能な値("standalone" など)が使用されます。

スタンドアロン モードで実行されている PWA。ウィンドウ コントロール オーバーレイが本文に表示されています。
タイトルバー用のコントロールを古いブラウザの本文に簡単に表示できます。

UI に関する考慮事項

ウィンドウ コントロール オーバーレイ領域に従来のプルダウン メニューを作成することは可能ですが、おすすめしません。そうすると、ユーザーが画面上部にメニューバー(システム提供のものとカスタムのもの両方)を期待するプラットフォームである macOS の設計ガイドラインに違反することになります。

アプリで全画面表示が提供される場合は、ウィンドウ コントロール オーバーレイを全画面表示の一部にすることが適切かどうかを慎重に検討してください。onfullscreenchange イベントが発生したときにレイアウトを再配置することがあります。

デモ

さまざまな対応ブラウザと非対応ブラウザ、インストール済みと未インストールの状態で試せるデモを作成しました。実際のウィンドウ コントロール オーバーレイ エクスペリエンスを利用するには、アプリをインストールする必要があります。以下に、期待されるエクスペリエンスの 2 つのスクリーンショットを示します。アプリのソースコードは Glitch で入手できます。

ウィンドウ コントロール オーバーレイを使用した Wikimedia Featured Content デモアプリ。
デモアプリはテストに利用できます。

ウィンドウ コントロールのオーバーレイの検索機能は完全に機能します。

Wikimedia Featured Content デモアプリ。ウィンドウ コントロール オーバーレイと「cleopa…」という検索語句のアクティブな検索が実行され、一致する語句「Cleopatra」を含む記事の 1 つがハイライト表示されている。
ウィンドウ コントロール オーバーレイを使用した検索機能。

セキュリティ上の考慮事項

Chromium チームは、強力なウェブ プラットフォーム機能へのアクセスの制御で定義されているユーザー制御、透明性、人間工学などの基本原則を使用して、Window Controls Overlay API を設計、実装しました。

なりすまし

サイトにタイトルバーの制御を部分的に許可すると、デベロッパーが以前は信頼できるブラウザ制御の領域でコンテンツをスプーフィングする余地が生まれます。現在、Chromium ブラウザのスタンドアロン モードにはタイトルバーが含まれており、初回起動時には、左側にウェブページのタイトル、右側にページのオリジンが表示されます(その後に [設定など] ボタンとウィンドウ コントロールが続きます)。数秒後に元のテキストが消えます。ブラウザが右から左(RTL)の言語に設定されている場合、このレイアウトは反転され、元のテキストが左側に表示されます。これにより、ウィンドウ コントロール オーバーレイが開き、オーバーレイの右端とオリジンとの間に十分なパディングがない場合にオリジンをスプーフィングします。たとえば、送信元「evil.ltd」に信頼できるサイト「google.com」が追加され、ユーザーが送信元を信頼できると信じてしまう可能性があります。この元のテキストは、ユーザーがアプリの出所を把握し、期待どおりであることを確認できるように、そのまま残す予定です。RTL が構成されたブラウザでは、悪意のあるウェブサイトが安全でないオリジンを信頼できるオリジンに追加するのを防ぐために、オリジン テキストの右側に十分なパディングが必要です。

フィンガープリント

ウィンドウ コントロールのオーバーレイとドラッグ可能な領域を有効にしても、機能検出以外のプライバシー上の大きな問題は発生しません。ただし、オペレーティング システムによってウィンドウ コントロール ボタンのサイズと位置が異なるため、navigator.windowControlsOverlay.getTitlebarAreaRect() メソッドは、ブラウザが実行されているオペレーティング システムに関する情報を明らかにする位置と寸法を持つ DOMRect を返します。現在、デベロッパーはユーザー エージェント文字列から OS を検出できますが、フィンガープリントに関する懸念から、UA 文字列のフリーズと OS バージョンの統合について議論されています。ブラウザ コミュニティでは、ウィンドウ コントロール オーバーレイのサイズがプラットフォーム間でどの程度の頻度で変化するかを把握するための取り組みが進行中です。現在の仮定では、これらのサイズは OS バージョン間でかなり安定しているため、マイナー OS バージョンの観察には役立たないと考えられています。これはフィンガープリントの問題になる可能性がありますが、カスタム タイトルバー機能を使用するインストール済み PWA にのみ適用され、一般的なブラウザの使用には適用されません。また、navigator.windowControlsOverlay API は PWA 内に埋め込まれた iframe では利用できません。

PWA 内で別のオリジンに移動すると、上記の条件を満たし、ウィンドウ コントロール オーバーレイで起動された場合でも、通常のスタンドアロン タイトルバーに戻ります。これは、別のオリジンに移動したときに表示される黒いバーに対応するためです。元のオリジンに戻ると、ウィンドウ コントロール オーバーレイが再び使用されます。

オリジン外のナビゲーション用の黒い URL バー。
ユーザーが別のオリジンに移動すると、黒いバーが表示されます。

フィードバック

Chromium チームは、Window Controls Overlay API の使用感について皆様のご意見をお待ちしています。

API 設計について教えてください

API が想定どおりに動作しない点はありますか?アイデアを実装するために必要なメソッドやプロパティが不足している場合はどうすればよいですか?セキュリティ モデルについてご質問やご意見がある場合は、対応する GitHub リポジトリで仕様に関する問題を報告するか、既存の問題に意見を追加します。

実装に関する問題を報告する

Chromium の実装でバグが見つかりましたか?それとも、実装が仕様と異なるのでしょうか?new.crbug.com でバグを報告します。できるだけ詳細な情報、再現手順を記載し、[Components](コンポーネント)ボックスに UI>Browser>WebAppInstalls と入力してください。

API のサポートを表示する

Window Controls Overlay API を使用する予定はありますか?公開サポートは、Chromium チームが機能の優先順位を決定するのに役立ち、他のブラウザ ベンダーにサポートの重要性を示すことができます。

#WindowControlsOverlay ハッシュタグを付けて @ChromiumDev にツイートし、どこでどのように使用しているかをお知らせください。

関連情報

謝辞

Window Controls Overlay は、Microsoft Edge チームの Amanda Baker によって実装され、指定されました。この記事は、Joe MedleyKenneth Rohde Christiansen によってレビューされました。ヒーロー画像: UnsplashSigmund