Logo

CSS의 :where() 의사 클래스 함수

이번 포스팅에서는 CSS에 비교적 최근에 추가된 의사 클래스(pseudo class) 함수인 :where()에 대해서 알아보겠습니다.

기본 문법

:where() 의사 클래스 함수는 인자로 여러 개의 선택자를 쉼표(,)로 구분하여 넘길 수 있습니다.

:where(선택자1, 선택자2, 선택자3, ...) {
  /* 속성명1: 속성값1 */
  /* 속성명2: 속성값2 */
  /* 속성명3: 속성값3 */
  /* ... */
}

위와 같은 CSS 규칙은 마치 아래와 같이 CSS 코드를 작성한 효과가 발생하게 되는데요.

선택자1,
선택자2,
선택자3,
... {
  /* 속성명1: 속성값1 */
  /* 속성명2: 속성값2 */
  /* 속성명3: 속성값3 */
  /* ... */
}

여기까지만 보면 뭐하러 굳이 :where() 함수를 사용해야될까 싶죠? 😅

지금부터 사례를 통해서 :where()를 사용하면 어떠한 이점이 있는지 살펴보겠습니다.

여러 상태 스타일링

:where()를 활용할 수 있는 가장 간단한 사례로 여러 개의 상태에 동일한 스타일을 적용할 때를 들 수 있는데요.

예를 들어, 버튼이 포커스를 받거나 버튼 위로 마우스 커서가 오거나 버튼을 누르고 있는 상태에서 배경을 파란색으로 바꾸고 싶다면 대개 다음과 같이 CSS 코드를 작성할 것입니다.

button:focus,
button:hover,
button:active {
  background: blue;
}

동일한 CSS 규칙을 :where()를 이용하면 button을 3번 반복하지 않고 좀 더 간결하게 작성할 수 있습니다.

button:where(:focus, :hover, :active) {
  background: blue;
}

여러 요소 스타일링

HTML에서 버튼은 다음과 같이 다양한 방식으로 마크업할 수가 있는데요.

<button>버튼 1</button>
<input type="button" value="버튼 2" />
<input type="reset" value="초기화" />
<input type="submit" value="제출" />

이 4가지 버튼을 일관적으로 스타일하기 위해서는 다음과 같은 CSS 규칙이 필요하겠죠?

button,
input[type="button"],
input[type="reset"],
input[type="submit"] {
  appearance: none;
  cursor: pointer;
  margin: 0;
  border: none;
  border-radius: 1em;
  padding: 0.5em 1em;
  color: white;
  font-weight: bold;
  background: royalblue;
}

:where()는 이렇게 여러 요소에 동일한 스타일을 적용할 때도 사용할 수 있습니다.

:where(button, input[type="button"], input[type="reset"], input[type="submit"]) {
  appearance: none;
  cursor: pointer;
  margin: 0;
  border: none;
  border-radius: 1em;
  padding: 0.5em 1em;
  color: white;
  font-weight: bold;
  background: royalblue;
}

:where()의 장점은 CSS 코드를 작성할 때 발생할 수 있는 실수에 대해 좀 더 너그럽다는 것인데요.

예를 들어, 첫 번째 선택자에 오타를 내서 button 대신에 :button라고 썼다면 어땠을까요?

:button,
input[type="button"],
input[type="reset"],
input[type="submit"] {
  /* 스타일 */
}

이와 같이 여러 개의 선택자를 나열했을 때 그 중에서 하나라도 잘못되었다면 그 CSS 규칙이 몽땅 날라가게 되는데요. 따라서 4가지 버튼 모두 전혀 스타일이 적용되지 않게 됩니다. 🙁

하지만 다음과 같이 :where()를 쓰게 되면, 문제가 있는 첫 번째 선택자를 제외한 나머지 선택자에는 여전히 스타일이 적용됩니다.

:where(:button, input[type="button"], input[type="reset"], input[type="submit"]) {
  /* 스타일 */
}

여러 요소와 상태 스타일링

사실 :where()의 진정한 위력은 다른 선택자와 조합해서 사용할 때 극대화 됩니다.

방금 살펴본 4가지 요소에, 처음 다뤘던 3가지 상태를 조합하면 무려 12가지 선택자가 필요한데요. 이 모든 선택자를 대상으로 동일한 스타일을 적용해볼까요? 🤮

button:focus,
button:hover,
button:active,
input[type="button"]:focus,
input[type="button"]:hover,
input[type="button"]:active,
input[type="reset"]:focus,
input[type="reset"]:hover,
input[type="reset"]:active,
input[type="submit"]:focus,
input[type="submit"]:hover,
input[type="submit"]:active {
  background: blue;
}

동일한 CSS 규칙을 이 번에는 :where()를 활용해서 작성해볼께요.

:where(button, input[type="button"], input[type="reset"], input[type="submit"]):where(:focus, :hover, :active) {
  background: blue;
}

:where()를 두 번 연속해서 사용했더니 필요한 선택자를 대폭 줄어 들었네요! 👍

CSS 명시도

CSS의 의사 클래스(pseudo class) 함수 중에서는 :where()와 형제 뻘인 :is()라는 녀석도 있는데요. 지금까지 작성한 예제 코드에서 :where():is()로 대체해보시면 아무런 차이가 발생하지 않을 것입니다.

이 두 개의 함수 중에 무엇을 쓰든 무방한 경우가 많지만, 사실 CSS 명시도(specificity) 측면에서 큰 차이가 있는데요. :where() 함수는 CSS 명시도가 0이라서, :where()이 사용된 CSS 규칙은 다른 선택자를 통해서 덮어쓰기가 용이합니다. 반면에 :is() 함수의 CSS 명시도는 인자로 넘어온 선택자 중에서 가장 명시도가 큰 것이 되기 때문에 다른 선택자로 덮어쓰기를 방지하고자 할 때 더 적합합니다.

그래서 저는 개인적으로는 아직 이 두 함수에 익숙하지 않으시다면 :where()를 사용하는 것을 먼저 고려하시고, 꼭 필요한 경우에만 :is()를 사용하시라고 추천드리고 싶습니다. 이 부분에 대해서는 추후 기회가 되면 :is()에 대해서 다룰 때 좀 더 자세히 살펴보도록 하겠습니다.

마치면서

지금까지 CSS에 추가된 매우 유용한 의사 클래스(pseudo class) 함수인 :where()을 간단한 실습을 통해서 살펴보았습니다. :where()를 잘 활용하셔서 좀 더 깔끔하고 읽기 편한 CSS 코드를 작성하실 수 있으셨으면 좋겠습니다.