Introduction to Sass in Astro
This lesson is a preview from our interactive course
Sass is a powerful CSS preprocessor that adds various extra functionalities on top of CSS.
A preprocessor is a program that processes input data (in our case, a Sass file) and produces an output (a CSS file) that can be used as input elsewhere (in our HTML documents).
Why Sass?#
Sass comes with many benefits over regular CSS. First of all, Sass is completely compatible with all versions of CSS. This means any valid CSS file is also a valid Sass file. This results in a shallow learning curve, as most of its syntax is regular CSS. Apart from compatibility, it also comes with the following benefits:
- Feature-rich: Sass comes with a lot of features and abilities that are not available in other CSS preprocessors or tools. We'll take a look at the most important ones one by one. These features make it easy to simplify, shorten, and make our CSS more readable.
- Mature: Sass has been actively supported for more than 15 years. It's one, if not the most mature CSS preprocessor available that has stood the test of time over and over again.
- Large community: Over the years, it gathered a large community. This is an important aspect, as running into an issue with Sass, you'll likely find an existing solution for it on Google by the community. The framework itself is also developed by several tech companies and hundreds of developers.
- Framework support: Sass supports a large number of CSS frameworks, meaning you won't be locked in with a single technology, separated from other CSS flavors. Bootstrap, Bourbon, or Susy are supported, just to name a few.
Installing Sass#
To get started with Sass, we first need to install it. Astro supports the most widely used CSS preprocessors out of the box. Apart from Sass, Stylus, and Less are also supported. This means we only need to install the dependency, then we can start using Sass right away. We can install Sass by running the following in the terminal:
npm i sass
Sass has already been installed for your convenience in this project. Open the package.json
file to verify that Sass is available as a dependency. That's everything we have to do. Now we can start using Sass in Astro by specifying the lang
attribute on style
tags in our component:
<!-- Regular CSS: -->
<style>...</style>
<!-- Sass: -->
<style lang="scss">...</style>
Core Concepts of Sass#
The core concept of Sass is to make CSS fun to write, easy to read and maintain, and also shorter most of the time. Sass has crucial features that CSS doesn't support out of the box, such as nesting, inheritance, advanced variables and functions, loops, and many more. Let's take a look at the ones we're going to use in this project, and you'll likely use them in any project where Sass is used.
Nesting#
One of the most important benefits of using Sass is the ability to nest CSS rules. In HTML, we've gotten used to seeing a visual hierarchy, where child elements are nested inside parents. This is not possible in CSS. However, Sass supports nesting. Take a look at the following comparison:
ul {
columns: 2;
}
ul li {
display: flex;
}
ul li img {
margin-top: 2px;
}
ul {
columns: 2;
li {
display: flex;
img {
margin-top: 2px;
}
}
}
Using nesting, we can visually see which elements belong to which parent. Now we can understand at a glance that each style is only applicable within the ul
.
As Astro scopes CSS to the components, we don't need to worry about specificity. Because of this, we'll mostly avoid nesting CSS to keep our styles flat, apart from a few exceptions.
Imports#
Another great feature of Sass is that it allows the use of imports, also known as partials. This opens the possibility to modularize our CSS. For example, if we have global styles for certain elements, we can separate the styles for those elements into a distinct Sass file and import them in other places:
// Inside tooltip.scss
.tooltip { ... }
// Inside global.scss
@import './tooltip.scss';
Sass files use the .scss
extension.
To import other SCSS files, we can use the @import
at-rule. Each built-in functionality in Sass is prefixed with an @
and is called an at-rule. The statement must include a path to the CSS file, similar to how imports are used in JavaScript.
The .tooltip
styles are now generated into the global.scss
file. Note that we can also use //
for comments inside SCSS files. The regular /**/
CSS syntax is also available.
Variables#
Sass also supports the use of variables. Unlike CSS variables, Sass variables are prefixed with a $
sign, and they can contain any valid CSS unit. For example, we can use variables to collect brand colors in one place. This means if we need to change the colors of the site, we only need to do it in one place. Take the following as an example:
// Inside color-palette.scss
$primary: #2980b9;
// Inside resets.scss
@import 'color-palette.scss';
a {
color: $primary;
}
Here we defined a $primary
color variable inside a file called color-palette.scss
. We can use the @import
at-rule to import this file into any other SCSS file to use them. We can reference the variable using its name. In the above example, we assigned the $primary
color to all anchors.
Lists and maps#
Variables can also take the form of other Sass objects, such as lists and maps. A map is a collection of values represented by key-value pairs, just like an object in JavaScript. Values can include CSS units or even lists, which is another structured data type in Sass. Lists are comma-separated sequences of values:
// Lists in Sass:
$fontFamily: Inter, sans-serif;
// Map in Sass:
$fontSizes: (
'xs': 12px,
'sm': 14px,
'md': 16px
);
// Using lists in maps:
$fontTypes: (
'regular': (Inter, sans-serif),
'bold': (InterBold, sans-serif)
);
// Reference lists and maps:
a {
font-family: $fontFamily; // Referencing a list
font-weight: map-get($fontTypes, 'regular'); // Referencing a map
}
- We can reference lists just like regular Sass variables.
- To reference a value in a map, we can use the built-in
map-get
function, which expects the map as the first parameter and the key of the value as the second parameter. In this example, it'll returnInter, sans-serif
.
Mixins#
So far, we only looked at how to work with variables, but Sass also supports the use of more complex functions, called mixins. Mixins in Sass are functions for encapsulating styles. Mixins are defined using the @mixin <mixinName> { ... }
syntax. When a mixin is used, all CSS-related content within the mixin is generated into the stylesheets. Let's see an example:
@mixin media($size: 'xs') {
$mediasizes: (
'xs': 600px,
'sm': 800px,
'md': 1024px,
'lg': 1200px
);
@media (min-width: #{map-get($mediasizes, $size)}) {
@content;
}
}
This mixin is called media
. It can be used for using predefined media queries in CSS. As we can see, mixins can accept parameters just like JavaScript functions. In our case, it accepts a $size
parameter, which is set to xs
by default.
To create a mobile-first media query when the mixin is invoked, we can use the map-get
function we've seen before to retrieve a value ($size
) from the provided map ($mediasizes
). Note that we need to use the #{}
interpolation to include Sass functions in the CSS output. When the mixin is called, anything inside the mixin is placed in place of the @content
keyword, which is called a content block. To call a mixin, we can use the @include
at-rule:
@include media('xs') {
// This will be placed in place of the @content keyword in the mixin
ul {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
// The generated CSS output:
@media (min-width: 600px) {
ul {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
Here we called the media
mixin and also passed xs
as the parameter. Notice that since xs
is the default parameter for the mixin, it's also possible to call this mixin without a parameter to get the same result.
Note that the generated @media
is a regular CSS at-rule and not specific to Sass.
Adding Configuration Files#
Now that we know everything we need to know about Sass, let's put theory into practice and set up the Sass configurations. To keep Sass files easy to manage, we'll create a couple of configuration files, each with a different purpose. Create a config
folder inside the scss
folder in the code editor, and add the following four new files:
color-palette.scss
: This file is dedicated to colors. Separating colors into a distinct file not only makes it easier to maintain color consistency and make changes later on but also simplifies the process of introducing new themes to a project.mixins.scss
: We'll also store mixins in a separate file. Again, this helps in maintaining consistent styles across components.typography.scss
: Just like for colors, we want to have a consistent typography style with specified font types and sizes. This file will contain the necessary variables for typography.spacings.scss
: We also want consistent spacing for elements. This will contain a Sass function that can be used for consistent paddings, margins, and gaps.
src
ββ scss
β ββ config
β β ββ color-palette.scss
β β ββ mixins.scss
β β ββ typography.scss
β β ββ spacings.scss
Adding colors#
For this landing page, we'll only work with six individual colors. Open the color-palette.scss
file and add the following variables:
$primary: #2980b9;
$primary-light: #33AEFF;
$secondary: #d8a752;
$black: #111111;
$white: #FFF;
$off-white: #FAFAFA;
Except for shades of black and white, it's best to avoid using color names for variables to prevent variable names from becoming meaningless and needing updates after a color change. For instance, if we initially name $primary
as $blue
and later change the primary color to purple, $blue
would lose its meaning. On the other hand, $primary
remains meaningful.
Adding typography#
For typography, we want to define two font types: regular and bold, along with font weights and sizes for each. To achieve this, we can use maps. Open the typography.scss
file and add the following variables:
$fontTypes: (
'regular': (Regular, sans-serif),
'bold': (Bold, sans-serif)
);
$fontSizes: (
'xs': 12px,
'sm': 14px,
'md': 16px,
'default': 18px,
'lg': 21px,
'xl': 24px,
'2xl': 28px,
'3xl': 32px
);
Here we combined a map with lists, similar to a previous example. For the $fontTypes
map, each key references a list, which needs to be wrapped in parentheses for the correct syntax, otherwise it would be treated as regular CSS.
The $fontTypes
variable references a custom font that we'll import into the project in the next lesson.
Adding spacings#
For spacings.scss
, we'll only need to define a map and return one of its values when the function is called. Open the spacings.scss
file and add the following:
@use 'sass:map';
@function size($size: 'default') {
$sizes: (
'xs': 5px,
'sm': 10px,
'md': 15px,
'default': 20px,
'lg': 25px
);
@return map.get($sizes, $size);
}
Note that this time, we use the @function
at-rule, as opposed to @mixin
. This is because we don't want any CSS rule to be generated by this function. Instead, we only want to return a value. When you need a function to generate CSS output, use a mixin. Otherwise, use @function
. To return a value from the function, we can use the @return
keyword.
Similar to how we used map-get
in one of the previous examples, we can use the same functionality here to return a value from the map. However, we can also use map.get
as shown here, which will replace map-get
in future versions of Sass. They both have the same functionality.
When using map.get
, however, we need to include the map
object in the file using @use 'sass:map'
at the top of our Sass file. This is because map-get
is a global function, whereas map.get
is only available through an import.
For now, both map-get
and map.get
can be used interchangeably. For future versions, prefer map.get
as map-get
will be deprecated in the long term.
Adding mixins#
To easily use these typography rules, we'll create a couple of mixins. Inside the mixins.scss
file, add the following three mixins:
@mixin media($size: 'xs') {
$mediasizes: (
'xs': 600px,
'sm': 800px,
'md': 1024px,
'lg': 1200px
);
@media (min-width: #{map-get($mediasizes, $size)}) {
@content;
}
}
@mixin fontType($type: 'regular') {
font-family: #{map-get($fontTypes, $type)};
}
@mixin fontSize($size: 'regular') {
font-size: #{map-get($fontSizes, $size)};
}
media
: This mixin is used for easily creating predefined media queries. It works the same way as shown in the previous example.fontType
: We can follow a similar pattern for font types. This mixin relies on the variables defined intypography.scss
. We can also set default values for mixin parameters, making them optional when calling the mixin. This way, the mixin defaults to a regular font type.fontSize
: Similar to thefontType
mixin, but it requires passing the$fontSizes
map to themap-get
function.
CSS Resets#
Now that all configurations are in place, let's focus on adding reset styles. To use these configurations, we need to import the files. However, to avoid importing them individually, create a config.scss
file next to the empty resets.scss
and add the following:
@import './config/color-palette';
@import './config/mixins';
@import './config/typography';
@import './config/spacings';
We can import this file into resets.scss
to automatically import other configurations for us. Open resets.scss
and add an import for config.scss
, along with the following CSS rules:
@import './config';
body {
@include fontType('regular');
@include fontSize('default');
margin: 0;
background: $white;
color: $black;
}
div {
box-sizing: border-box;
}
a {
color: $primary;
}
h1,
h2 {
margin: 0;
}
ul {
margin: 0;
padding: 0;
list-style-type: none;
}
.container {
position: relative;
max-width: 1200px;
margin: 0 auto;
padding: 25px size('default') 0;
z-index: 1;
}
Here we imported config.scss
, which imports all other configuration files. This means we can use mixins, variables, and functions with a single import, while still maintaining them in different files. We also defined a common .container
class that we can use across different sections on the page.
This file can be used to reset CSS styles or create common classes that can be used across different components. This file is imported into index.scss
, which, in turn, is imported into our index.astro
page. Note that we need to add the is:global
directive on the style
tag to make the styles global. Otherwise, they would be scoped to the component. And now we have everything set up from the CSS side of things!
Rocket Launch Your Career
Speed up your learning progress with our mentorship program. Join as a mentee to unlock the full potential of Webtips and get a personalized learning experience by experts to master the following frontend technologies: