Though I haven’t started writing yet, I’m already coming to you with a new “what I learned” success story. This week, I attended a Frontend Team update call where the working group that I previously attended came up. The the organizers shared what the group had been up to and encouraged everyone else to join. One of the organizers even offered to answer individual questions after the meeting, so I took the time to reach out to her. Long story short, she took 30 minutes out of her day to teach me the reasoning behind some of the linter warnings we were facing and preview the future of the working group! And so, in following advice she gave, I’ll be looking into Stitches, which is a library that the Frontend Team at my job will be using to style their components.
What is a Styling Library?
Stitches avoids unnecessary prop interpolations at runtime, making it significantly more performant than other styling libraries. Each CSS property in your application is mapped to an atomic class and cached. Once a class is injected, it will never be injected again. From then on, it will just return the same class.
I’m not terribly familiar with prop interpolation or atomic classes, but based on some reading, my understanding is that this all comes back to the age-old challenge of fetching and parsing data. Rather than rebuild or redefine our styling properties on a new render, Stitches can refer to a cached and static atomic class. Instead of getting any deeper into how that is achieved, let’s take a look at the syntax and some of the features of Stitches.
How can I use Stitches to style my component?
To save some time, we’ll work with the very simple React component I built in my blog about useCallback(). When I wrote that blog, I created a button to illustrate React’s rendering and re-rendering patterns. Styling wasn’t a part of that lesson, so my button ended up looking like this:
If we want to use Stitches, we’ll start by installing it:
npm install @stitches/react
Next, we create a config file called stitches.config.js (the docs suggest stitches.config.ts first but I’m using JS to avoid any typing bugs since I’m a TypeScript novice). I created my file right in the src directory:
No surprise here, we have to import the library at the top. Note that we’re importing a function called createStyled:
This function accepts one argument, though the argument is an object with four possible keys:
- prefix: A string that will be automatically added to all classnames in case we’re worried about conflicts with existing classes.
- tokens: We can build tokens as useful shorthand to give easy-to-remember names to specific stylings. We’ll see what they look like and why they’re useful later in this post.
- breakpoints: Another custom object used to set dynamic styling rules. Screen size is an easy example of a breakpoint — if width exceeds 640px, change the styling.
- utils: Utils allow us to bundle CSS properties together under a new name. I could make a fullScreenBackground util and assign it all the properties from this post, for example.
Much like some of our favorite Hooks, createStyled returns two functions, though they are contained inside of an object rather than an array. We can deconstruct it like so:
Note that we’re exporting the functions, which are called styled and css. Styled will allow us to create a component with custom styling. I created a file called Button.js, imported my new functions, and designed a stylish button:
The styled function accepts two arguments: the first is an html tag (button, div, li, etc) and the second is an object with CSS properties and values. I imported the Button component into App.js and replaced my plain old ‘button’ element; get a load of that button:
Stitches can do CSS — how is that better than regular CSS?
Right off the bat, we’re seeing a system that fits in better with a React framework since it’s building components rather than relying on classnames or inline styling. But it’s the arguments that can be passed into createStyled that take things to the next level:
We’ll start out simple by just adding a prefix to our classnames. Here’s the HTML of my React app with an empty string as the prefix value:
And here it is after I’ve assigned a prefix value:
We can imagine this is a useful tool for identifying the changes this library is making or parsing them using CSS selectors in adjacent JS.
Tokens are variables that we can insert into our styled components to simplify uniform display throughout our app. If you’re like me, you may have had this experience:
- My blue is a little too dark, maybe I should change it.
- Great, I found the perfect color!
- Now I’ll just go through this giant styles.css file and update every instance I used the old blue with the new blue…
Rather than do that, I can define a color as a token and then reference it whenever I want to use the shade of blue that I’ve decided fits my app’s style:
Stitches recommends using a symbol to prefix tokens to prevent autocompletion errors in TypeScript. If a developer defines a token that ends in a number, ‘px’ will automatically be appended and could cause issues.
We may want to make a change to our styling depending on factors like screen size. Setting up a breakpoint like the ones used in the Stitches documentation requires a few steps. First, we have to set up variants in our styled object:
I’m happy that this came up because it’s one of my favorite features in Stitches so far. Variants allow us to assign a single or a group of properties to a single keyword. In this case, we define a color property, which can be assigned either $blue (which we previously defined) or purple (the default CSS color). Providing these values will assign our element’s backgroundColor. I view this as a huge win because it allows us to:
- Simplify the terms we use for styling (backgroundColor => color)
- Bundle styles within a variant (I could update my color variants to change both backgroundColor and the shape of the button, or the font, or whatever else I want)
- Give me options for variations without having to rewrite all of my CSS. This is ideal if I want my buttons to share 90% of the same properties, but also look different sometimes.
As we know, breakpoints is part of our createStyled object argument. Inside of breakpoints we’ll find key-value pairs where the value is a function (Stitches recommends using bp1, bp2, etc as naming convention for the keys). This function accepts an argument that will be conditionally interpolated into our inline styling. Here’s what it looks like:
As before, we see that the ultimate value we’re expecting is a string. This string describes CSS logic which uses the @media rule to check for size of the viewport. In this case, we’re saying “activate this breakpoint when the viewport’s width is at least 640px.” Finally, we add the breakpoint to our inline styling:
The default backgroundColor of our Button component is being overridden by our variant statement, which uses color to set the backgroundColor. The logic inside is intuitive: initially we’ll use the color coral but once we hit bp1, we’ll change it to purple. Check it out:
Utils more or less allow us to define our own CSS properties. Here’s the first example from the documentation:
We have a key of m which has a value of a function that defines a function. Ultimately, we see that we’ve basically just created the existing margin CSS property, but utils gives us the freedom to customize the name or add/remove functionality. If for some reason I wanted to edit all margin values except marginTop, I could create a util for that. We can reference our created util when defining our component:
And we see that the result is the element’s margin has increased:
I spent a long time trying to figure out why utils has to define a function that creates a function and, more importantly, why that first function requires an argument that’s apparently never used. The config argument is our configuration itself, so my belief is that it’s used on the React side to help unpack our layers of redefinition, but I couldn’t find the code that actually uses this parameter on GitHub. What I can say is that this appears to be an optional param, as I removed it and the util still worked the same way.
What about the CSS function?
When we run createStyled, we get two values back, but the Stitches documentation uses styled much more frequently than it uses css. I used VS Code’s “go to definition” feature (cmd + click on a Mac) to find createStyled in index.d.ts (it must be rearranged for node_modules since I couldn’t find it on GitHub). The definition simply states that it returns two properties, of which css is type TCss. I found TCss in types.d.ts as an interface that extends TConfig. Despite the fact that the Stitches documentation refers to css as a function, this appears to be another object:
The object properties are styles, getStyles, keyframes, global, and theme. This made more sense as I had seen the css object invoked via property calls in a few examples. I learned that keyframe is an animation technique used for smooth transitions. Here I’ve implemented Stitches’ example:
Not exactly a smooth transition, but I think that’s more about the implementation than the library itself. The important thing here is that createStyled builds a css object (or function — after all the authors of the Stitches documentation know more than I do) which gives us even more functionality if we choose to use it.
I’m looking forward to seeing how the Frontend Team at my company uses Stitches and hopefully getting the chance to clarify some of the functionality that confused me as I was researching. As with any new framework, the possibilities are limitless!