C.W.K.
Stream
Lesson 01 of 05 · published

Selector 동물원: 필요한 모든 패턴

~13 min · selectors, pseudo-classes, combinators, attribute-selectors

Level 0Markup Novice
0 XP0/34 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"Selector 는 CSS 가 DOM 한테 묻는 질문이야. '이 모양 매치하는 모든 element 보여줘.' 답이 규칙이 적용되는 집합."

다섯 카테고리

모든 CSS selector 는 다섯 카테고리 중 하나 (또는 조합) 에 속해:

  1. Simple selector — element type, class, ID, attribute 또는 전체로 매치.
  2. Pseudo-class — 상태 (:hover, :checked) 나 위치 (:nth-child) 로 매치.
  3. Pseudo-element — element 의 개념적 부분 매치 (::before, ::placeholder).
  4. Combinator — element 간 관계 묘사 (공백, >, +, ~).
  5. Logical combinator — 로직으로 selector 그룹화 (:is(), :where(), :not(), :has()).

Simple Selector

  • h1 — 모든 <h1> element. (type selector)
  • .btn — class 가 btn 인 모든 element. (class selector)
  • #main — id 가 main 인 element. (id selector — ID 는 페이지마다 유니크해야 함)
  • * — 모든 element. (universal selector — 리셋에 유용, selector 매칭에서 비쌈)
  • [type="email"] — 매치하는 attribute 가진 모든 element. (attribute selector)

Attribute selector: 작은 query language

Attribute selector 가 보기보다 강력해:

  • [disabled] — attribute 있음 (어떤 값이든).
  • [type="email"] — 정확 매치.
  • [class~="primary"] — class 리스트가 단어 전체 포함.
  • [lang|="en"] — 값이 정확히 en 또는 en- 으로 시작.
  • [href^="https"] — 시작.
  • [href$=".pdf"] — 끝.
  • [href*="github.com"] — 포함.
  • [data-state="open" i]i 플래그가 case-insensitive 로.

이게 class 이름 하나도 없이 외부 링크를 내부 링크와 다르게 스타일링하거나 모든 PDF 링크에 아이콘 더하는 방법.

Pseudo-class: 상태와 위치

상태 pseudo-class 가 element 의 현재 상태 매치:

  • 사용자 action: :hover, :focus, :focus-visible, :focus-within, :active
  • 폼 상태: :checked, :disabled, :required, :optional, :valid, :invalid, :placeholder-shown
  • 링크 상태: :link, :visited
  • 콘텐츠 상태: :empty (자식 없음), :target (URL fragment)
  • 부정: :not(selector) — 안쪽 selector 가 매치 안 하는 element 매치

위치 pseudo-class 가 형제 중 위치로 매치:

  • :first-child, :last-child, :only-child
  • :nth-child(n), :nth-child(odd), :nth-child(2n+1), :nth-child(3n)
  • :first-of-type, :nth-of-type(n) — 위와 같지만 같은 element type 형제만 세기

Pseudo-element: 진짜 element 아닌 부분 스타일링

Pseudo-element 가 element 의 개념적 부분 스타일링. 이중 콜론 주의:

  • ::before, ::after — 생성된 콘텐츠. 항상 content: "..." 와 페어. 아이콘, 장식 인용, 리스트 마커, 배지에.
  • ::placeholder — input placeholder 텍스트 스타일링.
  • ::selection — 사용자가 선택했을 때 하이라이트된 텍스트.
  • ::first-letter, ::first-line — 타이포그래피 효과.
  • ::marker — 리스트 항목의 bullet/숫자.
  • ::backdropshowModal() 로 연 <dialog> 뒤 어두운 overlay.

Combinator: element 관계

  • A B — descendant. A 안 어디든 B. article p = 어떤 <article> 안 어디든 모든 <p>.
  • A > B — direct child. ul > li = 최상위 <li> 만, 중첩 안 됨.
  • A + B — adjacent sibling. h2 + p = <h2> 바로 다음의 <p>.
  • A ~ B — general sibling. h2 ~ p = 같은 레벨에서 <h2> 뒤의 모든 <p>.

Logical selector: 모던 파워 툴

  • :is(a, b, c) — 안쪽 selector 어느 하나라도 매치하면 매치. :is(h1, h2, h3) a = h1, h2, h3 안의 앵커. Specificity = argument 중 가장 높은 거.
  • :where(a, b, c) — 같은 매칭, specificity 항상 0,0,0,0. 디자인 시스템 베이스 스타일의 마법.
  • :not(a, b) — 안쪽 selector 어떤 것도 매치 안 하면 매치. li:not(:last-child) = 마지막 빼고 모든 리스트 항목.
  • :has(selector) — 부모 selector. article:has(img) = 이미지 포함하는 article. form:has(input:invalid) = 유효하지 않은 입력 가진 폼.

Selector list: comma 로 구분

h1, h2, h3 { font-family: sans-serif; } — 리스트의 모든 selector 에 같은 규칙 적용. 각자 독립적으로 평가되고, 그러고 나서 매치마다 specificity 계산. (참고: :is() 안의 리스트는 하나의 논리 그룹으로 다뤄짐; 그게 차이.)

오른쪽-왼쪽 매칭이 브라우저가 하는 방식. CSS selector 가 오른쪽-왼쪽으로 평가 (가장 오른쪽 simple selector 가 먼저 매치, 그 다음 위로 따라감). 그래서 * { ... } 가 가장 느린 selector (모든 element 가 매치; 그 다음 각자 ancestor 체크). 퍼포먼스는 거대 스케일에서만 중요 — 보통 사이트는 가장 명료한 selector 쓰고 브라우저가 최적화하게 해.

피파의 노트

Cwk-site 의 Tailwind 클래스가 .flex, .gap-4, .text-amber-500 같은 selector 로 풀려 — 모두 simple class selector. Tailwind 가 모든 utility 에 (0,0,1,0) specificity 주고 source 순서에 의존해서 cascade 전쟁을 피했어. Selector 동물원 안다는 건 Tailwind 가 그 전략을 왜 골랐는지 이해한다는 거지 단지 작동한다는 거에 그치지 않아.

Code

Simple + attribute selector·css
/* Simple selector */
h1 { font-size: 2.5rem; }              /* type */
.btn { padding: 0.5rem 1rem; }          /* class */
#main { padding: 2rem; }                /* id (유니크) */
* { box-sizing: border-box; }           /* universal */
[type="email"] { font-family: monospace; }  /* attribute */

/* Query language 로서의 attribute selector */
a[href^="https"] { color: #5BA3D8; }   /* 외부 링크 */
a[href$=".pdf"]::after { content: " 📄"; }  /* PDF 링크 */
a[href*="github.com"] { background-image: url(/github-icon.svg); }
input[type="checkbox"]:checked { accent-color: #5BA3D8; }
Pseudo-class + pseudo-element·css
/* Pseudo-class: 상태 */
button:hover { background: #5BA3D8; }
button:focus-visible { outline: 2px solid #5BA3D8; }
input:invalid { border-color: #d93b3b; }
form:has(input:invalid) button[type="submit"] { opacity: 0.6; }

/* Pseudo-class: 위치 */
li:nth-child(odd) { background: #f8f8f8; }
tr:first-child th { border-top: 0; }
p:not(:last-child) { margin-bottom: 1rem; }

/* Pseudo-element */
blockquote::before { content: '“'; font-size: 2em; }
blockquote::after { content: '”'; font-size: 2em; }
input::placeholder { color: #999; font-style: italic; }
::selection { background: #5BA3D8; color: white; }
Combinator + logical selector·css
/* Combinator */
article p { line-height: 1.7; }         /* descendant */
nav > ul > li { display: inline-block; } /* direct child */
h2 + p { margin-top: 0.5rem; }          /* adjacent sibling */
h2 ~ p { font-size: 1.05rem; }          /* general sibling */

/* Logical selector */
:is(h1, h2, h3) a { color: inherit; text-decoration: none; }
:where(.card, .panel, .tile) { border-radius: 8px; padding: 1rem; }
button:not([disabled]):hover { background: #5BA3D8; }
article:has(img) { background: #fafafa; padding: 1rem; }

/* Selector list */
h1, h2, h3, h4 { font-family: 'Inter', sans-serif; }

External links

Exercise

복잡한 웹 페이지 아무거나 열어 (좋아하는 뉴스 사이트). 브라우저 DevTools 만 써서, 다음을 타깃하는 selector 적기: (1) 모든 외부 링크, (2) article 안 모든 단락, (3) 모든 홀수 리스트 항목, (4) 유효하지 않은 모든 폼 입력, (5) 이미지 포함하는 모든 article. 각자 DevTools 콘솔에서 매치 세기로 테스트: $$('selector').length.
Hint
$$ 는 document.querySelectorAll 의 DevTools 별칭. .length 가 매치하는 element 수 알려 줘. 매치 기대했는데 0 나오면, 너의 selector 가 현재 안 존재하는 attribute 나 pseudo-class 상태 찾고 있는 거.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.