속성보다 매번 헷갈리는 건 선택자 쪽이다. 결합자와 가상 클래스, 가상 요소, 그리고 커스텀 프로퍼티를 상황별로 정리했다.
1. 결합자.
/* 자손 (하위 전체) */
article p {}
/* 자식 (바로 아래 한 단계만) */
article > p {}
/* 인접 형제 (바로 다음에 오는 하나) */
h2 + p {}
/* 일반 형제 (뒤따르는 모든 형제) */
h2 ~ p {}
2. 가상 클래스 (콜론 하나).
요소의 상태나 위치를 조건으로 거는 선택자다. 콜론을 하나 붙인다.
/* 첫 번째, 마지막 자식 */
li:first-child {}
li:last-child {}
/* n번째 자식 (홀수 / 3의 배수마다) */
li:nth-child(odd) {}
li:nth-child(3n) {}
/* 조건에 해당하지 않는 요소 */
li:not(.active) {}
/* 여러 선택자를 하나로 묶는다 (특정도가 인자 중 가장 높은 값) */
:is(h1, h2, h3) {}
/* :is와 같지만 특정도가 항상 0이라 덮어쓰기 쉽다 */
:where(h1, h2, h3) {}
:first-child는 타입이 아니라 순서를 본다. 아래 둘은 다르게 동작한다.
/* 부모의 첫 자식이면서 그게 p일 때만 적용된다 */
p:first-child {}
/* 형제 중 p 타입의 첫 번째에 적용된다 */
p:first-of-type {}
3. 가상 요소 (콜론 두 개).
가상 클래스가 상태를 고른다면, 가상 요소는 실제로 없는 요소를 만들어낸다. 그래서 콜론을 두 개 붙여 가상 클래스와 구분한다. :before처럼 하나만 써도 옛 문법으로 동작하지만, 표준은 콜론 두 개다.
/* 요소 앞뒤에 콘텐츠를 삽입한다. content가 반드시 필요하다 */
.tag::before { content: "#"; }
.price::after { content: " 원"; }
/* 첫 줄, 첫 글자에만 스타일을 준다 */
p::first-line {}
p::first-letter {}
/* 사용자가 드래그로 선택한 영역 */
::selection { background: yellow; }
4. 속성 선택자.
/* 해당 속성을 가진 요소 */
a[target] {}
/* 값이 정확히 일치하는 요소 */
input[type="text"] {}
/* 값이 특정 문자열로 시작 */
a[href^="https"] {}
/* 값이 특정 문자열로 끝남 */
a[href$=".pdf"] {}
/* 값에 특정 문자열이 포함 */
a[href*="github"] {}
5. 커스텀 프로퍼티 (변수).
:root에 선언하면 문서 전역에서 쓸 수 있다. var()로 꺼내 쓰며, SCSS 변수와 달리 런타임에 살아 있어 미디어 쿼리나 특정 범위에서 값을 덮어쓸 수 있다.
/* :root에 전역 변수를 선언한다 */
:root {
--main-color: #3b82f6;
--gap: 16px;
}
/* var()로 꺼내 쓴다 */
.button {
background: var(--main-color);
padding: var(--gap);
}
/* 변수가 없을 때 쓸 대체값을 두 번째 인자로 준다 */
.card {
color: var(--text-color, #333);
}
/* 특정 범위에서만 값을 덮어쓴다 */
.dark {
--main-color: #60a5fa;
}
6. calc.
/* 서로 다른 단위를 섞어 계산한다 (연산자 양옆 공백은 필수다) */
.sidebar {
width: calc(100% - 240px);
}
/* 변수와 함께 쓸 수 있다 */
.box {
padding: calc(var(--gap) * 2);
}