Tab을 누를 때마다 링크는 바뀌는데 화면에서는 어디를 선택했는지 보이지 않습니다. 페이지 위에 붙어 있는 헤더가 초점을 받은 링크를 덮었을 수 있습니다. 초점 테두리를 굵게 만드는 것만으로는 덮개 뒤의 링크가 나타나지 않습니다. 이 문제는 초점 표시가 있는지와 초점 대상이 보이는지를 따로 확인해야 해결 방향이 잡힙니다.

테두리 없음과 헤더 뒤 가림을 구분합니다

먼저 키보드로 링크를 선택한 상태에서 초점이 실제로 어디에 있는지 확인합니다. 개발자 도구의 document.activeElement는 현재 초점 요소를 찾을 때 쓸 수 있습니다. 요소가 예상 위치에 있는데 테두리만 없으면 초점 스타일을, 요소의 위쪽이 헤더 아래로 들어갔으면 스크롤과 배치를 조사합니다.

WCAG의 Focus Visible은 키보드 초점 표시를 다루고, Focus Not Obscured (Minimum)은 초점을 받은 요소가 제작자가 만든 콘텐츠에 완전히 가려지지 않도록 요구합니다. 후자의 AA 최소 기준을 “한 픽셀도 겹치면 위반”으로 읽어서는 안 됩니다. 다만 독자가 링크 문장과 초점 표시를 모두 쉽게 읽도록 완전한 노출을 목표로 삼을 수 있습니다.

같은 초점 문제처럼 보여도 다른 원인
화면에서 보이는 현상먼저 살필 대상수정 방향
링크는 보이지만 선택 위치를 모름outline 제거·:focus-visible 규칙명확한 초점 스타일 복원
Tab 뒤 링크가 헤더 안으로 숨음헤더 경계·대상 좌표·스크롤 위치위쪽 노출 공간 마련
마우스 이동은 정상, Shift+Tab만 숨음역방향 초점 이동 경로위·아래 양방향 재현
본문 바로가기를 눌러도 메뉴부터 다시 이동본문 목적지와 실제 초점앵커 이동과 초점 이동을 함께 확인

예제에서는 헤더 아래 16px의 여유를 남깁니다

헤더가 화면 상단 0~88px를 차지하고, 초점을 받은 링크가 64~96px에 놓였다고 가정해 보겠습니다. 링크 높이는 32px이며 그중 24px가 헤더와 겹칩니다. 일부가 보여도 제목을 읽기 어렵다면 위쪽 시작점을 104px 이상으로 가져오는 배치를 검토할 수 있습니다. 104px는 이 예제의 헤더 88px와 여유 16px를 더한 값입니다.

한글 메뉴가 두 줄로 바뀌어 헤더 높이가 112px가 되면 같은 여유의 기준점은 128px입니다. 데스크톱에서 잰 88px를 모든 화면에 고정해 쓰면 좁은 화면에서 문제가 돌아옵니다. 상단 공지 바가 더해지는 경우도 합산 대상인지 살펴야 합니다.

문서의 위쪽 스크롤 여유를 지정합니다

MDN의 scroll-padding-top 설명은 스크롤 영역에서 사용자가 볼 최적 영역의 위쪽 여유를 정하는 속성으로 안내합니다. 고정 도구막대가 가리는 공간을 제외하는 데 사용할 수 있습니다. 아래 CSS는 문서 자체가 스크롤되는 예이며 88px는 실제 헤더 높이로 바꿔야 합니다.

:root {
  --header-height: 88px;
  --focus-gap: 16px;
  scroll-padding-top:
    calc(var(--header-height) + var(--focus-gap));
}
a:focus-visible,
button:focus-visible {
  outline: 3px solid #174f80;
  outline-offset: 3px;
}

위쪽 패딩은 초점 테두리를 만드는 속성이 아니므로 두 문제를 각각 다룹니다. 초점 색은 예시이며 실제 배경과 인접 색에서 구별되는지 확인해야 합니다. 색을 바꾸는 경우에는 텍스트·컨트롤의 대비를 나누는 방법도 참고할 수 있습니다.

문서 안에 자체 스크롤되는 패널이 있다면 최상위 html에 넣은 값만으로 그 패널을 제어할 수 없습니다. 어느 요소가 스크롤 컨테이너인지 먼저 찾고 거기에 맞게 적용합니다. 개별 목적지에 scroll-margin-top을 주는 접근도 있지만 두 값을 무작정 크게 합치면 불필요한 빈 공간이 생길 수 있습니다. 실제 이동 결과를 보고 역할을 정합니다.

헤더 높이 변화는 실제 경계를 따라갑니다

고정 헤더는 페이지를 내리면 축소되거나 공지 닫기 후 낮아질 수 있습니다. 처음 높이만 한 번 읽고 끝내면 이후 상태가 달라집니다. 다음은 헤더의 실제 높이를 CSS 변수에 반영하는 구현 예입니다. 사용하는 헤더 선택자와 바깥 공지 영역의 포함 여부를 프로젝트에 맞게 정해야 합니다.

const header = document.querySelector('.site-header');
if (header) {
  const updateHeight = () => {
    const height = Math.ceil(header.getBoundingClientRect().height);
    document.documentElement.style.setProperty(
      '--header-height', `${height}px`
    );
  };
  new ResizeObserver(updateHeight).observe(header);
  updateHeight();
}

이 코드는 높이 값만 연결합니다. 변수를 바꿀 때 헤더 자체의 높이가 다시 바뀌는 순환 CSS를 만들지 않도록 하고, 화면 위를 실제로 덮는 영역이 헤더의 높이와 일치하는지도 봅니다. 아래로 일부 내려온 배너나 그림자는 요소 높이 하나로 표현되지 않을 수 있습니다.

Tab만 누르지 말고 되돌아오는 길도 확인합니다

긴 본문의 중간 링크에서 Shift+Tab을 누르면 브라우저가 이전 링크를 화면 위쪽 가까이 가져오는 상황이 생깁니다. 이때 헤더가 겹칠 수 있으므로 아래로 가는 경로만 확인해서는 부족합니다. 메뉴를 열고 닫는 동작보다, 메뉴가 닫힌 일상적인 읽기 상태에서 초점이 어떻게 이동하는지부터 봅니다.

  1. 스크롤 전 첫 링크부터 Tab으로 본문까지 이동합니다.
  2. 본문 중간에서 Shift+Tab으로 이전 링크를 여러 번 찾아갑니다.
  3. 페이지 내 목차 링크로 이동한 뒤 다음 Tab의 출발점을 확인합니다.
  4. 헤더 공지를 열고 닫거나 메뉴가 두 줄이 되는 폭에서 같은 경로를 반복합니다.
  5. 브라우저 확대 후 화면 위쪽의 사용 가능 영역을 다시 확인합니다.

본문 바로가기를 눌렀을 때 화면만 내려가고 키보드 위치는 헤더에 남는 문제는 별도입니다. 건너뛰기 링크와 랜드마크에서 다루듯 목적지와 초점의 연결을 확인해야 합니다. 반면 페이지 아래 고정 구매 바가 문제라면 하단 CTA 겹침처럼 문서 끝의 공간도 필요합니다.

초점 순서를 바꾸기 전에 가림부터 해결합니다

숨는 링크를 건너뛰려고 tabindex="-1"로 빼거나 양수 tabindex로 순서를 재배열하면 읽기 순서와 조작 순서가 달라질 수 있습니다. 원래 필요한 링크라면 접근 경로를 없애기보다 보이도록 배치를 고치는 편이 맞습니다. 사용자가 스스로 스크롤해야만 현재 초점을 찾을 수 있는지도 함께 살펴봅니다.

테두리, 가림, 이동 후 문맥은 각각 다른 관찰입니다. “키보드 사용 가능” 한 줄 대신 어느 링크가 어떤 헤더 상태에서 보였는지 기록하면 화면 변화에 맞춰 원인을 찾기 쉽습니다. 한 화면에서 문제를 없앴다는 사실을 전체 접근성 인증으로 넓히지 않고, 남은 초점 경로를 계속 확인할 수 있는 구조로 만듭니다.

참고한 공식 자료

원문 확인일: 2026년 10월 4일. 수치와 화면 구성은 설명용 예제이며 실제 고객 사이트의 측정 결과가 아닙니다.