Swizec Teller - a geek with a hatswizec.com

    Why CSS-in-JS is winning, an example

    Stepper component built with CSS-in-JS

    Modern CSS-in-JS is the best thing that's happened to the web since components. Hands down. Let me explain. πŸ‘‡

    First we had the style prop and that sucked. I hated CSS-in-JS and thought it was stupid. You can pry my CSS from my dead cold hands! shakes fist

    Then came experiments.

    Can we make the style prop easier to use? Can we still use class names to style with BEM like we used to? What about CSS files with fat semantic classes? Scoped styles maybe?

    None of it quite worked. Everything was awkward in some way.

    The style prop is great for individual components and breaks down when you want to change your general font size.

    Class names give you all the power of CSS and BEM (tiny classes like .font1 .bold .bla). And it breaks down when you reuse components like building blocks. A component has the color defined and if you want to overwrite that you need something that understands classes and decides which one takes power and ugh don't get me started.

    I may have once tried to make that work ...

    Scoped CSS is better. That's what Vue uses. But it still sucks, if you ask me. You get a weird mix of global styles with local overwrites and mixing BEM and semantic classes and it gets messy fast. Leads to weird code too.

    But then, then some beautiful soul came up with styled-components. I was in love.

    All the simplicity of scoped CSS with the power of the style prop. You write CSS, yes, but you can inject JavaScript too.

    Why styled-components are win, an example

    The best way to make my argument is with an example. I recently implemented a colorful React stepper component for a client project.

    Styled components and a couple libraries @Diegohaz built on top made it so easy it feels like cheating.

    Swizec Teller published ServerlessHandbook.dev avatarSwizec Teller published ServerlessHandbook.dev@Swizec
    Wow @diegohaz's libs made this ridiculously easy to build. Blog coming soon
    Tweet media

    You have 9 steps. Each a different color once done. Blank with a grey outline otherwise.

    Here's a CodeSandbox you can try.

    Pretty isn't it? I think it is.

    Here's all the component code πŸ‘‡

    <code lang="javascript">const Step = ({ index, done, last }) => (
    <Circle index={index} done={done} last={last}>
    {last ? "⭐️" : index + 1}
    </Circle>
    );
    export default ({ step, maxSteps }) => (
    <Steps row>
    {range(maxSteps).map(i => (
    <Step index={i} key={i} last={i === maxSteps - 1} done={i < step} />
    ))}
    </Steps>
    );

    Yep that's it. 12 lines of code.

    A <Stepper> component, the default export, takes the current step and max number of steps, maxSteps. It renders a <Steps> component which deals with layouting. Inside goes an array of <Step>s.

    Each <Step> gets an index, a flag saying whether it's last, and whether it's been done yet. Highlighted vs. unhighlighted state.

    Steps render a <Circle> component, pass-in the props, and inside goes either a number or a star emoji. Star is last.

    All style logic goes in styles

    Moving your visual UI logic out of your components and into styles is where CSS-in-JS shines. Focus your components on business logic, user experience, and hierarchy. Let your styles take care of your styling.

    Just like our CSS Garden forebears envisioned. Remember CSS Gardens?

    You might be too young. The world has moved on. CSS's initial promise is that we would completely decouple styling from document hierarchy and live a Zen stress free life.

    Now look at us.

    BUT ALL IS NOT LOST! Look at this beauty.

    <code class="javascript">import { Box, Flex, Heading, styled } from "reakit";
    const Steps = styled(Flex)`
    align-items: center;
    justify-content: center;
    padding-top: 40px;
    `;

    I'm using Diegohaz's reakit for base CSS stuff. He built a bunch of components on top of styled-components that have sane defaults and great composability.

    No styles, just some basics. Flex, for example, is a div with display: flex. That's all.

    <Steps> then is a flexbox div that centers its children and adds a 40px padding on top.

    Easy πŸ‘Œ

    And then comes the <Circle> component. The individual steps.

    <code class="javascript">import { theme, palette, withProp, ifProp } from "styled-tools";
    const Circle = styled(Box)`
    border-radius: 100%;
    border: 1px solid;
    border-color: ${ifProp(
    "done",
    withProp("index", index => palette("steps", index)),
    palette("grey", 2)
    )}
    color: ${ifProp("done", "white", palette("grey", 2))};
    background: ${ifProp(
    "done",
    withProp("index", index => palette("steps", index)),
    "white"
    )}
    font-size: ${palette("size", 1)};
    ${theme("bold")}
    width: 30px;
    height: 30px;
    line-height: 30px;
    text-align: center;
    margin-right: 20px;
    &:last-child {
    margin-right: 0px;
    }
    `;

    A few things are happening here. Let me explain.

    1. I'm using another of Diegohaz's libraries styled-tools. It's a suite of helper functions that make it easier to use component props from your CSS-in-JS styles
    2. I'm using a theme behind the scenes. A concept built into Reakit that lets you define global styles. Things like UI colors, font sizes, stuff like that.
    3. ALL of my visual logic is in this style

    Let's break it down.

    We start with a Box, which is just a div.

    <code class="javascript">const Circle = styled(Box)`

    Then we turn it into a circle with a border.

    <code class="css"> border-radius: 100%;
    border: 1px solid;

    Border and background color come from our theme, based on the combination of index and done props.

    <code class="javascript"> border-color: ${ifProp(
    "done",
    withProp("index", index => palette("steps", index)),
    palette("grey", 2)
    )}
    background: ${ifProp(
    "done",
    withProp("index", index => palette("steps", index)),
    "white"
    )}

    Here's how you read that:

    1. If prop done is true, get first argument, otherwise the second.
    2. With prop index run method.
    3. Get the steps palette from theme and take the index color.
    4. Otherwise, get the grey palette and take the 2nd color

    Palettes are arrays in your theme object. Pure JavaScript. You can see it in the CodeSandbox above.

    A similar process gives you the font color.

    <code class="javascript"> color: ${ifProp("done", "white", palette("grey", 2))};

    If step is done, color is white, otherwise the 2nd color from the grey palette.

    Then we have font size, bold fonts, widths and heights, and other normal CSS stuff to round off each step.

    <code class="javascript"> font-size: ${palette("size", 1)};
    ${theme("bold")}
    width: 30px;
    height: 30px;
    line-height: 30px;
    text-align: center;
    margin-right: 20px;
    &:last-child {
    margin-right: 0px;
    }
    `;

    You can still use CSS nesting and meta classes just like you're used to. &:last-child matches the last <Step> and removes its margin so it doesn't mess with the layout.

    πŸ‘Œ

    Conclusion

    This flavor of CSS-in-JS is the best most ridiculously easy way I've ever seen to style modern JavaScript components. I pray that it works for Vue as well as it does for React because I want to get my frontend peeps at the dayjob excited about it.

    To recap:

    1. Components deal with business logic and UX
    2. Styles handle all visual logic
    3. Global theme handles generics

    Yes that means you ca rebrand your whole app just by changing theme.js.

    Did you enjoy this article?

    Published on January 30th, 2019 in Front End, Technical

    Learned something new?
    Want to become an expert?

    Here's how it works πŸ‘‡

    Leave your email and I'll send you thoughtfully written emails every week about React, JavaScript, and your career. Lessons learned over 20 years in the industry working with companies ranging from tiny startups to Fortune5 behemoths.

    Join Swizec's Newsletter

    And get thoughtful letters πŸ’Œ on mindsets, tactics, and technical skills for your career. Real lessons from building production software. No bullshit.

    "Man, love your simple writing! Yours is the only newsletter I open and only blog that I give a fuck to read & scroll till the end. And wow always take away lessons with me. Inspiring! And very relatable. πŸ‘Œ"

    ~ Ashish Kumar

    Join over 14,000 engineers just like you already improving their careers with my letters, workshops, courses, and talks. ✌️

    Have a burning question that you think I can answer?Β I don't have all of the answers, but I have some! Hit me up on twitter or book a 30min ama for in-depth help.

    Ready to Stop copy pasting D3 examples and create data visualizations of your own? Β Learn how to build scalable dataviz components your whole team can understand with React for Data Visualization

    Curious about Serverless and the modern backend? Check out Serverless Handbook, modern backend for the frontend engineer.

    Ready to learn how it all fits together and build a modern webapp from scratch? Learn how to launch a webapp and make your first πŸ’° on the side with ServerlessReact.Dev

    Want to brush up on your modern JavaScript syntax?Β Check out my interactive cheatsheet: es6cheatsheet.com

    By the way, just in case no one has told you it yet today: I love and appreciate you for who you are ❀️

    Created bySwizecwith ❀️