Skip to content

Instantly share code, notes, and snippets.

@mxstbr

mxstbr/story.md Secret

Last active November 16, 2022 05:27
Show Gist options
  • Star 9 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mxstbr/8aee996a7442477248968de04d0a2fbf to your computer and use it in GitHub Desktop.
Save mxstbr/8aee996a7442477248968de04d0a2fbf to your computer and use it in GitHub Desktop.
The styled-components story

How the styled-components API came to be

NOTE: This gist was quickly whipped up, isn't for reference and is how I (@mxstbr) remember what happened. Lots of this is likely inaccurate and wrong. Please don't share it or refer to it as the canonical source of the styled-components story.

Thinkmill (where I was working at the time) was trying to build ElementalUI (our shared open source component library) so that users wouldn't need to have a specific build setup. (at the time we were using less, so folks had to be using webpack + css-loader + less-loader) Wanted to use CSS in JS, but no library had theming support and there was no good solution for shared, reusable components.

Seprately, Glen realised that putting styles into JavaScript might not be the worst idea. We met over a Whisky with @twalve while I was in Sydney, next day @JedWatson invited Glen to the office and we all chatted more about the whole CSS thing. This ended up with us next to each other with our editors open, just typing out possible APIs to figure out what felt nice.

First breakthrough

We both wanted styles to live in JavaScript, but neither of us was a fan of CSS as objects. Glen's first experiment was a function syntax where each possible CSS rule was a function, e.g. margin(1). (I still don't quite understand where he went there)

As we just set next to each other thinking about APIs we got talking about style components like <Grid />, <Row /> etc and how nice they'd been in our apps. This led to our first breakthrough discovery:

First breakthrough: What if we move styling to a component level by default?

This is where we came up with the function.tag syntax for creating styled components. That was already great, but we weren't sure how we were going to write the CSS bits so the API mostly looked like this:

const Comp = elem('div')(/* styles here somehow */)

Sidenote: The name styled-components came up way way later, so for a long time we just used elem('div').

Second breakthrough

As we talked more about how people were going to write CSS we realised we liked none of the options we'd come up with so far.

Second breakthrough: What if you could write actual CSS in your JavaScript?

Our first API, based on the other breakthrough, looked something like this:

const Comp = elem('div')(`
  color: blue;
`)

Note: That is not a tagged template literal!

Overnight Glen whipped up a super simple prototype, but he used this super weird syntax I'd never seen before to make it easier to type:

const Comp = elem('div')`
  color: blue;
`

Third breakthrough

As we realised how nice this API was beginning to shape up with the tagged template literals we realised that we needed a way to change styling based on props.

Glen, reading through the MDN docs I think, figured that contrary to normal template literals functions interpolated into tagged template literals get persisted and can be called from the called method.

Third breakthrough: Use functions for conditional styling and pass in the props dynamically

This led us to the following API:

const Comp = elem('div')`
  color: ${props => props.primary ? 'blue' : 'red'};
`

Fourth breakthrough

I think this was the point when we whipped up the first seriously usable package to make this happen. We both used it to rework a small app and figured out how nice it was to use, so we started figuring out the finer decisions.

The first thing we tackled IIRC was theming. This was big for both of our use cases, so we wanted to make sure we got it right. We knew that we wanted to have dynamic themes. Now that all of the styling was bound to a component we quickly got the fourth breakthrough:

Fourth breakthrough: Use React context for theming

Modeled off of react-redux et al we went with a Provider-style API for passing the theme down the tree:

<ThemeProvider theme={someTheme}></ThemeProvider>

Getting the theme into context was the easy part, but how were we going to inject it into the components? There were three possibilities:

const Comp = elem('div')`
  // First idea: Two arguments
  ${(props, theme) => props.primary ? theme.main : theme.background};

  // Second idea: One argument destructured
  ${({ props, theme }) => props.primary ? theme.main : theme.background}

  // Third idea: Injected as props.theme
  ${props => props.primary ? props.theme.main : props.theme.background}
`

This might've been the decision we spent the most time on.

The first idea was nice, but if you just wanted to use the theme you had to assign an empty variable which is annoying and unnecessary.

The second idea improved upon the first idea by faking "named arguments" in JavaScript with ES6 destructuring to allow you to pick just the variables you need. That was our preferred API for a while, until we realised that we had no idea what to do in ES5. What do you even call that argument to the function if you're not destructuring?! It also added unnecessary cognitive overhead for folks who hadn't seen destructuring before.

The third idea was the one we eventually settled on. It still allowed you to just get the theme (({ theme }) =>, props =>), and on top of that the interplay with Reacts default component APIs were nice:

const Comp = elem('div')`
  color: ${({ theme }) => theme.primary};
`

// Want to set a default theme on a component level? Just use defaultProps 🎉
Comp.defaultProps = {
  theme: { primary: '#000' },
}

// Want to override the theme in this one specific case of the component? Just pass in the `theme` prop!
<Comp theme={{ primary: 'blue' }} />

There's a bunch of other API decisions we made along the way, like the name, styled(Comp) to override any components' styling, supporting Sass-style nesting in the CSS, etc etc, that I might write about sometime else.

@ryasmi
Copy link

ryasmi commented Mar 29, 2020

@mxstbr, I'm grateful that you took some time to type this up from your point of view. It's particularly interesting to read through the breakthroughs in order and I remember discovering the third one via Styled Components. I don't think I would have discovered that functionality without styled components and the decision you made in the second breakthrough, which leads me to my question.

I'm curious to understand why you and Glen weren't fans of "CSS as objects", do you think that is still true today?

I think personally, I prefer CSS as objects because I think it must be quicker to parse, but also because you can get type checking and autocompletion without installing new extensions. Our company uses styled-components almost exclusively and we've been talking about whether to use the object API over the template API, so I'm wondering if there are some benefits to the template API that we're missing.

@mxstbr
Copy link
Author

mxstbr commented Mar 29, 2020

No, I prefer CSS objects nowadays, particularly because I work in a very technical team. I think it mostly depends on the team that you're working with and their familiarity with JS 👍

@ryasmi
Copy link

ryasmi commented Mar 30, 2020

That's good to know and I think you're right that it probably depends on your team's familiarity with JS. Thanks for your reply, really appreciate it 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment