Inline styles with React - thoughts?

Nope. In that example container is declared with ‘className’ so it will pull in global CSS.

For sure. A little while back I spent a good amount of time investigating the different options out there and was initially sold on inline styles after watching the video @abhiaiyer shared. It is really nice to have styles inside your component file and there are certainly some advantages to it. However, even Michael Chan (the guy in the video) has admitted on a number of podcasts that inline styles have some very glaring weaknesses. I personally prefer to use CSSModules for component specific styles because there aren’t really any drawbacks to doing so. You get the full power of CSS and any preprocessor you want to use while still being able to easily write component specific styles. Different strokes for different folks though.

As a side note, I have a number of friends here locally that have run into trouble with inline styles on big projects at work. Sure, it works great for little examples here and there, but it can also turn into a big mess. Duplicating styles everywhere is always a bad idea and inline styles naturally lead to that.

1 Like

Great talk by Michael Chan. This part right here (26:18 in) is exactly how I feel sometimes, and why inline styles can make absolute sense in certain scenarios.

@ryanswapp - I tried using CSS Modules, but failed miserably. My component module is below:

import React from 'react';
import CSSModules from 'react-css-modules';
import styles from './MyObject';

class MyObject extends React.Component {
  render() {
    return <div>
      <button>Blammo!</button>
    </div>
  }
}

export default CSSModules(MyObject, styles);

I get this console error:

Uncaught Error: Cannot find module ‘./MyObject’

And there’s definitely a MyObject.css in the same folder as MyObject.jsx. I can’t figure out what’s going wrong.

He’s also resolving the .css as part of his main webpack config:
https://github.com/ryanswapp/react-starter-template/blob/master/webpack.config.js (line 117)
This is how webpack knows how to import files without extensions, by looking at what it’s allowed to resolve.

@mordrax is exactly right. You can setup webpack to resolve extensions so in my example webpack is looking at ‘./style’ and resolving it to ‘./style.css’. Are you using Meteor 1.3? Or are you using Webpack?

One good thing I can think of about using inline styles is that … your component won’t rely on external styles.
So yes, it’s a little more verbose, but then there’s no risk to break your components display when changing CSS.

We’re not using inline styles, and CSS breaks happens quite frequently, yet we’re only 5 devs.
Personally, I would definitely go inline, then all components will be completely stand-alone.

But then again, our CSS code base is a mess, so there’s that.

2 Likes

I’m using Meteor 1.3.

Ok… You likely will run into trouble with the import style from './style' because you need webpack’s style and css loaders to pull css into javascript. I’ve never used them without webpack so I’m not sure how that would translate over to Meteor.

I could see the benefit of inline styles in a component if it’s a fully encapsulated self standing component that you could just drop into an app and it’s pre-styled etc… but for everything else I think style sheet is the way to go.

1 Like

I’d definitely go with CSS Modules if you can, but inline isn’t that bad. Just don’t do ugly namespacing + globals! The funny thing that people seem to forget is how powerful compression is. If you repeat a bunch of strings (like names of CSS props), your compression engine is going to far exceed the standard 40% seen with gzip. Better to fret over UX than bytes :smile:

1 Like

Javascript being more powerful does not warrant doing inline styling. In anycase inline styling creates more page weight thus making more for users to download. Secondly how do you style your component differently if inside a different parent? Thirdly you might be happy to duplicate all your styles in every customisation of a component but will you be the one to tweak all the styles of the whole site when you have to change all the margins by a few pixels? Fourthly how are you going to style media queries? Have you spent much time discovering what css does and doesnt work on htc, samsung, iphone 6 etc. Are you happy to put all that extra browser quirks css into every component? Have you spent anytime dealing with improving code maintenance and/or performance. Inline styling is only ever good for dynamic animation, and even then its just tidier to manage that in external css transitions by javascript changing a data-state only. [Data-state=‘on’]{ /* transition here*/}

No need to even worry about that stuff, when there are plenty of packages that handle this for you, such as Radium.

You could very easily just put any global styles in their own JS file, and then import them for use in several components. Easy fix.

I’d like to see someone do a test to see if this actually makes a difference. Even if you were to use CSS, that data still has to be downloaded by the browser. The only thing inline styles add weight to is the DOM.

1 Like

Has anyone tried React-JSS? https://github.com/jsstyles/react-jss
Style React through Javascript objects. It does what SCSS does, and also loads lazily.

I use it like so, for example, in my graphs…

import React, { Component, PropTypes } from 'react';
import useSheet from 'react-jss';

import jss, {} from 'jss';
import camelCase from 'jss-camel-case';
import vendorPrefixer from 'jss-vendor-prefixer';
import nested from 'jss-nested';

import d3 from 'd3';

// ----------------------------------------------------------------------- Component JSS Stylesheet

jss.use(vendorPrefixer());
jss.use(camelCase());
jss.use(nested());

const stylesheet = {
  circleGraph: {
    background: 'repeating-linear-gradient(135deg, #FFFFFF, #FFFFFF .25em, rgba(252, 163, 17, 0.0875) 0, rgba(252, 163, 17, 0.0875) .30em)',
    border: '1px dashed rgba(252, 163, 17, 0.141)',
    '& text': {
      pointerEvents: 'none',
    },
    '& text.parent': {
      fontSize: '11px',
      fill: '#000000',
      fillOpacity: '0.675',
      textDecoration: 'underline',
    },
    '& text.child': {
      fontSize: '9px',
      fill: '#000000',
      fillOpacity: '0.375',
    },
    '& circle': {
      fill: '#FCA311',
      stroke: '#000000',
      strokeWidth: '.25px',
      pointerEvents: 'all',
    },
    '& circle.parent': {
      fill: '#000000',
      fillOpacity: '.0875',
      stroke: '#000000',
      transition: 'fill 0.375s ease, fillOpacity 0.375s ease, stroke 0.375s ease',
    },
    '& circle.parent:hover': {
      fill: '#FCA311',
      fillOpacity: '.234',
      stroke: '#FCA311',
    },
    '& circle.child': {
      pointerEvents: 'none',
    },
  },
};

// ----------------------------------------------------------------------- D3 Graph

/**
* @summary graph maker
* @param {object} el, the element to make the graph in
* @param {object} data, the data object
* @return {null} does not return anything
*/
function makeGraph(el, data, dimensions) {
  const w = dimensions.width;
  const h = dimensions.height;
  const r = (dimensions.height - 4); // The top and bottom of the circle was being shredded without this
  const x = d3.scale.linear().range([0, r]);
  const y = d3.scale.linear().range([0, r]);
  let node = {};
  let root = {};

  /**
  * @summary zooms into a packed circle
  * @param {object} d, data
  * @param {object} i, point
  * @return {null} does not return anything
  */
  function zoom(d) {
    
    let k = r / d.r / 2;
    
    x.domain([d.x - d.r, d.x + d.r]);
    y.domain([d.y - d.r, d.y + d.r]);

    const t = vis.transition()
      .duration(d3.event.altKey ? 7500 : 750);

    t.selectAll('circle')
      .attr('cx', function (d) { return x(d.x); })
      .attr('cy', function (d) { return y(d.y); })
      .attr('r', function (d) { return k * d.r; });

    t.selectAll('text')
      .attr('x', function (d) { return x(d.x); })
      .attr('y', function (d) { return y(d.y); })
      .style('opacity', function (d) { return d.children ? 1 : k * d.r > 20 ? 1 : 0; });

    node = d;
    d3.event.stopPropagation();
  }

  const pack = d3.layout
    .pack()
    .size([r, r])
    .value(function (d) { return d.size; });

  const vis = d3.select(el)
    .insert('svg:svg', 'h2')
    .attr('width', w)
    .attr('height', h)
    .append('svg:g')
    .attr('transform', `translate(${(w - r) / 2},${(h - r) / 2})`);

  root = data;
  node = root;

  const nodes = pack.nodes(root);

  vis.selectAll('circle')
    .data(nodes)
    .enter()
    .append('svg:circle')
    .attr('class', function (d) { return d.children ? 'parent' : 'child'; })
    .attr('cx', function (d) { return d.x; })
    .attr('cy', function (d) { return d.y; })
    .attr('r', function (d) { return d.r; })
    .on('click', function (d) { return zoom(node === d ? root : d); });

  vis.selectAll('text')
    .data(nodes)
    .enter()
    .append('svg:text')
    .attr('class', function (d) { return d.children ? 'parent' : 'child'; })
    .attr('x', function (d) { return d.x; })
    .attr('y', function (d) { return d.y; })
    .attr('dy', '.35em')
    .attr('text-anchor', 'middle')
    .attr('opacity', function (d) { return d.children ? 1 : d.r > 20 ? 1 : 0; })
    .text(function (d) { return d.name; });

  d3.select(el).on('click', function () { zoom(root); });
}

// -----------------------------------------------------------------------

/**
   * @summary CirclePackingGraph D3 graph component
   * @returns {object} null
   */
class CirclePackingGraph extends Component {

  /**
   * @summary componentDidMount function
   * @returns {object} null
   */
  componentDidMount() { // http://nicolashery.com/integrating-d3js-visualizations-in-a-react-app/

    const parentWidth = this.refs.circleGraph.parentNode.getBoundingClientRect().width;
    const dimensions = {
      width: Math.round(parentWidth - 30), // Compensate for Bootstrap 15px padding on each side
      height: Math.round(parentWidth * 0.67),
    };

    makeGraph(this.refs.circleGraph, this.props.data, dimensions);
  }

  /**
   * @summary render function
   * @returns {object} Returns DOM Object
   */
  render() {
    
    const classes = this.props.sheet.classes;

    return (
      <div className={classes.circleGraph} ref="circleGraph"></div>
    );
  }
}

CirclePackingGraph.propTypes = {
  data: PropTypes.object,
  sheet: PropTypes.object,
  media: PropTypes.func,
};

export default useSheet(CirclePackingGraph, stylesheet);

Mefinds React-JSS very handy.

(PS: React-JSS is created by the creator of Redux)

1 Like

You can also have a look at JSS https://github.com/jsstyles/jss.

export default {
  button: {
    fontSize: 12,
    '&:hover': {
      background: 'blue'
    }
  },
  ctaButton: {
    extend: 'button',
    '&:hover': {
      background: 'red'
    }
  },
  '@media (min-width: 1024px)': {
    button: {
      minWidth: 200
    }
  }
}

compiles to…

.button--jss-0-0 {
  font-size: 12px;
}
.button--jss-0-0:hover {
  background: blue;
}
.ctaButton--jss-0-2 {
  font-size: 12px;
}
.ctaButton--jss-0-2:hover {
  background: red;
}
@media (min-width: 1024px) {
  .button--jss-0-0 {
    min-width: 200px;
  }
}
1 Like

Im sorry but why would i load plugins to do what i can do manually and at speed? In a format familiar to every good css developer that has been around over 10 years? Why would i want to make it more difficult to resource work out in version control by combining css into the js where most new js developers cant style and most good css developers would struggle? Regarding page load performance that depends on how big your app gets, but essentially inline styling is multiplying 1character * 8 bytes ( approx 300 bytes per block) * reuse of your component. Who knows, maybe thats 10 input rows / 10 buttons / 3 autocompletes per relative pg ie 20 x 300 = 6kb added weight per pg. Could dbl that maybe triple if you are outputting a product lister component. Granted that may not seem like much, and its probably a small flaw, but id argue more it doesnt make styling easier when your inspecting from the dom and seeing everything in context. The visible inline styles steal all the visual space in the inspector making it take nearly three times longer to style a page.

Just because the format has been around for over 10 years doesn’t mean it’s the best approach today, in the context of this particular JS library. If the React team officially recommends this way of doing things, it’s probably for a good reason.

To each his own. There are plenty of ways to tackle CSS in React, as we’ve seen in this thread. :slight_smile:

@dbx834 Very cool! That wasn’t my quote, BTW, you meant to quote @inspiraller instead :slight_smile:

So React JSS actually creates classes? I’ll have to check that out. I think eventually, once people figure out what works best in React, it’ll come down to either JS styles, CSS Modules, or something like React JSS.

1 Like

In this current technology explosion, one needs to retain some confidence. Css works. Sass or less works better. Inline styling is just slower and adds tech debt. Facebook React may endorse it. I dont and many others who css for a living dont. If you spend most of your time doing just the css you will discover you work faster and have better code management.

So, FB created React because they were fed up of HTML and it’s constraints. I come from a science background and TBH, CSS is lightyears behind LaTeX. Even the science nerds have much better typography tools then so called ‘web designers’. The following document is created in LaTeX,

CSS is still figuring out how to do paddings and margins. I think things should change wildly.