14
votes

I'm currently building out a large React app. Css, has never been my strong point. But now CSS has sass / cssNext / inline styles with React. I've been using BEM with sass, but as my other applications have grown huge even that starts to break down. Especially when you had on the ability to "re-skin" or "theme" the pages outside of the primary color schemes etc..

so -- can someone point me to a proven way to create css with react that can scale very well, and allows for custome theme'ing when people want to borrow my components. For instance,

<MyComponent />
// this component has its styles but lets say Joe Schmoe wants be import 
// it? But, Joe wants to overlay his custom styles?
// Is there a new paradigm that allows for an overlay or reskin within the component?

Or even the idea of the whole application being skinnable some time down the line. I know this is sorta a very base question, but whenever I build out projects my pain points also seem to be the CSS - so I want to know what really works.

2

2 Answers

22
votes

This was, until recently, not a solved problem in the React world.

I'm one of the maintainers of ElementalUI, a React component library, and we've been trying out all the different ways of styling for the past 6-12 months. (!) You name it, we've tried it. (I talked about my experiences with some of the most popular libraries during my ReactNL keynote and where they break down)

The issue is that none of the current styling libraries have built-in support for theming at all. You can do it with most of them in a very hacky, non user-friendly way, but that's not what you want when you distribute a component, right?


That's why we built styled-components. styled-components has a bunch of interesting properties, and one of them is that theming is directly built into the library, making it the perfect choice for building distributable components!

Here is the short explanation, though I encourage you to go through our documentation which explains everything!

This is what the basic usage of styled-components looks like:

import React from 'react';
import styled from 'styled-components';

// Create a <Wrapper> react component that renders a <section> with
// some padding and a papayawhip background
const Wrapper = styled.section`
  padding: 4em;
  background: papayawhip;
`;

This variable, Wrapper, is now React components you can render:

// Use them like any other React component – except they're styled!
<Wrapper>
  <Title>Hello World, this is my first styled component!</Title>
</Wrapper>

What this looks like rendered

(if you click on the image you'll get a live playground)

When you pass an interpolated function into the tagged template literal, we pass you the properties passed to the component. This means you can adapt to the props:

import styled from 'styled-components';

const Button = styled.button`
  /* Adapt the colors based on primary prop */
  background: ${props => props.primary ? 'palevioletred' : 'white'};
  color: ${props => props.primary ? 'white' : 'palevioletred'};

  font-size: 1em;
  margin: 1em;
  padding: 0.25em 1em;
  border: 2px solid palevioletred;
  border-radius: 3px;
`;

Here, we've created a Button component that you can make primary like any other React component:

<Button>Normal</Button>
<Button primary>Primary</Button>

A normal button and a bigger primary button

Now comes the theming aspect. We export a component called ThemeProvider that you can pass a theme to and wrap your app (or parts of the app) in:

import { ThemeProvider } from 'styled-components';

const theme = {
  main: 'mediumseagreen',
};

ReactDOM.render(
  <ThemeProvider theme={theme}>
    <MyApp />
  </ThemeProvider>,
  myElem
);

Now any styled component within that ThemeProvider, no matter how deep thanks to context, will get this theme injected into the props.

This is what a themable Button would look like:

import styled from 'styled-components';

const Button = styled.button`
  /* Color the background and border with theme.main */
  background: ${props => props.theme.main};
  border: 2px solid ${props => props.theme.main};

  /* …more styles here… */
`;

Now your Button will take the theme it gets passed and change it's styling based on that! (you can also provide defaults via defaultProps)

That's the gist of styled-components and how it helps to build distributable components!

We have a currently WIP doc about writing third-party component libraries which I'd encourage you to check out, and of course the normal documentation is a good read too. We've tried to cover all the bases, so if you see anything you dislike or that you think is missing please immediately let us know and we'll discuss!

If you have any other questions about styled-components feel free to reply here or reach out on Twitter. (@mxstbr)

1
votes

I don't know what is the best way. I think it's more like a personal preference. I will just share the tools that I'm using. What I'm using is css-modules with postcss.

css-modules gives you the power to create a local-scoped identifier with a global unique name for each of your CSS classes and also enable a modular and reusable CSS! You can also use postcss-modules-values to create a global settings file which contains all the settings variables. So that you can change the theme for your website.

Here's how I structure the components. Each of my component will have one css file which is very easy to maintain or make changes.

enter image description here

Here's the code for the Button component.

function Button(props) {
  const className = props.className ? `${styles.button} ${props.className}` : styles.button;

  return (
    <button disabled={props.disabled} className={`${className}`} type="button" onClick={props.onClick}>
      {Children.toArray(props.children)}
    </button>
  );
}

Notice that I have a className for the component which allows other component to pass in the class for the button component. So that when someone borrow your component they can extend or override your button styles.

If you need to create a customisable website, I will also recommend to use Less.js which provides the live preview of the customised website.