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

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

PWA をアプリのようにするという記事で、アプリのようなエクスペリエンスを実現するための戦略として、アプリのタイトルバーをカスタマイズする方法について説明しました。macOS の Podcasts アプリの例を次に示します。

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

ポッドキャストはブラウザで実行されないプラットフォーム固有の macOS アプリであり、ブラウザのルールに従うことなく自由に動作できるため、異議を唱えたくなるかもしれません。確かにそうですが、この記事で説明するウィンドウ コントロール オーバーレイ機能により、まもなく PWA でも同様のユーザー インターフェースを作成できるようになります。

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

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

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

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

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

現在のステータス

ステップ ステータス
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 がインストールされたオリジンと一致している。

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

空のタイトルバーと左側のウィンドウ コントロールが表示されたアプリ ウィンドウ。
カスタム コンテンツを表示する空のタイトルバー。

コンテンツをタイトルバーに移動する

タイトルバーにスペースができたので、何かを移動できます。この記事では、ウィキメディアの注目コンテンツの 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 コンテンツで構成されているかのように動作します。実際はそうである場合もあります。

タイトルバーに検索バーが表示されたアプリ ウィンドウ。
新しいタイトルバーはアクティブでレスポンシブです。

タイトルバーのどの部分をドラッグできるかを判断する

上記のスクリーンショットでは完了しているように見えますが、まだ完了していません。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 要素のみがインタラクティブであるため、検索クエリを入力できます。

特徴検出

ウィンドウ コントロール オーバーレイのサポートは、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 をサポートするブラウザでアプリが実行されているが、アプリがブラウザのタブで使用されている場合。
  • ウィンドウ コントロール オーバーレイをサポートしていないブラウザでアプリが実行されている場合。

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

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

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

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

UI に関する考慮事項

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

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

デモ

サポートされているブラウザとサポートされていないブラウザ、インストールされている状態とインストールされていない状態のさまざまな環境で試すことができるデモを作成しました。実際のウィンドウ コントロール オーバーレイ エクスペリエンスを確認するには、アプリをインストールする必要があります。以下に、どのような画面が表示されるかを示すスクリーンショットを 2 枚示します。アプリのソースコードは Glitch で入手できます。

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

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

ウィンドウ コントロール オーバーレイと「cleopa…」というキーワードのアクティブな検索を備えた Wikimedia 注目コンテンツのデモアプリ。一致するキーワード「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 でバグを報告します。できるだけ詳細な情報を含め、再現手順を簡単に説明してください。[コンポーネント] ボックスに UI>Browser>WebAppInstalls を入力します。Glitch は、簡単な再現手順をすばやく共有するのに適しています。

API のサポートを表示する

Window Controls Overlay API を使用する予定はありますか?公開サポートは、Chromium チームが機能の優先順位付けを行ううえで役立ち、他のブラウザ ベンダーに、その機能のサポートがどれほど重要であるかを示します。

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

関連情報

謝辞

ウィンドウ コントロール オーバーレイは、Microsoft Edge チームの Amanda Baker によって実装および指定されました。この記事は、Joe MedleyKenneth Rohde Christiansen が確認しました。UnsplashSigmund によるヒーロー画像。