개발자 의견 요청: 포커스 그룹

Jacques Newman
Jacques Newman

게시일: 2026년 3월 5일

focusgroup HTML 속성은 roving-tabindex JavaScript를 작성하지 않고도 툴바, tablist, 메뉴, listbox 등 복합 위젯에 키보드 화살표 키 탐색을 추가하는 선언적 방법입니다. 하나의 속성이 수백 줄의 상용구를 대체합니다. 이 기능이 출시되기 전에 의견을 보내주세요.

사용해 보고 의견을 보내주세요

다음 두 가지 방법 중 하나로 사용 설정하여 Chrome, Edge 및 기타 Chromium 브라우저에서 focusgroup를 사용해 볼 수 있습니다.

  1. 로컬 테스트: 브라우저에서 about://flags 페이지를 열고 실험용 웹 플랫폼 기능 플래그를 사용 설정합니다. 또는 --enable-blink-features=Focusgroup 명령줄 매개변수를 사용하여 명령줄에서 브라우저를 실행합니다.
  2. 오리진 트라이얼: focusgroup 오리진 트라이얼에 등록하여 실제 사용자와 함께 사이트에서 테스트합니다.

그런 다음 대화형 데모를 살펴보고 모든 패턴이 실제로 어떻게 작동하는지 확인하세요.

여러분의 의견이 필요합니다. 포커스 그룹 문제를 신고하여 의견을 알려주세요.

크로스 브라우저 노력: 이 제안은 Microsoft에서 OpenUI 커뮤니티 그룹을 통해 시작되었으며 Google의 강력한 지원을 받고 있습니다. API 모양은 사용자 의견에 따라 변경될 수 있습니다. focusgroup이 해결하는 문제와 API의 작동 방식을 자세히 살펴보겠습니다.

문제: 수동 로빙 tabindex

툴바, 탭 목록, 메뉴 또는 목록 상자를 빌드한 적이 있다면 이 코드의 일부 버전을 작성한 것입니다. ARIA 제작 관행 가이드 (APG)에서는 복합 위젯이 단일 탭 정지를 표시하고 사용자가 화살표 키로 항목 간에 이동할 수 있도록 하는 것이 좋습니다. 이 패턴을 '이동하는 tabindex'라고 합니다. 많은 UI 프레임워크가 처음부터 다시 구현합니다.

<div role="toolbar" aria-label="Text formatting" id="toolbar">
  <button type="button" tabindex="0">Bold</button>
  <button type="button" tabindex="-1">Italic</button>
  <button type="button" tabindex="-1">Underline</button>
  <button type="button" tabindex="-1">Strikethrough</button>
</div>

여기에서 개발자는 화살표 키를 수신 대기하여 포커스를 이동하고 모든 요소의 tabindex 속성을 조정하는 JavaScript를 사용해야 합니다. 간소화된 버전입니다. 프로덕션 구현은 다음도 처리해야 합니다.

  • 쓰기 모드 및 RTL: 콘텐츠 방향에 따라 화살표 키 방향을 조정합니다.
  • 마지막 포커스 메모리: 사용자가 탭으로 돌아갈 때 이전에 활성 상태였던 항목으로 포커스를 복원합니다.
  • 사용 중지되고 숨겨진 항목: 탐색 중에 건너뜁니다.
  • 동적 항목: 항목이 추가되거나 삭제될 때 이동 색인을 업데이트합니다.

React, Angular CDK, Fluent UI를 비롯한 대부분의 UI 라이브러리는 각각 자체 버전의 이 로직을 제공합니다. 플랫폼 기본 요소가 될 수 있는 것을 얻기 위해 중복된 노력을 많이 기울여야 합니다.

해결 방법: focusgroup 속성

focusgroup을 사용하면 동일한 툴바가 다음과 같이 됩니다.

<div focusgroup="toolbar" aria-label="Text formatting">
  <button type="button">Bold</button>
  <button type="button">Italic</button>
  <button type="button">Underline</button>
  <button type="button">Strikethrough</button>
</div>
이탤릭체 버튼에 포커스가 있는 메뉴 바

라이브로 사용해 보기: 툴바 패턴 > 기본 툴바 이상입니다. 화살표 키 탐색을 위한 JavaScript가 없습니다. 수동 tabindex 관리 없음 이제 브라우저에서 처리하는 작업은 다음과 같습니다.

  • 화살표 키 탐색: 쓰기 모드와 방향을 고려하여 항목 간에 이동합니다.
  • 단일 탭 정지: 브라우저가 참여 항목을 하나의 탭 정지로 자동으로 축소합니다. 개발자는 활성 상태가 아닌 항목에 tabindex="-1"를 설정할 필요가 없습니다.
  • 마지막 포커스 기억: 사용자가 focusgroup을 떠났다가 돌아오면 포커스가 떠날 때 있던 항목으로 복원됩니다.
  • ARIA 시맨틱: 일반 요소가 사용될 때 선택한 동작에 따라 브라우저에서 적절한 역할 (예: role="toolbar")을 제공합니다.

개발자는 눌린 상태 전환, 메뉴 열기, 선택 관리, 맞춤 명령어 등 기능에 고유한 로직만 유지합니다.

API 개요

focusgroup 속성은 공백으로 구분된 토큰 목록을 사용합니다. 첫 번째 토큰은 항상 위젯 패턴을 선언하는 동작 토큰입니다. 선택적 수정자 토큰은 focusgroup="<behavior> [inline|block] [wrap] [nomemory]"입니다.

행동 토큰

동작 토큰은 필요합니다 (none를 사용하여 상위 포커스 그룹을 선택 해제하지 않는 한). 이는 복합 위젯 패턴을 선언하여 달리 지정되지 않은 경우 올바른 역할을 추론할 수 있도록 합니다. 토큰은 Aria 작성 관행 가이드에 설명된 패턴을 따르며 다음 표에 나열되어 있습니다.

동작 APG 패턴 최소 컨테이너 역할 (적용된 경우) 최소 하위 역할
(적용된 경우)
기본 수정자
toolbar 툴바 툴바 (없음) inline
tablist APG 탭 tablist inline wrap
radiogroup 라디오 그룹 radiogroup radio (없음)
listbox 목록 상자 목록 상자 option (없음)
menu 메뉴 메뉴 menuitem block wrap
menubar 메뉴 바 menubar menuitem inline wrap
none 해당 사항 없음 해당 없음 해당 없음 해당 사항 없음

역할 매핑 작동 방식에 관한 자세한 내용은 설명을 참고하세요.

축 제한 (inlineblock)

선택한 동작에 기본 수정자가 없으면 네 개의 화살표 키가 모두 포커스를 이동하는 데 사용됩니다. inline 또는 block 수정자를 사용하여 탐색을 단일 논리 축으로 제한할 수 있습니다.

  • inline: 포커스 그룹은 인라인 축의 화살표 키에만 응답합니다. 대부분의 영어 컨텍스트에서는 왼쪽과 오른쪽 (가로, 위에서 아래로)입니다.
  • block: 포커스 그룹은 블록 축의 화살표 키에만 응답합니다 (대부분의 영어 컨텍스트에서 가로, 위에서 아래로).

축 제한은 CSS 논리적 속성과 정렬되며 쓰기 모드와 방향에 자동으로 적응합니다.

랩어라운드 탐색

기본적으로 화살표 키 탐색은 focusgroup의 가장자리에서 중지됩니다. wrap 수정자를 추가하여 마지막 항목에서 첫 번째 항목으로 (그리고 첫 번째 항목에서 마지막 항목으로) 루프합니다. 동작이 기본적으로 래핑되는 경우 nowrap 수정자를 사용하여 이 동작을 사용 중지합니다.

라이브로 사용해 보기: Tablist Pattern > Horizontal Tablist with Wrapping 이 예에서 포커스가 FAQ 탭에 있고 사용자가 오른쪽 화살표 키를 누르면 포커스가 개요 탭으로 다시 돌아갑니다.

focusgroupstart 속성

focusgroupstart 속성은 탭으로 포커스 그룹에 처음 (또는 메모리가 사용 중지된 경우 매번) 들어갈 때 포커스를 받는 요소를 표시합니다.

<div focusgroup="toolbar nomemory" aria-label="Entry point demo">
  <button type="button">First</button>
  <button type="button" focusgroupstart>Middle (Entry)</button>
  <button type="button">Last</button>
</div>
중간 버튼에 포커스가 있는 메뉴 바

탭과 Shift+탭은 모두 'Middle (Entry)'에 배치됩니다. focusgroupstart가 있고 메모리가 nomemory 수정자로 사용 중지되어 있기 때문입니다. 실시간으로 사용해 보세요. 툴바 패턴 > focusgroupstart이 있는 진입점

메모리 사용 중지 (nomemory)

기본적으로 focusgroup은 마지막으로 포커스가 지정된 항목을 기억하고 Tab을 사용하여 다시 입력할 때 복원합니다. 포커스가 항상 고정된 진입점으로 돌아가야 하는 패턴 (이전 데모와 같이)의 경우 focusgroup 속성에서 nomemory 수정자를 사용하여 사용 중지합니다.

이 수정자는 focusgroupstart의 프로그래매틱 이동과 결합하여 그룹에 진입할 때 포커스가 있는 항목을 완전히 제어할 수도 있습니다. 기억된 요소가 사용할 수 없게 되면 메모리가 지워집니다. 예를 들어 요소가 삭제되거나, 숨겨지거나, 사용 중지되거나, 비활성 상태가 되거나, focusgroup에서 제외된 경우입니다.

선택 해제 (focusgroup="none")

focusgroup="none"를 사용하여 상위 포커스 그룹의 화살표 탐색에서 요소와 하위 트리를 제외합니다. 선택 해제된 요소와 하위 트리는 Tab 키를 사용하여 계속 도달할 수 있지만 화살표 키는 이를 건너뜁니다.

<div focusgroup="toolbar" aria-label="Segmented toolbar">
  <button type="button">New</button>
  <button type="button">Open</button>
  <button type="button">Save</button>
  <span focusgroup="none">
    <button type="button">Help</button>
    <button type="button">Shortcuts</button>
  </span>
  <button type="button">Close</button>
  <button type="button">Exit</button>
</div>
도움말 및 단축키 버튼이 회색으로 표시된 메뉴

오른쪽 화살표 키를 사용하면 도움말 및 단축키 버튼을 완전히 건너뛰고 새로 만들기, 열기, 저장, 닫기, 종료로 이동합니다. 하지만 사용자는 탭을 눌러 도움말 섹션으로 이동하여 이러한 버튼에 액세스할 수 있습니다. 라이브로 사용해 보기: 추가 개념 > focusgroup="none"을 사용한 선택 해제 세그먼트

일반적인 패턴

Tablist

탭 간에 화살표 키 탐색이 있는 탭 컨트롤입니다.

<div focusgroup="tablist nomemory" aria-label="Sections">
  <button type="button" aria-selected="true" aria-controls="panel-overview" id="tab-overview" focusgroupstart>Overview</button>
  <button type="button" aria-selected="false" aria-controls="panel-features" id="tab-features">Features</button>
  <button type="button" aria-selected="false" aria-controls="panel-pricing" id="tab-pricing">Pricing</button>
  <button type="button" aria-selected="false" aria-controls="panel-faq" id="tab-faq">FAQ</button>
</div>
<div role="tabpanel" id="panel-overview" aria-labelledby="tab-overview" tabindex="0">...</div>
<div role="tabpanel" id="panel-features" aria-labelledby="tab-features" tabindex="0">...</div>
<div role="tabpanel" id="panel-pricing" aria-labelledby="tab-pricing" tabindex="0">...</div>
<div role="tabpanel" id="panel-faq" aria-labelledby="tab-faq" tabindex="0">...</div>
개요 탭에 포커스가 있습니다.

라이브로 사용해 보기: Tablist Pattern > Horizontal Tablist with Wrapping

유의할 사항:

  • focusgroupstart 속성이 선택됨 탭에 있으므로 포커스가 항상 여기에 들어갑니다.
  • nomemory 수정자는 사용자가 이전에 다른 탭에 포커스를 맞춘 경우에도 다시 진입하면 항상 선택된 탭으로 이동하도록 합니다.
  • inline 수정자는 화살표 탐색을 왼쪽 및 오른쪽 키로만 제한합니다. 이는 APG 탭 패턴에 설명된 예상 동작과 일치합니다.
  • wrap 수정자를 사용하면 사용자가 모든 탭에서 화살표 키를 계속 사용할 수 있습니다.
  • 간결성을 위해 생략된 개발자 코드는 실제 선택을 처리합니다. 즉, aria-selected를 업데이트하고, 패널 공개 상태를 전환하고, 선택 변경 시 focusgroupstart 속성을 이동합니다.

위쪽 및 아래쪽 화살표 탐색이 있는 간단한 세로 메뉴입니다.

<div focusgroup="menu" aria-label="File actions" class="menu-vertical">
    <button type="button" class="menu-item">New</button>
    <button type="button" class="menu-item">Open…</button>
    <button type="button" class="menu-item">Save</button>
    <button type="button" class="menu-item">Exit</button>
</div>
&#39;열기&#39; 메뉴 항목이 포커스된 세로 메뉴

실시간으로 사용해 보세요. 메뉴 및 메뉴 모음 패턴 > 단순 세로 메뉴 block 수정자를 사용하면 위쪽 및 아래쪽 화살표 키만 항목을 탐색합니다. 왼쪽 및 오른쪽 화살표 키는 사용자가 정의한 동작 (예: 하위 메뉴 열기)에 자유롭게 사용할 수 있습니다. 중첩된 하위 메뉴가 있는 메뉴 바의 경우 각 수준은 독립적인 포커스 그룹입니다. 라이브로 사용해 보기: 메뉴 및 메뉴 바 패턴 > 팝오버 하위 메뉴가 있는 메뉴 바

<ul role="menubar" focusgroup="menubar"
     aria-label="Application Menu" class="menubar">
    <li role="none">
        <button role="menuitem" type="button" class="menubar-item"
             aria-haspopup="menu" aria-expanded="false"
             popovertarget="filemenu">File</button>
        <ul role="menu" focusgroup="menu"
             id="filemenu" popover aria-label="File submenu" class="submenu">
            <li role="none"><button type="button" class="submenu-item"
                 autofocus>New</button></li>
            <li role="none"><button type="button" class="submenu-item">Open</button></li>
            <li role="none"><button type="button" class="submenu-item">Save</button></li>
        </ul>
    </li>
    <!-- More menu items... -->
</ul>
복사 항목이 포커스된 드롭다운 메뉴

라이브로 사용해 보기: 메뉴 및 메뉴 모음 패턴 > 팝오버 하위 메뉴가 있는 메뉴 모음 메뉴 모음은 왼쪽 및 오른쪽 탐색에 inline 수정자를 사용하는 반면 하위 메뉴는 위쪽 및 아래쪽 탐색에 block 수정자를 사용합니다. 중첩된 focusgroup은 완전히 독립적이므로 서로 간섭하지 않습니다.

Radiogroup

화살표 키 탐색 및 전체 스타일 지정 제어가 있는 맞춤 라디오 그룹

<div focusgroup="radiogroup" aria-label="Favorite color">
  <span aria-checked="false" tabindex="0">Red</span>
  <span aria-checked="false" tabindex="0">Green</span>
  <span aria-checked="true" tabindex="0" focusgroupstart >Blue</span>
  <span aria-checked="false" tabindex="0">Purple</span>
</div>
파란색에 포커스가 있는 라디오 버튼 그룹

라이브로 사용해 보기: 라디오 그룹 패턴 > 비교: 네이티브 대 포커스 그룹

focusgroup 속성은 화살표 키 탐색을 처리하지만 선택 코드는 구현해야 합니다. 이 데모에서는 JavaScript 코드가 aria-checked 속성을 사용하여 선택된 상태를 관리합니다.

주요 개념

포커스 그룹 항목 참여

focusgroup이 유효한 동작으로 설정된 요소의 모든 순차적으로 포커스 가능한 하위 요소는 해당 포커스 그룹에 참여하는 것으로 간주됩니다. 즉, tabindex가 음수인 요소는 고려되지 않지만 <button>와 같은 기본적으로 포커스 가능한 요소와 음수가 아닌 tabindex를 지정한 요소는 고려됩니다.

탭 정지

tabindex 값을 관리할 필요가 없습니다. 여러 하위 요소가 자연스럽게 탭으로 이동할 수 있는 경우 (예: 여러 <button> 요소)에도 focusgroup은 이를 단일 탭 정지로 축소합니다. 브라우저는 특정 시점에 탭으로 이동할 수 있는 항목을 처리합니다. 툴바 패턴 > tabindex 관리가 필요하지 않음을 실시간으로 사용해 보세요.

마지막으로 포커스가 맞춰진 메모리

기본적으로 사용자가 Tab 키를 눌러 focusgroup을 벗어난 후 나중에 Tab 키를 다시 누르면 포커스가 마지막으로 포커스가 있던 항목으로 돌아갑니다. 이는 사용자가 자신의 위치를 잃지 않도록 대규모 목록과 툴바에 매우 중요합니다. 포커스가 항상 첫 번째 요소로 복원되도록 하거나 focusgroupstart를 사용하여 처음에 포커스가 지정된 요소를 제어하려면 nomemory 수정자를 사용하여 이 동작을 사용 중지하세요.

중첩된 포커스 그룹

각 focusgroup 선언은 독립적인 범위를 만듭니다. 중첩된 focusgroup은 상위 요소의 화살표 탐색에서 자동으로 선택 해제됩니다. 탭을 사용하여 포커스 그룹 간에 이동하고 화살표 키를 사용하여 현재 포커스 그룹 내에서 이동합니다. 라이브로 사용해 보세요(추가 개념 > 중첩된 포커스 그룹).

Shadow DOM 지원

focusgroup은 기본적으로 섀도우 DOM 경계에 적용됩니다. 섀도우 호스트에 선언된 focusgroup에는 해당 호스트의 섀도우 트리 내에 있는 포커스 가능 요소가 포함됩니다. 선택 해제하려면 구성요소의 섀도우 트리 내에서 focusgroup="none"를 사용하면 됩니다.

키 충돌 처리

focusgroup 내부의 일부 요소(예: <input>, <textarea> 및 기타 컨트롤)는 자체 목적으로 화살표 키를 사용합니다. focusgroup의 탐색 키와 네이티브 요소의 화살표 키 동작 간에 충돌이 있는 경우:

  • 화살표 키는 상호작용 요소 (예: 텍스트 커서 이동)에 의해 사용되며 focusgroup은 방해하지 않습니다.
  • Tab 또는 Shift+Tab은 기본 이스케이프 메커니즘을 제공하여 사용자가 Tab 탐색을 사용하여 focusgroup에 '다시 입력'할 수 있도록 합니다.

이러한 이스케이프 동작은 실제 키 충돌이 있는 경우에만 적용되며 충돌하지 않는 축에는 영향을 미치지 않습니다. keydown 이벤트에서 preventDefault()를 호출하여 특정 요소의 focusgroup 화살표 키 동작을 재정의할 수도 있습니다. 즉, 동작을 중단하지 않고 focusgroup 내부에 입력 및 텍스트 영역을 포함할 수 있습니다.

포커스 그룹에 참여하는 자체 요소에 키 핸들러를 추가하는 경우 사용자가 그룹의 나머지 부분에 액세스할 수 있도록 유사한 이스케이프 메커니즘을 제공해야 합니다.

하위 항목 심층 검색

포커스 그룹 항목은 포커스 그룹 컨테이너의 직계 하위 요소일 필요가 없습니다.

브라우저는 중첩된 focusgroup 내에 있거나 focusgroup="none"로 선택 해제되지 않는 한 모든 순차적으로 포커스 가능한 하위 요소 (음수가 아닌 tabindex)가 focusgroup에 참여하는 것으로 간주합니다.

<div focusgroup="toolbar" aria-label="Nested wrappers">
  <div>
    <span>
      <button type="button">Alpha</button>
    </span>
    <span>
      <button type="button">Beta</button>
    </span>
    <span>
      <button type="button">Gamma</button>
    </span>
  </div>
</div>

버튼이 <div><span> 래퍼 내에 중첩되어 있어도 화살표 키 탐색이 작동합니다. 플랫 목록 요구사항이 없으므로 스타일 지정용 래퍼 요소가 적합합니다.

라이브로 사용해 보기: 추가 개념 > 하위 요소

reading-flow 속성과의 통합

순차적 (Tab) 탐색과 방향 (화살표 키) 탐색 모두 CSS reading-flow 속성이 있으면 이를 따르며 DOM 소스 순서가 아닌 시각적 읽기 순서를 따릅니다.

이렇게 하면 화살표 키 탐색이 사용자가 화면에 보는 레이아웃과 일치합니다.

<div focusgroup="toolbar" aria-label="Visual order"
     style="display: flex; flex-direction: row-reverse; reading-flow: flex-visual;">
  <button type="button">A (DOM first)</button>
  <button type="button">B (DOM second)</button>
  <button type="button">C (DOM third)</button>
</div>
항목 A에 포커스가 있습니다.

DOM 순서는 A, B, C이지만 레이아웃에서 flex-direction: row-reverse를 사용하므로 시각적 순서는 C, B, A입니다. 하지만 코드에서 reading-flow: flex-visual도 사용하므로 읽기 순서는 다시 A, B, C가 되고 focusgroup이 이 순서와 일치합니다.

Tab 키를 누르면 먼저 C에 포커스가 지정되고, 오른쪽 키를 누르면 B, A 순으로 포커스가 지정됩니다. 추가 개념 > CSS 읽기 흐름 통합에서 라이브로 사용해 보세요.

접근성

ARIA 역할 추론

포커스 그룹에서 동작 토큰은 브라우저가 컨테이너와 참여 항목 모두의 최소 역할을 추론하는 데 사용됩니다. 즉, 일반 역할이 있는 요소에 focusgroup 속성이 설정되면 선택한 동작에 따라 올바른 역할이 적용됩니다. 일반 역할이 있는 참여 항목이나 지정한 역할이 없는 버튼의 역할은 그에 따라 추론됩니다. 예를 들어 다음 HTML은

<div focusgroup="tablist">
  <button>Tab 1</button>
  <button>Tab 2</button>
  <button>Tab 3</button>
</div>

버튼에 역할이 정의되지 않았지만 다음과 같은 접근성 트리가 생성됩니다.

+   tablist
  |
  +   tab
  |
  +   tab
  |
  +   tab

언제든지 역할을 직접 설정하여 동작을 제어할 수 있습니다.

접근성 고려사항

포커스 그룹을 만들 때 선택한 동작을 준수해야 합니다.

포커스 그룹 사용은 지정한 동작과 최대한 일치해야 합니다. 이는 접근성 도구를 사용하는 사용자가 콘텐츠를 탐색하고 맞춤 컨트롤을 사용할 수 있도록 하는 데 중요합니다.

역할 추론은 적절한 기본값을 제공하지만, 일반적이지 않은 역할을 가진 요소를 사용할 때는 제공하는 기능에 적절한 역할이 설정되어 있는지 확인해야 합니다.

focusgroup을 사용할 때는 사용자가 화살표 키로 스크롤하여 콘텐츠를 볼 수 있어야 합니다. 키보드 사용자가 페이지의 콘텐츠를 읽고 액세스할 수 있는 방법이 항상 있어야 합니다.

기능 감지

브라우저에서 완전히 지원되기 전에 오늘부터 focusgroup을 사용하려면 JavaScript에서 focusgroup 지원을 감지하면 됩니다.

if ('focusgroup' in HTMLElement.prototype) {
  // focusgroup is supported.
} else {
  // fall back to manual roving tabindex.
}

결론

focusgroup 속성은 표준 기관을 통해 진행 중이며, Chromium에서 프로토타입을 적극적으로 빌드하고 API를 개선하고 있습니다.

사용해 보고 Open-UI GitHub 문제 추적기에 포커스 그룹 문제를 제출하세요. 특히 다음 사항에 대한 의견을 듣고 싶습니다.

  • API 노출 영역이 빌드하는 패턴에 적합한가요?
  • 누락된 패턴이나 시나리오가 있나요?
  • focusgroup 속성이 허용되지 않아야 하는 요소가 있나요?
  • 접근성 스토리가 사용 사례에 어떻게 적용되나요?

웹에서 키보드 탐색을 개선하는 데 도움을 주셔서 감사합니다.

자세히 알아보기

focusgroup을 다시 가져오는 데 도움을 주신 Mason Freed, Sara Higley, Scott O'Hara 및 기타 Open-UI 커뮤니티에 감사드립니다.