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

Applying styles to DOM elements that aren't React components? #750

Closed
FoxxMD opened this issue Dec 29, 2015 · 10 comments
Closed

Applying styles to DOM elements that aren't React components? #750

FoxxMD opened this issue Dec 29, 2015 · 10 comments

Comments

@FoxxMD
Copy link
Contributor

FoxxMD commented Dec 29, 2015

I like the idea that I should have

const styles = require('./someStyle.scss');

return (<Panel className={styles.aClass}>...);

but in practice this isn't helpful when I want to apply styles to DOM elements that aren't react components.

In my scenario I am using react-bootstrap and want to style the DOM tag <div class="body-panel">, but only have access to a <Panel> react component where the className only applies to the parent DOM element of the body-panel div.

When I try to do something like this:

.aClass {
  .panel-body {
    padding: 15px 0px 0px 0px;
  }
}

No style is applied. How can I make this work? Thanks

@oyeanuj
Copy link

oyeanuj commented Dec 30, 2015

+1 to this. It is also problematic when using another component that comes with its styling.

I wonder, what other CSS setups are folks using with this boilerplate besides the default?

@FoxxMD FoxxMD changed the title Applying styles to a component without using className? Applying styles to DOM elements that aren't React components Dec 30, 2015
@FoxxMD FoxxMD changed the title Applying styles to DOM elements that aren't React components Applying styles to DOM elements that aren't React components? Dec 30, 2015
@FoxxMD
Copy link
Contributor Author

FoxxMD commented Dec 30, 2015

@oyeanuj I found a workaround, though it's ugly and definitely not a solution.

You can import any sass/css files in client.js using this syntax

import '!style!css!sass!components/aComponent/someStyles.scss';

And it will be included as an inline style in <Head>

EDIT: Another potential workaround is to set css inline dangerously so at least you can subscribe to a "keep styles in components" mentality -- still not a great solution though.

@Dattaya
Copy link

Dattaya commented Dec 30, 2015

@FoxxMD try this:

.aClass {
  :global(.panel-body) {
    padding: 15px 0px 0px 0px;
  }
}

I use regular css and it works .aClass :global(.panel-body) { but I don't know about scss.

@OkuraSt
Copy link

OkuraSt commented Jan 4, 2016

I had this problem too, with styling for a datetime component, I've used this workaround:

:global {
  @import "../../theme/react-datetime.scss";
}

Is this recommended?

@FoxxMD
Copy link
Contributor Author

FoxxMD commented Jan 6, 2016

@Dattaya @OkuraSt that works but can't be a true solution. I've also run into problems building for production because of this. (the whole problem)

@Dattaya
Copy link

Dattaya commented Jan 7, 2016

If we're talking about react-bootstrap, usually when you add className, react-bootstrap reapplies it on the element you would expect. Only in rare cases I need to use :global in my style sheets which IMO is not such a big deal. If I want my class to have higher specificity that of provided by react-bootstrap(that sits next to it), I double the specificity by duplicating my class name: .deleteButton.deleteButton {.

I've also run into problems building for production because of this. (the whole problem)

Could you explain what problems exactly you have run into?

@FoxxMD
Copy link
Contributor Author

FoxxMD commented Jan 7, 2016

If we're talking about react-bootstrap, usually when you add className, react-bootstrap reapplies it on the element you would expect.

In my case I didn't see that occurring. As I said in my OP the element <div class="body-panel"> created by <Panel> isn't accessible from react, and using className on <Panel> only applied that class to the parent dom element of <Panel>.

Only in rare cases I need to use :global in my style sheets

I feel like this should be the case, but when dealing with packages/components that aren't highly polished being able to style dom elements that don't map 1:1 with a react component is essential functionality, at least for me. Plus I'd prefer being able to apply inline styles without having to be required to attach specific selectors to specific react components -- it's just seems like a very limited method to force users of this boilerplate to use. Am I missing some key functionality here or this really the way it works?

Could you explain what problems exactly you have run into?

As mentioned above, in order to get inline styles, I had to require my files directly inside client.js so they looked like this:

import '!style!css!sass!components/aComponent/someStyles.scss';
import '!style!css!react-select/dist/react-select.css';

This works fine for development, but when trying to build production I kept running into the error

return /msie [6-9]\b/.test(window.navigator.userAgent.toLowerCase());
                                       ^
ReferenceError: window is not defined

Finally figured out I needed to use ExtractTextPlugin on the css test in the prod webpack loaders, as well as specify the relative path to the files (regardless of whether they were in node_modules or in my app).

However after that I discovered that, though ExtractTextPlugin was pulling css and transformed css (from my scss files) correctly, it (or something in webpack) was also obfuscating the selectors for any scss files but was not using those new selectors in the actual dom.
So I ended up with a css file with selectors like f5vo2_sz:{margin: auto 0} while in the dom the class name remained filterSelector.

So I had to end up converting all my scss files to css in order to use them in production.

My client.js file now looks like this:

require('./components/Meta/MetaFilter.css');
require('./containers/App/App.css');
require('../node_modules/react-bootstrap-table/css/react-bootstrap-table.min.css');
require('../node_modules/react-select/dist/react-select.css');
require('../node_modules/ladda/dist/ladda-themeless.min.css');

const client = new ApiClient();
...

and prod.config.js

loaders: [
      { test: /\.js$/, exclude: /node_modules/, loaders: [strip.loader('debug'), 'babel']},
      { test: /\.css$/, loader: ExtractTextPlugin.extract('style-loader', 'css-loader')},
      { test: /\.json$/, loader: 'json-loader' },
      { test: /\.less$/, loader: ExtractTextPlugin.extract('style', 'css?modules&importLoaders=2&sourceMap!autoprefixer?browsers=last 2 version!less?outputStyle=expanded&sourceMap=true&sourceMapContents=true') },
      { test: /\.scss$/, loader: ExtractTextPlugin.extract('style', 'css?modules&importLoaders=2&sourceMap!autoprefixer?browsers=last 2 version!sass?outputStyle=expanded&sourceMap=true&sourceMapContents=true') },
      { test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, loader: "url?limit=10000&mimetype=application/font-woff" },
      { test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, loader: "url?limit=10000&mimetype=application/font-woff" },
      { test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, loader: "url?limit=10000&mimetype=application/octet-stream" },
      { test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, loader: "file" },
      { test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, loader: "url?limit=10000&mimetype=image/svg+xml" },
      { test: webpackIsomorphicToolsPlugin.regular_expression('images'), loader: 'url-loader?limit=10240' }
    ]

I really hope that either a) I'm missing something critical pertaining to how requiring scss in react components is supposed to work or b) there is a less convoluted way to go about applying inline styles and requiring third-party css.

Sorry for the wall of text! This was what I spent most of my day yesterday trying to figure out...

@Dattaya
Copy link

Dattaya commented Jan 8, 2016

I haven't encountered these problems, yet. There are few things I have in mind you could try. Firstly, I think webpack-isomorphic-tools doesn't work that well with imports, so I always use require. Also, for some reason, even require doesn't work for me when it's outside of the render method of a component. Try to load your global styles inside of the render of Html helper component or App component like so:

  render() {
    require('medium-editor/dist/css/medium-editor.css');
    require('medium-editor/dist/css/themes/bootstrap.css');

This code is from my Editable component and I can see the styles are extracted properly in the prod mode. Also check you config files, this line https://github.com/erikras/react-redux-universal-hot-example/blob/master/webpack/webpack-isomorphic-tools.js#L65 should be extensions: ['css', 'less', 'scss'], In dev.config you most likely have css loader already: { test: /\.css$/, loader: 'style!css?sourceMap'} and your prod.config seems to be correct.

@FoxxMD
Copy link
Contributor Author

FoxxMD commented Jan 8, 2016

@Dattaya that did the trick! 👍 I guess this was one of those "down the rabbit hole" problems where it hadn't even occurred to me to try requiring and not assigning to a variable.

I also solved the obfuscated selectors problem by removing modules from the scss loader in webpack.

Thanks for your help!

@OkuraSt @oyeanuj You'll find the above advice very useful I hope :)

@gaving
Copy link

gaving commented Jun 18, 2016

@Dattaya Thanks for this 👍

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

No branches or pull requests

5 participants