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

ウィンドウ コントロールの横にあるタイトルバー エリアを使用すると、PWA がアプリのように感じられます。

PWA をアプリの使い心地にする」の記事を覚えている方は、アプリに近いエクスペリエンスを作成するための戦略として、アプリのタイトルバーのカスタマイズについて述べたのを覚えているかもしれません。以下は macOS Podcasts アプリの例です

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

ここで、Podcasts はプラットフォーム固有の macOS アプリであり、ブラウザでは実行されないため、ブラウザのルールに従って再生しなくても必要なことを実行できると言い、反対したくなるかもしれません。確かにそうですが、幸いなことに、この記事のトピックであるウィンドウ コントロール オーバーレイ機能を使用すると、まもなく 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)でアプリの操作性を実現できます。これにより、以前はブラウザで制御されていたタイトルバーの領域にカスタム コンテンツを配置できます。

現在のステータス

ステップ ステータス
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 コンテンツで構成されているかのように反応します。

タイトルバーに検索バーがあるアプリ ウィンドウ。
新しいタイトルバーが有効でレスポンシブになります。

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

上のスクリーンショットを見ると、この作業は完了していることがわかりますが、まだ完了ではありません。ウィンドウ コントロールのボタンはドラッグ領域ではなく、タイトルバーの残りの部分は検索ウィジェットで構成されるため、PWA ウィンドウは(非常に小さな領域を除き)ドラッグできなくなります。これを修正するには、CSS プロパティ app-region の値を drag に設定します。具体的なケースでは、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 を配置すると、ユーザーは divimg、または label をドラッグして、通常どおりアプリ ウィンドウをドラッグできます。input 要素のみがインタラクティブであるため、検索クエリを入力できます。

機能検出

ウィンドウ コントロール オーバーレイのサポートは、windowControlsOverlay の存在をテストすることで検出できます。

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

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

これまでのコードには問題が 1 つあります。それは、ウィンドウ コントロールが右側に配置されているプラットフォームと左側に配置されていることです。さらに悪いことに、Chrome メニューの「その他」の位置も、プラットフォームに応じて変わります。つまり、線形グラデーションの背景画像は、#131313maroon または maroon#131313maroon で実行されるように動的に調整し、<meta name="theme-color" content="maroon"> によって決定されるタイトルバーの maroon 背景色に溶け込ませる必要があります。これを行うには、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 つのケースが考えられます。

  • ウィンドウ コントロール オーバーレイをサポートしているブラウザでアプリが実行されているが、そのアプリがブラウザタブで使用されている場合。
  • ウィンドウ コントロール オーバーレイをサポートしていないブラウザでアプリが実行されている場合。

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

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

非対応のブラウザは、ウェブアプリ マニフェスト プロパティ "display_override" をまったく考慮しないか、"window-controls-overlay" を認識しないため、フォールバック チェーンに応じて次の有効な値("standalone" など)を使用します。

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

UI に関する考慮事項

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

アプリで全画面表示を提供する場合は、ウィンドウ コントロール オーバーレイを全画面ビューに含めることが妥当かどうかを慎重に検討してください。onfullscreenchange イベントが発生したときに、レイアウトを再配置することをおすすめします。

デモ

さまざまなサポート ブラウザと非サポート ブラウザ、インストール状態と非インストール状態でプレイできるデモを作成しました。ウィンドウ コントロール オーバーレイを実際に使用するには、アプリをインストールする必要があります。以下に、想定される内容を示す 2 つのスクリーンショットを示します。アプリのソースコードは Glitch で入手できます。

ウィンドウ コントロール オーバーレイが表示された Wikimedia の注目コンテンツのデモアプリ。
デモアプリで試験運用版を試すことができます。

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

ウィンドウ コントロール オーバーレイと「cleopa...」という単語のアクティブ検索により、一致したキーワード「Cleopatra」を含む記事の 1 つがハイライト表示されている、Wikimedia の注目コンテンツのデモアプリ。
ウィンドウ コントロール オーバーレイを使用した検索機能

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

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」と入力します。Glitch を使えば、再現をすばやく簡単に共有できます。

API のサポートを表示する

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

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

関連情報

謝辞

ウィンドウ コントロール オーバーレイは、Microsoft Edge チームの Amanda Baker によって実装、指定されました。この記事は、Joe MedleyKenneth Rohde Christiansen によってレビューされました。ヒーロー画像(作成者: Sigmund、出典: Unsplash