Web/HTML&CSS

[SCSS] 02. Style Rules

은서파 2022. 9. 13. 22:16

이번 포스트에서는 Scss에서 style을 작성하는 규칙인 Style Rule에 대해서 살펴보자.

 

기본 작성법

 

그냥 쓰려면 scss나 css나 동일

scss의 특별한 기능을 사용하지 않을꺼라면 scss나 css의 사용법은 동일하다.

// scss

.button {
  padding: 3px 10acpx;
  font-size: 12px;
}
// css

.button {
  padding: 3px 10acpx;
  font-size: 12px;
}

앞으로 코드를 비교할 때 왼쪽 블록은 scss, 오른쪽 블록은 css임을 밝혀둔다.

 

삶을 윤택하게 해주는 nesting

scss의 진가는 선택자의 중첩 기능에서부터 발휘된다. 일반적인 css의 선택자는 재사용이나 계층적인 구조화가 어렵다. 예를 들어 nav ul, nav>li, nav~a 등을 나타낼 때 선택자들의 시작이 nav로 모두 같음에도 불구하고 매번 따로 작성해야 했다. 

하지만 scss에서는 산택자를 계층적으로 사용해서 조상-자손의 관계를 표현할 수 있다.

nav {
    padding: 10px;
    
    // 기본은 자손 선택자로 연결
    ul {
        list-style: none;
    }

    >li {
        display: inline-block;
    }

    ~a {
        display: block;
    }
}
nav {
  padding: 10px;
}

nav ul {
  list-style: none;
}

nav > li {
  display: inline-block;
}

nav ~ a {
  display: block;
}

한눈에 봐도 코드가 깔끔하고 관계의 추적이 용이해보인다. 나만 그러는건 아니겠지..

기본적으로 nav를 선택자로한 스타일이 잘 적용된 것을 볼 수 있고 nav ul, nav>li, nav~a 가 잘 생성된 것을 확인할 수 있다.

 

combinator( , >, ~, + 등) 의 위치는 외부, 내부 모두 가능

선택자를 중첩할 때 combinator의 위치는 외부와 내부 모두 가능하기 때문에 구성할 선택자에 따라 편하게 작성할 수 있다.

div> {
    ul {
        list-style-type: none;
    }

    a {
        font-size: 2em;
    }
}

div {
    >p {
        border-top: 1px solid gray;
    }

    div {
        border-top: 1px solid red;
    }
}
div > ul {
  list-style-type: none;
}

div > a {
  font-size: 2em;
}

div > p {
  border-top: 1px solid gray;
}

div div {
  border-top: 1px solid red;
}

위 예에서 첫번째 블록은 "div>" 를 상위 선택자로 해서 중첩하고 있도 두 번째 블록은 "div"를 상위 선택자로 사용하는 형태이다.

 

selector 목록의 중첩

comma(,)를 이용해서 선택자의 목록을 사용하는 경우에도 nesting을 잘 동작한다. 하지만 헷갈리니까 확인하고 넘어가자.

.alert,.warning {
  ul,  p {
    margin-right: 0;
  }
}
.alert ul, .alert p, .warning ul,.warning p {
  margin-right: 0;
}

좌측의 선택자가 사용된 형태를 보면 외부 선택자로 .alert과 .warning의 목록이 사용되고 있고 내부에 다시 ul과 p의 목록이 중첩되어있다. 이 경우는 마치 2중 반복문이 적용되는 것 처럼 우측과 같이 4개의 선택자 목록에 적용된다.

 

속성의 선언

 

변수의 선언과 사용

css에서는 변수의 개념이 없기 때문에 중복되는 내용이 있을 때 계속해서 값을 써주는 수밖에 없었다. 일괄적으로 변경하려면? 당연히 모든 선언을 쫒아다니며 수정해줘야 한다. 하지만 scss에서는 변수를 사용할 수 있게 되었다! 변수를 선언할 때에는 $를 사용한다.

.circle {
    $size: 100px; // 변수의 선언
    width: $size; // 변수의 사용 
    border-radius: $size * 0.5;
}
.circle {
  width: 100px;
  border-radius: 50px;
}

$size로 설정된 100px이 width와 border-radius에서 사용되고 있어 나중에 변경이 필요할 때 한 번만 변경하면 처리가 가능하다. 선언된 변수는 컴파일된 css에는 당연히 보이지 않는다.

 

보간법(interpolation)

이 변수를 이용해서는 속성을 구성할 수 있는 보간법 즉 interpolation을 적용할 수 있다. 이때는 #을 사용한다.

.rect {
    $size: 100px; // 변수의 선언
    $위: top;
    margin-#{$위}: $size; // 보간법
    padding-#{$위}: $size;
    border-radius: $size * 0.5;
}
.rect {
  margin-top: 100px;
  padding-top: 100px;
  border-radius: 50px;
}

컴파일된 css에서는 margin-top과 margin-top 처럼 #{$위}의 내용이 top으로 치환 된 것을 볼 수 있다. 역시 변경이 필요하다면 $위의 값을 변경하면 된다.

 

속성의 중첩

앞서 선택자의 중첩에 대해서 살펴봤었는데 선택자와 마찬가지로 속성도 중첩될 수 있다. 많은 속성에는 prefix가 있는데 scss에서는 이 prefix를 마치 namespace 처럼 사용해서 중첩할 수 있다. 예를 들어 font-size, font-family 처럼 동일한 prefix로 시작하는 속성들을 여러개 설정해야 한다면 매우 유용하다.

.enlarge {
    font: {
        size: 2em;
        weight: bolder;
    }

    margin: auto {
        bottom: 10px;
        top: 2px;
    }
}
.enlarge {
  font-size: 2em;
  font-weight: bolder;
  
  margin: auto;
  margin-bottom: 10px;
  margin-top: 2px;
}

 

상황에 따른 속성 적용

어떤 경우에는 상황(변수 값)에 따라서 속성의 값을 달리 하거나 또는 설정하지 말아야 할 경우도 있다. 이때는 if 같은 함수를 이용할 수 있다.

// 전역 변수 선언
$rounded: false;

.button {
    border: 1px solid black;
    border-radius: if($rounded, 5px, 1px);
}
.button {
  border: 1px solid black;
  border-radius: 1px;
}

여기서는 $rounded를 전역 레벨로 선언하고 있다. 현재는 할당된 값이 false이기 때문에 border-radius는 1px가 설정된다. 만약 $rounded가 true가 되면 당연히 border-radius는 5px이 설정된다. 또는 null을 할당하면 선언 자체를 없애버리기도 한다.

 

외부 선택자 참조 &

 

외부 선택자 참조 &

선택자를 중첩해서 사용하는데 중첩의 대상이 가상 선택자라면 한가지 고민이 필요하다. 단순히 하위 선택자를 사용하면 자손의 관계가 자동으로 추가되어버리기 때문이다. 따라서 이때는 외부 선택자에 대한 참조가 필요한데 &를 사용할 수 있다.

div {
    // 자손 선택자 적용: div의 자손 중 :hover
    :hover {
        color: blue;
    }

    // 조상 선택자 활용: div 중 hover
    &:hover {
        color: red;
    }

    // 파라미터로 전달 가능
    :not(&) {
        color: green;
    }
}
div :hover {
  color: blue;
}

div:hover {
  color: red;
}

:not(div) {
  color: green;
}

 

전체적으로 div를 외부 선택자로 하고 내부에 여러가지 선택자들이 중첩되어있다. 첫번째 경우는 div :hover처럼 자손 선택자로 분리되어버린 것을 확인할 수 있다. 하지만 두번째의 경우는 div:hover로 div에서 hover가 발생했을 때로 처리된다. 이처럼 외부 선택자 참조시 &를 사용할 수 있다. 

마지막 형태처럼 파라미터로 외부 선택자를 전달할 수도 있는데 이때는 외부 선택자와의 결합은 발생하지 않는다.

 

placeholder selector %

 

placeholder selector %

sass에서는 새로운 종류의 선택자가 추가되었는데 %를 기호로 하는 placeholder 선택자이다. 이 녀석은 재밋는 점이 자체적으로 보이지 않으며 심지어는 css에 포함되지도 않는다.

.li:hover, %strong {
    color: red;
}

%strong:hover {
    color: red;
}
.li:hover {
  color: red;
}

위의 예는 %strong 처럼 뭔가 선택자가 선언되었고 심지어는 %strong:hover처럼 가상선택자를 이용해서 확장도 하고 있다. 하지만  sass를 컴파일한 결과에는 strong에 대한 내용이 존재하지 않는다. 그럼 %는 왜 사용하는가? % 선택자는 @extend에 의해 재사용하기 위해 사용된다. (마치 자바의 abstract class와 유사하다. 자체적으로는 사용되지 않는 상속 전용의 클래스.)

%base-box {
    box-sizing: border-box;
    display: flex;

    &:hover {
        color: white;
    }
}

.action-box {
    @extend %base-box;
    color:green;
}

.reset-box {
    @extend %base-box;
    color:gray;
}
.reset-box, .action-box {
  box-sizing: border-box;
  display: flex;
}
.reset-box:hover, .action-box:hover {
  color: white;
}

.action-box {
  color: green;
}

.reset-box {
  color: gray;
}

여기서는 base-box라는 placeholder 선택자를 선언하고 .action-box와 .reset-box에서 @extend를 통해서 사용하고 있는 것을 볼 수 있다.

컴파일된 결과를 살펴보면 상당히 놀라운점이 있는데 동일한 속성을 가지는 스타일 끼리 헤처모여하고 있다는 점이다. 단순히 컴파일만 하지 않고 코드 정리까지 깔끔하게 되니 인정! (모든 스타일을 저렇게 정리해주지는 않는다 ㅎ)