1. What is SCSS? πŸ‘©β€πŸ’»

1. SCSS is Superset

SCSS(Sassy CSS) λŠ” CSS Pre Processor(μ „μ²˜λ¦¬κΈ°) 쀑 ν•˜λ‚˜λ‘œ CSS 의 κΈ°λŠ₯을 ν™•μž₯ν•˜λŠ” Script Languageλ‹€. κΈ°μ‘΄ CSS κ°€ 가진 ν•œκ³„λ₯Ό κ·Ήλ³΅ν•˜κΈ° μœ„ν•΄ λ‚˜μ™”μœΌλ©°, 슀크립트 μ–Έμ–΄, 즉, ν”„λ‘œκ·Έλž˜λ° 언어에 ν•΄λ‹Ήν•œλ‹€!

λ”°λΌμ„œ λ‹€μ–‘ν•œ μžλ£Œν˜•μ€ 기본이고 λ³€μˆ˜, ν•¨μˆ˜, 반볡문, 쑰건문 ν™•μž₯κ³Ό 같은 κΈ°λŠ₯ μ—­μ‹œ μ œκ³΅ν•œλ‹€.

ν•˜μ§€λ§Œ λΈŒλΌμš°μ €λŠ” CSS 만 μΈμ‹ν•œλ‹€. λ”°λΌμ„œ 웹에 배포되기 μ „ SCSS λ₯Ό CSS 둜 λ³€κ²½ν•΄μ•Όν•œλ‹€. 이것은 JavaScript 의 문제λ₯Ό ν•΄κ²°ν•˜κΈ° μœ„ν•΄ TypeScript λ₯Ό λ§Œλ“€μ—ˆμ§€λ§Œ, λΈŒλΌμš°μ € μƒμ—μ„œ JavaScript 만 λ™μž‘ν•˜λ‹€λ³΄λ‹ˆ TypeScript λ₯Ό JavaScript 둜 트랜슀파일 ν•΄μ„œ λ°°ν¬ν•˜λŠ” κ²ƒμ²˜λŸΌ SCSS μ—­μ‹œ CSS 둜 트랜슀파일 ν•΄μ„œ λ°°ν¬ν•΄μ•Όν•œλ‹€.

2. SCSS? SASS?

SCSS 와 SASS λŠ” λͺ¨λ‘ CSS μ „μ²˜λ¦¬κΈ°λ‘œ 같은 역할을 ν•œλ‹€. μ‹€μ œλ‘œ SCSS 의 ν™ˆνŽ˜μ΄μ§€λŠ” SASS 와 κ°™λ‹€. SASS 에 기쑴의 SASS ν‘œκΈ°λ²•κ³Ό μƒˆλ‘œμš΄ SCSS ν‘œκΈ°λ²• 두 가지가 μžˆλŠ” 것이닀. 그리고 SASS 3.0 λΆ€ν„°λŠ” SCSS ν‘œκΈ°λ²•μ΄ κΈ°λ³Έ ν‘œκΈ°λ²•μ΄ λ˜μ—ˆλ‹€.

  • SASS : Python, Yaml 파일과 같이 indent 둜 κ΅¬λΆ„ν•˜λ©° ;둜 μ’…λ£Œν•˜μ§€ μ•ŠλŠ”λ‹€.
  • SCSS : CSS 문법과 더 μœ μ‚¬ν•˜λ©°, { }둜 κ΅¬λΆ„ν•˜κ³  ;둜 μ’…λ£Œλ₯Ό ν•œλ‹€.

2. Installation and Watch πŸ‘©β€πŸ’»

기쑴의 Ruby λ‚˜ Node λ²„μ „μ˜ SASS λŠ” 였λ₯˜λ‘œ 인해 Dart λ²„μ „μ˜ SCSS μ„€μΉ˜λ₯Ό ꢌμž₯ν•˜κ³ μžˆλ‹€.

npm i -g sass

sass --version
1.59.3 compiled with dart2js 2.19.4

SCSS νŒŒμΌμ„ λ³€ν™”κ°€ μžˆμ„ λ•Œλ§ˆλ‹€ CSS 파일둜 λ³€ν™˜λ˜λ„λ‘ Watch 섀정을 해주도둝 ν•˜μž.

# μ„œλ‘œ λ‹€λ₯Έ 디렉토리λ₯Ό μ‚¬μš©ν•˜λŠ” 경우
sass --watch scss/style.scss:css/style.css
# 같은 디렉토리λ₯Ό μ‚¬μš©ν•˜λŠ” 경우
sass --watch css/style.scss:css/style.css

3. SCSS Variable Declaration and Scope πŸ‘©β€πŸ’»

μ—¬κΈ°μ„œ μ†Œκ°œν•˜λŠ” λͺ¨λ“  λ‚΄μš©μ€ SCSS κ°€ 트랜슀파일 λ˜μ–΄ μ–΄λ–»κ²Œ CSS 둜 λ³€ν™˜λ˜λŠ”μ§€λ₯Ό 보여쀀닀.

1. Nesting

.enlarge {
  font-size: 14px;
  transition: {
    property: font-size;
    duration: 4s;
    delay: 2s;
  }

  &:hover { font-size: 36px; }
}
.enlarge {
  font-size: 14px;
  transition-property: font-size;
  transition-duration: 4s;
  transition-delay: 2s;
}
.enlarge:hover {
  font-size: 36px;
}


.info-page {
  margin: auto {
    bottom: 10px;
    top: 2px;
  }
}
.info-page {
  margin: auto;
  margin-bottom: 10px;
  margin-top: 2px;
}

2. Hidden Declarations

λ‹€μŒκ³Ό 같이 쑰건을 μ£Όμ–΄ CSS 둜 트랜슀파일 할지, ν•˜μ§€ μ•Šμ„μ§€ 쑰건을 쀄 수 μžˆλ‹€.

$rounded-corners: false;

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

3. Custom Properties

λ‹€μŒκ³Ό 같이 SCSS λ³€μˆ˜λ₯Ό CSS 의 :root에 μ „μ—­ν™” ν•  수 μžˆλ‹€.

$primary: #81899b;
$accent: #302e24;
$warn: #dfa612;

:root {
  --primary: #{$primary};
  --accent: #{$accent};
  --warn: #{$warn};

  // Even though this looks like a Sass variable, it's valid CSS so it's not
  // evaluated.
  --consumed-by-js: $primary;
}
:root {
  --primary: #81899b;
  --accent: #302e24;
  --warn: #dfa612;
  --consumed-by-js: $primary;
}

4. Variable Scope

$global-variable: global value;

.content {
  $local-variable: local value;
  global: $global-variable;
  local: $local-variable;
}

.sidebar {
  global: $global-variable;

  // This would fail, because $local-variable isn't in scope:
  // local: $local-variable;
}
.content {
  global: global value;
  local: local value;
}

.sidebar {
  global: global value;
}


$variable: global value;

.content {
  $variable: local value;
  value: $variable;
}

.sidebar {
  value: $variable;
}
.content {
  value: local value;
}

.sidebar {
  value: global value;
}


@use "sass:map";

$theme-colors: (
  "success": #28a745,
  "info": #17a2b8,
  "warning": #ffc107,
);

.alert {
  // Instead of $theme-color-#{warning}
  background-color: map.get($theme-colors, "warning");
}
.alert {
  background-color: #ffc107;
}

5. Flow Control Scope

$dark-theme: true !default;
$primary-color: #f8bbd0 !default;
$accent-color: #6a1b9a !default;

@if $dark-theme {
  $primary-color: darken($primary-color, 60%);
  $accent-color: lighten($accent-color, 60%);
}

.button {
  background-color: $primary-color;
  border: 1px solid $accent-color;
  border-radius: 3px;
}
.button {
  background-color: #750c30;
  border: 1px solid #f5ebfc;
  border-radius: 3px;
}

4. SCSS Basic @Rules πŸ‘©β€πŸ’»

1. @use

@import κ°€ λ™μΌν•œ 역할을 ν•œλ‹€. 단 Deprecated μƒνƒœλ‹€ λ§ˆμ°¬κ°€μ§€μ΄λ―€λ‘œ @useλ₯Ό μ„ ν˜Έν•˜λΌκ³  λ§ν•˜κ³  μžˆλ‹€.

@useλŠ” JavaScript 의 import와 μœ μ‚¬ν•˜λ‹€. 단, 파일 λ‹¨μœ„λ‘œλ§Œ κ°€μ Έμ˜¬ 수 μžˆλ‹€.

  • foundation/_code.scss
code {
  padding: .25em;
  line-height: 0;
}
  • foundation/_lists.scss
ul, ol {
  text-align: left;

  & & {
    padding: {
      bottom: 0;
      left: 0;
    }
  }
}

이제 μœ„ 두 νŒŒμΌμ„ style.scss 에 ν•©μ³λ³΄μž.

  • style.scss
@use 'foundation/code';
@use 'foundation/lists';

그리고 λ‹€μŒκ³Ό 같이 트랜슀파일 될 것이닀.

code {
  padding: .25em;
  line-height: 0;
}

ul, ol {
  text-align: left;
}
ul ul, ol ol {
  padding-bottom: 0;
  padding-left: 0;
}

2. @mixin and @include vs. @extend

μŠ€νƒ€μΌμ„ μž¬μ‚¬μš© ν•  수 μžˆλŠ” 방법은 크게 두 κ°€μ§€λ‘œ λ‚˜λˆŒ 수 μžˆλ‹€.

1 ) @mixin and @include

λ‹€μŒκ³Ό 같이 μž¬μ‚¬μš© ν•  μŠ€νƒ€μΌμ„ μ •μ˜ν•΄λ³΄μž.

@mixin text-style {
  font-family: Arial, sans-serif;
  font-size: 16px;
  line-height: 1.5;
  color: #333;
}

이제 이 @mixin을 λ‹€μŒκ³Ό 같이 @includeλ₯Ό μ΄μš©ν•΄ μž¬μ‚¬μš© ν•  수 μžˆλ‹€.

.heading {
  @include text-style;
  font-weight: bold;
}

.paragraph {
  @include text-style;
}

트랜슀파일 κ²°κ³ΌλŠ” λ‹€μŒκ³Ό κ°™λ‹€.

.heading {
  font-family: Arial, sans-serif;
  font-size: 16px;
  line-height: 1.5;
  color: #333;
  font-weight: bold;
}

.paragraph {
  font-family: Arial, sans-serif;
  font-size: 16px;
  line-height: 1.5;
  color: #333;
}


2 ) @extend

.btn {
  display: inline-block;
  padding: 10px 20px;
  font-size: 16px;
  text-align: center;
  color: #fff;
  background-color: #333;
}

.btn-primary {
  @extend .btn;
  background-color: #007bff;
}

트랜슀파일 κ²°κ³ΌλŠ” λ‹€μŒκ³Ό κ°™λ‹€.

.btn, .btn-primary {
  display: inline-block;
  padding: 10px 20px;
  font-size: 16px;
  text-align: center;
  color: #fff;
  background-color: #333;
}

.btn-primary {
  background-color: #007bff;
}


μ–΄λ–€ 것을 써야 ν• κΉŒ?

1 ) @mixin and @include

  • λͺ©μ : μŠ€νƒ€μΌ Text μ •μ˜ 자체λ₯Ό μž¬μ‚¬μš©ν•˜κΈ° μœ„ν•¨.
  • μž₯점: @mixinκ³Ό @include의 μ‚¬μš© λͺ©μ μ€ μ½”λ“œ 자체의 μž¬μ‚¬μš©μ„±μ΄λ‹€. ν•œ 번만 μ •μ˜ν•˜κ³ , ν•„μš”ν•œ κ³³μ—μ„œ @includeλ₯Ό μ‚¬μš©ν•΄ μ£Όμž…λ§Œ ν•˜λ©΄ λœλ‹€. @mixinλ₯Ό 톡해 킀값이 λͺ…μ‹œμ μœΌλ‘œ 제곡되기 λ•Œλ¬Έμ— μ‚¬μš©μ΄ 쉽고 가독성이 μ’‹λ‹€.
  • 단점: μŠ€νƒ€μΌ Text μžμ²΄κ°€ λ³΅μ‚¬λ˜μ–΄ 맀번 μ£Όμž…λ˜κΈ° λ•Œλ¬Έμ— SCSS λ₯Ό μž‘μ„±ν•  λ•ŒλŠ” μ½”λ“œμ˜ 쀑볡이 μ€„μ–΄λ“€μ§€λ§Œ μ΅œμ’… 결과물인 CSS 트랜슀파일의 쀑볡이 λ°œμƒν•œλ‹€. 즉, DRY(Don't repeat yourself) 원칙에 μœ„λ°°λœλ‹€. λ”°λΌμ„œ λ„ˆλ¬΄ λ§Žμ€ @mixin의 μ‚¬μš©μ€ 쒋지 μ•Šλ‹€.


2 ) @extend

  • λͺ©μ : μŠ€νƒ€μΌ Text μ •μ˜κ°€ μ•„λ‹Œ CSS μ„ νƒμžλ₯Ό μž¬μ‚¬μš©ν•˜κΈ° μœ„ν•¨.
  • μž₯점: 결과적으둜 상속을 μ²˜λ¦¬ν•œλ‹€. 트랜슀파일의 결과물만 보면 DRY 원칙에 μœ„λ°°λ˜μ§€ μ•Šμ•„ 맀우 쒋은 결과물을 보여쀀닀.
  • 단점: μŠ€νƒ€μΌ Text μ •μ˜ 자체λ₯Ό μž¬μ‚¬μš©ν•˜μ§€λŠ” μ•ŠκΈ° λ•Œλ¬Έμ— CSS μ •μ˜ 자체λ₯Ό 잘 ν•˜λŠ” 것이 μ€‘μš”ν•˜λ‹€. 즉, SCSS λ₯Ό κΈ°μ€€μœΌλ‘œ CSS λ₯Ό λ§Œλ“œλŠ” λŠλ‚Œ 보닀 CSS λ₯Ό κΈ°μ€€μœΌλ‘œ μž¬μ‚¬μš© ν•  뢀뢄을 곡톡 λΆ€λΆ„μœΌλ‘œ μΆ”μΆœν•΄ 상속을 톡해 μ²˜λ¦¬ν•˜λŠ” λŠλ‚Œμ— 가깝닀. λ”°λΌμ„œ SCSS 파일의 가독성이 @mixin and @include λŒ€λΉ„ λΆ€μ‘±ν•˜λ‹€.

5. @if and @else πŸ‘©β€πŸ’»

$light-background: #f2ece4;
$light-text: #036;
$dark-background: #6b717f;
$dark-text: #d2e1dd;

@mixin theme-colors($light-theme: true) {
  @if $light-theme {
    background-color: $light-background;
    color: $light-text;
  } @else {
    background-color: $dark-background;
    color: $dark-text;
  }
}

.banner {
  @include theme-colors($light-theme: true);
  body.dark & {
    @include theme-colors($light-theme: false);
  }
}
.banner {
  background-color: #f2ece4;
  color: #036;
}
body.dark .banner {
  background-color: #6b717f;
  color: #d2e1dd;
}

6. Loops πŸ‘©β€πŸ’»

SCSS λ₯Ό 톡해 for, forEach, while 을 λͺ¨λ‘ μ‚¬μš©ν•  수 μžˆλ‹€. μš°μ„  κ°€μž₯ κ°„λ‹¨ν•œ κΈ°λ³Έ ν˜•νƒœλ₯Ό ν™•μΈν•œ ν›„ μ‹€μ œλ‘œ μ–΄λ–€μ‹μœΌλ‘œ μ‚¬μš©λ  수 μžˆλŠ”μ§€ μžμ„Ένžˆ 확인해보도둝 ν•˜μž.

$colors: red, green, blue;

// for loop
@for $i from 1 through length($colors) {
  .color-#{$i} {
    color: nth($colors, $i);
  }
}

// for loop (1λΆ€ν„° 5 λ―Έλ§ŒκΉŒμ§€)
@for $i from 1 to 4 {
  .color-#{$i} {
    color: nth($colors, $i);
  }
}

// each loop
@each $color in $colors {
  .color-#{index($colors, $color)} {
    color: $color;
  }
}

// while loop
$i: 1;
@while $i <= length($colors) {
  .color-#{$i} {
    color: nth($colors, $i);
  }
  $i: $i + 1;
}

1. @each with List or Map

1 ) @each with List

$sizes: 40px, 50px, 80px;

@each $size in $sizes {
  .icon-#{$size} {
    font-size: $size;
    height: $size;
    width: $size;
  }
}
.icon-40px {
  font-size: 40px;
  height: 40px;
  width: 40px;
}

.icon-50px {
  font-size: 50px;
  height: 50px;
  width: 50px;
}

.icon-80px {
  font-size: 80px;
  height: 80px;
  width: 80px;
}


2 ) @each with Map

$icons: ("eye": "\f112", "start": "\f12e", "stop": "\f12f");

@each $name, $glyph in $icons {
  .icon-#{$name}:before {
    display: inline-block;
    font-family: "Icon Font";
    content: $glyph;
  }
}
.icon-eye:before {
  display: inline-block;
  font-family: "Icon Font";
  content: "\f112";
}

.icon-start:before {
  display: inline-block;
  font-family: "Icon Font";
  content: "\f12e";
}

.icon-stop:before {
  display: inline-block;
  font-family: "Icon Font";
  content: "\f12f";
}

2. @for

$base-color: #036;

@for $i from 1 through 3 {
  ul:nth-child(3n + #{$i}) {
    background-color: lighten($base-color, $i * 5%);
  }
}
ul:nth-child(3n + 1) {
  background-color: #004080;
}

ul:nth-child(3n + 2) {
  background-color: #004d99;
}

ul:nth-child(3n + 3) {
  background-color: #0059b3;
}

7. Use & Operator πŸ‘©β€πŸ’»

& μ—°μ‚°μžλ₯Ό μ‚¬μš©ν•˜λ©΄ @if와 같은 쑰건문 없이 νŠΉμ • CSS 쑰건을 ν† κΈ€ν•˜λ„λ‘ ν•  수 μžˆλ‹€.

main {
  figure {
    em {
      opacity: 0;

      &.on {
        opacity: 0.8;
      }
    }
  }
}
main figure em {
  opacity: 0;
}
main figure em.on {
  opacity: 0.8;
}


main {
  &.dark_text {
    header h1,
    header #gnb a {
      color: #555;
    }
  }

  header {
    h1 {
      color: #fff;
    }

    #gnb {
      font-weight: bold;
      a {
        color: #fff;
      }
    }
  }
}
main.dark_text header h1,
main.dark_text header #gnb a {
  color: #555;
}
main header h1 {
  color: #fff;
}
main header #gnb {
  font-weight: bold;
}
main header #gnb a {
  color: #fff;
}


$times: morning, afternoon, evening, night;

.container {
  @each $time in $times {
    &.#{$time} {
      background-image: url('../img/bg_#{$time}.jpg');

      figure {
        background-image: url('../img/phone_#{$time}.png');
      }
    }
  }
}
.container.morning {
  background-image: url("../img/bg_morning.jpg");
}
.container.morning figure {
  background-image: url("../img/phone_morning.png");
}
.container.afternoon {
  background-image: url("../img/bg_afternoon.jpg");
}
.container.afternoon figure {
  background-image: url("../img/phone_afternoon.png");
}
.container.evening {
  background-image: url("../img/bg_evening.jpg");
}
.container.evening figure {
  background-image: url("../img/phone_evening.png");
}
.container.night {
  background-image: url("../img/bg_night.jpg");
}
.container.night figure {
  background-image: url("../img/phone_night.png");
}

8.keyframes πŸ‘©β€πŸ’»

λ‹¨μˆœ CSS μ§€λ§Œ SCSS κ°€ μ΄λŸ°μ‹μ˜ 쀑첩 ꡬ쑰λ₯Ό κΉ”λ”ν•˜κ²Œ μ²˜λ¦¬ν•˜λŠ” λ°μ„œ 강점을 κ°–λŠ”λ‹€.

@keyframes cloud {
  0% {
    opacity: 0;
    left: -50%;
  }
  5% {
    opacity: 0.5;
  }
  95% {
    opacity: 0.5; /* μ–˜κ°€ μžˆμ–΄μ•Ό 5 ~ 95% κ΅¬κ°„μ˜ opacity κ°€ 0.5 둜 μœ μ§€κ°€ λœλ‹€ */
  }
  100% {
    left: 80%;
    opacity: 0;
  }
}

Reference

  1. β€œSASS.” Sass-lang. accessed Apr. 15, 2023, SASS.