Sass

블로그 이사 완료·2022년 12월 3일
0
post-thumbnail

📍 Basics : Sass의 소개, 설치와 간단한 명령어 사용법

#1. Introduction

Sass(Syntactically Awesome StyleSheets)는 CSS pre-processor로서 CSS의 한계와 단점을 보완하여 보다 가독성이 높고 코드의 재사용에 유리한 CSS를 생성하기 위한 CSS의 확장(extension)이다.

CSS의 간결한 문법은 배우기 쉬우며 명확하여 프로젝트 초기에는 문제가 없이 보이지만 프로젝트의 규모가 커지고 수정이 빈번히 발생함에 따라 쉽게 지저분해지고 유지보수도 어려워지는 단점도 가지고 있다.

이러한 CSS의 태생적 한계를 보완하기 위해 Sass는 다음과 같은 추가 기능과 유용한 도구들을 제공한다.

  • 변수의 사용
  • 조건문과 반복문
  • Import
  • Nesting
  • Mixin
  • Extend/Inheritance

CSS와 비교하여 Sass는 아래와 같은 장점이 있다.

  • CSS보다 심플한 표기법으로 CSS를 구조화 하여 표현할 수 있다.
  • 스킬 레벨이 다른 팀원들과의 작업 시 발생할 수 있는 구문의 수준 차이를 평준화할 수 있다.
  • CSS에는 존재하지 않는 Mixin 등의 강력한 기능을 활용하여 CSS 유지보수 편의성 을 큰 폭으로 향상시킬 수 있다.

#2. Install

브라우저는 Sass의 문법을 알지 못하기 때문에 Sass(.scss) 파일을 css 파일로 트랜스파일링(컴파일) 하여야 한다. 따라서 Sass 환경의 설치가 필요하다.

Sass는 2006년 Ruby로 처음 개발되었고 이후 Ruby Sass를 C++로 포팅한 Libsass, node.js 환경에서 Libsass를 사용할 수 있는 node-sass 등 다양한 포팅 버전이 등장했다. Sass는 최근에 Dart Sass로 재구현되었다. Libsass나 node-sass는 현재 유지 관리는 되고 있으나 폐지되었으므로 Dart Sass를 사용 하는 것이 좋다.

다음 명령을 사용해 Sass를 설치한다.

$ npm install -g sass

#3. Command

#3.1. version

$ sass --version
1.30.0 compiled with dart2js 2.10.4

#3.2. 트랜스파일링

sass-project 디렉터리를 생성하고 트랜스파일링할 foo.scss 파일을 아래와 같이 생성한다.

$site_max_width: 960px;
$font_color: #333;
$link_color: #00c;
$font_family: Arial, sans-serif;
$font_size: 16px;
$line_height: percentage(20px / $font_size);

body {
  color: $font_color;

  // Property Nesting
  font: {
    size: $font_size;
    family: $font_family;
  }

  line-height: $line_height;
}

#main {
  width: 100%;
  max-width: $site_max_width;
}

트랜스파일링할 SCSS 파일의 경로와 트랜스파일링 후 생성될 css 파일의 경로를 지정한다.

$ cd sass-project

## foo.scss를 트랜스파일링해서 foo.css를 생성
$ sass foo.scss:foo.css

foo.scss 파일이 드랜스파일링되어 다음과 같이 foo.css 파일이 생성된다.

body {
  color: #333;
  font-size: 16px;
  font-family: Arial, sans-serif;
  line-height: 125%;
}

#main {
  width: 100%;
  max-width: 960px;
}

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

특정 디렉터리 내의 모든 scss 파일을 css 파일로 일괄 트랜스파일링해서 지정한 디렉터리에 저장하려면 다음과 같이 인풋 디렉터리와 아웃풋 디렉터리를 지정한다.

## sass input-directory-path:output-directory-path
$ sass src/sass:dist/css

npm scripts를 사용하면 매번 긴 명령어를 입력하지 않고 좀 더 간단히 명령어를 사용할 수 있다.

프로젝트 디럭터리에 아직 package.json이 없다면 다음 명령으로 package.json을 생성한다.

$ cd sass-project
$ npm init -y

생성된 package.json을 다음과 같이 수정한다.

{
  "name": "sass-project",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "build:sass": "sass src/sass:dist/css"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

이제 다음 명령으로 좀 더 간단히 트랜스파일링할 수 있다.

$ npm run build:sass

#3.3. style

scss 파일을 트랜스파일링하여 css 파일을 생성할 때 2가지 스타일 중 하나를 선택할 수 있다.

expanded
표준적인 스타일의 css 파일이 생성된다. 기본값이다.

$ sass --style expanded src/sass:dist/css
# 위와 같은 결과가 만들어진다.
$ sass src/sass:dist/css

compressed
가능한 빈공간이 없는 압축된 스타일의 css 파일이 생성된다.

$ sass --style compressed src/sass:dist/css

#3.4. watch

watch 옵션은 scss 파일의 변경을 감지 하여 변경될 때마다 scss 파일을 트랜스파일링하여 css 파일을 자동 업데이트 한다.

## watch src/sass -> dist/css
$ sass --watch src/sass:dist/css

#4. SASS vs. SCSS

Sass는 SASS 표기법(.sass)SCSS 표기법(.scss) 이 있다. 이전 버전에서는 SASS 표기법이 기본 표기법이었으나 Sass 3.0부터 CSS 친화적인 SCSS(Sassy CSS) 표기법이 기본 표기법이 되었다.

CSSSCSSSASS
중괄호 {}필요필요불필요(공백 2문자 들여쓰기가 코드 블록을 의미)
세미콜론 ;필요필요불필요
: 뒤의 공백불필요불필요필요
Mixin없음@mixin=
Include없음@include+
확장자.css.scss.sass

ㅤㅤ

📍 SassScript : CSS를 프로그래밍 언어와 같이 작성할 수 있게 확장한 SassScript

SassScript는 CSS에서는 불가능한 연산, 변수, 함수 등의 확장 기능을 의미한다.

#1. 데이터 타입

프로퍼티 값으로 사용할 수 있는 값에는 각각의 데이터 타입(Data type)이 존재한다. SassScript는 7가지의 데이터 타입을 제공한다.

숫자형
e.g) 1.2, 13, 10px

문자열
CSS는 2종류의 문자열을 사용할 수 있다. 따옴표를 사용하는 경우(“Lucida Grande”, ‘http://sass-lang.com’)와 사용하지 않는 경우(bold, sans-serif)가 있다. Sass는 2종류의 문자열 모두를 인식할 수 있으며 트랜스파일링 후의 CSS에는 Sass에서 사용한 문자열이 그대로 출력된다.
e.g. “Lucida Grande”, ‘http://sass-lang.com’, sans-serif

컬러
e.g. blue, #04a3f9, rgba(255, 0, 0, 0.5)

boolean
e.g. true, false

null
프로퍼티 값에 값이 null인 변수가 지정되면 해당 룰셋은 트랜스파일링되지 않는다.

list
margin과 padding 프로퍼티값 지정에 사용되는 0 auto와 font-family 프로퍼티값 지정에 사용되는 Helvetica, Arial, sans-serif 등은 공백 또는 콤마 구분된 값의 list이다.
e.g. 1.5em 1em 0 2em, Helvetica, Arial, sans-serif

map
객체와 유사한 방식으로 map-get 함수를 사용하여 원하는 값은 추출할 수 있다.
e.g. (key1: value1, key2: value2)

// map
$foundation-palette: (
  primary: #E44347,
  mars: #D7525C,
  saturn: #E4B884,
  neptune: #5147D7
);

.mars {
  color: map-get($foundation-palette, mars);
}

// => .mars { color: #D7525C; }

데이터 타입을 확인하려면 빌트인 함수인 type-of를 사용한다.

#2. 변수

Sass에서는 변수를 사용할 수 있다. 문자열, 숫자, 컬러(#aa443f) 등을 사전에 변수에 저장하고 필요할 때 불러 사용할 수 있다.

변수명은 $ 로 시작한다.

$site_max_width: 960px;
$font_color: #333;
$link_color: #00c;
$font_family: Arial, sans-serif;
$font_size: 16px;
$line_height: percentage(20px / $font_size);

body {
  color: $font_color;

  // Property Nesting
  font: {
    size: $font_size;
    family: $font_family;
  }

  line-height: $line_height;
}

#main {
  width: 100%;
  max-width: $site_max_width;
}
body {
  color: #333;
  font-size: 16px;
  font-family: Arial, sans-serif;
  line-height: 125%;
}

#main {
  width: 100%;
  max-width: 960px;
}

#3. 변수의 Scope

변수에는 유효범위(scope)가 존재한다. 코드 블록 내에서 선언된 변수는 지역변수가 된다. 지역변수의 유효범위는 자신이 속한 코드 블록과 하위 코드 블록이다.

$width: 960px; // 전역 변수

header {
  width: $width;
  margin: 0 auto;
}

#main {
  $color: #333; // 지역 변수
  width: $width;
  margin: 20px auto;
  section {
    p {
      color: $color;

      a:link {
        color: $color;
      }
    }
  }
}

footer {
  width: $width;
  margin: 0 auto;
  color: $color; // Error: Undefined variable: "$color".
}

위 코드에서 $width 는 top level에 기술되었으므로 전역 변수 다. 전역변수는 전역은 물론 하위의 어떤 코드 블록 내에서도 유효하다.

위 코드를 트랜스파일링하면 Undefined variable: “$color” 라는 에러가 발생한다. 이는 #main에서 선언한 $color 는 #main 내에서만 유효한 지역 변수 이기 때문이다.

코드 블록 내에서 선언한 변수를 전역 변수로 지정하는 방법은 아래와 같다.

#main {
  $color: #333 !global; // 전역 변수
  width: $width;
  ...

#4. 연산자

#4.1. 숫자 연산자

연산자설명
+덧셈
-뺄셈
*곱셈
/나눗셈
%나머지
==동등
!=부등
$width: 100px;

#foo {
  width: $width + 10; // 110px
}

#bar {
  width: $width + 10in; // 1060px
}

변수 $width의 값 100px에 10 또는 10em과 같이 다른 단위의 값을 연산하여도 에러없이 연산이 수행된다. 이때 연산자의 왼쪽 값을 기준으로 단위가 설정된다.

$width에 10em을 더하면 어떻게 될까?

$width: 100px;

#foo {
  width: $width + 10em; // 100px + 10em => Error: Incompatible units em and px.
}

트랜스파일링 결과 Error: Incompatible units em and px. 이라는 에러를 출력한다.

Sass 연산은 대상을 변환하여 연산할 수 없는 경우 에러를 출력한다.

% em rem vh vw vmin vmax 과 같이 상대적인 값을 Sass는 알지 못한다. 상대적인 값의 결과값은 브라우저만이 알 수 있기 때문이다.

따라서 상대적인 값을 갖는 단위의 연산은 동일한 단위를 갖는 값과의 연산만이 유효하다.

#foo {
  width: 5% + 10%; // 15%
}

CSS3의 calc 함수를 사용하면 이런 문제를 해결할 수 있다.

#foo {
  width: calc(25% - 5px);
}

CSS에서의 / 는 나눗셈의 의미가 아니라 값을 구분하는 의미를 갖는다.

p {
  /*
    font: font-style font-variant font-weight font-size/line-height font-family
  */
  font: italic bold 12px/30px Georgia, serif;
}

따라서 Sass의 / 연산자를 사용하기 위해서는 몇가지 조건이 필요하다.

  • 변수에 대해 사용
  • 괄호 내에서 사용
  • 다른 연산의 일부로서 사용
p {
  // font와 border-radius의 '/'는 CSS문법에 맞는 표현이므로 연산되지 않는다.
  font: italic bold 12px/30px Georgia, serif;
  // 타원형 둥근 모서리
  border-radius: 10px 20px/20px;
  /*
  border-top-left-radius: 10px 20px;
  border-top-right-radius: 20px;
  border-bottom-right-radius: 10px 20px;
  border-bottom-left-radius: 20px;
  */

  $width: 1000px;

  width: $width / 2;            // 변수에 대해 사용 → width: 500px;
  height: (500px / 2);          // 괄호 내에서 사용 → height: 250px;
  margin-left: 5px + 8px / 2px; // 다른 연산의 일부로서 사용 → margin-left: 9px;
}

변수를 CSS의 /와 함께 사용하고자 하는 경우 #{} (Interpolation)를 사용한다.

p {
  $font-size: 12px;
  $line-height: 30px;
  font: #{$font-size}/#{$line-height};  // 12px/30px
}

#4.2. 컬러 연산자

모든 산술 연산자는 컬러값에도 사용할 수 있다.

p {
  color: #010203 + #040506;
  // R: 01 + 04 = 05
  // G: 02 + 05 = 07
  // B: 03 + 06 = 09
  // => #050709
}

p {
  color: #010203 * 2;
  // R: 01 * 2 = 02
  // G: 02 * 2 = 04
  // B: 03 * 2 = 06
  // => #020406
}

p {
  color: rgba(255, 0, 0, 0.75) + rgba(0, 255, 0, 0.75);
  // alpha(투명도)는 연산되지 않는다
  // color: rgba(255, 255, 0, 0.75);
}

rgba의 alpha값은 연산되지 않는다. alpha값의 연산을 위해서는 opacify 함수 또는 transparentize 함수 를 사용한다.

  • opacify 함수: 첫번째 인수로 전달받은 alpha값에 두번째 인수를 더해 불투명도를 증가시킨다.(더 불투명해진다)
  • transparentize 함수: 첫번째 인수로 전달받은 alpha값에 두번째 인수를 빼서 불투명도를 감소시킨다.(더 투명해진다)
$translucent-red: rgba(255, 0, 0, 0.5);

p {
  color: opacify($translucent-red, 0.3);
  // => color: rgba(255, 0, 0, 0.8);

  background-color: transparentize($translucent-red, 0.25);
  // => background-color: rgba(255, 0, 0, 0.25);
}

#4.3. 문자열 연산자

+ 연산자는 자바스크립트와 같이 문자열을 연결할 수 있다.

p {
  cursor: e + -resize;  // e-resize
}

따옴표가 있는 문자열과 없는 문자열을 함께 사용하는 경우 좌항의 문자열을 기준으로 따옴표를 처리한다.

p:before {
  content: "Foo " + Bar;        // "Foo Bar"
  font-family: sans- + "serif"; // sans-serif
}

#4.4. 불린 연산자

연산자설명
&&and
llor
!not

#4.5. 리스트 연산자

리스트를 위한 별도의 연산자는 제공되지 않지만 리스트 함수를 사용하여 필요한 처리를 수행할 수 있다.

#5. Interpolation: #{}

인터폴레이션은 변수의 값을 문자열 그대로 삽입한다. 인터폴레이션에 의해 삽입된 문자열은 연산의 대상으로 취급되지 않는다.

변수는 프로퍼티값으로만 사용할 수 있으나 #{} 을 사용하면 프로퍼티값은 물론 셀렉터와 프로퍼티명에도 사용할 수 있다.

$name: foo;
$attr: border;

p.#{$name} {            // p.foo
  #{$attr}-color: blue; // border-color: blue;
}

.someclass {
  $font-size: 12px;
  $line-height: 30px;
  // 연산의 대상으로 취급되지 않도록
  font: #{$font-size} / #{$line-height}; // 12px / 30px
}

#7. Ampersand(&)

& 는 부모요소를 참조하는 셀렉터이다.

a {
  color: #ccc;

  &.home {
    color: #f0f;
  }

  &:hover {
    text-decoration: none;
  }

  // & > span (X)
  > span {
    color: blue;
  }

  span {
    color: red;
  }
}

위 Sass의 트랜스파일링 결과는 아래와 같다.

a {
  color: #ccc;
}

a.home {
  color: #f0f;
}

a:hover {
  text-decoration: none;
}

a > span {
  color: blue;
}

a span {
  color: red;
}

#8. !default

!default flag는 할당되지 않은 변수의 초기값 을 설정한다.

$content: null;
$content: "Non-null content" !default;

#main {
  content: $content; // "Non-null content"
}

이미 값이 할당되어 있는 변수에 !default flag 를 사용하면 적용되지 않는다.

$content: "First content";
$content: "Second content?" !default;
$new_content: "First time reference" !default;

#main {
  content: $content; // "First content"
  new-content: $new_content; // "First time reference"
}

이러한 특성은 partial에 매우 유용하다.

2개의 파일 _font.scssmain.scss 를 생성해서 main.scss은 내부에서 _font.scss을 import 해본다.

// _font.scss
$font-size: 16px !default;
$line-height: 1.5 !default;
$font-family: "Helvetica Neue", "Helvetica", "Arial", sans-serif !default;

body {
  font: #{$font-size}/$line-height $font-family;
}
// main.scss
$font-family: "Lucida Grande", "Lucida Sans Unicode", sans-serif;

@import "font";

위 코드의 트랜스파일링 결과는 아래와 같다. !default 는 변수에 값이 할당되지 않았을 때 사용할 기본값을 지정할 때 사용한다.
main.scss에서 변수에 값을 할당하였기 때문에 !default와 같이 사용한 변수값은 무력화된다.

body {
  font: 16px/1.5 "Lucida Grande", "Lucida Sans Unicode", sans-serif; }

만일 font.scss$font-family 변수에 !default 설정이 없었다면 후위에 선언된 font.scss$font-family 변수값이 적용돠어 아래와 같은 결과가 생성되었을 것이다.

body {
  font: 16px/1.5 "Helvetica Neue", "Helvetica", "Arial", sans-serif; }

ㅤㅤ

📍 CSS Extensions : Nesting, import, extend, 조건과 반복, Mixin, Function

#1. Nesting

Nesting은 Sass의 유용한 확장 기능으로 선언을 중첩(nesting)하는 것이다.

CSS는 후손 셀렉터(Descendant combinator) 의 경우 부모요소를 기술하여야 한다.

#navbar {
  width: 80%;
  height: 23px;
}

#navbar ul {
  list-style-type: none;
}

#navbar li {
  float: left;
}

#navbar li a {
  font-weight: bold;
}

Sass의 nesting 을 사용하면 후손 셀렉터를 간단히 기술할 수 있다. 또한 HTML의 구조를 반영한 CSS를 기술할 수 있다.

#navbar {
  width: 80%;
  height: 23px;

  ul {
    list-style-type: none;
  }

  li {
    float: left;
    a {
      font-weight: bold;
    }
  }
}

너무 깊은 nesting은 가독성을 나쁘게 하고 셀렉터를 복잡하게 만든다.

// Bad case
div#main {
  #sidebar {
    #navbar {
      width: 80%;
      height: 23px;

      aside {
        div {
          ul {
            list-style-type: none;

            li {
              float: left;

              a {
                font-weight: bold;
              }
            }
          }
        }
      }
    }
  }
}

부모 요소의 참조가 필요한 경우 & 를 사용한다. 예를 들어, :hover 또는 ::before 등의 가상 클래스 선택자 (Pseudo-class selector) 를 지정하는 경우 부모 요소의 참조가 필요하다.

.myAnchor {
  color: blue;

  // .myAnchor:hover
  &:hover {
    text-decoration: underline;
  }

  // .myAnchor:visited
  &:visited {
    color: purple;
  }
}

nesting은 프로퍼티 에도 사용할 수 있다.

.funky {
  font: {
    family: fantasy;
    size: 30em;
    weight: bold;
  }
}

위 코드의 트랜스파일링 결과는 아래와 같다.

.funky {
  font-family: fantasy;
  font-size: 30em;
  font-weight: bold;
}

#2. @-Rules and Directives

#2.1 @import

1개의 CSS 파일에 모든 스타일을 기술하면 유지보수하기 힘들고 가독성이 좋지 않다. 기능에 따라 CSS 파일을 분리하면 재사용 및 유지보수 측면에서 유리하다. 따라서 룰을 정하여 파일을 분리 하여 개발하는 것은 효과적인 방법이다.

Sass는 @import directive 를 사용하여 분리된 stylesheet 파일을 import할 수 있다. Sass는 기존의 CSS @import보다 편리한 기능을 제공한다.

@import "foo.scss";

// 확장자는 생략 가능하다
@import "foo";

// 여러 개의 파일을 한번에 임포트할 수 있다.
@import "rounded-corners", "text-shadow";

// 변수를 사용해 문자열을 생성하여 임포트할 수도 있다.
$family: "Open+Sans";
@import url("http://fonts.googleapis.com/css?family=#{$family}");

여러 개의 파일로 분할하는 것 또는 분할된 파일을 partial 이라 하며 partial된 Sass 파일명의 선두에는 underscore(_) 를 붙인다.
(_reset.scss, _module.scss, _print.scss)

예를 들어, “_foo.scss” 라는 partial된 Sass 파일이 있고 이 파일을 import하는 경우 아래와 같이 기술한다. 파일명 선두의 _확장자 는 생략할 수 있다.

@import "foo";

partial된 Sass 파일명 선두에 붙인 _의 의미는 import는 수행하되 CSS로의 트랜스파일링은 수행하지 말라는 의미를 갖는다. 따라서 partial은 import시에는 CSS 파일로 트랜스파일링되지 않기 때문에 최종적으로 CSS로 트랜스파일링을 수행할 Sass 파일에서 import한다.

예를 들어, 위 그림과 같이 partial된 _vars.scss _header.scss _sidebar.scss _footer.scss 를 style.scss가 import하는 경우를 생각해 본다.

// partial/_vars.scss
$width: 960px;
// partial/_header.scss
#header {
  width: $width;
}
// partial/_sidebar.scss
#sidebar {
  width: $width;
}
// partial/_footer.scss
#footer {
  width: $width;
}
// style.scss
@import "partial/vars";
@import "partial/header";
@import "partial/sidebar";
@import "partial/footer";

_vars.scss 에는 변수가 선언되어 있으므로 partial된 _vars.scss _header.scss _sidebar.scss _footer.scss 를 import가 수행되어 하나의 파일이 되기 이전에 트랜스파일링을 실행하면 에러가 발생한다. 즉, partial된 Sass 파일명 선두에 붙인 _ 을 제거하면 에러가 발생한다. 따라서 partial된 Sass 파일명 선두에는 반드시 _ 를 붙여서 import 시에는 partial이 CSS 파일로 트랜스파일링되지 않고 import가 완료된 이후 CSS로 트랜스파일링을 수행도록 한다.

최신 버전에서는 _ 을 붙이지 않아도 에러가 발생하지 않는다. @import 대신 @use 를 사용하는 방법도 있다.

@import는 top-level에서 사용하는 것이 일반적이지만 CSS rule 또는 @media rule 내에 포함시키는 것도 가능하다.

// _example.scss
.example {
  color: red;
}
#main {
  @import "example";
}

위 코드의 트랜스파일링 결과는 아래와 같다.

#main .example {
  color: red;
}

#2.2 @extend

기존 스타일을 상속하고자 경우 @extend 를 사용한다.

<div class="error seriousError">
  Oh no! You've been hacked!
</div>

기존에 선언되어 있는 error class를 사용하면서 일부 rule set에 대해서는 다른 선언이 필요한 경우 자주 사용하는 방법이다.

이러한 경우 사용할 수 있는 방법이 상속 이다. 상속되는 rule set을 그대로 상속받아 다른 부분만 별도 선언 하면 된다.

.error {
  border: 1px #f00;
  background-color: blue;
}

.seriousError {
  @extend .error;

  border-width: 3px;
  border-color: darkblue;
}

위 코드의 트랜스파일링 결과는 아래와 같다. .error.seriousError 가 공통으로 사용하는 프로퍼티를 묶어 합리적인 룰셋을 생성한다.

.error, .seriousError {
  border: 1px #f00;
  background-color: blue;
}

.seriousError {
  border-width: 3px;
  border-color: darkblue;
}

이제는 하나의 클래스만 적용시키면 된다.

<div class="seriousError">
  Oh no! You've been hacked!
</div>

@extend@media 블록과 같이 사용하는 경우, 제대로 작동하지 않는다. @media 안에서 외부의 선택자를 @extend할 수 없다.

.foo {
  color: red;
}

@media print {
  .bar {
    @extend .foo; // ERROR: You may not @extend selectors across media queries.
  }
}

@extend를 사용하면 트랜스파일링 후 자신의 셀렉터가 어디에 첨부될 것인지 예상하기 어렵고, 예상치 못했던 부작용이 발생할 수 있다. 따라서 @extend의 사용은 가급적 자제하고 Mixin은 사용하는 것을 추천한다.

#2.3 Placeholder Selectors

Placeholder Selector는 재사용이 가능한 rule set을 % 키워드로 지정하는 @extend 전용 Selector이다.

Placeholder Selector은 상속만을 위한 rule set 으로 자신은 트랜스파일링되지 않는다.

%input-style {
  font-size: 14px;
}

.input-black {
  @extend %input-style;

  color: black;
}

.input-red {
  @extend %input-style;

  color: red;
}

트랜스파일링 결과는 아래와 같다.

.input-black, .input-red {
  font-size: 14px;
}

.input-black {
  color: black;
}

.input-red {
  color: red;
}

#3. 조건과 반복

Sass는 프로그래밍 언어와 유사하게 제어문을 사용할 수 있는 기능을 제공한다.

#3.1 if()

built-in if() 함수는 주어진 조건을 판단하여 결과를 리턴한다. Javascript의 삼항연산자와 유사하게 동작한다.

if(condition, if_true, if_false)

condition이 true이면 if_true를, false이면 if_false를 반환한다.

$type: ocean;

p {
  color: if($type == ocean, blue, black); // color: blue;
}

#3.2 @if…@else

@if…@else를 사용하면 조건 분기가 가능하다.

$type: monster;

p {
  @if $type == ocean {
    color: blue;
  } @else if $type == matador {
    color: red;
  } @else if $type == monster {
    color: green;
  } @else {
    color: black;
  }
}

트랜스파일링 결과는 아래와 같다.

p {
  color: green;
}

#3.3 @for

@for으로 반복문을 사용할 수 있다.

@for $i from 1 through 3 {
  .item-#{$i} { width: 2em * $i; }
}

트랜스파일링 결과는 아래와 같다.

.item-1 {
  width: 2em;
}
.item-2 {
  width: 4em;
}
.item-3 {
  width: 6em;
}

#3.4 @each

@each와 list 또는 map의 요소에 대해 반복을 실시한다.

// List
@each $animal in puma, sea-slug, egret, salamander {

  .#{$animal}-icon {
    background-image: url('/images/#{$animal}.png');
  }
}

// Map
// $header: h1, $size: 2em
// $header: h2, $size: 1.5em
// $header: h3, $size: 1.2em
@each $header, $size in (h1: 2em, h2: 1.5em, h3: 1.2em) {
  #{$header} {
    font-size: $size;
  }
}

트랜스파일링 결과는 아래와 같다.

.puma-icon {
  background-image: url("/images/puma.png");
}

.sea-slug-icon {
  background-image: url("/images/sea-slug.png");
}

.egret-icon {
  background-image: url("/images/egret.png");
}

.salamander-icon {
  background-image: url("/images/salamander.png");
}

h1 {
  font-size: 2em;
}

h2 {
  font-size: 1.5em;
}

h3 {
  font-size: 1.2em;
}

#3.5 @while

@while으로 반복문을 사용할 수 있다.

$i: 6;
@while $i > 0 {
  .item-#{$i} { width: 2em * $i; }
  $i: $i - 2;
}

트랜스파일링 결과는 아래와 같다.

.item-6 {
  width: 12em;
}

.item-4 {
  width: 8em;
}

.item-2 {
  width: 4em;
}

#4. Mixin

Mixin은 Sass의 매우 유용한 기능으로 중복 기술을 방지하기 위해 사용 빈도가 높은 마크업을 사전에 정의하여 필요할 때에 불러 사용하는 방법이다.

@extend와 유사하나 프로그래밍 언어의 함수와 같이 인수를 전달받을 수 있다는 차이가 있다.

사용법은 매우 간단하다. @mixin 선언하고 @include로 불러들인다.

// 지름이 50px인 원
@mixin circle {
  width: 50px;
  height: 50px;
  border-radius: 50%;
}

// 지름이 50px인 원을 위한 mixin을 include한 후 배경을 추가 지정
.box {
  @include circle;

  background: #f00;
}

트랜스파일링 결과는 아래와 같다. 배경이 red이고 지름이 50px인 원을 나타낸다.

.box {
  width: 50px;
  height: 50px;
  border-radius: 50%;
  background: #f00;
}

@extend와 차이가 없어 보이나 Mixin은 함수와 같이 매개 변수를 사용할 수 있다.

@mixin circle($size) {
  width: $size;
  height: $size * 2;
  border-radius: 50%;
}

.box {
  @include circle(100px);

  background: #f00;
}

트랜스파일링 결과는 아래와 같다.

.box {
  width: 100px;
  height: 200px;
  border-radius: 50%;
  background: #f00;
}

매개 변수의 초기값을 설정할 수도 있다.

@mixin circle($size: 10px) {
  width: $size;
  height: $size * 2;
  border-radius: 50%;
}

.box {
  // 인수가 전달되지 않으면 초기값을 사용한다.
  @include circle();
  background: #f00;
}

트랜스파일링 결과는 아래와 같다.

.box {
  width: 10px;
  height: 20px;
  border-radius: 50%;
  background: #f00;
}

vendor prefix의 사용 예제

@mixin vendorPrefix($property, $value) {
  @each $prefix in -webkit-, -moz-, -ms-, -o-, '' {
    #{$prefix}#{$property}: $value;
  }
}

.border_radius {
  @include vendorPrefix(transition, 0.5s);
}
.border_radius {
  -webkit-transition: 0.5s;
  -moz-transition: 0.5s;
  -ms-transition: 0.5s;
  -o-transition: 0.5s;
  transition: 0.5s;
}

opacity의 사용 예제

@mixin opacity($opacity) {
  opacity: $opacity; /* All modern browsers */
  $opacityIE: $opacity * 100;
  filter: alpha(opacity=$opacityIE); /* For IE5~IE9 */
}

.box {
  @include opacity(0.5);
}
.box {
  opacity: 0.5;
  /* All modern browsers */
  filter: alpha(opacity=50);
  /* For IE5~IE9 */
}

position의 사용 예제

@mixin position($position, $top: null, $right: null, $bottom: null, $left: null) {
  position: $position;
  top: $top;
  right: $right;
  bottom: $bottom;
  left: $left;
}

.box {
  @include position(absolute, $top: 10px, $left: 50%);
}
.box {
  position: absolute;
  top: 10px;
  left: 50%;
}

이와 같이 Mixin을 작성하여 사용할 수도 있으나 Sass Framework/Library를 사용하는 것은 매우 바람직한 방법이다.

#5. Function

Function은 mixin과 유사하나 반환값에 차이가 있다.

  • mixin : style markup을 반환
  • function : @return directive를 통하여 값을 반환
$grid-width: 40px;
$gutter-width: 10px;

@function grid-width($n) {
  @return $n * $grid-width + ($n - 1) * $gutter-width;
}

#sidebar { width: grid-width(5); }  // width: 240px;

#6. Comment

CSS는 멀티 라인 주석 /* */ 만을 지원하지만 Sass는 /* */// 모두 사용할 수 있다.

한 줄 주석 // 은 트랜스파일링 후 CSS에서 사라지고, 멀티 라인 주석은 CSS에 나타난다.


ㅤㅤ

📍 Built-in Function : Sass가 제공하는 기본 내장 함수

#1. Number Functions

#1.1. percentage()

숫자값을 %로 변환한다.

percentage(0.2)          => 20%
percentage(100px / 50px) => 200%

#1.2. round()

소숫점 이하를 반올림한다.

round(10.4px) => 10px
round(10.6px) => 11px

#1.3. ceil()

소숫점 이하를 올림한다.

ceil(10.4px) => 11px
ceil(10.6px) => 11px

#1.4. floor()

소숫점 이하를 절사한다.

floor(10.4px) => 10px
floor(10.6px) => 10px

#1.5. abs()

절대값을 취득한다.

abs(10px) => 10px
abs(-10px) => 10px

#2. Introspection Functions

#2.1. type-of()

데이터타입을 취득한다.

type-of(100px)  => number
type-of(asdf)   => string
type-of("asdf") => string
type-of(true)   => bool
type-of(#fff)   => color
type-of(blue)   => color

#2.2. unit()

데이터의 단위(unit)를 취득한다.

unit(100)   => ""
unit(100px) => "px"
unit(3em)   => "em"
unit(10px * 5em) => "em*px"
unit(10px * 5em / 30cm / 1rem) => "em*px/cm*rem"

#2.3. unitless()

값에 단위가 있는지 확인한다.

unitless(100)   => true
unitless(100px) => false

#2.4.comparable()

2개의 값을 합산, 감산, 비교 가능한지 확인한다.

comparable(2px, 1px)   => true
comparable(100px, 3em) => false
comparable(10cm, 3mm)  => true

#3. String Functions

#3.1. quote()

따옴표를 붙인다.

quote("foo") => "foo"
quote(foo)   => "foo"

#3.2. unquote()

따옴표를 제거한다.

unquote("foo") => foo
unquote(foo)   => foo

#4. List Functions

#4.1. length()

리스트의 요소 수를 취득한다.

length(10px)                        => 1
length(10px 20px 30px)              => 3
length((width: 10px, height: 20px)) => 2

#4.2. nth()

리스트의 n번째 요소를 취득한다.

nth(10px 20px 30px, 1)                 => 10px
nth((Helvetica, Arial, sans-serif), 3) => sans-serif
nth((width: 10px, length: 20px), 2)    => length 20px

$n: nth(width: 10px, length: 20px);
nth(($n, 2), 1)                        => length

#4.3. index()

요소의 index를 취득한다.

index(1px solid red, solid)                       => 2
index(1px solid red, dashed)                      => null
index((width: 10px, height: 20px), (height 20px)) => 2

#4.4. append()

리스트의 마지막에 단일 요소를 추가한다.

append(10px 20px, 30px)      => 10px 20px 30px
append((blue, red), green)   => blue, red, green
append(10px 20px, 30px 40px) => 10px 20px (30px 40px)
append(10px, 20px, comma)    => 10px, 20px
append((blue, red), green, space) => blue red green

#4.5. join()

리스트와 리스트를 결합한다.

join(10px 20px, 30px 40px)      => 10px 20px 30px 40px
join((blue, red), (#abc, #def)) => blue, red, #abc, #def
join(10px, 20px)                => 10px 20px
join(10px, 20px, comma)         => 10px, 20px
join((blue, red), (#abc, #def), space) => blue red #abc #def

#4.6. zip()

복수의 리스트를 각자의 순서에 맞추어 재결합한다.

zip(1px 1px 3px, solid dashed solid, red green blue)
=> 1px solid red, 1px dashed green, 3px solid blue

#5. Map Functions

// key로 value 취득
map-get(("foo": 1, "bar": 2), "foo") => 1
map-get(("foo": 1, "bar": 2), "bar") => 2
map-get(("foo": 1, "bar": 2), "baz") => null

#6. Color Functions

#6.1. 색상(hue) 변경

$base-color: #ad141e;

.adjust-hue {
  color: adjust-hue($base-color, 20%);
  // => #ad3d14
}

#6.2. 채도(saturation) 변경

$base-color: #ad141e;

p {
  .saturate {
    color: saturate($base-color, 20%);
  }

  .desaturate {
    color: desaturate($base-color, 20%);
  }
}

#6.3. 휘도(lightness) 변경

$base-color: #ad141e;

p {
  .darken {
    color: darken($base-color, 10%);
  }

  .lighten {
    color: lighten($base-color, 10%);
  }
}

#6.4. 투명도(opacity) 변경

$base-color: #ad141e;

.rgba {
  color: rgba($base-color, .7);
}

/*
.rgba {
  color: rgba(173, 20, 30, 0.7); }
*

#6.5. alpha 연산

$base-color: rgba(255, 0, 0, 0.5);

// alpha +
// 불투명도를 증가시킨다.(더 불투명해진다)
.opacify {
  color: opacify($base-color, 0.3);
}

// alpha -
// 불투명도를 감소시킨다.(더 투명해진다)
.transparentize {
  color: transparentize($base-color, 0.25);
}

/*
.opacify {
  color: rgba(255, 0, 0, 0.8); }

.transparentize {
  color: rgba(255, 0, 0, 0.25); }
*/

#6.6. Tint & Shade

색상은 흰색(tint)과 검정색(shade)의 값으로 혼합되며 darken, lighten과 유사하다.

$base-color: #ad141e;

.tint {
  color: tint($base-color, 10%);
}

.shade {
  color: shade($base-color, 10%);
}


ㅤㅤ

📍 Webpack + Sass : Webpack 개발 환경에서 Sass 사용하기

Webpack 개발 환경에서 Sass를 사용 할 수 있다.

터미널에서 npm을 사용해 필요한 패키지를 설치한다.

$ npm install --save-dev webpack webpack-cli css-loader sass sass-loader mini-css-extract-plugin

package.json은 다음과 같다. npm scripts에 build를 추가한다.

{
  "name": "sass-project",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "build": "webpack -w"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "css-loader": "^5.0.2",
    "mini-css-extract-plugin": "^1.3.6",
    "sass": "^1.32.7",
    "sass-loader": "^11.0.1",
    "webpack": "^5.21.2",
    "webpack-cli": "^4.5.0"
  }
}

프로젝트 루트에 webpack.config.js 파일을 생성하고 다음과 같이 수정한다.

const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
  entry: './src/sass/foo.scss',
  output: {
    path: path.resolve(__dirname, 'public')
  },
  plugins: [
    // 컴파일 + 번들링 CSS 파일이 저장될 경로와 이름 지정
    new MiniCssExtractPlugin({ filename: 'css/style.css' })
  ],
  module: {
    rules: [
      {
        test: /\.s[ac]ss$/i,
        use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'],
        exclude: /node_modules/
      }
    ]
  },
  devtool: 'source-map',
  mode: 'development'
};

빌드를 실행하면 webpack.config.js 파일에 지정한 경로(‘public/css/style.css’)에 컴파일되어 번들링된 css 파일이 저장된다.

$ npm run build

본 포스트는 [웹 프로그래밍 튜토리얼 PoiemaWeb의 Sass] 를 정독하며 정리한 글입니다.

출처 : 웹 프로그래밍 튜토리얼 | PoiemaWeb

profile
https://kyledev.tistory.com/

0개의 댓글