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

창 컨트롤 옆에 있는 제목 표시줄 영역을 사용하여 PWA가 앱처럼 느껴지도록 합니다.

PWA를 앱처럼 느끼게 만들기 도움말을 기억하시면 앱과 같은 환경을 만드는 전략으로 앱의 제목 표시줄 맞춤설정을 언급한 것을 기억하실 겁니다. 다음은 macOS 팟캐스트 앱을 보여주는 예입니다.

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

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

Window Controls Overlay는 다음 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 구성원을 통해 창 컨트롤 영역을 쿼리하고 해결하는 메커니즘입니다.

Window Controls Overlay(창 컨트롤 오버레이)란 무엇인가요?

제목 표시줄 영역은 창 컨트롤 (최소화, 최대화, 닫기 등의 버튼)의 왼쪽 또는 오른쪽에 있는 공간을 나타내며 애플리케이션의 제목이 포함되는 경우가 많습니다. 창 컨트롤 오버레이를 사용하면 프로그레시브 웹 애플리케이션 (PWA)이 기존의 전체 너비 제목 표시줄을 창 컨트롤이 포함된 작은 오버레이로 전환하여 더 앱 같은 느낌을 제공할 수 있습니다. 이를 통해 개발자는 이전에 브라우저에서 제어하던 제목 표시줄 영역에 맞춤 콘텐츠를 배치할 수 있습니다.

현재 상태

단계 상태
1. 설명 동영상 만들기 완전함
2. 사양의 초안 작성 완전함
3. 의견 수집 및 디자인 반복 진행 중
4. 오리진 트라이얼 완료
5. 출시 완료 (Chromium 104)

Window Controls Overlay 사용 방법

웹 앱 매니페스트에 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 창은 더 이상 드래그할 수 없습니다 (매우 작은 영역 제외). app-region CSS 속성을 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를 사용하면 사용자는 div, img 또는 label를 드래그하여 평소와 같이 앱 창을 드래그할 수 있습니다. input 요소만 상호작용이 가능하므로 검색어를 입력할 수 있습니다.

기능 감지

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

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

windowControlsOverlay로 창 컨트롤 영역 쿼리

지금까지 코드에는 한 가지 문제가 있습니다. 일부 플랫폼에서는 창 컨트롤이 오른쪽에 있고 다른 플랫폼에서는 왼쪽에 있습니다. 설상가상으로 '점 3개' 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 규칙에 배경 이미지를 직접 포함하는 대신 수정된 코드는 이제 위의 코드가 동적으로 설정하는 두 클래스를 사용합니다.

/* 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()를 사용하여 창 컨트롤 오버레이 영역을 쿼리하면 창 컨트롤의 위치에 따라 올바른 배경 이미지를 설정하는 등의 일회성 작업에는 충분하지만, 다른 경우에는 더 세부적인 제어가 필요합니다. 예를 들어 사용 가능한 공간에 따라 창 컨트롤 오버레이를 조정하고 공간이 충분할 때 창 컨트롤 오버레이에 바로 농담을 추가하는 것이 가능합니다.

좁은 창에서 텍스트가 잘린 창 컨트롤 오버레이 영역
좁은 창에 맞게 조정된 제목 표시줄 컨트롤

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

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

고려할 수 있는 두 가지 사례는 다음과 같습니다.

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

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

본문에 창 컨트롤 오버레이가 표시된 브라우저 탭에서 실행 중인 PWA
제목 표시줄용 컨트롤이 이전 브라우저의 본문에 쉽게 표시될 수 있습니다.

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

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

UI 고려사항

창 컨트롤 오버레이 영역에 기존 드롭다운 메뉴를 만드는 것은 좋지 않습니다. 이렇게 하면 사용자가 화면 상단에 메뉴 바 (시스템 제공 메뉴 바 및 맞춤 메뉴 바 모두)가 있을 것으로 예상하는 플랫폼인 macOS의 디자인 가이드라인을 위반하게 됩니다.

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

데모

지원되는 브라우저와 지원되지 않는 브라우저, 설치된 상태와 설치되지 않은 상태에서 모두 사용할 수 있는 데모를 만들었습니다. 실제 창 컨트롤 오버레이 환경을 사용하려면 앱을 설치해야 합니다. 아래에서 예상되는 환경을 보여주는 두 개의 스크린샷을 확인할 수 있습니다. 앱의 소스 코드는 Glitch에서 확인할 수 있습니다.

창 컨트롤 오버레이가 있는 Wikimedia 추천 콘텐츠 데모 앱
데모 앱을 실험용으로 사용할 수 있습니다.

창 컨트롤 오버레이의 검색 기능이 완전히 작동합니다.

창 컨트롤 오버레이와 &#39;cleopa…&#39;라는 검색어를 검색하는 Wikimedia 추천 콘텐츠 데모 앱으로, 일치하는 검색어인 &#39;클레오파트라&#39;가 포함된 기사 중 하나가 강조 표시되어 있습니다.
창 컨트롤 오버레이를 사용하는 검색 기능입니다.

보안 고려사항

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

스푸핑

사이트에 제목 표시줄의 일부 제어 권한을 부여하면 개발자가 이전에 신뢰할 수 있는 브라우저 제어 영역이었던 영역에서 콘텐츠를 스푸핑할 여지가 생깁니다. 현재 Chromium 브라우저의 독립형 모드에는 첫 실행 시 왼쪽에 웹페이지 제목을, 오른쪽에 페이지 출처를 표시하는 제목 표시줄이 포함되어 있습니다('설정 및 기타' 버튼과 창 컨트롤이 뒤에 표시됨). 몇 초 후 원본 텍스트가 사라집니다. 브라우저가 오른쪽에서 왼쪽(RTL) 언어로 설정된 경우 이 레이아웃은 원본 텍스트가 왼쪽에 오도록 뒤집힙니다. 그러면 창 컨트롤 오버레이가 열려 출처와 오버레이의 오른쪽 가장자리 사이에 패딩이 충분하지 않은 경우 출처를 스푸핑합니다. 예를 들어 출처 'evil.ltd'에 신뢰할 수 있는 사이트 'google.com'이 추가되어 사용자가 출처를 신뢰할 수 있다고 생각하게 될 수 있습니다. Google은 사용자가 앱의 출처를 파악하고 기대에 부합하는지 확인할 수 있도록 이 출처 텍스트를 유지할 계획입니다. 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에 트윗을 보내고 사용 위치와 사용 방법을 알려주세요.

유용한 링크

감사의 말씀

Window Controls Overlay는 Microsoft Edge팀의 아만다 베이커가 구현하고 지정했습니다. 이 도움말은 조 미들리케네스 로데 크리스티안센이 검토했습니다. UnsplashSigmund님 제공 히어로 이미지