Logo

CSS 포커스 관련 가상 클래스 사용법 :focus, :focus-visible, :focus-within

CSS에서는 포커스 상태를 스타일링하기 위해 focus, focus-visible, focus-within과 같은 가상 클래스(pseudo class)를 제공하는데요. 그런데 이 녀석들이 이름이 묘하게 비슷해서 정확하게 언제 어떻게 쓰시는지에 대해서 햇갈려하시는 분들이 많은 것 같습니다.

이번 포스팅에서는 CSS의 focusfocus-visible 그리고 focus-within 가상 클래스에 대해 자세히 알아보고, 각각이 나타내는 포커스 상황과 용도와 차이점을 비교해보겠습니다.

HTML 요소의 포커스 상태

CSS의 포커스(focus) 관련 가상 클래스를 알아보기 전에 HTML 요소(element)가 어떤 방식으로 포커스를 받을 수 있는지에 대해서 먼저 이해하고 넘어가면 좋을 것 같습니다.

우선 HTML에서 모든 요소에 포커스가 올 수 있는 것은 아니며 사용자와 상호작용(interactive)이 필요한 요소(element)만 포커스를 받을 수 있게 되어 있습니다 대표적으로 <input>, <select>, <button>과 같은 양식(form) 관련 요소와 링크(link)를 걸 때 사용하는 <a> 요소를 들 수 있습니다.

HTML의 tabindex 속성을 사용하여 원래 상호작용하지 않는 요소도 포커스를 받도록 하는 방법이 있는데요. 이 부분은 본 포스팅에서 다루고자 하는 범위에서 벗어나므로 관련 포스팅을 참고 바랍니다.

일반적으로 사람이 동시에 여러 곳에 집중할 수 없는 것처럼 웹 페이지 상에서 포커스는 항상 딱 하나의 요소에만 올 수 있으며, 사용자가 어떤 입력 장치를 쓰냐에 따라 다른 방법으로 요소 간에 포커스를 이동시킬 수 있습니다.

웹에서 HTML 요소가 포커스를 받는 가장 전통적인 방법은 사용자가 마우스나 터치 패드와 같은 포인터 입력 장치로 해당 요소를 클릭하는 것입니다. 모바일 사용자의 경우에는 스크린 상의 요소를 직접 손가락으로 터치(touch)하여 포커스를 줄 수 있죠. 테플릿 사용자의 경우에는 손가락 대신에 스티일러스 펜을 사용할 수도 있을 것입니다.

키보드 사용자의 경우에는 탭(tab) 키를 누르면 포커스가 다음 HTML 요소로 이동하고, 시프트(shift) 키와 탭 키를 같이 누르면 포커스가 이전 요소로 이동합니다. 그 밖에도 스크린 리더(screen reader)와 같은 브라우저 대안 소프트웨어에서도 자체적으로 HTML 요소에 포커스를 줄 수 있는 사용자 인테페이스를 제공하고 있습니다.

:focus 가상 클래스

CSS의 :focus 가상 클래스는 마우스 키보드 가리지 않고 HTML 요소가 포커스를 받은 상태를 나타내는데요. 그래서 :focus 가상 클래스를 사용하면 어떤 요소가 포커스를 받았을 때만 적용하고 싶은 스타일을 지정할 수 있으며, 이를 통해 사용자에게 포커스 받은 요소에 대한 시각적인 피드백을 제공할 수 있습니다.

:focus {
  /* 포커스 상태에서 적용할 스타일 지정 */
}

그런데 :focus를 사용하여 포커스 상태 스타일했을 때 널리 알려진 한 가지 문제점이 있는데요. 바로 마우스 포인터가 클릭했던 요소를 떠나더라도 페이지 상에서 상호작용한 다른 요소를 클릭하기 전까지는 먼저 클릭한 요소에 적용한 스타일이 그대로 남는 것입니다. 이것은 마우스 사용자들에게는 다소 혼란스러운 경험이 될 수 있죠.

예를 들어, 아래 세개의 버튼(button)은 :focus를 사용하여 포커스를 받으면 그 주변으로 링(ring)이 생기도록 스타일을 했는데요. 이 중 하나의 버튼을 클릭 후에 다른 버튼을 클릭하기 전에까지 먼저 클릭 한 버튼에 포커스 링이 사라지지 않는 것을 보실 수 있으실 거에요. 이러한 문제는 키보드의 탭 키를 이용해서 포커스를 이동하실 때는 발생하지 않을 것입니다.

button:focus {
  outline: 2px solid red;
  outline-offset: 1px;
}

이러한 문제는 특히 해당 요소에 :hover 가상 클래스를 사용하여 마우스 포인터가 요소 위에 있는 경우 다른 효과를 주었을 때 더욱 거슬릴 수 있는데요. 왜냐하면 보통 마우스 사용자들은 자연스럽게 시선이 마우스 포인터를 따라 다니며 굳이 키보드 사용자처럼 포커스를 이동을 위해 마우스 클릭을 할 필요가 없기 때문입니다.

하지만 너무 걱정마세요! 이 문제는 다음에 소개해드릴 :focus-visible 가상 클래스를 사용하면 해결할 수 있습니다. 😉

:focus-visible 가상 클래스

비교적 최근에 CSS에 추가된 :focus-visible 가상 클래스는 오직 키보드를 통해서 포커스를 받은 HTML 요소를 선택할 때 사용하는데요. 즉, 마우스 클릭이나 스크린 터치 등을 통해 요소에 바로 포커스를 줄 경우에는 스타일을 적용하지 않는다는 점에서 :focus 가상 클래스와 차이점이 있습니다.

:focus-visible {
  /* 키보드 포커스를 받은 경우만 적용할 스타일 지정 */
}

그럼 위에서 버튼에 :focus를 사용하여 적용한 동일한 스타일을 이번에는 :focus-visible를 사용해서 적용해볼까요?

button:focus-visible {
  outline: 2px solid red;
  outline-offset: 1px;
}

이제 더 이상 마우스로 버튼을 클릭했을 때 포커스링이 나타나지 않는 것을 볼 수 있으실 거에요. 하지만 키보드의 탭 키를 이용해서 버튼에 포커스를 주시면 포커스링이 나타날 것 입니다.

이와 같이 :focus-visible 가상 클래스를 사용하면 키보드 사용자와 마우스 사용자를 동시에 만족시킬 수 있는 스타일링이 가능해집니다. 🤗

:focus-within 가상 클래스

마지막으로 살펴볼 :focus-within 가상 클래스는 스타일의 적용 대상과 스타일 활성화시키는 주체가 달라서 살짝 더 복잡한데요. 어떤 요소에 :focus-within를 사용하여 어떤 요소에 스타일을 적용하면 해당 요소를 포함해서 그 내부에 요소 중에 하나가 포커스를 받으면 그 스타일이 활성화됩니다.

따라서 주로 하위 요소에 포커스가 왔을 때 시각적인 피드백을 상위 요소로 주고 싶을 때 사용합니다.

:focus-within {
  /* 내부 요소가 포커스를 받으면 적용할 스타일 지정 */
}

예를 들어서, 아래에 제가 <input> 요소와 <button> 요소를 조합하여 간단한 검색바를 구현해보았는데요. 검색바 내부 영역을 클릭해보시면 검색바 주변으로 포커스링이 생기는 것을 생기는 것을 볼 수 있습니다.

div:focus-within {
  outline: 2px solid red;
}

이것은 <input> 요소와 <button> 요소를 감싸고 있는 <div> 요소가 :focus-within를 사용하여 스타일되었기 때문인데요. <input> 요소가 됐든 <button> 요소가 됐든 <div> 요소 내부에 있기 때문에 포커스를 받는 순간 <div> 요소에 포커스 링이 생기는 것이지요.

이처럼 :focus-within 가상 클래스를 잘 활용하면 HTML의 구조가 좀 더 복잡한 경우에도 효과적으로 피드백을 구현할 수 있습니다. 👍

마치면서

지금까지 포커스 상태를 스타일하기 위해서 사용되는 :focus, :focus-visible, :focus-within 가상 클래스에 대해서 살펴보았습니다. 각 가상 클래스가 나타내는 포커스 상황이 다르기 때문에 차이점을 정확히 이해하고 적지적소에 활용하시는 것이 무엇보다 중요하겠습니다.

어떤 방법으로 HTML 요소가 포커스를 받든 사용자가 해당 요소가 포커스 상태라는 것을 알려주는 것은 사용자 경험과 웹 접근성(accessibility) 측면에서 매우 중요합니다. 특히 키보드 사용자는 현재 어떤 HTML 요소가 포커스가 잡혀있는지를 시각적으로 구분할 수 없다면 해당 웹페이지를 정상적으로 이용하기가 상당히 어려울 것입니다.

본 포스팅에서 다룬 포커스 상태 스타일 방법을 효과적으로 활용하셔서 마우스 사용자의 경험을 해지지 않으면서도 키보드 사용자에게 우수한 접근성을 제공할 수 있으셨으면 좋겠습니다.