Search code examples
reactjssassdart-sass

Effectively Using SASS/SCSS


At work, I've been using SCSS for a few years. I've now just started my own React project and want to use it as well. To this end I installed dart-sass. I prefer building React component "bundles" where a folder with the same name as the component (ex. Button) contains the following files:

  • Button.tsx (the component, written in TypeScript)
  • Button.module.scss
  • Button.test.ts
  • Button.md

The main point here is that I mostly prefer the *.module.scss approach because it keeps the styles isolated.

However, I realize that there will be occasions for global styles. To this end, I've created a sass/styles.scss file into which I can import partial style files like _variables.scss, _fonts.scss, etc.

What I'm unclear on is where should I best be importing styles.scss? The highest level files, index.tsx or App.tsx seem like candidates but I'm hoping that the community could give me some best practice tips on this. Thank you.


Solution

  • That depends on the nature of the different SCSS files. Bear with me if I repeat anything you know already, I try to make my answer somewhat comprehensive.

    • Silent files: SCSS files that produce no CSS output are silent; mixins, functions, and variables ($-scss-vars, not --css-vars) are silent. So typically we would import _mixins.scss / _functions.scss / _variables.scss only into other SCSS files, but we can do so without regret, anywhere and as often as we like without fear of introducing duplicates into the CSS.

    • Component style files: The typical Button.module.scss files; scoped to one component only; and imported only there.

    • App scoped style files: If your app does not "own" the whole page and needs to play nicely with potential neighboring content on the page without leaking any styling, you can scope it to your app root (technically this is just a css module for your App.tsx). Skip this step if your app is basically the whole page and just apply global styles.

    // App.scoped.normalize.module.scss
    @import "style/font";
    
    .app-scoped-normalize {
      &, *, ::after, ::before {
        box-sizing: border-box;
        margin: 0;
        padding: 0;
        border: none;
        outline: none;
      }
    
      font-family: $bodycopy-font-family;
      color: var(--font-color);
      font-size: $font-size-regular;
      line-height: $line-height;
      
      h1, h2, h3, h4, h5, h6 {
        font-family: $heading-font-family;
      }
    
      ul, ol {
        list-style: none;
      }
    }
    
    // App.tsx - here import order is relevant if css rules have equal specifity
    import normalize from './App.scoped.normalize.module.scss';
    import colors from './App.scoped.colors.module.scss';
    import style from './App.module.scss';
    
    // ...
    
    return (
      <div className={classnames(
        normalize.appScopedNormalize,
        colors.appScopedColors,
        style.appRoot,
      )}>
      // ...
      </div>
    );
    
    • Global style files: import it in your entry file; typically it is called index.tsx; order of imports is relevant, so you might want to import it before App.tsx:
    // index.tsx
    import './global.scss';
    import { App } from './App';
    
    // ...render App...
    

    As others have pointed out, you could import any global styles in App.tsx. I always try to reach a lucid mental model of all involved parts, and translate that mental model into code to the best of my abilities; and to me it feels more natural to keep the globals out of the App.tsx. And it helps with keeping the order of imports clear.