Sass

Syntatically Awesome Stylesheets의 약자로써 문법적으로 어썸한 스타일시트 즉, 기존의 CSS코드를 좀 더 어썸하게 사용할 수 있게 해주는 툴.

  • CSS COMPATIBLE
  • INDUSTRY APPROVED
  • FEATURE RICH
  • LARGE COMMUNITY
  • MATURE
    • 개발한지 12년이 됐기 때문에 안정적이다. 루비에서 다트로 변경해서 버그가 있지만 괜찮음.
  • FRAMEWORKS
    • WEBPACK, GULP와 같은 TASKRUNNER와 함께 사용할 떄 유용하다.

설치하기

  • node 설치

    $ node -v
    v12.13.0
    $ npm -v
    6.12.0
    
  • SASS 설치

    // npm을 이용해 sass설치
    // window
    $ npm install -g sass
      
    // mac
    $ sudo npm install -g sass
    

  • SASS 버전 확인

    $sass --version
    1.25.0 compiled with dart2js 2.7.0
    

기본 사용법

우리의 편리함을 위해 SASS코드로 작성했지만 브라우저는 CSS코드밖에 이해하지 못하기 때문에 .sass파일을.css 파일로 변환해주는 작업이 필요합니다. 이 작업을 커맨드를 통해서 매번 실행시킬 수도 있고, TaskRunner를 통해서 .scss파일을 수정하고 저장하면 .css변환해서 브라우저를 리렌더링 하는 자동화도 할 수 있습니다. TaskRunner는 따로 정리할 것이기 때문에 커맨드 라인으로 변환하는 방법만 정리하도록 하겠습니다.

$ sass input.scss output.css

위와 같이 공백으로 입력할 .scss파일과 변환할 .css파일을 작성할 수 있지만 좀 더 명시적으로 :으로 구분하는 것이 좋습니다.

$ sass input.scss:output.css
  • --watch옵션으로 자동 컴파일

    $ sass --watch input.scss output.css
    

변환을 하게 되면 --source-map이 생기게 됩니다.

컴파일 시 아래와 같은 주석이 추가되고, css파일명과 동일한 .map파일이 추가 생성됩니다.

/*# sourceMappingURL=sub.css.map */

요소검사툴에서 style 확인시 scss파일명과 행번호를 표시해주어 디버깅을 용이하게 해줍니다.

.scss파일 문법

  • @import

    CSS에도 존재하는 @import구문과 유사한 형태로 사용되지만, CSS는 컴파일이 각각 된 후에 파일을 불러와 사용하는 것이기 때문에 효율적이지 않은 반면에, Sass에서는 컴파일 전에 파일을 불러와 하나의 .css 파일로 합쳐집니다. 덕분에 .scss 파일을 모듈 또는 레이아웃 기준으로 분리하여 css를 구조화할 수 있습니다.

    // _sub.scss
    @import 'sub'; // 시작 '_' 생략 가능 
    @import 'sub.scss'; // 확장자 생략 가능 
      
    @import 'scss/sub'; // 폴더의 하위 파일 선택 가능 
    @import 'sub1', 'sub2'. 'sub3'; // 여러 파일 한번에 import가능
    

  • Nesting

    React의 컴포넌트 스타일링 라이브러리인 styled-components에서 지원이 되는 Nesting은 css코드를 작성할 때 계층구조를 파악하기 쉽고 가독성을 높혀준다.

    해당 태그의 반응형 코드가 이곳저곳에 퍼져있지 않게 되고, 부모 요소를 반복적으로 작성해주던 불편함을 해결해준다.

    가급적 3depth 이내의 중첩 구조를 갖게 짜는 것이 좋다.

  • &(Referencing Parent Selectors, 부모 참조 셀렉터)

    블럭 안에 &를 추가하여 부모 셀렉터를 참조하는 형태의 셀렉터

    // scss
    .espresso {
    	color: red;
      
    	&:hover {
    		color: orange; // 가상 클래스
    	}
    	&::after {
    		color: yellow; // 가상 요소
    	}
    	&[id="text"] {
    		color: green; // 속성 선택자
    	}
    	&.americano {
    		color: blue; // 클래스 선택자
    	}
    	&#cappuccino {
    		color: purple; // 아이디 선택자
    	}
    }
    

    부모 참조 셀렉터를 사용할 때 주의 사항은 부모 셀렉터만 가져오는 것이 아니라 nesting된 모든 부모 셀렉터를 가져옴을 주의해야합니다.

    // scss
    .espresso {
      color: red;
      
      .cappuccino {
        color: orange;
      	
        .latte {
          .a_class {
            .b_class {						
              &.last { // 깊은 중첩에서의 부모 참조 선택자
                color: yellow;
              }						
            }
          }
        }
      }
    }
      
    // CSS
    .espresso { color: red; }
    .espresso .cappuccino { color: orange; }				
    .espresso .cappuccino .latte .a_class .b_class.last { color: yellow; }
    

    &의 위치에 따라서 다른 방법으로도 사용할 수 있습니다.

    // 접미사로 사용한 예시 
    // scss
    .latte {
    	.cappuccino & {
    		font-size: 11px;
    	}
    }
    // css
    .cappuccino .latte { 
    	font-size : 11px;
    }
      
    // 접두사로 사용한 예시, BEM 지원
    // scss
    .give_me_a {
      &-espresso { 
      	color : red;
      }
      .please &-americano { 
      	color : orange;
      }
    }
      
    // CSS
    .give_me_a-espresso { 
    	color : red;
    }
    .please .give_me_a-americano { 
    	color : orange;
    }
    
  • $Variables

    사스의 변수는 $를 변수명 앞에 붙임으로 선언이 가능합니다.

    //scss
    $color-normal: #666;
      
    a { 
    	color : $color-normal;
    }
    

    지역변수와 전역변수의 개념이 적용됩니다.

    // scss
    $size: 10px;
      
    .latte {
    	$size: 12px;				
    	width: $size;
      
    	.americano {
    		width: $size;
    	}
    }
    .cappuccino {				
    	width: $size;
    }
      
    // CSS
    .latte { width:12px }
    .latte .americano { width:12px }
    .cappuccino { width:10px }
    

    블록 안에서 전역변수를 선언할 수 있지만 굳이 이렇게 하지는 않을 것 같습니다.

    // scss
    $size: 10px;
      
    .latte {
    	$size: 12px !global;				
    	width: $size;
      
    	.americano {
    		width: $size;
    	}
    }
    .cappuccino {				
    	width: $size;
    }
      
    // CSS
    .latte { width:12px }
    .latte .americano { width:12px }
    .cappuccino { width:12px }
    
  • 변수의 타입

    • 숫자 1, 1.5, 10px
    • 문자열 “foo”, ‘bar’, baz
    • 색상 blue, #04a3f9, rgba(255,0,0,0.5)
    • 불리언 true, false
    • 널 null
    • 리스트 1.5em 1em 0 2em, Arial, sans-serif
    • 맵 (key1: value1, key2: value2)
    • function 참조 round(1.5)
  • Interpolation ${} 보간법

    변수를 selector명 혹은 속성 명에 사용할 수 있습니다.

    $name : foo;
    $attr : border;
    .#{$name} { 
      #{$attr}-color : blue;
    }
      
    .foo { 
    	border-color : blue;
    }
    
  • @Mixininclude

    자주 사용하는 css패턴이나, 재사용을 위한 css 구문을 @mixin으로 작성하고,

    작성된 @mixin@include를 이용하여 불러옵니다.

    @mixin ellipsis-text { 
    	overflow : hidden;
      text-overflow : ellipsis;
      width : 100%; 
      white-space : nowrap;
    }
      
    .text { 
    	@include ellipsis-text;
      color : #f00;
    }
      
    // CSS
      
    .text {
      overflow : hidden;
      text-overflow : ellipsis;
      width : 100%; 
      white-space : nowrap;
      color : #f00;
    }
    

    @mixin은 selector를 포함해서 룰셋을 작성하는 것이 가능합니다.

    @mixin links { 
      a { 
    		color : blue;
      }
    }
      
    @include links;
      
    // CSS
    a {
      color: blue;
    }
    

    또한, 다른 @mixin을 include하는 것도 가능합니다.

    @mixin compound { 
    	@include highlighted-background;
    	@include header-text;
    }
    

    함수처럼 Arguments를 전달해서 사용하는 것도 가능합니다.

    @mixin border($color,$width){
    	border-color : $color;
    	border-width. : $width;
    }
      
    p {
      @include border(blue,30px);
    }
    

    인자의 기본 값을 아래처럼 지정하는 것도 가능합니다.

    @mixin border($color,$width:20px){
    	border-color : $color;
    	border-width. : $width;
    }
      
    // 순서를 지키지 않고 키워드로 인자를 넘겨주는 것도 가능 
    p {
      @include border($width : 2px,$color : blue);
    }
    

    인자의 갯수를 복수형태로 받을 수도 있습니다.

    @mixin margin($var...) { 
    	margin : $var;
    }
      
    p { 
    	@include margin(5px)
    }
    p { 
    	@include margin(5px 5px)
    }
    p { 
    	@include margin(5px 5px 5px)
    }
    p { 
    	@include margin(5px 5px 5px 5px)
    }
    
  • @content

    @mixin에 인자를 넘기는 것이 아니라 블럭 자체를 넘기고 싶을 경우 사용하는 문법

    @mixin mq { 
      @media screen and  (max-width : 768px) { 
      	@content;
      }
    }
      
    p {
      width : 300px;
      @include mq { 
      	color : blue; // 전달할 블럭
      }
    }
      
      
    //CSS
      
    p { 
    	width : 300px;
    }
    @media screen and (max-width : 768px){
      p { 
      	color : blue;
      }
    } 
    

  • @extend%placeholder

    @mixin과 유사한 점이 있지만 반복적으로 사용하는 코드를 재사용하기 위함이 아니라 클래스의 상속과 비슷한 의미로 기존 스타일을 확장하는 목적이 있습니다.

    .americano { 
    	font-size : 12px;
    }
      
    .americano_ice { 
      @extend .americano;
      color : red;
    }
      
    // 예상 코드는 아래와 같았는데 실제로는 중복된 부분을 함께 선언해준다. 
    .americano { 
    	font-size : 12px;
    }
      
    .americano_ice{ 
      font-size : 12px;
      color : red;
    }
      
    // 실제 변환 CSS
    .americano, .americano_ice {
      font-size : 12px;
    }
      
    .americano {  
    	color : red;
    }
    
  • %placeholder

    extend를 하는 것과 기능은 동일하지만 %로 선언한 룰셋은 css에 존재하지 않게 됩니다. (컴파일 되지않기 때문에)

    %water { 
    	font-size : 12px;
    }
      
    .americano { 
    	@extend %water; 
      color : blue;
    }
      
    .latte { 
    	@extend %water; 
      color : red;
    }
      
    // CSS
    .americano, .latte { 
    	font-size : 12px;
    }
    .americano { 
    	color : blue;
    }
    .latte { 
    	color : red;
    }
    
  • @extend의 한계

    자식선택자, 인접선택자를 사용해서 extend를 할 수 없습니다.

    .latte { 
    	@extend .americano .cappucino;
    }
      
    .latte { 
    	@extend .americano + .cappucino;
    }
    

    미디어 구문 안에서 블럭밖의 클래스에 확장할 수 없습니다.

    // scss
    .espresso {
    	background-color: #391919;
    }
    @media print {
    	.latte {
    		@extend .espresso;
      
    		color: #887e61;
    	}
    }
    // Error 
    Error: You may not @extend an outer selector from within @media.
    You may only @extend selectors within the same directive.
    

폴더 및 파일의 구조화

- sass
	- common
		- _base.scss
		- _variables.scss
		- _placeholders.scss
		- _mixins.scss
	- main
		- _layout.scss
		- _icon.scss
		- _main.scss
		- _gnb.scss
	- main.scss

생각해보기

  • Media query를 어디에 놓는 것이 맞는가?
.main_food_content_writer_container {
  position: relative;
  margin-top: 16px;

  &::before {
    content: "";
    display: block;
    width: 30px;
    height: 1px;
    margin: 0 auto 16px;
    background-color: #333;
		
    // 이렇게 before에 대한 media-query를 before안에 놓는것이 좋은지
    @include mq(desktop) {
      width: 50px;
    }
  }
  
  // 아니면 이렇게 데스크탑을 아예 빼서 사용하는게 나은지 
  @include mq(desktop) {
      &::before { 
     	 	width: 50px;
      }
  }
	
  
  // before요소와 그냥 요소 둘다 미디어쿼리가 많아진다면 중복 미디어 쿼리가 발생하기 때문에 이게 더 나아보임
  @include mq(tablet) {
    margin-top: 30px;
    overflow: hidden;

    &::before {
      margin: 0 auto 30px 0;
      width: 40px;
    }
  }
}