Storybook: How do I include global styles (scss) once for all Storybook iframes?

Created on 1 Apr 2019  ·  39Comments  ·  Source: storybookjs/storybook

Describe the bug
I have been trying multiple ways of incorporating global styles (scss) once for all Storybook Iframes and have been unsuccessful. There doesn't seem to be a clear concise way to do this.

To Reproduce
Attempts:


    • Import the global styles within each component.

    • Results: Styles are applied, but, multiple global.scss styles is repeated {story#} x times.

    • One place recommend creating a decorator for Storybook and apply the "global" css that way.
    • Result: would not work for what I'm trying to do (multiple styles, mixins, variables, etc) This only works for small CSS changes
    • In config under .storybook require('../libs/storybook/global-styles.scss'); within the loadStories function
    • Result: Nothing
    • Tried importing the global styles within the Storybook index.ts
    • Result: Nothing
    • Tried adding the global styles via angular.json file

      • Result: Nothing

Expected behavior
I would expect to be able to import Global (scss) files in one place so that can be applied to every Story's Iframe. Could there be a config setting I'm not aware of?

System:

  • OS: MacOS
  • Device: Macbook Pro 2015
  • Browser: Chrome
  • Framework: Angular
  • Addons: - [addon-centered, addon-viewport, addon-info]
  • Version: [^5.0.1]

Additional context
I am hoping that I am missing some configuration within storybook that would make this simple to resolve.

angular has workaround inactive question / support

Most helpful comment

Hi @omaracrystal here is how I did it:
in the .storybook/config.jsfiles, add the import with the correct loaders inlined:

import '!style-loader!css-loader!sass-loader!./scss-loader.scss';

Then add/import all the styles you need in the scss-loader.scss file:

$font-path: '../projects/lib/src/theming/fonts/OpenSans/';

@import '../projects/lib/src/theming/reset.scss';
@import '../projects/lib/src/theming/main.scss';

html {
  font-family: $font-name;
  font-size: $font-size--small;
}

The solution proposed by @kroeder works as long as your using components from an Angular application but for libraries, you cannot add the styles property in the angular.json file

All 39 comments

Try adding your global.scss File into angular.json

Check your defaultProject (there's a defaultProject property inside the json) and add it to the styles list

"styles": [
              "projects/your-cli-project/src/lib/styles/your-styles.scss"
            ],

I do it that way and it works just fine. Is that an option for you?

Hi everyone! Seems like there hasn't been much going on in this issue lately. If there are still questions, comments, or bugs, please feel free to continue the discussion. Unfortunately, we don't have time to get to every issue. We are always open to contributions so please send us a pull request if you would like to help. Inactive issues will be closed after 30 days. Thanks!

Hi @omaracrystal here is how I did it:
in the .storybook/config.jsfiles, add the import with the correct loaders inlined:

import '!style-loader!css-loader!sass-loader!./scss-loader.scss';

Then add/import all the styles you need in the scss-loader.scss file:

$font-path: '../projects/lib/src/theming/fonts/OpenSans/';

@import '../projects/lib/src/theming/reset.scss';
@import '../projects/lib/src/theming/main.scss';

html {
  font-family: $font-name;
  font-size: $font-size--small;
}

The solution proposed by @kroeder works as long as your using components from an Angular application but for libraries, you cannot add the styles property in the angular.json file

Try adding your global.scss File into angular.json

Check your defaultProject (there's a defaultProject property inside the json) and add it to the styles list

"styles": [
              "projects/your-cli-project/src/lib/styles/your-styles.scss"
            ],

I do it that way and it works just fine. Is that an option for you?

I did this exact thing, but it doesn't seem to load this for me when I run storybook

Hi everyone! Seems like there hasn't been much going on in this issue lately. If there are still questions, comments, or bugs, please feel free to continue the discussion. Unfortunately, we don't have time to get to every issue. We are always open to contributions so please send us a pull request if you would like to help. Inactive issues will be closed after 30 days. Thanks!

Hey there, it's me again! I am going close this issue to help our maintainers focus on the current development roadmap instead. If the issue mentioned is still a concern, please open a new ticket and mention this old one. Cheers and thanks for using Storybook!

Hi @omaracrystal here is how I did it:
in the .storybook/config.jsfiles, add the import with the correct loaders inlined:

import '!style-loader!css-loader!sass-loader!./scss-loader.scss';

Then add/import all the styles you need in the scss-loader.scss file:

$font-path: '../projects/lib/src/theming/fonts/OpenSans/';

@import '../projects/lib/src/theming/reset.scss';
@import '../projects/lib/src/theming/main.scss';

html {
  font-family: $font-name;
  font-size: $font-size--small;
}

The solution proposed by @kroeder works as long as your using components from an Angular application but for libraries, you cannot add the styles property in the angular.json file

Is that working for everyone ? Not working my side :(

@ozanmanav did you try import it in .storybook/config.js?

PS: because for me it works but I import just .css file without webpack-loaders

This seems to no longer work using the main.js file structure of Storybook v5.3

@garrettmaring did you try with .storybook/preview.js rather than .storybook/config.js?

@garrettmaring
I finally got it working for .css files using import '!style-loader!css-loader!./main.css'.
At first I tried toying with the webpack config, but I removed all my custom rules, and inlined it like above. Obviously npm install the loaders.

@shilman yes, that did it 👍 Pretty clear once I saw some documentation on it. There might be some room for clearer docs in the main Storybook documentation. I found this article to be clear.

@garrettmaring Mind submitting a PR to help improve the docs?

@garrettmaring
I finally got it working for .css files using import '!style-loader!css-loader!./main.css'.
At first I tried toying with the webpack config, but I removed all my custom rules, and inlined it like above. Obviously npm install the loaders.

Just some FYI that i just figured out, this approach works wonders when serving storybook locally, but it does not work (from what I saw) with build-storybook.

My solution
Add <link rel="stylesheet" href="./your-global-styles.css" /> to the preview-head.html. Then use the -s flag on your-global-styles.css's directory copy the styles to the build directory of storybook. Now the iframe.html "should" be referencing your-global-styles.css from the same directory.

Only issue with this is when running locally, the browser console will say it cannot find localhost/your-global-styles.css but it still works as intended.

If anyone has a better way of approaching this, or knows of a proper solution, let me know :)

we serve scss files in storybook using the following:

import '!style-loader!css-loader!sass-loader!./main.scss';

This works fine for us

@blemaire Where are you using that line?

@blemaire Where are you using that line?

@lopis just create preview.js in the .storybook folder and put that line into it

Thanks everyone. Confirming that

  • adding the scss file to .storybook (or whatever place you wish to put it).
  • Creating .storybook/preview.js
  • adding import '!style-loader!css-loader!sass-loader!./styles.scss'; to the preview.js dir
  • adding "css-loader" as a devDependency to package.json

works for us; storybook i.c.w. an Angular 9 library (so no option to add it to the angular.json file)

In nextjs based setup simple line of import "../styles/main.css"; (as in main app) to .storybook/preview.js adds styles correctly.

The above solutions works if all your pathing is relative ../../src/main.scss but my current project uses SCSS global/alias.

My main.js webpack config for Storybook can compile the alias. But when I try import global/alias SCSS into my preview.js then it can't resolve the paths.

Not sure where to go.

import '!style-loader!css-loader!sass-loader!./scss-loader.scss';

I tried this, but getting error. I need Storybook to know about my global css variables and mixins.

ERR! import '!style-loader!css-loader!sass-loader!./scss-loader.scss';
ERR!        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ERR! 
ERR! SyntaxError: Unexpected string

In nextjs based setup simple line of import "../styles/main.css"; (as in main app) to .storybook/preview.js adds styles correctly.

Does this styles/main.css contain your global SCSS variables? Do they get applied in Storybook at all? I tried this, but still get SassError: Undefined variable: $my-variable-here

Hi @omaracrystal here is how I did it:
in the .storybook/config.jsfiles, add the import with the correct loaders inlined:

import '!style-loader!css-loader!sass-loader!./scss-loader.scss';

Then add/import all the styles you need in the scss-loader.scss file:

$font-path: '../projects/lib/src/theming/fonts/OpenSans/';

@import '../projects/lib/src/theming/reset.scss';
@import '../projects/lib/src/theming/main.scss';

html {
  font-family: $font-name;
  font-size: $font-size--small;
}

The solution proposed by @kroeder works as long as your using components from an Angular application but for libraries, you cannot add the styles property in the angular.json file

Is that working for everyone ? Not working my side :(

Nope. Doesn't help me either. I get this error now:

ERR! import '!style-loader!css-loader!sass-loader!./scss-loader.scss';
ERR!        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ERR! 
ERR! SyntaxError: Unexpected string

Describe the bug
I have been trying multiple ways of incorporating global styles (scss) once for all Storybook Iframes and have been unsuccessful. There doesn't seem to be a clear concise way to do this.

To Reproduce
Attempts:


    • Import the global styles within each component.

    • Results: Styles are applied, but, multiple global.scss styles is repeated {story#} x times.


    • One place recommend creating a decorator for Storybook and apply the "global" css that way.

    • Result: would not work for what I'm trying to do (multiple styles, mixins, variables, etc) This only works for small CSS changes


    • In config under .storybook require('../libs/storybook/global-styles.scss'); within the loadStories function

    • Result: Nothing


    • Tried importing the global styles within the Storybook index.ts

    • Result: Nothing

    • Tried adding the global styles via angular.json file

      • Result: Nothing

Expected behavior
I would expect to be able to import Global (scss) files in one place so that can be applied to every Story's Iframe. Could there be a config setting I'm not aware of?

System:

  • OS: MacOS
  • Device: Macbook Pro 2015
  • Browser: Chrome
  • Framework: Angular
  • Addons: - [addon-centered, addon-viewport, addon-info]
  • Version: [^5.0.1]

Additional context
I am hoping that I am missing some configuration within storybook that would make this simple to resolve.

Did you ever get this resolved? If so, what was the solution? Thanks!

@caseytrombley The solution is to add import '!style-loader!css-loader!sass-loader!./styles.scss'; to your preview.js.

For Create React App + Storybook, I added the following to config.js to get my CSS Reset applied to Storybook.

import '!style-loader!css-loader!../src/reset.css';

Anyone have a solution working for postcss-scss?

You can read the blog that i wrote that exactly speaks about implementing SCSS constants for your storybook via custom webpack config.

There is defect that i have logged explaining current config bug https://github.com/storybookjs/storybook/issues/11052 and blog that i wrote as a work around.

Hope this helps!

For installs that have main.js (i.e. you ran npx -p @storybook/cli sb init in a CreateReactApp project), just add preview.js as a sibling then do this:

// .storybook/preview.js
require('!style-loader!css-loader!../src/css/tailwind-utility-classes.css')

If it complains one of those loaders isn't installed (they should be if it's a CRA project), just manually install them (i.e. yarn add style-loader css-loader).

Lock thread so people can just jump to bottom for solution when the upvoted/deprecated solution fails?

@garrettmaring did you try with .storybook/preview.js rather than .storybook/config.js?

When I create a styles.css file inside the same directory and import it with import './styles.css'; inside my preview.js file, my storybook suddenly just is stuck in a loading state... :(

Any idea how to resolve that?

I use storybook with the open-wc config for webcomponents and have a preview.js and a main.js file inside my .storybook folder.

@dcts that could be because your ./styles.css is referencing Tailwind utilities, using some features that need to be pre-processed, or in some other way "broken" as a standalone file.

I needed to do three things:

  1. Create an input css file. For me, this is css/tailwind.css where I import anything needed for my Tailwind setup.
  2. When running storybook, build that file first. For me, this was a PostCSS command postcss css/tailwind.css -o css/index.css
  3. In .storybook/preview.js (create that file if needed) add import '../css/index.css

I am using Twin and I can use this component as expected:

import tw, { styled } from 'twin.macro'

const StyledReactionButton = styled.button`
  ${tw`bg-blue-400 bg-opacity-25`}
`

I think a better solution would be to edit the storybook webpack config to handle the postcss step.

I am using Vue.js with scss. My issue is that when importing global scss in preview.js my chrome devtolls is super slow and not useable. Anyone having the same issue? Any alternatives? I am trying not to import global scss in each of the my stories.

The above solutions worked in a way, but this method __from official docs__ eliminates the problem in the whole project.

In short, we need to extend Webpack configuration to set scss loaders.

Sample code from official docs

// .storybook/main.js

const path = require('path');

// Export a function. Accept the base config as the only param.
module.exports = {
  webpackFinal: async (config, { configType }) => {
    // `configType` has a value of 'DEVELOPMENT' or 'PRODUCTION'
    // You can change the configuration based on that.
    // 'PRODUCTION' is used when building the static version of storybook.

    // Make whatever fine-grained changes you need
    config.module.rules.push({
      test: /\.scss$/,
      use: ['style-loader', 'css-loader', 'sass-loader'],
      include: path.resolve(__dirname, '../'),
    });

    // Return the altered config
    return config;
  },
};

Usage

Now you can import scss files as aways in .storybook/preview.js:

// .storybook/preview.js

import "../src/styles/main.scss";

// ...

...and even in your components too:

<!-- src/components/MyComponent.vue -->

<template>
  ...
</template>

<script>
// ...
</script>

<style lang="scss">
@import "../styles/components/components.my-component";
</style>

I ran into an issue where importing a sizable SCSS project from preview.js is causing 5+ second recompile times on any change to anything in the project. Moving this out into a story somehow gets everything back down to <1s recompile times..

I am using Storybook with Gatsby and the standard scss presets. After importing the global.scss file to preview.js the styles are picked up, but if I define variables in global.scss and use them in a component the following error is triggered:

ERROR in ./src/stories/button.scss (./node_modules/css-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js!./src/stories/button.scss)
Module build failed (from ./node_modules/sass-loader/dist/cjs.js):
SassError: Undefined variable: "$black".

I'm suspecting that the global.scss file gets loaded in too late, but I don't know how to configure it correctly. Maybe it's even a bug, because adding global styles to preview.js is the recommended method.

Update

It's not the order. If I write the following in global.scss.

body {
    background-color: black;
}

and the following in Button.scss:

body {
  background-color:green;
}

The background color is in fact green. This means Buttons.scss is loaded after Global.scss like it should, but the variables are still not picked up.

Solution
Yeez, I learned a lot today. The main problem was that sass variables are only defined in the file they are declared in by default. If you want to use variables across files, you have to use sass-modules. The old way was to use @import, but the new preferred way is @use. I start every module like button.scss with @use pathtomodule/global.scss. In my case this makes the import in preview.js redundant.

There was another catch. @use is currently only supported by dart sass. I had node-sass installed (npm i node-sass --save-dev). There is a package called dart-sass (npm i dart-sass --save-dev), but that is not the correct one for the before mentioned presets. Dart sass has become regular sass. You can simply install it with npm i sass --save-dev.

Hi @Piepongwong,

I'm not using Gatsby but in Vue I solved that problem putting all mixin and variable imports into one file named _common.scss and import it in components like this:

<style lang="scss">
@import 'path/to/styles/_common.scss';
@import 'path/to/styles/components/_components.component.scss';
</style>

In this way I'm always sure that my mixins and variables exists but be aware of that you need to abstract all your components and variables correctly. You could look at inuitcss for best practices.

Hope this helps.

Hi @omaracrystal here is how I did it:
in the .storybook/config.jsfiles, add the import with the correct loaders inlined:

import '!style-loader!css-loader!sass-loader!./scss-loader.scss';

Then add/import all the styles you need in the scss-loader.scss file:

$font-path: '../projects/lib/src/theming/fonts/OpenSans/';

@import '../projects/lib/src/theming/reset.scss';
@import '../projects/lib/src/theming/main.scss';

html {
  font-family: $font-name;
  font-size: $font-size--small;
}

The solution proposed by @kroeder works as long as your using components from an Angular application but for libraries, you cannot add the styles property in the angular.json file

Is that working for everyone ? Not working my side :(

not for me:( i tried with next.js and reactstrap but it didnt load the app.scss from my style folder :/

Hi @omaracrystal here is how I did it:
in the .storybook/config.jsfiles, add the import with the correct loaders inlined:

import '!style-loader!css-loader!sass-loader!./scss-loader.scss';

Then add/import all the styles you need in the scss-loader.scss file:

$font-path: '../projects/lib/src/theming/fonts/OpenSans/';

@import '../projects/lib/src/theming/reset.scss';
@import '../projects/lib/src/theming/main.scss';

html {
  font-family: $font-name;
  font-size: $font-size--small;
}

The solution proposed by @kroeder works as long as your using components from an Angular application but for libraries, you cannot add the styles property in the angular.json file

Is that working for everyone ? Not working my side :(

not for me:( i tried with next.js and reactstrap but it didnt load the app.scss from my style folder :/

Try @hayatbiralem way - it should work.

This worked for me in latest version of storybook @storybook/[email protected] Hope this helps someone!

// .storybook/main.js
const path = require('path');

module.exports = {
  "stories": [
    "../stories/**/*.stories.mdx",
    "../stories/**/*.stories.@(js|jsx|ts|tsx)",
    "../app/frontend/components/**/*.stories.@(js|jsx|ts|tsx)"
  ],
  "addons": [
    "@storybook/addon-links",
    "@storybook/addon-essentials",
    '@storybook/preset-scss'
  ],
  webpackFinal: async (config) => {
    // this sets the default path for modules
    config.resolve.modules = [
      ...(config.resolve.modules || []),
      path.resolve(__dirname, "../app/frontend"),
    ];

    config.module.rules.map(rule => {
      if (rule.test instanceof RegExp && rule.test.toString() === '/\\.s[ca]ss$/') {
        rule.use.push({
          loader: require.resolve('sass-resources-loader'),
          options: {
            resources: [
              path.resolve(__dirname, '../app/frontend/styles/base/_variables.scss')
            ]
          }
        })
      }
      return rule
    })
    return config;
  },
}
Was this page helpful?
0 / 5 - 0 ratings

Related issues

miljan-aleksic picture miljan-aleksic  ·  3Comments

wahengchang picture wahengchang  ·  3Comments

dnlsandiego picture dnlsandiego  ·  3Comments

tlrobinson picture tlrobinson  ·  3Comments

levithomason picture levithomason  ·  3Comments