Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Global Sass imports don't work when you use variables, mixins, functions, etc! #11617

Closed
tr1s opened this issue Apr 2, 2020 · 15 comments · Fixed by #12277
Closed

Global Sass imports don't work when you use variables, mixins, functions, etc! #11617

tr1s opened this issue Apr 2, 2020 · 15 comments · Fixed by #12277

Comments

@tr1s
Copy link

tr1s commented Apr 2, 2020

I've been working in Gatsby for a while, and recently did a project in Nuxt.js, and I'd love to compare and hope for a similar feature in Next.js. I recently read the blog about Sass imports in version 9.3 and I thought now would be a great time to test out Next.js with a new project.

I would love to know if this is a bug that's getting fixed, or there are similar "community" packages that solve this problem, and/or if Next.js will implement this in the future ever?

Bug report

Global imports for Sass work via pages/_app.js, but the Sass mixins, variables, functions, etc don't apply globally as the styles do.

You can achieve this in Nuxt.js like so in the nuxt.config.js:

export default {
  modules: [
    '@nuxtjs/style-resources',
  ],
  // for global imports
  css: ['~/styles/normalize.scss', '~/styles/typography.scss', '~/styles/global.scss'],
  // for utility imports
  styleResources: {
    scss: [
      '~/styles/abstracts/_mixins.scss',
      '~/styles/abstracts/_variables.scss',
      '~/styles/abstracts/_functions.scss'
    ]
  },
}

and in Gatsby via the gatsby.config.js (but unfortunately they don't have global Sass imports)

module.exports = {
  plugins: [
    {
      resolve: `gatsby-plugin-sass`,
      options: {
        data: `
            @import './src/styles/_variables.scss';
            @import './src/styles/_mixins.scss';
            @import './src/styles/_functions.scss';
          `,
        includePaths: ['./src/styles/']
      }
    }
  ]
};

I tried to achieve the same thing in Next.js by adding this to the pages/_app.js

import '../styles/functions.scss';
import '../styles/keyframes.scss';
import '../styles/mixins.scss';
import '../styles/variables.scss';

// these work properly IF there's no variables referencing the above, otherwise they'll error
import '../styles/global.scss';
import '../styles/typography.scss';
import '../styles/normalize.scss';

export default function MyApp({ Component, pageProps }) {
  return <Component {...pageProps} />
}

I tried this method but next-sass wasn't playing well with built-in sass, so it doesn't seem like it's backwards compatible?

And when I try to just import the file in a page or component it will then just tell me to import via pages/_app.js which doesn't work.

CleanShot 2020-04-02 at 18 01 12@2x

I feel like I'm missing something.. (sorry if I am), but I'm not understanding why all these other issues are closed. I'm wondering why I can't use Sass features but yet there's documentation about use Sass modules?

There also claims to be a workaround here but provides no context on how to implement that or where to put the code? I would love if there was some more documentation around this. I would love to be able to use these Sass core features in Next!

Hopefully someone can help me out, thanks!

System information

  • Latest macOS Catalina
  • Next 9.3.4
@canrozanes
Copy link

If i'm understanding your issue correctly, whenever you'd like to consume sass variables and mixins in a .module.scss file, you have to individually import those partials to that specific .module.scss file.

The following works for me:

_colors.scss

$orange: #d36816;

button.module.scss

@import "../styles/partials/colors.scss";  

.button {
  border: 1.5px solid $orange;
}

button.js

import styles from './button.module.scss'

const Button = () => <button className={styles.button}/>

export default Button

For this to work all you need is to npm install sass.

That being said it'd be nice if we could import variable partials into _app.js file and be able to refer to them globally across different .module.scss file without having to repeatedly import them.

@tr1s
Copy link
Author

tr1s commented Apr 7, 2020

@canrozanes yes, that does work. I was importing the partials in the index.js and not the module.scss file, thanks!

And yes, I agree! That was essentially my main point.

It feels a bit unintuitive to allow one but not the other. I wouldn’t image it’s that uncommon to have global.scss files that use variables that are referencing a _variables.scss file in which both would apply globally. It would be nice to have some documentation on how to achieve this like my examples above in Gatsby / Nuxt.js!

@tr1s
Copy link
Author

tr1s commented Apr 13, 2020

Revisiting this, fortunately if I want to keep the project more simple, using CSS variables in the global styles works fine!

// pages/_app.js
import '../styles/global.scss';

// global.scss
:root {
    --color-primary: pink;
    --font-primary: monospace;
}

// index.module.scss
.title {
    background: var(--color-primary);
    font-family: var(--font-primary);
}

@rodrigoreeis
Copy link

Revisiting this, fortunately if I want to keep the project more simple, using CSS variables in the global styles works fine!

// pages/_app.js
import '../styles/global.scss';

// global.scss
:root {
    --color-primary: pink;
    --font-primary: monospace;
}

// index.module.scss
.title {
    background: var(--color-primary);
    font-family: var(--font-primary);
}

This works only for variables, it is not possible to use mixins

@Tylerian
Copy link
Contributor

I've made a PR to add support for this, luckily this will be possible soon

@rodrigoreeis
Copy link

I've made a PR to add support for this, luckily this will be possible soon

one of the ways to solve this problem is to import these styles when creating the very simple webpack

const path = require('path')
const withSass = require('@zeit/next-sass')

module.exports = withSass({
  webpack(config) {
    config.module.rules.push(
      {
        enforce: 'pre',
        test: /\.scss$/,
        use: [
          {
            loader: 'sass-resources-loader',
            options: {
              resources: [
                path.resolve('../styles/functions.scss'),
                path.resolve('../styles/keyframes.scss'),
                path.resolve('../styles/mixins.scss'),
                path.resolve('../styles/variables.scss'),
              ],
            },
          },
        ],
      }, 
    )
    return config
  },
})

The most recommended is to create a global settings and import all these settings and only import a single file in the webpack

@Tylerian
Copy link
Contributor

Tylerian commented Apr 28, 2020

I've made a PR to add support for this, luckily this will be possible soon

one of the ways to solve this problem is to import these styles when creating the very simple webpack

const path = require('path')
const withSass = require('@zeit/next-sass')

module.exports = withSass({
  webpack(config) {
    config.module.rules.push(
      {
        enforce: 'pre',
        test: /\.scss$/,
        use: [
          {
            loader: 'sass-resources-loader',
            options: {
              resources: [
                path.resolve('../styles/functions.scss'),
                path.resolve('../styles/keyframes.scss'),
                path.resolve('../styles/mixins.scss'),
                path.resolve('../styles/variables.scss'),
              ],
            },
          },
        ],
      }, 
    )
    return config
  },
})

The most recommended is to create a global settings and import all these settings and only import a single file in the webpack

That's indeed a neat solution but it requires the end user to have experience with webpack and its not very friendly for newcomers.

The convention used among other projects like nuxt, gatsby and the recommended way by sass-loader itself is to use the prependData option that sass-loader provides.

The end result is much more like the next.js way of doing things:

/// next.config.js
module.exports = {
  experimental: {
    sassOptions: {
      prependData: `
        /// Scss code that you want to be
        /// prepended to every single scss file.
        /// example: @import "../styles/variables.scss"
      `,
    },
  },
}

Perhaps this could be discussed in the PR instead? #12277

@rodrigoreeis
Copy link

That's indeed a neat solution but it requires the end user to have experience with webpack and its not very friendly for newcomers.

The convention used among other projects like nuxt, gatsby and the recommended way by sass-loader itself is to use the prependData option that sass-loader provides.

The end result is much more like the next.js way of doing things:

I tried to add this solution to my project, but get some error...

module.exports = {
  experimental: {
    sassOptions: {
      prependData: `
        @import './src/styles/settings/index.scss'
      `,
    },
  },
}

Screen Shot 2020-04-28 at 1 08 02 PM

@Tylerian
Copy link
Contributor

Tylerian commented Apr 28, 2020

That's indeed a neat solution but it requires the end user to have experience with webpack and its not very friendly for newcomers.
The convention used among other projects like nuxt, gatsby and the recommended way by sass-loader itself is to use the prependData option that sass-loader provides.
The end result is much more like the next.js way of doing things:

I tried to add this solution to my project, but get some error...

module.exports = {
  experimental: {
    sassOptions: {
      prependData: `
        @import './src/styles/settings/index.scss'
      `,
    },
  },
}
Screen Shot 2020-04-28 at 1 08 02 PM

You must wait until PR #12277 get merged, use your solution for now, it's pretty neat

@rodrigoreeis
Copy link

Aaa.... not yet implemented 😬, I will be waiting for it

@darrenbarklie
Copy link

Appreciate the succinct issue summary @tr1s and I am also tracking @Tylerian's PR. I agree prepending is the neatest solution, but can work with imports for now.

@tr1s
Copy link
Author

tr1s commented May 14, 2020

It says in Next 9.4 this support was added, but I can't get it to work yet. Anyone else having better luck? These webpack configs are always so confusing :/

@franknoel
Copy link

It says in Next 9.4 this support was added, but I can't get it to work yet. Anyone else having better luck? These webpack configs are always so confusing :/

I'm also unable to make it work for the 9.4 version. I'm wondering if this is wanted, see the discussion in #12277

@7iomka
Copy link
Contributor

7iomka commented May 16, 2020

My workaround:

webpack: (config) => {
  // Issue: https://github.com/zeit/next.js/issues/10339#issuecomment-596966509
  config.module.rules.map((rule) => {
    if (rule.oneOf) {
      rule.oneOf.find((configRule) => {
        if (Array.isArray(configRule.use)) {
          configRule.use.forEach((loaderItem) => {
            const isSassRule = loaderItem.loader && loaderItem.loader.includes('sass-loader');
            const isCssLoader = loaderItem.loader && loaderItem.loader.includes('css-loader');
            if (isSassRule) {
              configRule.use.push({
                loader: 'sass-resources-loader',
                options: {
                  // globally needed mixins, functions, etc.
                  // eslint-disable-next-line global-require
                  resources: require('./src/scss/scss-resources.js'),
                },
              });
            }
            if (isCssLoader) {
              if (loaderItem.options && loaderItem.options.modules) {
                // Remove 'pure' mode for css-modules, add new rules
                // TODO: need tests
                // Sources: https://github.com/zeit/next.js/blob/canary/packages/next/build/webpack/config/blocks/css/index.ts#L101
                delete loaderItem.options.modules.mode;
              }
              // Export '.class-name' as { className }
              loaderItem.options.localsConvention = 'camelCase';
            }
          });
        }
      });
    }
  });

  return config;
}

scss-resources.js

const path = require('path');

const resources = [
  // general functions
  'core/_functions.scss',
  // general color palette (opt)
  'core/_palette.scss',
  // general core variables (can be overridden with theme-settings)
  'core/_settings.scss',
  // media-queries mixins helper
  'core/helpers/_include-media.scss',
  // common mixins
  'core/mixins/_common-mixins.scss',
  // utility mixins
  'core/mixins/_utility-mixins.scss',
];
module.exports = resources.map(file => path.resolve(__dirname, file));

@kodiakhq kodiakhq bot closed this as completed in #12277 May 23, 2020
kodiakhq bot pushed a commit that referenced this issue May 23, 2020
This PR adds support for prepending sass code before the actual entry file.

It's common for developers to import their sass mixins and variables once on their project config so they don't need to import them on every file that requires it. Frameworks like gatsby and nuxt.js already support that handy feature.

The way it works is:

```
/// next.config.js
module.exports = {
  experimental: {
    sassOptions: {
      prependData: `
        /// Scss code that you want to be
        /// prepended to every single scss file.
      `,
    },
  },
}
```

Fixes #11617 and duplicates
rokinsky pushed a commit to rokinsky/next.js that referenced this issue Jul 11, 2020
This PR adds support for prepending sass code before the actual entry file.

It's common for developers to import their sass mixins and variables once on their project config so they don't need to import them on every file that requires it. Frameworks like gatsby and nuxt.js already support that handy feature.

The way it works is:

```
/// next.config.js
module.exports = {
  experimental: {
    sassOptions: {
      prependData: `
        /// Scss code that you want to be
        /// prepended to every single scss file.
      `,
    },
  },
}
```

Fixes vercel#11617 and duplicates
@balazsorban44
Copy link
Member

This issue has been automatically locked due to no recent activity. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you.

@vercel vercel locked as resolved and limited conversation to collaborators Jan 30, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants