dvdzkwsk / react-redux-starter-kit

Get started with React, Redux, and React-Router.
MIT License
10.29k stars 2.2k forks source link

Use bootstrap-sass instead of css #474

Closed fabiodcorreia closed 8 years ago

fabiodcorreia commented 8 years ago

Hi,

I'm trying to use bootstrap-sass instead of the CDN bootstrap css but I'm having some problems. Here are the steps that I did.

This is probably something easy but it's the first time that I use webpack and I'm probably missing something.

On _base.scss added the following lines

@import "node_modules/bootstrap-sass/assets/stylesheets/bootstrap-sprockets";
@import "node_modules/bootstrap-sass/assets/stylesheets/bootstrap";

On core.scss remove the following line since bootstrap-sass already provides normalize and keep the rest as it was.

@import 'vendor/normalize';

When I run this I get the following error

Module build failed: CssSyntaxError: postcss-modules-local-by-default:
(....)/project_name/core.scss:1768:30: Missing whitespace before :global
  background-color: #e6e6e6;
  border-color: #8c8c8c; }

If I remove the :global {} and keep just the code inside the style doesn't get applyed.

Best Regards, Fábio Correia

seripap commented 8 years ago

You have to format those files to be linter-friendly. or just disable the css linter.

fabiodcorreia commented 8 years ago

Can I disable the linter just for these files? Format bootstrap files looks a kind of overwhelming.

dvic commented 8 years ago

Any update on this?

dvic commented 8 years ago

I don't have this issue when I place the following code in CoreLayout.js (and bootstrap is being applied correctly):

import 'bootstrap-sass/assets/stylesheets/_bootstrap-sprockets.scss'
import 'bootstrap-sass/assets/stylesheets/_bootstrap.scss'
fabiodcorreia commented 8 years ago

So the problem is just when we add the bootstrap to _base.scss. But shouldn't we add the 3party scss to the _base.scss?

dvdzkwsk commented 8 years ago

@fabiodcorreia _base.scss is meant for variables/mixins/etc. only, not any style rules. If I have to time today I will switch the main repo to use bootstrap from an npm module.

dvic commented 8 years ago

@davezuko that would be awesome! I also got it working with the following project (this also supports bootstrap 4): https://github.com/shakacode/bootstrap-loader

However, I still need to manually load jquery to make things work, I'm pretty new to webpack and I'm not sure what the best practices are w.r.t. using bootstrap & jquery with react.

travisfisher commented 8 years ago

Running into this same error. @davezuko, is there a workaround while you work on getting the npm bootstrap module implemented?

aimuzov commented 8 years ago

@davezuko @tgfisher I got same error too.

My solution:

Go to node_modules/bootstrap/scss/mixins/_buttons.scss (30 and 70 lines):

&:active,
  &.active,
  .open > &.dropdown-toggle { // replace & -> .btn (.open > .btn.dropdown-toggle)
    color: $color;
    background-color: $active-background;
    // ....
}
// ...
  &:focus,
  &.focus,
  &:active,
  &.active,
  .open > &.dropdown-toggle {  { // replace & -> .btn (.open > .btn.dropdown-toggle)
    color: #fff;
    background-color: $color;
        border-color: $color;
  }

I duplicated _mixins.scss and mixins/_buttons.scss files in styles/vendor/bootstrap. It's works. I don't know why postcss can't compile this. I use bootstrap v4 (but this solution works fine too for bootstrap v3).

PS: Sry for my bad english.

travisfisher commented 8 years ago

@immuzov Thank you. I may give that a shot for the short-term, but it would be nice to be able to use bootstrap-sass (or boostrap 4) with the starter kit without having to monkey with the bootstrap npm packages.

travisfisher commented 8 years ago

@immuzov that did work. Thanks for the short-term fix. How did you discover that it was choking on that part in particular?

danwad commented 8 years ago

@tgfisher - I was interested in how @immuzov found that out too.

After a bit of digging around, I too managed to narrow it down to the same lines.

The exception is thrown by postcss-modules-local-by-default.

The postcss-modules-local-by-default plugin is called by css-loader and at this point sass-loader has already finished compiling bootstrap.scss similar to this simplified example:

@mixin button-variant() {
  .open > &.dropdown-toggle {
    color: blue;
  }
}

:global {
  .btn {
    @include mixin();
  }
}

Compiles to:

.open > :global .btn.dropdown-toggle {
  color: blue;
}

With CSS modules being local by default, we really want :global .open > .btn.dropdown-toggle.

Bootstrap doesn't currently like being wrapped in a :global {} because it uses the SASS parent selector (&) which breaks out of the :global {}, and worse because it does it after a >.

The actual error message Missing whitespace before :global is here because :global<space>selector cannot follow the immediate child selector (>). What would it mean? An immediate but not neccessarily immediate descendant of localised .open called .btn.dropdown-toggle? The postcss-modules-local-by-default plugin could understand it if there was a space (descendent selector) rather than > (immediate selector) preceding :global.

Who is at fault? Bootstrap, SASS, postcss-modules-local-by-default, or us for trying to wrap Bootstrap in :global {}?

Is there a neat way we can include bootstrap.scss globally without using :global {} and not sending it through css-loader's local-by-default plugin?

EDIT: I ended up adding a file src/styles/global/global.scss which includes bootstrap and updating my includes/excludes in webpack.config.js. All files outside src/ except src/styles/global/ are now global by default.

import path from 'path'

...

webpackConfig.module.loaders.push({
  test: /\.scss$/,
  include: pathStr => pathStr.startsWith(paths.client()),
  exclude: pathStr => pathStr.startsWith(paths.client(path.normalize('styles/global'))),
  loaders: [
    'style',
    cssLoader,
    'postcss',
    'sass?sourceMap'
  ]
})

webpackConfig.module.loaders.push({
  test: /\.css$/,
  include: pathStr => pathStr.startsWith(paths.client()),
  exclude: pathStr => pathStr.startsWith(paths.client(path.normalize('styles/global'))),
  loaders: [
    'style',
    cssLoader,
    'postcss'
  ]
})

// All other CSS/SCSS is global by default
webpackConfig.module.loaders.push({
  test: /\.scss$/,
  exclude: pathStr => pathStr.startsWith(paths.client()) && !pathStr.startsWith(paths.client(path.normalize('styles/global'))),
  loaders: [
    'style',
    'css?sourceMap&-minimize',
    'postcss',
    'sass?sourceMap'
  ]
})

webpackConfig.module.loaders.push({
  test: /\.css$/,
  exclude: pathStr => pathStr.startsWith(paths.client()) && !pathStr.startsWith(paths.client(path.normalize('styles/global'))),
  loaders: [
    'style',
    'css?sourceMap&-minimize',
    'postcss'
  ]
})
Grobim commented 8 years ago

@danwad do you confirm you had to add an import line in CoreLayout.js too ? Couldn't make your solution work w/o it (+ thanks for this workaround, cleaner solution I could find)

thangngoc89 commented 8 years ago

Do you have a better solution for this ? @davezuko

I had a solution that work for me for a long time. Comment it somewhere within this project.

If I had time, I'll send a PR for global styles.

Basicly, it's it like this :

https://github.com/thangngoc89/blog/blob/6d6d4127781ce3675b8602ca3a01ab14b81f7fd4/web_modules/styles/global.styles#L1-L2

and this

https://github.com/thangngoc89/blog/blob/5b4ddd51bde9e45e07bc49a1317f6edb9810e8e6/scripts/webpack.config.babel.js#L39-L42

danwad commented 8 years ago

@Grobim ah yes - also need to do import 'styles/global/global.scss' in CoreLayout :)

travisfisher commented 8 years ago

@danwad Thanks for the detailed breakdown and cleaner solution. I haven't put it in-place yet, but I like the approach better.

@davezuko it would be really nice if we could build bootstrap from SASS without the workarounds. Do you have any plans to implement bootstrap-loader or something else to make it seamless?

Thanks for your work on this project. It is great.

Grobim commented 8 years ago

@danwad Hi, is there a problem with the font import with your solution ? Or did I make a mistake someewhere ? I couldn't make it work and, being new with webpack, I don't really know where/how to fix :'(

danwad commented 8 years ago

@Grobim I'm using the bootstrap 4 alpha which doesn't seem to come with any additional fonts so didn't have any issues.

Are you using bootstrap-sass? Does this work:

$icon-font-path: '~bootstrap-sass/assets/fonts/bootstrap/';
@import '~bootstrap-sass/assets/stylesheets/_bootstrap.scss';
travisfisher commented 8 years ago

@danwad Can you post a gist of the files you changed to get it working? I couldn't get it to work with bootstrap 4. The webpack config you posted above was stating that "path" is undefined.

danwad commented 8 years ago

@tgfisher Sorry for missing that out.

path is node's path.

Stick import path from 'path' at the top and you should be good to go.

dvdzkwsk commented 8 years ago

Moving this to recipes, will flesh it out when time permits.

https://github.com/davezuko/react-redux-starter-kit/wiki/Recipes

^ not yet documented in README due to it being incomplete.

dkushner commented 8 years ago

What was the general solution to this issue? I am encountering the same exact problem when trying to import Bulma from its node package.

sunjay commented 7 years ago

For future people who get to this issue: The issue can also be caused if you accidentally nest :global

For example if you do this:

// styles.scss
:global {
  // make everything from something global
  @import 'something';
}

// _something.scss
:global {
   // ...
}

You will get the error:

Module build failed: Missing whitespace before :global

windsome commented 7 years ago

@danwad

$icon-font-path: '~bootstrap-sass/assets/fonts/bootstrap/'; @import '~bootstrap-sass/assets/stylesheets/_bootstrap.scss';

this doesn't work with font.

btw, bootstrap4 hasn't contain glyph fonts. how to solve it ?

windsome commented 7 years ago

I test all the methods about "glyph icons" mentioned above but all failed. the font-file have not generated. Method1:

global.scss:
$bootstrap-sass-asset-helper: true;
@import '~bootstrap-sass/assets/stylesheets/_bootstrap.scss';

result:
@font-face{font-family:Glyphicons Halflings;src:url(twbs-font-path("bootstrap/glyphicons-halflings-regular.eot"));src:url(twbs-font-path("bootstrap/glyphicons-halflings-regular.eot?#iefix")) format("embedded-opentype"),url(twbs-font-path("bootstrap/glyphicons-halflings-regular.woff2")) format("woff2"),url(twbs-font-path("bootstrap/glyphicons-halflings-regular.woff")) format("woff"),url(twbs-font-path("bootstrap/glyphicons-halflings-regular.ttf")) format("truetype"),url(twbs-font-path("bootstrap/glyphicons-halflings-regular.svg#glyphicons_halflingsregular")) format("svg")}

method2:

global.scss:
$icon-font-path: "~bootstrap-sass/assets/fonts/bootstrap/";
@import "~bootstrap-sass/assets/stylesheets/_bootstrap-sprockets.scss";
@import "~bootstrap-sass/assets/stylesheets/_bootstrap.scss";

result:
@font-face{font-family:Glyphicons Halflings;src:url(font-path("~bootstrap-sass/assets/fonts/bootstrap/glyphicons-halflings-regular.eot"));src:url(font-path("~bootstrap-sass/assets/fonts/bootstrap/glyphicons-halflings-regular.eot?#iefix")) format("embedded-opentype"),url(font-path("~bootstrap-sass/assets/fonts/bootstrap/glyphicons-halflings-regular.woff2")) format("woff2"),url(font-path("~bootstrap-sass/assets/fonts/bootstrap/glyphicons-halflings-regular.woff")) format("woff"),url(font-path("~bootstrap-sass/assets/fonts/bootstrap/glyphicons-halflings-regular.ttf")) format("truetype"),url(font-path("~bootstrap-sass/assets/fonts/bootstrap/glyphicons-halflings-regular.svg#glyphicons_halflingsregular")) format("svg")}

Method3:

global.scss:
@import "~bootstrap-sass/assets/stylesheets/_bootstrap-sprockets.scss";
@import "~bootstrap-sass/assets/stylesheets/_bootstrap.scss";

result:
@font-face{font-family:Glyphicons Halflings;src:url(font-path("bootstrap/glyphicons-halflings-regular.eot"));src:url(font-path("bootstrap/glyphicons-halflings-regular.eot?#iefix")) format("embedded-opentype"),url(font-path("bootstrap/glyphicons-halflings-regular.woff2")) format("woff2"),url(font-path("bootstrap/glyphicons-halflings-regular.woff")) format("woff"),url(font-path("bootstrap/glyphicons-halflings-regular.ttf")) format("truetype"),url(font-path("bootstrap/glyphicons-halflings-regular.svg#glyphicons_halflingsregular")) format("svg")}

Look at the result, they are all wrapped with a function 'font-path()' or 'twbs-font-path()'. maybe we need implement font-path()? then, I got the final solution:

global.scss:
@function font-path($path) {
   @return $path;
}
$icon-font-path: "~bootstrap-sass/assets/fonts/bootstrap/";
@import "~bootstrap-sass/assets/stylesheets/_bootstrap-sprockets.scss";
@import "~bootstrap-sass/assets/stylesheets/_bootstrap.scss";

result:
@font-face{font-family:Glyphicons Halflings;src:url(/node_modules/bootstrap-sass/assets/fonts/bootstrap/glyp    hicons-halflings-regular.eot);src:url(/node_modules/bootstrap-sass/assets/fonts/bootstrap/glyphicons-halflings-regular.eot?#iefix) format("embedded-    opentype"),url(/node_modules/bootstrap-sass/assets/fonts/bootstrap/glyphicons-halflings-regular.woff2) format("woff2"),url(/node_modules/bootstrap-s    ass/assets/fonts/bootstrap/glyphicons-halflings-regular.woff) format("woff"),url(/node_modules/bootstrap-sass/assets/fonts/bootstrap/glyphicons-half    lings-regular.ttf) format("truetype"),url(/node_modules/bootstrap-sass/assets/fonts/bootstrap/glyphicons-halflings-regular.svg#glyphicons_halflingsr    egular) format("svg")}

and the font files generated!

josephdburdick commented 7 years ago

@windsome Bootstrap 4 doesn't come with glyph fonts. See: https://v4-alpha.getbootstrap.com/migration/#components

Gtosta96 commented 7 years ago

someone find a solution for this one?

josephdburdick commented 7 years ago

@Gtosta96 one solution is to use fontawesome.

eduardoinnorway commented 7 years ago

Same issue with bootstrap-less

guotie commented 7 years ago

same issue when import bulma use :global {}