const TalkMeta = styled(Talk)`
  author: 'Nick Ribal';
  date: March/2020;
`

Best practices with styled-components/Emotion

WORK IN PROGRESS

Nick Ribal

image/svg+xml

Front-end & web consultant, architect, engineer, mentor

Family man, digital nomad, opinionated guy

Background

  • Where & why CSS fails in the age of components & apps 💔

  • Overview of styled/Emotion 💅

  • 3 styles of using styled + utility classes based styling

Best practices in styled ✨

Do ✔

Don't ❌

Co-location Component.style.js or separate styling object
Logical props API Visual props API: no styling
Style components Use selectors
Style/use components Use CSS/styled nesting
Style components Over abstraction

Co-location

Encapsulation

Portability

Disclaimer: I ❤️ CSS!

CSS is:

  • Powerful and ever improving

  • Flexible and styles the entire richness of the Web

  • Easy to start with

  • Hard to master

Sadly, I know only a couple of people in person who know CSS better than me 😭

CSS-in-JS is:

  • Not a replacement of CSS

  • Another way of authoring CSS

  • A decade of work, consulting and team leadeing of:

    • Content sites (SSR)

    • 3rd party widgets

    • SPAs, client and isomorphic

    • Juggling all the above 🤹

  • Web:

    • Inline styles, CSS, Less, Sass, CSS modules, styled-components, Emotion

    • A few years of policing BEM 👮

  • ​React Native:

    • Bare styling, Emotion, RNW, Native Base

CSS is my bread & butter

The trouble with CSS

Is not this

CSS isn't reusable

Modern CSS frameworks aren't interoperable

Each is like a country with its own language, concepts & conventions via classes, selectors and specificity

The result is incompatible monoliths:

  • no code sharing
  • no modularity
  • no code reuse

Who knows what's specificity?

How to "fix" specificity conflicts?

Specificity poll!

How reliable is that "fix"?

.foo.foo { prop: value }
html .foo { prop: value }
.foo[class] { prop: value }
/* Temporary fix, I swear! */
.foo { prop: value !important }

Why is CSS broken?

Times are changing

CSS fails us in a component/app world.

Our tooling must evolve too.

We're mostly writing apps - not pages!

Recent work poll!

  • Did you work on a content site?

  • No, really: with SSR, SEO and 1000's of URLs?

  • Did you need to style things dynamically?

  • Show me on the doll where the cascade touched you

Separation of concerns, old school

Remember "web pages" and "information superhighway"?

We used to write websites and webpages

Separation of concerns, modern

Today we write components

CSS-in-JS

CSS 

Apps

Sites

CSS in the component/app age

  • No scope or isolation: we write components - not sites
  • Specificity wars
  • Juggling selectors to match markup
  • Cascade instead of isolation

We can fix selectors

We can't fix the cascade (yet)

  • No scope or isolation: we write components - not sites. Selectors fail us
  • Specificity wars via selectors
  • Juggling selectors to match markup
  • Cascade instead of isolation

This is old news...

In November 2014, Christopher "vjeux" Chedeau, who is working on React and React Native at Facebook, gave a thought-provoking talk titled "CSS in JS".

He outlined the problems of CSS at scale and later showed how they solved those at Facebook. They were able to solve most, but in return had to add a lot of additional tooling.

Then he introduced a complete new approach to handle those issues without any actual extra work. Using inline styles. It seemed crazy, but just worked magically.

@vjeux's talk created shockwaves and turmoil in the front-end community

Preprocessors don't come anywhere near what's possible with CSS-in-JS 

Aren't we re-inventing the wheel?

Don't CSS, CSS modules, Sass and PostCSS + plugins do the same?

Can't BEM and the likes help?

Conventions need policing, which fails

CSS-in-JS lets us reimagine what's possible compared to plain old CSS!

  • Dead code elimination, ​Code coverage and Testing
  • Critical CSS: above the fold inlining
  • ​Smarter optimizations:
    • html-in-js + css-in-js = super minification with name mangling, like we've had in JavaScript for years
    • Stuff like webpack's CommonsChunkPlugin

Optimizations and

static analysis

Standardization, modularity and code reuse

  • Standard libraries instead of current CSS silos: lodash, only for styling
  • CSS component packages

BUT WAIT, THERE'S MOAR!!1111

Ready for the next best thing since sliced bread?

Non browser styling

styled ecosystem

We 💜 React because it's component model and API make a good DOM abstraction

Guess what? The same applies to styling!

Styled-components

Competition is good! Both are always improving and are practically identical in React

Emotion has source maps and isn't limited to React

Emotion

Ancient history: how did React transform HTML/JS?

  • Components - a good abstraction model

  • JSX - syntactic sugar to compose components

It was the first library which made HTML-in-JS not suck via:

JSX has taught us how

Juggling HTML and JS isn't fun. They belong together

separation of concerns

!==

separation of technologies

styled/Emotion are the same, only for styling!

Without classnames, selectors & specificity

You get to focus on what CSS is for

styling!

"Do you HATE classnames?"

  • They have NO semantic meaning

  • They are a means to an end

  • They are the vessel, not the message

  • They are painful to maintain and juggle when changing styling

  • String manipulation isn't fun

  • They are global (which CSS modules work around)

No, but I'd rather not deal with them

.primary

"primary"

.primary {
  color: red;
}
<button class="primary">
  Text
</button>
import styled from '@emotion/styled'

const RedButton = styled.button`
  color: red;
`

render(
  <RedButton>My button</RedButton>
)

Examples are straight from the docs

All I want is a red button!!!!

<button class="css-ck1kby-RedButton">
  My button
</button>

No, it is not inline styles! 😅

Isn't that just inline styles? 😱

.css-ck1kby-RedButton {
  color: red;
}

The glue is abstracted away

  • Pseudoclasses
  • Pseudoelements
  • Media queries
  • Feature queries
  • Animations
  • Present and future CSS will just work

Older CSS-in-JS libraries used inline styles. They are dead ☠

NO COMPROMISES!

You get all of CSS!

import styled from '@emotion/styled'

const Basic = ({ className }) => (
  <div className={className}>Some text</div>
)

const Fancy = styled(Basic)`
  color: hotpink;
`

render(<Fancy />)

Styling any component

As long as it accepts a className prop. So ANY component, really

import styled from '@emotion/styled'

const Section = styled.section`
  background: #333;
`

const Aside = Section.withComponent('aside')

render(
  <div>
    <Section>A section</Section>
    <Aside>Aside styled like Section</Aside>
  </div>
)

Style reuse with another component

const Child = styled.div`color: red;`

const Parent = styled.div`
  ${Child} {
    color: green;
    .global-css {
      like-in: Sass;
      scoped-like-in: css-modules;
    }
  }
`
render(<>
  <Parent>
    <Child>green, with nested .global-css</Child>
  </Parent>
  <Child>red</Child>
</>)

If you do need selectors, you have them

A superset of CSS/Sass!

  • Autoprefixing built-in
  • SSR + style extraction, inlining and client hydration
  • Source maps 
  • Theming
  • Dynamic global styles via exact same component API
  • Code splitting
const Button = styled.button`
  color: ${(props) =>
    props.primary ? 'hotpink' : 'turquoise'};
`
// ^ will create two classes and toggle them
const Container = styled.div(props => ({
  display: 'flex',
  flexDirection: props.vertical && 'column',
}))

render(
  <Container vertical>
    <Button>This is turquoise</Button>
    <Button primary>This is hotpink</Button>
  </Container>
)

Dynamic styling? No problem!

import styled from '@emotion/styled'
import { css } from '@emotion/core'

const withColor = (props) => css`
  color: ${props.color || 'currentColor'};
`

const H1 = styled.h1`
  ${withColor};
`
render(
  <H1 color="lightgreen">
    This is lightgreen
  </H1>
)

Dynamic style composition

Mature tooling

  • Snapshot testing in Jest
  • TypeScript 💕
  • eslint
  • stylelint
  • Editor/IDE extensions
  • Tree shaking that just works
  • Polished: lodash for styling
  • Rebass and styled-system: styling as React props
  • Reusable components
  • Component libraries
  • Icons
  • Grid systems

Styled ecosystem

  • Co-locate style with component
  • Don't abuse dynamic styling
  • Opinionated: don't style via props (opposite of rebass/styled-system)

styled best practices and demo

Trade-offs

  • Debugging and DX aren't as good as in JS
    • Still better than any CSS preprocessor 🎉
  • Concept is still considered controversial by many
    • People get real emotional and defensive over it
  • Client-side CSS-in-JS has an overhead:
    • Initial download and bootstrapping, mitigated by SSR and the fact your CSS is no longer append-only
    • There's runtime code, but you'd need to implement the same anyways - only manually by className juggling

Sources and references

Thank you and

stay curious :)

[WIP] Best practices with styled-components/Emotion

By Nick Ribal

[WIP] Best practices with styled-components/Emotion

After a decade of styling in various ways and platforms, I've settled on styled-components/Emotion as weapon of choice. However, with great power comes great responsibility. S-C can be used in a variety of ways. I'll present various approaches, their tradeoffs and my recommendations on how to harness the best of this awesome tech!

  • 1,156