논리적 순차 포커스 탐색에 CSS 읽기 흐름 사용

게시일: 2025년 5월 1일

CSS reading-flowreading-order 속성은 Chrome 137부터 사용할 수 있습니다. 이 게시물에서는 이러한 속성을 설계한 이유와 시작하는 데 도움이 되는 몇 가지 간단한 세부정보를 설명합니다.

그리드 및 플렉스와 같은 레이아웃 메서드는 프런트엔드 개발을 변화시켰지만, 이러한 유연성은 일부 사용자에게 문제를 일으킬 수 있습니다. 시각적 순서가 DOM 트리의 소스 순서와 일치하지 않는 상황을 매우 쉽게 만들 수 있습니다. 이 소스 순서는 키보드를 사용하여 사이트를 탐색할 때 브라우저에서 따르는 순서이므로 일부 사용자는 페이지를 탐색할 때 예상치 못하게 페이지가 갑자기 이동하는 현상을 경험할 수 있습니다.

reading-flowreading-order 속성은 이 오래된 문제를 해결하기 위해 설계되어 CSS 디스플레이 사양에 추가되었습니다.

reading-flow

reading-flow CSS 속성은 플렉스, 그리드 또는 블록 레이아웃의 요소가 접근성 도구에 노출되는 순서와 선형 순차 탐색 메서드를 사용하여 포커스가 설정되는 방식을 제어합니다.

키워드 값을 하나 사용하며 기본값은 normal입니다. 이 값은 DOM 순서로 요소를 정렬하는 동작을 유지합니다. 플렉스 컨테이너 내에서 사용하려면 값을 flex-visual 또는 flex-flow로 설정합니다. 그리드 컨테이너 내에서 사용하려면 값을 grid-rows, grid-columns 또는 grid-order로 설정합니다.

reading-order

reading-order CSS 속성을 사용하면 독서 흐름 컨테이너 내에서 항목의 순서를 수동으로 재정의할 수 있습니다. 그리드, 플렉스 또는 블록 컨테이너 내에서 이 속성을 사용하려면 컨테이너의 reading-flow 값을 source-order로 설정하고 개별 항목의 reading-order를 정수 값으로 설정합니다.

Flexbox의 예

예를 들어 행 순서가 역순인 요소 3개가 있는 Flex 레이아웃 컨테이너가 있고 order 속성을 사용하여 순서를 다시 셔플하려고 할 수 있습니다.

<div class="box">
 <a href="#">One</a>
 <a href="#">Two</a>
 <a href="#">Three</a>
</div>
.box {
  display: flex;
  flex-direction: row-reverse;
}

.box :nth-child(1) {
  order: 2;
}

TAB 키를 사용하여 다음 포커스 가능 요소를 찾고 TAB+SHIFT 키를 사용하여 이전 포커스 가능 요소를 찾으면 이러한 요소를 탐색할 수 있습니다. 소스 순서(1, 2, 3)를 따릅니다.

최종 사용자의 관점에서 보면 이는 이해하기 어렵고 혼란스러울 수 있습니다. 접근성 공간 탐색 도구를 사용하여 페이지를 탐색해도 동일한 결과가 나타납니다.

이 문제를 해결하려면 reading-flow 속성을 설정합니다.

.box {
  reading-flow: flex-visual;
}

이제 포커스 순서가 1, 3, 2입니다. 이는 영어로 왼쪽에서 오른쪽으로 읽을 때의 시각적 순서와 동일합니다.

대신 포커스 순서를 원래 의도한 대로 역순으로 유지하려면 다음을 설정하면 됩니다.

.box {
  reading-flow: flex-flow;
}

이제 포커스 순서가 역순 플렉스 순서(2, 3, 1)입니다. 두 경우 모두 CSS order 속성이 고려됩니다.

그리드 레이아웃 예시

그리드에서 이 기능이 작동하는 방식을 알아보려면 12개의 포커스 가능 영역이 있는 CSS 그리드 자동 배치 항목으로 레이아웃을 만들고 있다고 가정해 보세요.

<div class="wrapper">
 <a href="#">One</a>
 <a href="#">Two</a>
 <a href="#">Three</a>
 <a href="#">Four</a>
 <a href="#">Five</a>
 <a href="#">Six</a>
 <a href="#">Seven</a>
 <a href="#">Eight</a>
 <a href="#">Nine</a>
 <a href="#">Ten</a>
 <a href="#">Eleven</a>
 <a href="#">Twelve</a>
</div>

다섯 번째 하위 요소가 맨 위에 가장 큰 공간을 차지하고 두 번째 하위 요소가 그리드 중앙을 향하도록 합니다. 다른 모든 하위 요소는 열 템플릿에 따라 그리드 내에 자동으로 배치될 수 있습니다.

.wrapper {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-auto-rows: 100px;
}
.wrapper a:nth-child(2) {
  grid-column: 3;
  grid-row: 2 / 4;
}
.wrapper a:nth-child(5) {
  grid-column: 1 / 3;
  grid-row: 1 / 3;
}

Tab 키를 사용하여 다음 포커스 가능 요소를 찾고 Tab+Shift 키를 사용하여 이전 포커스 가능 요소를 찾으세요. 소스 순서(1~12)에 따라 항목이 표시됩니다.

이 문제를 해결하려면 reading-flow 속성을 설정합니다.

.wrapper {
  reading-flow: grid-rows;
}

이제 포커스 순서는 5, 1, 3, 2, 4, 6, 7, 8, 9, 10, 11, 12입니다. 시각적 순서에 따라 행별로 표시됩니다.

읽기 흐름이 열 순서를 따르도록 하려면 대신 grid-columns 키워드 값을 사용하면 됩니다. 그러면 포커스 순서가 5, 6, 9, 7, 10, 1, 2, 11, 3, 4, 8, 12가 됩니다.

.wrapper {
  reading-flow: grid-columns;
}

grid-order를 사용해 볼 수도 있습니다. 포커스 순서는 1~12로 유지됩니다. 이는 상품에 CSS 순서가 설정되지 않았기 때문입니다.

reading-order를 사용하는 블록 컨테이너

reading-order 속성을 사용하면 읽기 흐름에서 항목을 방문해야 하는 시점을 지정하여 reading-flow 속성으로 설정된 순서를 재정의할 수 있습니다. reading-flow 속성이 normal가 아닌 유효한 읽기 흐름 컨테이너에만 적용됩니다.

.wrapper {
  display: block;
  reading-flow: source-order;
}

.top {
  reading-order: -1;
  inset-inline-start: 50px;
  inset-block-start: 50px;
}

다음 블록 컨테이너에는 5개의 항목이 포함되어 있습니다. 소스 순서에서 요소의 순서를 재정렬하는 레이아웃 규칙은 없지만 먼저 방문해야 하는 순서가 아닌 항목이 하나 있습니다.

<div class="wrapper">
  <a href="#">Item 1</a>
  <a href="#">Item 2</a>
  <a href="#">Item 3</a>
  <a href="#">Item 4</a>
  <a class="top" href="#">Item 5</a>
</div>

이 항목의 reading-order-1로 설정하면 포커스 순서는 나머지 읽기 흐름 항목의 소스 순서로 대체하기 전에 먼저 이 항목을 방문합니다.

chrome.dev 사이트에서 더 많은 예시를 확인할 수 있습니다.

tabindex와의 상호작용

이전에는 개발자가 HTML tabindex 전역 속성을 사용하여 HTML 요소에 포커스를 설정하고 순차 포커스 탐색의 상대 순서를 결정했습니다. 그러나 이 속성에는 많은 단점과 접근성 문제가 있습니다. 주된 문제는 양수 tabindex를 사용하여 생성된 tabindex 순서 지정된 포커스 탐색이 접근성 트리에서 인식되지 않는다는 점입니다. 잘못 사용하면 스크린 리더의 환경과 일치하지 않는 불안정한 포커스 순서가 발생할 수 있습니다. 이 문제를 해결하려면 aria-owns HTML 속성을 사용하여 순서를 추적합니다.

이전 flex 예에서 reading-flow: flex-visual를 사용할 때와 동일한 결과를 얻으려면 다음을 실행할 수 있습니다.

<div class="box" aria-owns="one three two">
  <a href="#" tabindex="1" id="one">One</a>
  <a href="#" tabindex="3" id="two">Two</a>
  <a href="#" tabindex="2" id="three">Three</a>
</div>

하지만 컨테이너 외부의 다른 요소에도 tabindex=1가 있으면 어떻게 될까요? 그런 다음 다음 증분 tabindex 값으로 이동하기 전에 tabindex=1가 있는 모든 요소가 함께 방문됩니다. 이러한 불안정한 순차 탐색은 사용자 환경을 저하시킵니다. 따라서 접근성 전문가는 양수 tabindex를 피하는 것이 좋습니다. reading-flow를 설계할 때 이 문제를 해결하려고 했습니다.

reading-flow 속성이 설정된 컨테이너는 포커스 범위 소유자가 됩니다. 즉, 웹 문서의 다음 포커스 가능 요소로 이동하기 전에 컨테이너 내의 모든 요소를 방문하도록 순차 포커스 탐색 범위를 지정합니다. 또한 직접 하위 요소는 reading-flow 속성을 사용하여 정렬되며 정렬 목적으로는 양수 tabindex가 무시됩니다. 읽기 흐름 항목의 자손에 여전히 양수 tabindex를 설정할 수 있습니다.

레이아웃 상위 요소에서 reading-flow 속성을 상속하는 display: contents가 있는 요소도 유효한 읽기 흐름 컨테이너가 됩니다. 사이트를 설계할 때 이 점을 유의하세요. reading-flowdisplay: contents에 관한 의견 요청에서 자세히 알아보세요.

의견 보내기

이 게시물과 chrome.dev의 reading-flow 예시의 예시를 사용해 보고 사이트에서 이러한 CSS 속성을 사용하세요. 의견이 있으면 CSS 작업 그룹 GitHub 저장소에서 문제를 제기하세요. tabindex 및 포커스 범위 지정 동작에 관한 의견이 있으면 HTML WHATNOT GitHub 저장소의 문제로 제출하세요. 이 기능에 관한 의견을 보내주세요.