Archive

SCSS Pre-processor 사용법

|

SCSS Pre-processor 사용법


  1. SCSS Pre-processor

    • CSS의 확장 기능으로 전처리기라고 부른다
    • 중첩, 조건문, 반복문, 연산 등 CSS에서 불가능했던 기능들을 작성할 수 있다
    • 웹은 CSS만 인식할 수 있기 때문에 컴파일 단계가 필요하다
    • CSS 확장 언어에는 SCSS (Sass), Less, Stylus 등이 있다
    • SCSS는 Sass 3버전에서 등장했으며 Sass의 모든 기능을 지원하고 CSS 구문과 완전히 호화되는 CSS의 상위집합이다


  2. Compile

    • SassMeister : 일종의 play-ground이다 링크

    • node-sass : Node.js의 컴파일러인 LibSass에 바인딩한 패키지로 SCSS 컴파일이 가능하다

      // npm install node-sass
           
      // node-sass [옵션] <입력파일경로> [출력파일경로]
      //ex) node-sass --watch scss/main.scss dist/style.css
      
    • Gulp : 빌드 자동화 도구인 Gulp에서 gulp-sass 컴파일러로 node-sass를 사용하도록 설정할 수 있다

      // npm install --save-dev gulp gulp-sass
           
      // gulpfile.js
      const gulp = require('gulp')
      const sass = require('gulp-sass')
           
      // 일반 컴파일
      gulp.task('sass', function () {
        return gulp.src('./src/scss/*.scss')  // 입력 경로
          .pipe(sass().on('error', sass.logError))
          .pipe(gulp.dest('./dist/css'));  // 출력 경로
      });
           
      // 런타임 중 파일 감시
      gulp.task('sass:watch', function () {
        gulp.watch('./src/scss/*.scss', ['sass']);  // 입력 경로와 파일 변경 감지 시 실행할 Actions(Task Name)
      });
           
      // gulp sass 혹은 gulp sass:watch 명령어로 컴파일
      
    • Webpack : sass-loader 사용

      const MiniCssExtractPlugin = require('mini-css-extract-plugin');
           
      module.exports = {
          //...
          module: {
              rules: [
                  {
                      test: /\.(sass|scss)$/,
                      use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'],
                  }
              ]
          }
      }
      
    • Parcel : 웹앱 번들러인 Parcel을 사용한다

      // npm install -g parcel-bundler
           
      // npm install --save-dev node-sass
           
      // html에서 링크 태그로 scss 파일 연결
           
      // parcel build xxx.html
           
      // 빌드된 파일은 dist/에서 확인, 별도 포트지정 안하면 localhost:1234에서 확인 가능
      
    • Live Sass Compiler : VS Code의 플러그인으로 컴파일을 자동으로 해준다

  3. Syntax

    • 주석 : // 컴파일되지 않는 주석, /* 컴파일되는 주석*/

    • 데이터 종류: Number, String, Color, Boolean, Null, List, Map Map은 Key: Value 형태로 () 안에 정의해야한다 ex) (foo: a, bar: b)

    • Nesting : 중첩 기능으로 상위 선택자의 반복을 피할 수 있다

      .foo {
          .bar {
              li {
                  color: red;
              }
          }
      }
      

      & (ampersand) 선택자로 상위(부모) 선택자를 참조할 수 있다

      .list {
          li {
              &:last-child {
                  margin-bottom: 10px;
              }
          }
      }
      

      엠퍼샌드를 클래스명에 사용할 수도 있다

      .foo {
          &-bar { color: blue; }
          &-baz { color: red; }
      }
           
      // 컴파일 후..
           
      .foo-bar { color: blue; }
      .foo-baz { color: red; }
      

      중첩에서 벗어나고 싶으면 @at-root 키워드를 사용한다

      특정 변수를 정의하면 변수를 정의한 스코프에서만 적용할 수 있기 때문에 이런 경우에 사용한다

      .foo {
          $w: 20px;
          li {
              width: $w;
          }
          @at-root .bar {
              width: $w;
          }
      }
           
      // 컴파일 후..
           
      .foo li {
          width: 30px;
      }
      .bar {
          width: 20px;
      }
      

      font-size, font-weight 등 동일한 네임스페이스 font-를 가진 속성에서도 중첩이 가능하다

      .foo {
          font: {
              size: 10px;
              weight: bold;
              family: sans-serif;
          }
          margin: {
              top: 10px;
              bottom: 20px;
          }
      }
      


    • Variable : 반복적으로 사용되는 값을 변수로 지정할 수 있다. $ 키워드 사용

      $w : 200px;
      $color-primary: red;
      $url-bg: "/public/assets/images/";
           
      .btn {
          width: $w;
          background: $color-primary url($url-bg + "bg.jpg");
      }
      

      변수는 블록 스코프 단위를 갖는다. 블록 안에서 정의된 변수는 다른 변수에서 접근 불가능

      .btn1 {
          $color: red;
          background: $color;
      }
           
      /*
      .btn2 {
          background: $color;
      } --------> error
      */
      

      변수에 변수를 할당하는 변수 재할당도 가능하다

      $red: red;
      $color-danger: $red;
           
      .box {
          color: $color-danger;
      }
      

      !global 키워드로 전역 변수로 설정할 수 있다. 블록 스코프 안에서 정의해도 !global 키워드를 붙이면 다른 블록에서도 접근이 가능하지만, 같은 이름이 있을 경우 덮어져 사용될 수 있다

      .btn1 {
          $color: red !global;
          background: $color;
      }
           
      .btn2 {
          background: $color;
      }
           
      .btn3 {
          $color: blue;
          background: $color;
      } // ------> blue로 덮어짐
      

      !default 키워드로 초기값 설정이 가능하다. 기존에 할당한 값이 있으면 그 값을 사용한다

      $color-primary: red;
           
      .box {
          $color-primary: blue !default;
          background: $color-primary;
      } // ------> red
      

      #{} 을 이용해서 scss 구문 안에 변수를 넣을 수 있다. 문자열병합템플릿 비슷한 기능

      $family: unquote("Sans");
      @import url("http://fonts.googleapis.com/css?family=#{$family}");
           
      // unquote는 sass 내장함수로 문자에서 따옴표를 제거함
      

      #{}(문자보간)을 이용하여 동적인 변수명 사용은 허용하지 않는다. 대신 map을 써야한다

      @use "sass:map";
           
      $theme-colors {
          "success": green;
          "info": yellow;
          "warning": red;
      }
           
      .alert {
          background-color: map.get($theme-colors, "warning");
          // $theme-color-#{warning} (X)
      }
      
    • import : @import 외부에서 가져온 Sass 파일은 모두 단일 css 출력 파일로 병합된다 import한 파일에 정의된 변수, Mixin 등을 사용할 수 있다 CSS의 import 규칙으로 컴파일되는 몇 가지 예외 사항이 있다

      • 파일 확장자가 .css 일 때

      • 파일 이름이 http://로 시작하는 경우

      • url()이 붙은 경우

      • 미디어쿼리가 있는 경우

        @import "hello.css";
        @import "http://hello.com/hello";
        @import url(hello);
        @import "hello" screen;
               
        // 여러 파일은 , 로 구분함
        @import "header", "footer";
        

      파일 분할(Partials) : main 안에 여러 scss 파일을 import 하여 컴파일하면 각각의 scss파일이 모두 css로 나눠서 컴파일된다. 프로젝트 규모가 클 경우, 유지보수가 쉽지 않다

      파일 생성 시 _를 붙이고 import 하면 main 하나만 css 파일로 컴파일 할 수 있다

      // _header.scss
      // _footer.scss
           
      // main.scss
      @import "header", "footer";
           
      // 컴파일은 main.css 하나의 파일만 생성된다
      

      webpack, parcel, gulp는 설정에 따라 빌드하지만, 되도록 _의 사용을 권하고 있다

    • calculation : Sass는 기본적인 연산 기능을 지원한다

      • + , -, *, /, % : 산술 연산자
      • 나누기 시 주의할 점은 오른쪽 숫자가 반드시 숫자여야함 ex) 10px / 5px (X)
      • 곱하기 시 주의할 점은 하나 이상의 값이 반드시 숫자여야함 ex) 10px * 5px (X)
      • ==, !=, <, >, <=, >= : 비교 연산자
      • and, or, not : Boolean 연산자

      일반적으로 px 절대 단위로 연산을 하지만, %, em, vw 등 상대적 단위 연산 시 calc() 사용

      / 연산자를 구분이 아닌 연산 기능으로 사용하려면 다음 조건을 충족해야한다

      • 값이 변수에 저장되거나 함수에 의해 반환되는 경우

      • 값이 ()로 묶여있는 경우

      • 값이 다른 산술 표현식의 일부로 사용되는 경우

        div {
            $x: 100px;
            width: $x / 2; // 변수에 저장된 값 사용
            height: (100px / 2); // 괄호로 묶어서 사용
            font-size: 10px + 12px / 3; // 다른 연산자와 같이 사용
        }
        

      문자 연산 시 + 사용, 첫번째 값이 따옴표가 있느냐 없느냐에 따라 결과도 같이 따라감

      div::after {
          content: "Hello" + World;
          flex-flow: row + "-reverse" + " " + wrap;
      }
           
      // ----------Compiled to
           
      div::after {
          content: "Hello World";
          flex-flow: row-reverse wrap;
      }
      

      색상도 연산이 가능하다

      div {
        color: #123456 + #345678;
        // R: 12 + 34 = 46
        // G: 34 + 56 = 8a
        // B: 56 + 78 = ce
        background: rgba(50, 100, 150, .5) + rgba(10, 20, 30, .5);
        // R: 50 + 10 = 60
        // G: 100 + 20 = 120
        // B: 150 + 30 = 180
        // A: Alpha channels must be equal
      }
           
      // --------------Compiled to
      div {
        color: #468ace;
        background: rgba(60, 120, 180, 0.5);
      }
      

      알파값은 연산되지 않고 서로 동일해야 다른 값이 연산이 된다

      알파값 연산을 위해서는 opacify(), transparentize() 함수를 사용한다

      $color: rgba(10, 20, 30, .5);
      div {
        color: opacify($color, .3);  // 30% 더 불투명하게
        background-color: transparentize($color, .2);  // 20% 더 투명하게
      }
      

      @if 문에서 논리 연산자가 쓰인다

      $width: 90px;
      div {
        @if not ($width > 100px) {
          height: 300px;
        }
      }
      
    • mixin / include : 스타일을 정의하여 재사용할 수 있다 @mixin 키워드로 선언하고 사용처에서 @include 키워드로 사용한다

      @mixin example {
          background: red;
      }
           
      .box {
          @include example
      }
      

      매개변수를 사용하여 동적으로 사용할 수도 있다

      @mixin color($color) {
          color: $color;
      }
           
      .btn1 {
          @include color(red);
      }
      .btn2 {
          @include color(blue);
      }
      

      매개변수는 default value 설정이 가능하다

      @mixin size($width: 100px, $height: 50px) {
          width: $width;
          height: $height;
      }
           
      .box1 {
          @include size; // 100px, 50px
      }
      .box2 {
          @include size($height: 100px); // 100px, 100px
      }
      

      입력할 인수의 개수가 불확실한 경우 가변인수를 사용할 수 있다

      가변인수는 매개변수 뒤에 ... 를 붙인다

      // 전달받을 값에 가변인수 사용
      @mixin bg($width, $height, $bg-values...) {
          width: $width;
          height: $height;
          background: $bg-values;
      }
           
      div {
          @include bg(
              100px,
              200px,
              url("/images/a.png") no-repeat 10px 20px,
              url("/images/b.png") no-repeat,
              url("/images/c.png")
          );
      }
      
      // 전달할 값으로 가변인수 사용
      @mixin font (
          $style: normal,
          $weight: normal,
          $size: 16px,
          $family: sans-serif
          ) {
              font: {
      			style: $style;
                  weight: $weight;
                  size: $size;
                  family: $family;
              }
      }
           
      div {
          // 순서와 개수에 맞게 매개변수 전달
          $font-values: italic, bold, 16px, sans-serif;
          @include font($font-values...);
      }
      span {
          // 필요한 값만 키워드 인수로 변수에 담아 전달
          $font-values: (style: italic, size: 22px);
          @include font($font-values...);
      }
      a {
          // 필요한 값만 키워드 인수로 전달
          @include font((weight: 900, family: monospace)...);
      }
      

      @content 키워드가 mixin에 있으면 원하는 스타일 블록을 전달할 수 있다

      기존 mixin이 가지고 있는 기능에 선택자나 속성 등을 추가할 수 있다

      @mixin icon($url) {
          $::after {
              content: $url;
              @content;
          }
      }
      .icon1 {
          // icon mixin의 기존 기능 사용
          @include icon("/images/icon.png");
      }
      .icon2 {
          // icon mixin에 스타일 블록을 추가하여 사용
          @include icon("/images/icon.png") {
              position: absolute;
          };
      }
      
    • extend : 확장 기능으로 다른 선택자의 모든 스타일을 가져올 수 있다

      .btn {
          padding: 10px;
          margin: 10px;
          background: blue;
      }
      .btn-danger {
          @extend .btn;
          background: red;
      }
      

      확장은 원치 않는 side-effect를 초래할 수 있으므로 mixin으로 대체하는 것이 좋다

    • function : 함수를 정의하여 연산된 @return 값을 이용할 수 있다

      $max-width: 980px;
           
      @function columns($number: 1, $columns: 12) {
          @return $max-width * ($number / $columns)
      }
           
      .box-group {
          .box1 {
              width: columns(3);
          }
      }
      

      sass 내장 함수와 겹치지 않도록 접두어 등을 붙여서 정의하는 것이 좋다.

    • if : @if @else 키워드로 조건문을 사용할 수 있다. 조건문의 ()는 생략이 가능하다 삼항연산자 스타일로도 사용할 수도 있다

      $width: 555px;
      div {
          width: if($width > 300px, $width, null);
      }
      
      $color: orange;
      div {
          @if $color == red {
              color: red;
          } @else if $color == orange {
              color: orange;
          } @else {
              color: green;
          }
      }
      

      조건에는 논리 연산자 and, or, not을 사용할 수 있다

      @function limitSize($size) {
          @if $size >= 0 and $size <= 200px {
              @return 200px;
          } @else {
              @return 800px;
          }
      }
      div {
          width: limitSize(180px);
      }
      
    • for / each / while : 반복문을 사용할 수 있다 @forthrough 방식과 to를 사용하는 방식이 있다. 종료조건이 해석되는 방식이 다름

      일반적으로 through의 사용을 권하고 있다

      @for $i from 1 through 3 {
          .through:nth-child(#{$i}) {
              width: 20px * $i
          }
      }
      // 1, 2, 3 세번 실행
           
      @for $i from 1 to 3 {
          .to:nth-child(#{$i}) {
              width: 20px * $i
          }
      }
      // 1, 2 두번 실행 (1부터 3 직전)
      

      @each는 List와 Map을 반복할 때 사용한다

      $zoo: (fox, rabbit, monkey, dog, cat);
           
      .zoo {
          @each $animal in $zoo {
              li.#{$animal} {
                  background: url("/images/#{$animal}.png");
              }
          }
          // index가 필요한경우, index() 내장메소드 사용
          @each $animal in $zoo {
              $i: index($zoo, $animal);
              li:nth-child(#{$i}) {
                  left: 50px * $i;
              }
          }
      }
      

      각 데이터의 length가 같은 경우 여러 List를 동시에 처리할 수도 있다

      $apple: (apple, korea);
      $orange: (orange: japan);
      $banana: (banana, china);
           
      @each $fruit, $country in $apple, $orange, $banana {
          .box-#($fruit) {
              background: url("/images/#{$country}.png");
          }
      }
      

      Map 데이터를 반복할 경우 하나의 데이터에 두 개의 변수(key,value)가 필요하다

      $fruits: (
      	apple: korea,
          orange: china,
          banana: japan
      );
      @each $fruit, $country in $fruits {
          .box-#{$fruit} {
              background: url("/images/#{$country}.png");
          }
      }
      

      @while은 조건이 true인 동안 실행되므로 무한루프에 빠지지 않도로 주의해야한다

      $i: 6;
      @while $i > 0 {
          .item-#{$i} {
              width: 2px * $i;
          }
          $i: $i - 2;
      }
      
  4. Built-in Functions



참고 자료


sass doc

Sass(SCSS) 완전 정복!

Sass Guidelines