부모 선택자 &: 중첩의 핵심
부모 선택자 &: 중첩의 핵심
Sass에서 선택자를 중첩해서 쓸 때, 밖의 선택자를 다시 가리키기 위해 쓰는 특별한 선택자가 있어요. 바로 부모 선택자(parent selector) &입니다. 이걸 쓰면 바깥 선택자를 더 복잡한 방식으로 재사용할 수 있어요. 가상 클래스를 붙이거나, 부모 앞에 선택자를 추가하는 식으로요. 중첩된 규칙에서 &의 위치에 바깥 선택자가 들어가고, 중첩이 기본으로 하는 방식 대신 그렇게 치환됩니다.
기본 사용
부모 선택자는 여러 방식으로 쓸 수 있어요. 바깥 선택자에 가상 클래스를 붙이거나, 특정 맥락에서의 스타일을 정의하거나, 가상 클래스 선택자의 인자로도 쓸 수 있습니다.
.alert {
// The parent selector can be used to add pseudo-classes to the outer
// selector.
&:hover {
font-weight: bold;
}
// It can also be used to style the outer selector in a certain context, such
// as a body set to use a right-to-left language.
[dir=rtl] & {
margin-left: 0;
margin-right: 10px;
}
// You can even use it as an argument to pseudo-class selectors.
:not(&) {
opacity: 0.8;
}
}
.alert:hover {
font-weight: bold;
}
[dir=rtl] .alert {
margin-left: 0;
margin-right: 10px;
}
:not(.alert) {
opacity: 0.8;
}
부모 선택자는 h1 같은 타입 선택자로 치환될 수 있기 때문에, 타입 선택자가 올 수 있는 복합 선택자의 맨 앞에서만 허용돼요. 예를 들어 span&은 쓸 수 없습니다.
접미사 붙이기
부모 선택자로 바깥 선택자에 접미사를 추가할 수도 있어요. BEM처럼 규칙적인 클래스 이름을 쓰는 방법론에서 특히 유용합니다. 바깥 선택자가 클래스·ID·요소 선택자처럼 영숫자 이름으로 끝나기만 하면, 부모 선택자에 텍스트를 이어붙일 수 있어요.
.accordion {
max-width: 600px;
margin: 4rem auto;
width: 90%;
font-family: "Raleway", sans-serif;
background: #f4f4f4;
&__copy {
display: none;
padding: 1rem 1.5rem 2rem 1.5rem;
color: gray;
line-height: 1.6;
font-size: 14px;
font-weight: 500;
&--open {
display: block;
}
}
}
.accordion {
max-width: 600px;
margin: 4rem auto;
width: 90%;
font-family: "Raleway", sans-serif;
background: #f4f4f4;
}
.accordion__copy {
display: none;
padding: 1rem 1.5rem 2rem 1.5rem;
color: gray;
line-height: 1.6;
font-size: 14px;
font-weight: 500;
}
.accordion__copy--open {
display: block;
}
중첩 밖에서
일반 CSS에서처럼 중첩 규칙 밖에서도 부모 선택자를 쓸 수 있어요. 이 경우엔 브라우저가 처리하도록 그대로 일반 CSS로 출력되고, 브라우저는 이를 스코핑 루트로 해석합니다. 중첩 밖의 부모 선택자에는 접미사를 붙일 수 없어요 — 붙일 대상이 없으니까요.
SassScript 안에서
부모 선택자는 SassScript 안에서도 쓸 수 있어요. 현재 부모 선택자 값을 선택자 함수와 같은 형식(쉼표로 구분된 선택자 리스트, 그 안의 공백으로 구분된 복합 선택자, 그 안의 따옴표 없는 문자열)으로 돌려주는 특별한 표현식입니다.
.main aside:hover,
.sidebar p {
parent-selector: &;
// => ((unquote(".main") unquote("aside:hover")),
// (unquote(".sidebar") unquote("p")))
}
.main aside:hover,
.sidebar p {
parent-selector: .main aside:hover, .sidebar p;
}
& 표현식을 어떤 스타일 규칙 밖에서 쓰면 null을 돌려줍니다. null은 거짓(falsy)이므로, 이걸 이용해 믹스인이 스타일 규칙 안에서 호출됐는지 아닌지를 쉽게 알아낼 수 있어요.
@mixin app-background($color) {
#{if(sass(&): '&.app-background'; else: '.app-background')} {
background-color: $color;
color: rgba(#fff, 0.75);
}
}
@include app-background(#036);
.sidebar {
@include app-background(#c6538c);
}
.app-background {
background-color: #036;
color: rgba(255, 255, 255, 0.75);
}
.sidebar.app-background {
background-color: #c6538c;
color: rgba(255, 255, 255, 0.75);
}
고급 중첩
&는 평범한 SassScript 표현식처럼 쓸 수 있어요. 함수에 넘기거나 보간(interpolation)에 넣을 수 있고, 심지어 다른 선택자 안에서도요. 선택자 함수나 @at-root 규칙과 함께 쓰면 선택자를 아주 강력하게 중첩할 수 있습니다.
예를 들어 바깥 선택자 그리고 요소 선택자에 모두 일치하는 선택자를 쓰고 싶다면, &를 사용자 선택자와 결합하는 selector.unify() 함수를 쓰는 믹스인을 만들 수 있어요.
@use "sass:selector";
@mixin unify-parent($child) {
@at-root #{selector.unify(&, $child)} {
@content;
}
}
.wrapper .field {
@include unify-parent("input") {
/* ... */
}
@include unify-parent("select") {
/* ... */
}
}
.wrapper input.field {
/* ... */
}
.wrapper select.field {
/* ... */
}
Sass가 선택자를 중첩할 때는 어떤 보간이 선택자 생성에 쓰였는지 알 수 없어요. 그래서 &를 SassScript 표현식으로 썼더라도 바깥 선택자를 안쪽 선택자에 자동으로 더해요. 그렇기 때문에 @at-root 규칙을 명시적으로 써서 바깥 선택자를 포함하지 말라고 알려줘야 합니다.