Skip to content
This repository has been archived by the owner on Jan 11, 2023. It is now read-only.

Global SASS/SCSS/Less/Stylus support #474

Closed
antony opened this issue Oct 14, 2018 · 35 comments
Closed

Global SASS/SCSS/Less/Stylus support #474

antony opened this issue Oct 14, 2018 · 35 comments
Assignees

Comments

@antony
Copy link
Member

antony commented Oct 14, 2018

I'm trying to convert my Nuxt application to Sapper and I've come across a bit of a roadblock.

In my Nuxt application, I have a layout file which simply includes bulma's SASS version:

<style lang="scss">
  @import '~bulma/scss/_all';
</style>

This is possible, and allows the styles to be used across the application, because Vue doesn't scope styles by default.

I'll caveat this by saying that I prefer scoped styles by default, but I feel like there needs to be a way to unscope the styles when necessary. Because of the way Sapper/Svelte handles styles, and scopes them, including bulma in a similar way to the above means that Svelte will scope all the styles, and then remove nearly all of them as it sees them as "unused".

This means I end up with an application with no styles!

Right now this is a roadblock to my conversion, as I can't see a workaround (other than some horrendous external compilation with gulp or similar and then including the compiled css in my head - which I really want to avoid as it takes from the speed of Sapper's development, as well as adding complexity where it really shouldn't be.

Any tips? Or is there a feature / capability missing from Sapper here?

@maxmilton
Copy link

In the Svelte docs there's a section about using :global() to ignore Svelte's scoping mechanism. You need to apply this to each selector you want globally scoped. It can get a bit tedious so I wrote a svelte style preprocessor which does this automatically when the style block has a "global" attribute, eg. <style global>... no docs though as it's just something we've been using internally and it's more for PostCSS.

For SCSS support you might want to have a look at https://github.com/kaisermann/svelte-preprocess and https://github.com/ls-age/svelte-preprocess-sass. Or if you want to write your own preprocessor see https://github.com/sveltejs/svelte#preprocessor-options.

@maxmilton
Copy link

It's also worth mentioning you can import your global styles in a JS file and then handle it with webpack/rollup plugins. This is actually what we do most of the time.

A caveat is all the styles will be included so I recommend using a tool like https://github.com/FullHuman/purgecss to remove any unused styles.

@antony
Copy link
Member Author

antony commented Oct 14, 2018

Hi @maxmilton - thanks for your help, but sadly none of these solutions fix the core issue. I'm quite happily using scss within components, but I need to include the bulma library. I can't (and shouldn't) wrap the entire library in :global { }.

@antony
Copy link
Member Author

antony commented Oct 14, 2018

@maxmilton this is basically what I'm doing now, based on another project I have found, but writing a build file and parallel building my scss outside the app with a separate watcher and then including the compiled css file out of band... is a total hack.

This feels like it should be a simple, easy thing that should be supported out of the box. I would vouch that most non-trivial projects are based on a CSS library (Bootstrap, Bulma, Pure, Semantic etc) - with a customisation file.

@maxmilton
Copy link

In that case it's probably best to import global styles it in your src/client.js (assuming you're using sapper-template) and handle via webpack/rollup. Then in your components you can import variables as you need them.

@antony
Copy link
Member Author

antony commented Oct 14, 2018

@maxmilton thanks - I'll give that a go. If it works then it's more a matter of documentation than it is functionality.

@antony
Copy link
Member Author

antony commented Nov 1, 2018

Probably worth updating this with my current solution. I still think this should be native to Sapper as I think it's quite a common usecase and therefore having to get people rolling up their sleeves and fumbling with webpack to do this isn't ideal, but:

https://github.com/kaisermann/svelte-preprocess

Is a very nice solution to getting preprocessed styles working in components, and also allowing processing and live-reloading of a third-party css framework like Bulma / Bootstrap / Semantic.

@laurentpayot
Copy link

laurentpayot commented Nov 2, 2018

@antony I had the same issue with Bulma and global CSS. By the way I'm using svelte-preprocess as well. Here is how I made it work by generating a global.css file with Bulma stuff:

Install the following dependencies:

npm i -D node-sass postcss cssnano bulma

Add the following lines to your webpack.config.js file:

const fs = require('fs')
const sass = require('node-sass')
const postcss = require('postcss')
const cssnano = require('cssnano')({preset: 'default'})

// generating global css file https://github.com/sveltejs/sapper/issues/357
sass.render({
  file: './src/style/global.sass',
  indentedSyntax: true,
  outFile: './static/global.css'
}, function (error, result) {
  if (!error) {
    postcss([cssnano])
    .process(result.css, {
      from: './static/global.css',
      to: './static/global.css'
    })
    .then(result =>
      fs.writeFile('./static/global.css', result.css, function (err) {
        if (err) {
          console.log(err)
        }
      })
    )
  } else {
    console.log(error)
  }
})

And create a /src/style/ subdirectory with the 3 following files:

  • variables.sass
// Import a Google Font
@import url('https://fonts.googleapis.com/css?family=Nunito:400,700')

// Set your brand colors
$purple: #8A4D76
$pink: #FA7C91
$brown: #757763
$beige-light: #D0D1CD
$beige-lighter: #EFF0EB

// Update Bulma's global variables
$family-sans-serif: "Nunito", sans-serif
$grey-dark: $brown
$grey-light: $beige-light
$primary: $purple
$link: $pink
$widescreen-enabled: false
$fullhd-enabled: false

// Update some of Bulma's component variables
$body-background-color: $beige-lighter
$control-border-width: 2px
$input-border-color: transparent
$input-shadow: none
  • bulma.sass
// Import only what you need from Bulma
@import "../../node_modules/bulma/sass/utilities/_all.sass"
@import "../../node_modules/bulma/sass/base/_all.sass"
@import "../../node_modules/bulma/sass/elements/button.sass"
@import "../../node_modules/bulma/sass/elements/container.sass"
@import "../../node_modules/bulma/sass/elements/form.sass"
@import "../../node_modules/bulma/sass/elements/title.sass"
@import "../../node_modules/bulma/sass/components/navbar.sass"
@import "../../node_modules/bulma/sass/layout/hero.sass"
@import "../../node_modules/bulma/sass/layout/section.sass"
  • global.sass
@charset "utf-8"
@import "./variables"
@import "./bulma"

// your global styles
p.foo
  color: red

and in your .html files (if you use svelve-preprocess) use

<style lang="sass">
@import "../style/variables"

p.foo
  color: $primary
</style>

Note that you have to re-run npm run dev every time you modify these global Bulma files.

Hope it helps.

@laurentpayot
Copy link

Waiting for #357

@aheissenberger
Copy link

I used @maxmilton 's code and created this preprocessor which will make any css code global including support for external files and postcss plugins:
https://www.npmjs.com/package/svelte-preprocess-css-global

@jaybeemind
Copy link

Probably worth updating this with my current solution. I still think this should be native to Sapper as I think it's quite a common usecase and therefore having to get people rolling up their sleeves and fumbling with webpack to do this isn't ideal, but:

https://github.com/kaisermann/svelte-preprocess

Is a very nice solution to getting preprocessed styles working in components, and also allowing processing and live-reloading of a third-party css framework like Bulma / Bootstrap / Semantic.

Hey man, can you share how you implemented this? I'm new to this build step thing in web development, forgive my ignorance.

Thanks in advance.

@antony
Copy link
Member Author

antony commented Aug 14, 2019

@jaybeemind svelte-preprocess has some excellent documentation about how to integrate it into rollup/webpack, you'd just need to follow it. If you need more help - come and talk to us in chat.

@jaybeemind
Copy link

jaybeemind commented Aug 15, 2019

To those that would need a simple solution for this (using rollup template), with help from @antony , I was able to do it like this:

you will need:

then in your rollup.config.js file, add these:

...
import autoPreprocess from 'svelte-preprocess'

const preprocessOptions = {
	transformers: {
		scss: {
			includePaths: [
				'node_modules',
				'src'
			]
		},
		postcss: {
			plugins: [
				require('autoprefixer'),
			]
		}
	},
}
...
export default {
  client: { // (and probably 'server' too)
    ...
    svelte({
      ...
      preprocess: autoPreprocess(preprocessOptions)
}),

after that, I added a style directory under src, and made a main.scss file.

inside the main.scss file, mine looks like these atm:

@import "../../node_modules/spectre.css/src/spectre.scss";

then import it in _layout.svelte

<style global lang="scss">
	@import './style/main.scss';
...

I came from Vue.js and was impressed by how simple it is to do things with svelte.
I hope many would adapt sapper too, would love for it to mature.

EDIT: Removed transformers as per the authors message on discord.
You can do it this way too:

const preprocessOptions = {
  scss: {
    includePaths: [
      'node_modules',
      'src'
    ]
  },
  postcss: {
    plugins: [
      require('autoprefixer'),
    ]
  }
}

@antony
Copy link
Member Author

antony commented Aug 15, 2019

You shouldn't need ../../node_modules/ prefixing your scss path. I believe that includePaths makes this possible.

@rgfx
Copy link

rgfx commented Aug 15, 2019

@jaybeemind Thanks for this solution. I have to hit refresh for the changes to show, is that the expected behavior? Maybe I did something wrong.

@YogliB I got it working, The code for Svelte and Sapper are different. https://github.com/kaisermann/svelte-preprocess#with-sapper

@YogliB
Copy link

YogliB commented Aug 17, 2019

@jaybeemind Tried doing something like that, in order to use Ionic 4 with Sapper and it didn't work...

@jaybeemind
Copy link

You shouldn't need ../../node_modules/ prefixing your scss path. I believe that includePaths makes this possible.

I see, thanks! But honestly, I only did it that way because of vscode autocomplete lol 🤣

@jaybeemind
Copy link

@jaybeemind Tried doing something like that, in order to use Ionic 4 with Sapper and it didn't work...

Can't help bro unless we see what you did.

@fanckush
Copy link

fanckush commented Aug 30, 2019

Edit:
So this seems to not be possible at the moment sveltejs/svelte-preprocess#58

Original:
is there a way to avoid importing the variables.scss in every <style>?
Maybe there is some option in the config that makes the _variables.scss globally available. is it possible?

i'm using rollup and want to get Zurb foundation (SCSS ver.) to work as nice as possible

@YogliB
Copy link

YogliB commented Aug 31, 2019

@fankush
I think that the way to go right now is with rollup-plugin-postcss, that lets you import scss files in the client.js

@antony
Copy link
Member Author

antony commented Aug 31, 2019

svelte-preprocess is a better solution, but yeah, you need to import scss variables wherever you need to use them, this is how scss works, not a limitation of svelte or the plugin.

@fanckush
Copy link

maybe so but still.. other frameworks have scssLoaders that allow this kind of behaviour

@antony
Copy link
Member Author

antony commented Aug 31, 2019 via email

@kaisermann
Copy link
Member

@fanckush For scss/sass, you can prepend the @import 'variables.scss' with the data prop:

preprocess({
  scss: {
    data: `@import 'variables.scss';`
  }
})

@tamari-gray
Copy link

you are my hero

@bonttimo
Copy link

@kaisermann How would you implement this with a relative path? I can't get this to work when I have components in different folders. it only works on the path I set manually.

@kaisermann
Copy link
Member

@bonttimo Can you give me an example of what you want?

@kaisermann kaisermann self-assigned this May 27, 2020
@bonttimo
Copy link

@kaisermann I have a folder structure like this

src

  • components
    • Button.svelte
    • Header.svelte
  • scss
    • variables.scss
  • App.svelte
  • main.js

My rollup.config has currently:

preprocess: autoPreprocess(),
    css: (css) => {
        css.write("public/build/bundle.css");
    },
    preprocess: [
        scss({
            ´data: `@import '../scss/variables.scss';`,
        }),
        globalStyle(),
],

But this only works if the component is inside one subfolder: src/components/Button.svelte. App.svelte that is in the src root fails to locate the variables.scss file. For App.svelte the data path sould be: data: @import './scss/variables.scss'; And all the components need the path to be: data: @import '../scss/variables.scss';,.

is there a way to make the data path relative so that it doesn't matter what the folder structure is?

App.svelte style section currently fails. Error msg: (plugin svelte) Error: File to import not found or unreadable: ../scss/variables.scss.

<style lang="scss">
  #app--container {
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    flex-direction: column;
    padding: 3rem;
  }
  .app--content {
    display: flex;
    height: auto;
    align-items: center;
    flex-direction: column;
  }
</style>

Hopefully, this clarifies the problem.

@kaisermann
Copy link
Member

kaisermann commented May 27, 2020

I think you can pass an absolute path instead of using a relative one!

import { join } from 'path'

scss({
    data: `@import '${join(process.cwd(), 'src/scss/variables.scss')}';`,
})

(process.cwd() or anything that can point to your root)

(haven't tested it though)

@martonlederer
Copy link

martonlederer commented Jun 22, 2020

@kaisermann i tried doing this in a simple svelte project, but it doesn't seem to work. Can you check it for me please, I tried everything:
https://github.com/MartonDev/Svelte-starter-kit/tree/0f17b4138bc8dcf9b2474d405d856b7b0866accd
Or maybe check my issue: sveltejs/svelte-preprocess#188

@rafrasenberg
Copy link

rafrasenberg commented Sep 19, 2020

For anyone running into this problem and looking at the approach of @kaisermann

That is for V3, the current V4 config should look like this:

import sveltePreprocess from 'svelte-preprocess';

plugins: [
	svelte({
		preprocess: sveltePreprocess({
			scss: {
				prependData: `@import 'src/styles/global.scss';`
			}
		}),
...

EDIT:
Just used this in a different project and it didn't work. Then I found out it only works when you add this to App.svelte

<style global lang="scss">
</style>

Basically just adding a global and lang="scss" in the App.svelte file and everything gets compiled. Hope it helps!

@begonaalvarezd
Copy link

begonaalvarezd commented Oct 9, 2020

I am using something similar to what @rafrasenberg wrote, but since all my components use scss, what I am prepending with prependData is getting added to all my components, making my style massive. Is there a way to prevent this behavior and prepend only to App.svelte? I basically want to use this to add a theme stylesheet that comes from an external package.

preprocess: sveltePreprocess({
            postcss: true,
            scss: {
                prependData: `@import 'shared-modules/style/style.scss';`
            }
        })

@martonlederer
Copy link

martonlederer commented Oct 9, 2020

@begonaalvarezd if you only want to prepend to App.svelte, why don't you just use the import there?

Just add the global attr to the tag

@begonaalvarezd
Copy link

shared-modules is an external package, so it is inside node_modules and I didnt want to write the import like

@import 'node_modules/shared-modules/style/style.scss';

But importing it like that to App.svelte is working, I was just exploring.
Thanks!

@asdw741111
Copy link

first import svelte-preprocess-less

yarn add svelte-preprocess-less less --dev

then modify rollup.config.js

import { less } from 'svelte-preprocess-less';

// add preprocess to server and client
client: {
    plugins: [
	    svelte({
                    // add preprocess 
		    preprocess: {
			    style: less(),
		    },
	    }),
// server must add too

and you can use less such as

<style lang="less">
input {
  &:focus {
    outline: none;
  }
}
</style>

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests