Syntactically Awesome Style Sheets
lastmod 2025-11-03created 2018-03-2541 edits
Sass (syntactically awesome stylesheets) is a style sheet language designed by Hampton Catlin and developed by Natalie Weizenbaum. After its initial versions, Weizenbaum and Chris Eppstein continued to extend Sass with SassScript, a simple scripting language used in Sass files.
How to install
How to compile
node-sass
Sass based on node. See the page for details.
dart-sass
Sass based on dart.
SCSS
Include file
To share variables between Sass files, you can use Sass's @use rule. For example, src/App.scss and other component style files could include with variable definitions.
This will allow you to do imports like
@use "./shared.scss"; // 상대경로 적용.
@use 'styles/_colors.scss'; // 'src/' 안의 경로 적용.
@use '~nprogress/nprogress'; // '~' 는 node_module 디렉토리 경로. 즉, 패키지 폴더 적용.
SCSS vs SASS
SCSS, introduced in version 3 of Sass (Syntactically Awesome Style Sheets), is a superset of CSS that supports all Sass features, built by introducing a new syntax that is fully compatible with CSS syntax. In other words, SCSS supports Sass features with nearly the same syntax as CSS. The easier and simpler difference is the presence or absence of {} (curly braces) and ; (semicolons).
Sass:
.list
width: 100px
float: left
li
color: red
background: url("./image.jpg")
&:last-child
margin-right: -10px
SCSS:
.list {
width: 100px;
float: left;
li {
color: red;
background: url("./image.jpg");
&:last-child {
margin-right: -10px;
}
}
}
Sass delimits the scope of selectors with 'indentation', while SCSS delimits scope with {}.
Below is a Mixins example.
Sass uses a shorthand syntax.
=border-radius($radius)
-webkit-border-radius: $radius
-moz-border-radius: $radius
-ms-border-radius: $radius
border-radius: $radius
.box
+border-radius(10px)
SCSS:
@mixin border-radius($radius) {
-webkit-border-radius: $radius;
-moz-border-radius: $radius;
-ms-border-radius: $radius;
border-radius: $radius;
}
.box { @include border-radius(10px); }
- Sass uses the Mixins feature with the
=and+symbols, - while SCSS uses the feature with
@mixinand@include.
Variables
Sass provides variables for reusability. Styles registered as variables can easily be used in other code.
$font-stack : Helvetical, sans-serif;
$primary-color : #333;
body{
font : 100% &font-stack;
color: $primary-color;
}
Selector Combinators
ul > {
li {
list-style-type: none;
}
}
h2 {
+ p {
border-top: 1px solid gray;
}
}
p {
~ {
span {
opacity: 0.8;
}
}
}
Functions
Nesting
When writing HTML, there is a nested, visual hierarchy. CSS, however, does not have one. With Sass, you can write code in a way that matches this visual hierarchy of HTML.
nav{
ul{
margin: 0;
padding: 0;
list-style: none;
}
li{
display: inline-block;
}
a{
display: block;
padding: 6px 12px;
text-decoration: none;
}
}
When this code is compiled to CSS, it looks like this:
nav ul {
margin: 0;
padding: 0;
list-style: none;
}
nav li {
display: inline-block;
}
nav a {
display: block;
padding: 6px 12px;
text-decoration: none;
}
Unlike SCSS, which follows the HTML hierarchy such as nav -> ul(li,a), CSS is written as separate pieces of code, which shows the advantage.
Partials
According to the official documentation, you can modularize the file structure of SCSS. However, assuming there is modularized code, compiling only the SCSS while excluding the SCSS split off into modules using the earlier command could be quite a hassle. Therefore
you can exclude a file from the build by putting an underscore in front of the file name, as in _partial.scss. Let's look at the modularizing code right in the next section.
Modules
In #Partial, we learned how to name files so they are excluded from the build list. Then how should SCSS code that is excluded from the build and modularized like this be written, and how should it be imported?
With @use, you can load SCSS files as you like. ('However, _ files must always exist as modules.')
-
_base.scss: A partial file excluded from the build.
$font-stack: Helvetica, sans-serif
$primary-color: #333
body {
font: 100% $font-stack;
color: $primary-color;
}
-
styles.sass
@use 'base'; // _파일명의 파일명만 use 하면, 이의 속성을 사용할 수 있다.
.inverse {
background-color: base.$primary-color;
color: white;
}
Mixins
With mixins, you can create CSS declaraations to reuse throughout the site. You can also pass values into mixins (parameters), which lets you build a more flexible and dynamic CSS structure. Let's look at an example.
@mixin transform($property) {
-webkit-transform: $property;
-ms-transform: $property;
transform: $property;
}
.box { @include transform(rotate(30deg)); }
The steps to create a mixin are as follows.
- Use the
@mixindirective first, - Define names (
$변수) like function parameters - Use the
mixindirective and give it a name
To use it, use @include.
( Media queries for responsive design can also be written this way. )
Extend / Inheritance
One of the useful features of Sass is that @extend lets you share a set of CSS properties from one selector to another.
%message-shared {
border: 1px solid #ccc;
padding: 10px;
color: #333;
}
%equal-heights {
display: flex;
flex-wrap: wrap;
}
.message {
@extend %message-shared;
}
.success {
@extend %message-shared;
border-color: green;
}
.error {
@extend %message-shared;
border-color: red;
}
.warning {
@extend %message-shared;
border-color: yellow;
}
Looking at this code, you can see that error, warning, message, and success all share a placeholder called message-shared. By adding extra features on top of it, each expresses a different feature.
Operators
Operators in CSS are very useful. You can process styles with the standard math operators +, -, *, / and operators such as %.
.container {
width: 100%;
}
article[role="main"] {
float: left;
width: 600px / 960px * 100%;
}
aside[role="complementary"] {
float: right;
width: 300px / 960px * 100%;
}
@import
Sass can also import files with the same syntax as CSS. The difference is that you can import multiple files separated by commas.
@content
@mixin media($types...) {
@each $type in $types {
@media #{$type} {
@content($type);
}
}
}
@include media(screen, print) using ($type) {
h1 {
font-size: 40px;
@if $type == print {
font-family: Calluna;
}
}
}
The CSS output is as follows:
@media screen {
h1 {
font-size: 40px;
}
}
@media print {
h1 {
font-size: 40px;
font-family: Calluna;
}
}
Parent selector reference (Ampersand; &)
Using & references the selector the current block applies to. Strictly speaking, it is substitution rather than reference. It is especially useful when applying a pseudo-selector to the selector whose properties are currently being set.
The declaration above compiles as follows.
String substitution and interpolation (interpolation; #{...})
With #{...}, you can interpolate the result of an expression into a string or substitute the contents of another variable. This applies not only to part or all of a property value but also to property names and selectors.
$foo: bar;
$fontsize: 12px;
$lineheight: 30p;
p {
font: #{$fontsize}/#{$lineheight};
&.#{$foo} { color: red; }
}
This example compiles as follows.
Useful for @apply ... in Tailwind:
map
Code sample using a map and @each to create @font-face rules for multiple weights
$font-weights: (
'Thin': 100,
'ExtraLight': 200,
'Light': 300,
'Regular': 400,
'Medium': 500,
'SemiBold': 600,
'Bold': 700,
'ExtraBold': 800,
'Black': 900,
);
@each $name, $weight in $font-weights {
@font-face {
font-family: 'Hahmlet';
font-style: normal;
font-weight: $weight;
font-display: swap;
src: local('Hahmlet #{$name}'),
url('/fonts/Hahmlet/Hahmlet-#{$name}.woff2') format('woff2'),
url('/fonts/Hahmlet/Hahmlet-#{$name}.woff') format('woff'),
url('/fonts/Hahmlet/Hahmlet-#{$name}.ttf') format('ttf');
}
}
TypeScript integration
- Can't import CSS/SCSS modules. TypeScript says "Cannot Find Module" - Stack Overflow
- When *.module.scss cannot be imported in tsx
Create a declaration.d.ts file and write the following:
Examples
no-select.scss
Sass (SCSS) mixin to disable user-select on an element
@mixin no-select {
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
Troubleshooting
gyp ERR
An error may occur when installing the package:
g++ -o Release/obj.target/binding/src/binding.o ../src/binding.cpp '-DNODE_GYP_MODULE_NAME=binding' '-DUSING_UV_SHARED=1' '-DUSING_V8_SHARED=1' '-DV8_DEPRECATION_WARNINGS=1' '-DV8_DEPRECATION_WARNINGS' '-DV8_IMMINENT_DEPRECATION_WARNINGS' '-D_GLIBCXX_USE_CXX11_ABI=1' '-D_LARGEFILE_SOURCE' '-D_FILE_OFFSET_BITS=64' '-D__STDC_FORMAT_MACROS' '-DOPENSSL_NO_PINSHARED' '-DOPENSSL_THREADS' '-DBUILDING_NODE_EXTENSION' -I/home/yourname/.cache/node-gyp/16.14.2/include/node -I/home/yourname/.cache/node-gyp/16.14.2/src -I/home/yourname/.cache/node-gyp/16.14.2/deps/openssl/config -I/home/yourname/.cache/node-gyp/16.14.2/deps/openssl/openssl/include -I/home/yourname/.cache/node-gyp/16.14.2/deps/uv/include -I/home/yourname/.cache/node-gyp/16.14.2/deps/zlib -I/home/yourname/.cache/node-gyp/16.14.2/deps/v8/include -I../../nan -I../src/libsass/include -fPIC -pthread -Wall -Wextra -Wno-unused-parameter -m64 -O3 -fno-omit-frame-pointer -fno-rtti -fno-exceptions -std=gnu++14 -std=c++0x -MMD -MF ./Release/.deps/Release/obj.target/binding/src/binding.o.d.raw -c
In file included from /home/yourname/.cache/node-gyp/16.14.2/include/node/v8.h:30,
from /home/yourname/.cache/node-gyp/16.14.2/include/node/node.h:63,
from ../../nan/nan.h:56,
from ../src/binding.cpp:1:
/home/yourname/.cache/node-gyp/16.14.2/include/node/v8-internal.h: In function ‘void v8::internal::PerformCastCheck(T*)’:
/home/yourname/.cache/node-gyp/16.14.2/include/node/v8-internal.h:492:38: error: ‘remove_cv_t’ is not a member of ‘std’; did you mean ‘remove_cv’?
492 | !std::is_same<Data, std::remove_cv_t<T>>::value>::Perform(data);
| ^~~~~~~~~~~
| remove_cv
/home/yourname/.cache/node-gyp/16.14.2/include/node/v8-internal.h:492:38: error: ‘remove_cv_t’ is not a member of ‘std’; did you mean ‘remove_cv’?
492 | !std::is_same<Data, std::remove_cv_t<T>>::value>::Perform(data);
| ^~~~~~~~~~~
| remove_cv
/home/yourname/.cache/node-gyp/16.14.2/include/node/v8-internal.h:492:50: error: template argument 2 is invalid
492 | !std::is_same<Data, std::remove_cv_t<T>>::value>::Perform(data);
| ^
/home/yourname/.cache/node-gyp/16.14.2/include/node/v8-internal.h:492:63: error: ‘::Perform’ has not been declared
492 | !std::is_same<Data, std::remove_cv_t<T>>::value>::Perform(data);
| ^~~~~~~
../src/binding.cpp: In function ‘Nan::NAN_METHOD_RETURN_TYPE render(Nan::NAN_METHOD_ARGS_TYPE)’:
../src/binding.cpp:284:98: warning: cast between incompatible function types from ‘void (*)(uv_work_t*)’ {aka ‘void (*)(uv_work_s*)’} to ‘uv_after_work_cb’ {aka ‘void (*)(uv_work_s*, int)’} [-Wcast-function-type]
284 | int status = uv_queue_work(uv_default_loop(), &ctx_w->request, compile_it, (uv_after_work_cb)MakeCallback);
| ^~~~~~~~~~~~
../src/binding.cpp: In function ‘Nan::NAN_METHOD_RETURN_TYPE render_file(Nan::NAN_METHOD_ARGS_TYPE)’:
../src/binding.cpp:320:98: warning: cast between incompatible function types from ‘void (*)(uv_work_t*)’ {aka ‘void (*)(uv_work_s*)’} to ‘uv_after_work_cb’ {aka ‘void (*)(uv_work_s*, int)’} [-Wcast-function-type]
320 | int status = uv_queue_work(uv_default_loop(), &ctx_w->request, compile_it, (uv_after_work_cb)MakeCallback);
| ^~~~~~~~~~~~
In file included from ../../nan/nan.h:56,
from ../src/binding.cpp:1:
../src/binding.cpp: At global scope:
/home/yourname/.cache/node-gyp/16.14.2/include/node/node.h:842:43: warning: cast between incompatible function types from ‘void (*)(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE)’ {aka ‘void (*)(v8::Local<v8::Object>)’} to ‘node::addon_register_func’ {aka ‘void (*)(v8::Local<v8::Object>, v8::Local<v8::Value>, void*)’} [-Wcast-function-type]
842 | (node::addon_register_func) (regfunc), \
| ^
/home/yourname/.cache/node-gyp/16.14.2/include/node/node.h:876:3: note: in expansion of macro ‘NODE_MODULE_X’
876 | NODE_MODULE_X(modname, regfunc, NULL, 0) // NOLINT (readability/null_usage)
| ^~~~~~~~~~~~~
../src/binding.cpp:358:1: note: in expansion of macro ‘NODE_MODULE’
358 | NODE_MODULE(binding, RegisterModule);
| ^~~~~~~~~~~
make: *** [binding.target.mk:133: Release/obj.target/binding/src/binding.o] Error 1
make: Leaving directory '/home/yourname/Project/answer-dev/fe/node_modules/node-sass/build'
gyp ERR! build error
gyp ERR! stack Error: `make` failed with exit code: 2
gyp ERR! stack at ChildProcess.onExit (/home/yourname/Project/answer-dev/fe/node_modules/node-gyp/lib/build.js:194:23)
gyp ERR! stack at ChildProcess.emit (node:events:526:28)
gyp ERR! stack at Process.ChildProcess._handle.onexit (node:internal/child_process:291:12)
gyp ERR! System Linux 5.13.0-39-generic
gyp ERR! command "/usr/local/bin/node" "/home/yourname/Project/answer-dev/fe/node_modules/node-gyp/bin/node-gyp.js" "rebuild" "--verbose" "--libsass_ext=" "--libsass_cflags=" "--libsass_ldflags=" "--libsass_library="
gyp ERR! cwd /home/yourname/Project/answer-dev/fe/node_modules/node-sass
gyp ERR! node -v v16.14.2
At least with Node version v16.14.2, the error occurred when the node-sass version was 5. Upgrading to ^7.0.1 fixes it.
SassError: Undefined function
- Stackoverflow - Sass math.div function is undefined in angular app, but is defined in angular lib
- Sass math.div function is undefined in angular app, but is defined in angular lib - ErrorsFixing
Error: Module build failed (from ./node_modules/sass-loader/dist/cjs.js):
SassError: Undefined function.
╷
4 │ min-height: math.div(100, 2);
│ ^^^^^^^^^^^^^^^^
╵
src\app\app.component.scss 4:17 root stylesheet
Check the following.
-
math.divfunction is available in sass since 1.33.0 -
@use 'sass:math'to@use 'sass:math' as math;
See also
Favorite site
Online Tools
- [Recommended] Sass: Playground
References
-
SCSS_and_Sass_Complete_Guide_-_HEROPY.DEV.pdf ↩