PWA 제목 표시줄의 창 컨트롤 오버레이 맞춤설정

창 컨트롤 옆의 제목 표시줄을 사용하여 PWA를 앱처럼 연출할 수 있습니다.

PWA를 앱처럼 연출하기 도움말을 기억하시나요? 앱과 더 유사한 환경을 만들기 위한 전략으로 앱의 제목 표시줄 맞춤설정을 설명한 방법을 떠올려 보세요. 다음은 macOS 팟캐스트 앱을 표시하는 방법의 예입니다.

현재 재생 중인 팟캐스트에 관한 미디어 컨트롤 버튼과 메타데이터를 보여주는 macOS 팟캐스트 앱 제목 표시줄
맞춤 제목 표시줄을 사용하면 PWA가 플랫폼별 앱처럼 느껴집니다.

이제 팟캐스트가 브라우저에서 실행되지 않는 플랫폼별 macOS 앱이므로 브라우저 규칙에 따라 재생하지 않고도 원하는 작업을 할 수 있다고 말하면서 이의를 제기하고 싶을 수 있습니다. 맞습니다. 하지만 다행인 점은 이 도움말에서 다루는 창 컨트롤 오버레이 기능을 통해 곧 PWA에 유사한 사용자 인터페이스를 만들 수 있다는 것입니다.

창 컨트롤 오버레이 구성요소

창 컨트롤 오버레이는 다음 4가지 하위 기능으로 구성됩니다.

  1. 웹 앱 매니페스트의 "display_override" 필드에 대한 "window-controls-overlay"
  2. CSS 환경 변수 titlebar-area-x, titlebar-area-y, titlebar-area-width, titlebar-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가 설치된 출처와 일치합니다.

그 결과, 운영체제에 따라 왼쪽이나 오른쪽에 일반 창 컨트롤이 있는 빈 제목 표시줄 영역이 생성됩니다.

왼쪽에 창 컨트롤이 있는 빈 제목 표시줄이 있는 앱 창
맞춤 콘텐츠를 위한 빈 제목 표시줄

제목 표시줄로 콘텐츠 이동

이제 제목 표시줄에 공간이 생겼으므로 이제 제목 표시줄로 항목을 이동할 수 있습니다. 이 문서에서는 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 창은 더 이상 드래그할 수 없습니다 (매우 작은 영역 제외). 창 컨트롤 버튼은 드래그 영역이 아니고 제목 표시줄의 나머지 부분은 검색 위젯으로 구성되어 있기 때문입니다. 값이 dragapp-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를 배치하면 사용자는 평소와 같이 div, img 또는 label를 드래그하여 앱 창을 드래그할 수 있습니다. input 요소만 상호작용하므로 검색어를 입력할 수 있습니다.

기능 감지

창 컨트롤 오버레이 지원은 windowControlsOverlay의 존재 여부를 테스트하여 감지할 수 있습니다.

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

windowControlsOverlay로 창 제어 영역 쿼리

지금까지 코드에는 한 가지 문제가 있습니다. 즉, 일부 플랫폼에서는 창 컨트롤이 오른쪽에 있고 다른 플랫폼에서는 왼쪽에 있습니다. 더 심각한 문제는 플랫폼에 따라 Chrome 메뉴 '점 3개'의 위치도 변경됩니다. 즉, 선형 그라데이션 배경 이미지는 #131313maroon 또는 maroon#131313maroon에서 실행되도록 동적으로 조정되어 제목 표시줄의 maroon 배경 색상(<meta name="theme-color" content="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 규칙에 배경 이미지를 직접 포함하는 대신 이제 수정된 코드는 위의 코드에서 동적으로 설정하는 두 개의 클래스를 사용합니다.

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

또는 자바스크립트 또는 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()를 사용하여 창 컨트롤 오버레이 영역을 쿼리하면 창 컨트롤의 위치에 따라 올바른 배경 이미지를 설정하는 것과 같이 일회성으로 충분할 수 있지만 다른 경우에는 더 세분화된 제어가 필요합니다. 예를 들어 사용 가능한 공간에 기반하여 창 컨트롤 오버레이를 조정하고 공간이 충분할 때 창 컨트롤 오버레이에 바로 농담을 추가하는 것이 가능한 사용 사례일 수 있습니다.

짧은 텍스트와 함께 창 컨트롤이 좁은 창의 영역을 오버레이합니다.
좁은 창에 맞게 조정된 제목 표시줄 컨트롤.

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에 이벤트 리스너를 추가할 수도 있습니다. 이 둘의 차이는 MDN에서 확인할 수 있습니다.

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

탭 및 지원되지 않는 브라우저에서 실행 시 호환성

다음과 같은 두 가지 경우가 있습니다.

  • 앱이 창 제어 오버레이를 지원하는 브라우저에서 실행되지만 브라우저 탭에서 앱이 사용되는 경우입니다.
  • 창 컨트롤 오버레이를 지원하지 않는 브라우저에서 앱이 실행되는 경우입니다.

두 경우 모두 기본적으로 창 컨트롤 오버레이용으로 빌드된 HTML은 일반 HTML 콘텐츠와 같이 인라인으로 표시되며 env() 변수의 대체 값이 배치에 사용됩니다. 지원되는 브라우저에서 오버레이의 visible 속성을 확인하고 false를 보고하는 경우 HTML 콘텐츠를 숨기는 방식으로 창 컨트롤 오버레이용으로 지정된 HTML을 표시하지 않도록 결정할 수도 있습니다.

브라우저 탭에서 실행 중인 PWA로, 본문에 창 컨트롤 오버레이가 표시되어 있습니다.
이전 브라우저에서는 제목 표시줄을 위한 컨트롤이 본문에 쉽게 표시될 수 있습니다.

참고로, 지원되지 않는 브라우저는 "display_override" 웹 앱 매니페스트 속성을 전혀 고려하지 않거나 "window-controls-overlay"를 인식하지 않으므로 대체 체인에 따라 가능한 다음 값을 사용합니다(예: "standalone").

본문에 창 컨트롤 오버레이가 표시된 독립형 모드로 실행되는 PWA
이전 브라우저에서는 제목 표시줄을 위한 컨트롤이 본문에 쉽게 표시될 수 있습니다.

UI 고려사항

흥미로울 수도 있지만, Window Controls Overlay 영역에서 기존 드롭다운 메뉴를 만들지 않는 것이 좋습니다. 이렇게 하면 사용자가 화면 상단에 메뉴 바 (시스템에서 제공하는 메뉴 바와 맞춤 메뉴 바 모두)를 기대하는 플랫폼인 macOS의 디자인 가이드라인을 위반하게 됩니다.

앱에서 전체 화면 환경을 제공한다면 창 컨트롤 오버레이를 전체 화면 뷰의 일부로 포함하는 것이 적절한지 신중하게 고려하세요. onfullscreenchange 이벤트가 실행될 때 레이아웃을 다시 정렬하는 것이 좋습니다.

데모

지원되는 브라우저와 지원되지 않는 다양한 브라우저와 설치 및 비설치 상태에서 사용해 볼 수 있는 데모를 만들었습니다. 실제 Window Controls Overlay 환경을 사용하려면 앱을 설치해야 합니다. 아래에서 예상되는 두 가지 스크린샷을 확인할 수 있습니다. 앱의 소스 코드는 Glitch에서 사용할 수 있습니다.

창 컨트롤 오버레이가 표시된 Wikimedia 추천 콘텐츠 데모 앱
데모 앱을 실험할 수 있습니다.

창 컨트롤 오버레이의 검색 기능은 완벽하게 작동합니다.

창 컨트롤 오버레이가 표시되어 있고 &#39;cleopa...&#39;라는 검색어를 사용하며 검색된 &#39;Cleopatra&#39;가 포함된 기사 중 하나를 강조 표시하는 Wikimedia 추천 콘텐츠 데모 앱
창 컨트롤 오버레이를 사용하는 검색 기능

보안 고려사항

Chromium팀은 사용자 제어, 투명성, 인체공학 등 강력한 웹 플랫폼 기능에 대한 액세스 제어에 정의된 핵심 원칙을 사용하여 Window Controls Overlay API를 설계하고 구현했습니다.

스푸핑

사이트에 제목 표시줄을 부분적으로 제어할 수 있는 권한을 주면 개발자가 이전에 신뢰할 수 있고 브라우저 제어 영역이었던 영역에서 콘텐츠를 스푸핑할 여지를 남겨두게 됩니다. 현재 Chromium 브라우저에서 독립형 모드에는 최초 실행 시 왼쪽에 웹페이지의 제목을 표시하고 오른쪽에 페이지의 출처('설정 및 기타' 버튼과 창 컨트롤)를 표시하는 제목 표시줄이 있습니다. 몇 초 후 원본 텍스트가 사라집니다. 브라우저가 RTL(오른쪽에서 왼쪽) 언어로 설정되어 있으면 원본 텍스트가 왼쪽에 오도록 이 레이아웃이 뒤집힙니다. 그러면 원점과 오버레이의 오른쪽 가장자리 사이에 패딩이 충분하지 않은 경우 원점을 스푸핑하는 창 컨트롤 오버레이가 열립니다. 예를 들어 출처 'evil.ltd'를 신뢰할 수 있는 사이트 'google.com'과 함께 추가하면 사용자는 해당 소스를 신뢰할 수 있다고 믿게 됩니다. 사용자가 앱의 출처를 알고 예상과 일치하는지 확인할 수 있도록 이 출처 텍스트를 유지할 계획입니다. RTL로 구성된 브라우저의 경우 악성 웹사이트가 신뢰할 수 있는 출처의 안전하지 않은 출처를 추가하지 못하도록 출처 텍스트 오른쪽에 충분한 패딩이 있어야 합니다.

디지털 지문 수집

창 컨트롤 오버레이와 드래그 가능한 영역을 사용 설정하면 기능 감지 외에는 상당한 개인 정보 보호 문제가 발생하지 않습니다. 그러나 운영체제마다 창 컨트롤 버튼의 크기와 위치가 다르기 때문에 navigator.windowControlsOverlay.getTitlebarAreaRect() 메서드는 DOMRect를 반환합니다. 이 반환의 위치와 크기는 브라우저가 실행되는 운영체제에 관한 정보를 보여줍니다. 현재 개발자는 이미 사용자 에이전트 문자열에서 OS를 찾을 수 있지만 디지털 지문 수집 문제로 인해 UA 문자열 고정 및 OS 버전 통합에 대한 논의가 있습니다. 브라우저 커뮤니티에서는 창 컨트롤의 크기가 플랫폼 간에 얼마나 자주 변경되는지 파악하기 위해 지속적으로 노력하고 있습니다. 현재 이 컨트롤은 OS 버전 간에 상당히 안정적이어서 부 OS 버전을 관찰하는 데 유용하지 않다고 가정하기 때문입니다. 이는 디지털 지문 수집의 잠재적인 문제이지만 맞춤 제목 표시줄 기능을 사용하는 설치된 PWA에만 적용되며 일반적인 브라우저 사용에는 적용되지 않습니다. 또한 PWA 내부에 삽입된 iframe에는 navigator.windowControlsOverlay API를 사용할 수 없습니다.

PWA 내에서 다른 출처로 이동하면 위의 기준을 충족하고 창 컨트롤 오버레이로 실행된 경우에도 일반적인 독립형 제목 표시줄로 돌아갑니다. 이는 다른 원점으로 이동하는 내비게이션에 표시되는 검은색 막대에 맞추기 위함입니다. 원래 출처로 다시 이동하면 창 컨트롤 오버레이가 다시 사용됩니다.

외부 탐색용 검은색 URL 표시줄
사용자가 다른 출발지로 이동하면 검은색 막대가 표시됩니다.

의견

Chromium팀은 Window Controls Overlay API 사용 경험에 관한 의견을 듣고자 합니다.

API 설계에 대해 알려주세요.

API에서 예상한 대로 작동하지 않는 부분이 있나요? 아니면 아이디어를 구현하는 데 필요한 메서드나 속성이 누락되었나요? 보안 모델에 대한 질문이나 의견이 있으신가요? 관련 GitHub 저장소에서 사양 문제를 제출하거나 기존 문제에 대한 의견을 추가하세요.

구현 관련 문제 신고

Chromium 구현에서 버그를 발견하셨나요? 아니면 구현이 사양과 다른가요? new.crbug.com에서 버그를 신고합니다. 가능한 한 많은 세부정보와 간단한 재현 안내를 포함하고 구성요소 상자에 UI>Browser>WebAppInstalls를 입력합니다. Glitch는 쉽고 빠른 재현을 공유하는 데 효과적입니다.

API 지원 표시

Window Controls Overlay API를 사용할 계획인가요? 공개 지원은 Chromium팀이 기능의 우선순위를 정하는 데 도움이 되며 다른 브라우저 공급업체에 이러한 기능을 지원하는 것이 얼마나 중요한지 보여줍니다.

#WindowControlsOverlay 해시태그와 함께 @ChromiumDev로 트윗을 보내 해시태그를 어디에 어떻게 사용했는지 알려주세요.

유용한 링크

감사의 말

창 제어 오버레이는 Microsoft Edge팀의 Amanda Baker가 구현 및 지정했습니다. 이 문서는 Joe 메들리케네스 로데 크리스티안슨이 검토했습니다. Unsplash에 있는 Sigmund의 히어로 이미지