Astro JS Sass Styling: SCSS Astro Setup
Tags: webdev, astro, javascript
Published: 2022-04-20 10:14:40

---
title: "Astro JS Sass Styling: SCSS Astro Setup"
published: "true"
description: "Astro JS Sass styling: how you can add SCSS or Sass styling in Astro JS — covering global styles, scoped styles and styled components."
tags: "webdev, astro, javascript"
canonical_url: "https://rodneylab.com/astro-js-sass-styling/"
cover_image: "https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e0n6j8l8e2daq5262cxd.png"
---

## 💄 Astro JS Sass Styling
We see how to set up Astro JS Sass styling in this post.  Although modern CSS now supports many of the features which developers looked to Sass or SCSS to provide in the past, many developers still appreciate Sass.  Astro’s Framework agnosticism as regards to using React together with Svelte, Vue or something else is already well known.  You will expect this support of all popular frameworks to extend from rendering to styling.  Astro does not disappoint here.  In this post we see how you can use Sass to add not just global styles but also scoped styles to your Astro and Svelte components.  In fact we can extend that to React by using SCSS modules for styling.  To add polish we look at setting up Stylelint to enforce a particular code style or even just to make sure your SCSS stays just how you like it!

## 🧱 Astro JS Sass Styling: What we&rsquo;re Building

Rather than build a new app, we have taken the <a aria-label="Open the AStro J S Tutorial" href="https://rodneylab.com/astro-js-tutorial">demo app from our Astro JS Tutorial</a> and converted styling to SCSS.  In the tutorial, we used vanilla CSS and you will see there is not much to change to get Sass up and running.  In this post we just look at the main changes you need to make to your files (rather than provide a follow along tutorial).  Let me know if you like this style instead of building an app as you follow along.  If you really do want to build something as you read along though, you can <a aria-label="Open the Astro J S tutorial post" href="https://rodneylab.com/astro-js-tutorial">clone the Astro JS Tutorial</a> and make changes, converting it to SCSS.

## ⚙️ Astro JS Sass Styling: Config

We add the Sass package as well as Stylelint here, which we use later.

```shell
 pnpm add -D sass stylelint stylelint-config-recommended-scss stylelint-config-standard
```

## 🌍 Astro JS Sass Styling: Global Styles

We will create a global styles file at `src/styles/styles.scss`.  As well as changing the syntax to make it more idiomatic Sass, we also import a couple of files:

```scss
@charset "UTF-8";

@import './fonts.scss';
@import './variables.scss';

body {
  margin: $spacing-0;
  background: hsl($colour-dark-hue $colour-dark-saturation $colour-dark-luminance);
  font-family: Roboto;
  text-align: center;
}

h1 {
  font-size: 3.815rem;
  margin-bottom: $spacing-12;
}

/* TRUNCATED... */
```

We place the fonts in a separate file just to keep the global styles file cleaner.  However for the variables, beyond style, placing variables in a separate file makes it easier to import them when we want to use the variables in scoped styles within component files.  Having a separate variables file lets us import just the variables without needing to 	import the entire global styles files in these cases.  In line `1` we set the `charset`.

### Variables and Fonts

Here is the fonts file for completeness:

```scss
/* roboto-regular - latin */
@font-face {
  font-family: 'Roboto';
  font-style: normal;
  font-weight: 400;
  src: local(''), url('/fonts/roboto-v29-latin-regular.woff2') format('woff2'),
    url('/fonts/roboto-v29-latin-regular.woff') format('woff');
}

/* roboto-700 - latin */
@font-face {
  font-family: 'Roboto';
  font-style: normal;
  font-weight: 700;
  src: local(''), url('/fonts/roboto-v29-latin-700.woff2') format('woff2'),
    url('/fonts/roboto-v29-latin-700.woff') format('woff');
}
```
and also the variables file:

```scss
/* colours */
/* honey-yellow */
$colour-brand-hue: 39;
$colour-brand-saturation: 92%;
$colour-brand-luminance: 57%;

/* safety orange blaze orange */
$colour-secondary-hue: 21;
$colour-secondary-saturation: 89%;
$colour-secondary-luminance: 52%;

/* moss green */
$colour-alternative-hue: 84;
$colour-alternative-saturation: 29%;
$colour-alternative-luminance: 43%;

/* pine tree */
$colour-dark-text-hue: 100;
$colour-dark-text-saturation: 18%;
$colour-dark-text-luminance: 13%;

/* old lace */
$colour-light-text-hue: 50;
$colour-light-text-saturation: 66%;
$colour-light-text-luminance: 95%;

$colour-dark-hue: 206;
$colour-dark-saturation: 46%;
$colour-dark-luminance: 37%;

/* spacing */
$spacing-px: 1px;
$spacing-0: 0;
$spacing-4: 1rem;
$spacing-6: 1.5rem;
$spacing-8: 2rem;
$spacing-12: 3rem;
$spacing-32: 8rem;
```


## 💅🏽 Scoped Styles in Astro and Svelte Components

Next, let&rsquo;s look at the home page to see Astro JS Sass style imports together with scoped styling in an Astro file.

```astro
---
import '$styles/styles.scss';
import { Markdown } from 'astro/components';
import ReactVideo from '$components/Video.jsx';
import SvelteVideo from '$components/Video.svelte';
---

<html lang="en-GB">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="/favicon.png" />
    <meta name="viewport" content="width=device-width" />

    <title>RodneyLab Minimal Astro Example</title>
  </head>
  <body>
    <main class="container">
      <h1>Astro JS Tutorial Site</h1>
      <p>This demo is not endorsed by Ben Awad, just thought the video content was fitting!</p>
      <ReactVideo client:load />
      <SvelteVideo client:load />
      <section class="mdx-container">
        <Markdown>
          ## Astro in 100 Seconds

          <div class="video-container">
          <iframe 
          title="Astro in 100 Seconds"
          width="560"
          height="315"
          src="https://www.youtube-nocookie.com/embed/dsTXcSeAZq8"
          frameborder="0"
          allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
          allowfullscreen
          ></iframe>
          </div>
        </Markdown>
      </section>
    </main>
  </body>
</html>

<style lang="scss">
  @import '../styles/variables';

  .container {
    display: flex;
    flex-direction: column;
    background: hsl($colour-dark-hue $colour-dark-saturation $colour-dark-luminance);
    color: hsl($colour-light-text-hue $colour-light-text-saturation $colour-light-text-luminance);
    padding: $spacing-8 $spacing-0 $spacing-32;
  }

  .container a {
    color: hsl($colour-secondary-hue $colour-secondary-saturation $colour-secondary-luminance);
  }
  .mdx-container {
    display: flex;
    flex-direction: column;
    background: hsl(
      $colour-alternative-hue $colour-alternative-saturation $colour-alternative-luminance
    );
    align-items: center;
    width: 100%;
    padding: $spacing-8 $spacing-0 $spacing-12;
    color: hsl($colour-light-text-hue $colour-light-text-saturation $colour-light-text-luminance);
  }
</style>
```

In line `2` we import the global Sass stylesheet.  This is not really that different to how we would import a CSS stylesheet.  In fact, the only real difference is the change in filename.  Note that we have defined an alias `$styles` as `src/styles` in the `tsconfig.js` file.  This lets us use the shorthand which you see in the code.  You can see a more detailed explanation of this in the <a href="https://rodneylab.com/astro-js-tutorial/">Astro JS tutorial</a>.

We can add scoped styles using a `<style>` tag (just like with vanilla CSS). We should remember though, when using Sass to add a `lang="scss"` attribute to the style tag, as in line 43.  Because we want to use variables defined in the `src/styles/variables.scss` within this style element, we can just import that file.  We do that in line `44`.


### Astro JS Sass Styling: Svelte Component

Scoped styles in a Svelte component work just like the Astro file we just looked at.  Because this is a component, rather than a page or a layout, we do not import the global style sheet in the Svelte file.  Since the component will be embedded in an Astro page, we can rely on the page or its template to ensure global styles are imported.

For completeness, here is the Svelte file.  Notice that the `container` class does not collide with the `container` we used in the Astro component, because of the scoping.

```svelte
<script>
  $: altColours = false;
</script>

<section class={`container${altColours ? ' container-alt' : ''}`}>
  <h2>Svelte Component</h2>
  <div class="video-container">
    <iframe
      title="Trying Svelte for the Third Time"
      width="560"
      height="315"
      src="https://www.youtube-nocookie.com/embed/xgER1OutVvU"
      frameborder="0"
      allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
      allowfullscreen
    />
  </div>
  <button
    class={`button${altColours ? ' button-alt' : ''}`}
    on:click={() => {
      altColours = !altColours;
    }}><span class="screen-reader-text">Toggle colours</span></button
  >
</section>

<style lang="scss">
  @import '../styles/variables';

  .container {
    display: flex;
    flex-direction: column;
    background: hsl($colour-brand-hue $colour-brand-saturation $colour-brand-luminance);
    align-items: center;
    width: 100%;
    padding: $spacing-8 $spacing-0;
    color: hsl($colour-dark-text-hue $colour-dark-text-saturation $colour-dark-text-luminance);
  }

  .container-alt {
    background: hsl($colour-secondary-hue $colour-secondary-saturation $colour-secondary-luminance);

    color: hsl($colour-light-text-hue $colour-light-text-saturation $colour-light-text-luminance);
  }

  .button {
    background: hsl($colour-secondary-hue $colour-secondary-saturation $colour-secondary-luminance);
  }

  .button-alt {
    background: hsl($colour-brand-hue $colour-brand-saturation $colour-brand-luminance);
  }
</style>
```

## 🧩 React Styled Components

React in Astro does not have an out-of-the-box scoped styling solution as convenient as what we have seen for Svelte and Astro files.  Having said that, it is still fairly easy to scope styles using SCSS modules.  This involves defining styles in a side file (typically named like the component which uses it) then importing them into the component file.  Let&apos;s take a closer look.

We have our React Video component defined in `src/components/Video.jsx`.  Then we define its styles in Sass in `src/components/Video.module.scss`:

```scss
@import '../styles/variables.scss';

.container {
  display: flex;
  flex-direction: column;
  background: hsl($colour-secondary-hue $colour-secondary-saturation $colour-secondary-luminance);
  align-items: center;
  width: 100%;
  padding: $spacing-8 $spacing-0;
  color: hsl($colour-light-text-hue $colour-light-text-saturation $colour-light-text-luminance);
}

.container-alt {
  background: hsl(
    $colour-alternative-hue $colour-alternative-saturation $colour-alternative-luminance
  );
}

.button {
  background: hsl(
    $colour-alternative-hue $colour-alternative-saturation $colour-alternative-luminance
  );
}

.button-alt {
  background: hsl($colour-secondary-hue $colour-secondary-saturation $colour-secondary-luminance);
}
```

Notice, once more, we import the variables for use in the local Sass code.  Then, we import all of the styles as a `styles` object in the React file.  The actual scoped class names are defined in the JavaScript fields which we import.  Remembering they are encoded in the JavaScript, we use them in the React `className` attributes:

```jsx
import styles from '$components/Video.module.scss';
import { useState } from 'react';

export const ReactExample = function ReactExample() {
  const [altColours, setAltColours] = useState(false);

  return (
    <section className={`${styles.container}${altColours ? ` ${styles['container-alt']}` : ''}`}>
      <h2>Example React Component</h2>
      <div className="video-container">
        <iframe
          width="560"
          height="315"
          src="https://www.youtube-nocookie.com/embed/PJ0QSJpJn2U"
          title="Should you Stop Using React"
          frameBorder="0"
          allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
          allowFullScreen
        />
      </div>
      <button
        className={`${styles.button}${altColours ? ` ${styles['button-alt']}` : ''}`}
        onClick={() => {
          setAltColours(!altColours);
        }}
      >
        <span className="screen-reader-text">Toggle colours</span>
      </button>
    </section>
  );
};

export default ReactExample;
```

In line `1`, we import the styles from the module file.  Our style module uses the container class.  In line `8` we attach the scoped class to the `<section>` element.  We access it via `styles.container`.  You can also add scoped class styles where the styles are attached to a kebab-case class (for example, `button-alt`).  We see this in action where we define styles for the `container-alt` class in line `13` of `src/components/Video.scss`.  Our code uses the <a aria-label="Read about bracket property accessors on M D N" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_Accessors#bracket_notation">JavaScript object accessor bracket notation</a>, quoted notation for this kebab-case class in our React component.  So in this case it is `styles['container-alt']`.  We see the concrete use of this in line `8`.

## 👼🏽 Astro JS Sass Styling: Stylelint

Stylelint is a great tool for enforcing code style in your app styles helpful both when working in a team and on your own personal projects.  You can easily build stylelint checks into your continuous integration process.

First create a `.stylelintrc.json` config file:

```json
{
  "extends": "stylelint-config-recommended-scss",
  "rules": {
    "color-named": "never",
    "font-family-name-quotes": "always-where-required",
    "font-weight-notation": "named-where-possible",
    "function-url-no-scheme-relative": true,
    "function-url-quotes": "always",
    "string-quotes": "single",
    "value-keyword-case": "lower",
    "unit-disallowed-list": [],
    "max-empty-lines": 2,
    "no-descending-specificity": true,
    "no-duplicate-selectors": true,
    "font-family-no-missing-generic-family-keyword": null,
    "property-no-unknown": [
      true,
      {
        "ignoreProperties": ["/^lost-/"]
      }
    ],
    "selector-pseudo-class-no-unknown": [true, { "ignorePseudoClasses": ["global"] }]
  },
  "ignoreFiles": ["node_modules/*"],
  "defaultSeverity": "error",
  "customSyntax": "postcss-html"
}
```

Feel free to customise this to your own tastes.  See <a aria-label="OPen style lint docs" href="https://stylelint.io/">stylelint docs</a> for details.

Next we will add a lint script to package.json:

```json
{
  "name": "astro-js-sass-styling",
  "version": "0.0.1",
  "private": true,
  "scripts": {
    "dev": "astro dev",
    "start": "astro dev",
    "build": "astro build",
    "preview": "astro preview",
    "format": "prettier --write --plugin-search-dir=. .",
    "lint:scss": "stylelint \"src/**/*.{astro,scss,svelte}\"",
    "prettier:check": "prettier --check --plugin-search-dir=. ."
  },
```

We define the new script in line `11`. Here we set stylelint to check `.astro`, `.scss` and `.svelte` files.  To run the script, in the Terminal type:

```shell
pnpm run lint:scss
```

![astro-js-sass % pnpm lint:scss > astro-js-sass-styling@0.0.1 lint:scss /Users/ra598o6i/Documents/lab/sites/astro/demos/astro-js-sass-styling > stylelint src/**/*.{astro,scss,svelte} src/pages/index.astro 44:11  ✖  Expected single quotes  string-quotes ELIFECYCLE Command failed with exit code 2.  astro-js-sass % astro-js-sass % astro-js-sass % astro-js-sass % pnpm lint:scss > astro-js-sass-styling@0.0.1 lint:scss /Users/ra598o6i/Documents/lab/sites/astro/demos/astro-js-sass-styling > stylelint src/**/*.{astro,scss,svelte}](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cnq0weiv9ln9d8n5ph72.png)

You can add this into existing Husky configuration.  If you want to set this up have a look at the post on <a href="https://rodneylab.com/sveltekit-tooling/">7 Tools to Streamline your CI Workflow</a>.

## 🙌🏽 Astro JS Sass Styling: Wrapping Up

In this post we have run through the key parts of setting up Astro JS Sass styling.  In particular, we looked at:

- how you can add global styles with Sass in Astro as well as scoped styles in Astro pages and components,
- using SCSS modules in Astro to add scoped styling to React components,
- configuration for stylelint in Astro.

The <a aria-label="Open the Astro JS tutorial code in the Rodney Lab Git Hub Repo" href="https://github.com/rodneylab/astro/tree/main/demos/astro-js-sass-styling">Astro JS Sass styling demo code is in the Rodney Lab GitHub repo</a>.  You can also <a aria-label="Open the Astro blog Markdown starter in Stack Blitz" href="https://stackblitz.com/github/rodneylab/astro/tree/main/demos/astro-js-sass-styling">try it on Stackblitz</a>.

I hope you found this article useful and am keen to hear how you will the starter on your own projects as well as possible improvements.

## 🙏🏽 Astro JS Sass Styling: Feedback

Have you found the post useful? Would you prefer to see posts on another topic instead? Get in touch with ideas for new posts. Also if you like my writing style, get in touch if I can write some posts for your company site on a consultancy basis. Read on to find ways to get in touch, further below. If you want to support posts similar to this one and can spare a few dollars, euros or pounds, please <a aria-label="Support Rodney Lab via Buy me a Coffee" href="https://rodneylab.com/giving/">consider supporting me through Buy me a Coffee</a>.

Finally, feel free to share the post on your social media accounts for all your followers who will find it useful. As well as leaving a comment below, you can get in touch via <a aria-label="REach out on Twitter" href="https://twitter.com/messages/compose?recipient_id=1323579817258831875">@askRodney</a> on Twitter and also <a aria-label="Contact Rodney Lab via Telegram" href="https://t.me/askRodney">askRodney on Telegram</a>. Also, see <a aria-label="Get in touch with Rodney Lab" href="https://rodneylab.com/contact">further ways to get in touch with Rodney Lab</a>. I post regularly on <a aria-label="See posts on Astro" href="https://rodneylab.com/tags/astro/">Astro</a> as well as <a aria-label="See posts on svelte kit" href="https://rodneylab.com/tags/sveltekit/">SvelteKit</a>. Also <a aria-label="Subscribe to the Rodney Lab newsletter" href="https://rodneylab.com/about/#newsletter">subscribe to the newsletter to keep up-to-date</a> with our latest projects.