As a Frontend Developer, You constantly have to style elements. To do that, we have CSS that’s obvious. Right now we there are many existing solutions to achieve the same result from pure CSS, preprocessors like SCSS, tailwind to full-scale libraries (which I'm not a big fan of) like Material-UI. In this series, I will try some of the more interesting ways to make your app look cool and modern.
I am a big supporter of basically everything that Vercel does. When I saw their projects being made with Radix and Stitches I had to try it. Here is how it went.
Why would You want to use Stitches?
As their website claims, it’s a near-zero runtime CSS-in-JS solution with multi-variant support. I don’t know about You, but this doesn’t sound very interesting. Another CSS-in-JS huh?
Not so fast. It has many advantages that for me were maybe not groundbreaking but definitely awesome to use. Especially the combination of them all together.
Tokens
What are tokens? They are basically variables in CSS. They allow us to write much faster code, making repetitions obscure. Now think how many times you wrote the same value with the same unit in your CSS code. A lot right?
But you can say that you can make tokens in pretty much any CSS. But here is how they usually look
/* Typography
================================================================= */
/* Font sizes - 4x4 grid */
--font-size-12: calc(var(--font-base-unit) * 3);
--font-size-16: calc(var(--font-base-unit) * 4);
--font-size-20: calc(var(--font-base-unit) * 5);
--font-size-24: calc(var(--font-base-unit) * 6);
--font-size-40: calc(var(--font-base-unit) * 10);
--font-size-48: calc(var(--font-base-unit) * 12);
--font-size-64: calc(var(--font-base-unit) * 16);
--line-height-base-unit: 1;
/* Line heights */
--line-height-small: calc(var(--line-height-base-unit) * 1.1);
--line-height-medium: calc(var(--line-height-base-unit) * 1.3);
--line-height-large: calc(var(--line-height-base-unit) * 1.5);
/* Font weights */
--font-weight-lighter: 100;
--font-weight-light: 200;
--font-weight-normal: 400;
--font-weight-bold: 700;
--font-weight-bolder: 900;
/* Space
================================================================= */
--space-base-unit: var(--base-unit);
/* Margin, Padding - 8x8 grid */
--space-8: calc(var(--space-base-unit) * 2);
--space-16: calc(var(--space-base-unit) * 4);
--space-24: calc(var(--space-base-unit) * 6);
--space-32: calc(var(--space-base-unit) * 8);
--space-40: calc(var(--space-base-unit) * 10);
--space-48: calc(var(--space-base-unit) * 12);
--space-64: calc(var(--space-base-unit) * 16);
And sure you can understand what’s going on, but for me isn’t too complicated. IT SHOULD BE EASIER. And it is.
Here is a sample from Stitches doing exactly the same
const spacing = {
1: '0.25rem',
2: '0.5rem',
3: '0.75rem',
4: '1rem',
5: '1.25rem',
6: '1.5rem',
7: '1.75rem',
8: '2rem',
...
};
fontSizes: {
xs: '0.75rem',
sm: '0.875rem',
md: '1rem',
lg: '1.125rem',
xl: '1.25rem',
'2xl': '1.5rem',
'3xl': '1.875rem',
'4xl': '2.25rem',
'5xl': '3rem',
'6xl': '4rem',
'7xl': '4.5rem',
'8xl': '6rem'
},
fontWeights: {
hairline: 100,
thin: 200,
light: 300,
normal: 400,
medium: 500,
semibold: 600,
bold: 700,
extrabold: 800,
black: 900
},
space: {
...spacing
},
Maybe I prefer JSON format but it’s much clearer for me.
Variants
This is the key feature of Stitches that allows You to create typed, composable component APIs. For example, you can create a component and use variants to add multiple, compound, or default styles to the component. You can have sharable properties that are applicable to all variants. But by using variant property you can change one of them, are more or do anything you would like to do.
import { styled } from '@stitches/react';
export const Button = styled('button', {
padding: '$4',
margin: 0,
boxSizing: 'border-box',
font: '$publicoText',
fontWeight: '$medium',
color: '$text',
fontSize: '$small',
'&:focus': {
outline: 'none',
boxShadow: '0 0 0 2px $colors$shadow'
},
'&:disabled': {
pointerEvents: 'none',
opacity: '50%',
cursor: 'not-allowed'
},
variants: {
variant: {
primary: {
backgroundColor: '$elementBackground',
border: '1px solid $border',
'&:hover': {
backgroundColor: '$elementHover'
}
},
secondary: {
backgroundColor: '$elementGreenBackground',
border: '1px solid $subtleBorder',
'&:hover': {
backgroundColor: '$highlight'
}
}
},
icon: {
true: {
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
gap: '$1'
}
}
}
});
Here we can see tokens and variants in one harmonious way of working. We use tokens to set properties like colors or sizes.
I’ve created two variants of buttons that differentiate in color.
<Button onClick={handleStart} variant="secondary" size="sm" icon="true">
<PlayIcon />
</Button>
Here I use “secondary” variant button and in one go we initiate icon inside it.
I think creating variants is very useful, maybe not in your Sunday project, but when You work hand in hand with designers to create a proper design system you really can simplify your code.
Utils
The last advantage I want to bring up is utils. They are these little tricks that really speed up development. And for me, this is possibly the best feature of stitches. You can recreate one of the best features of Tailwind inside Stitches with just a few config lines.
Here is what I use them for but for sure they are many other ways and applications.
Want to use tailwind like shortcuts like p,py,px,mt etc? No problem
utils: {
p: (value) => ({
paddingTop: value,
paddingBottom: value,
paddingLeft: value,
paddingRight: value
})
}
By using this you abbreviate CSS properties and can call them what You want and aggregate them like in example above.
Why would You NOT want to use Stitches?
To be honest, it ain’t perfect. It has many flaws like every library, but here are the things that bugged me while working with it.
Resources
They are very sparse and to find how to use some of the more complicated and advanced features you have to work with YouTube tutorials that are quite outdated. Also, they are docs that are good but not covering every use case I wanted to read about.
When you go with a more popular solution when You find yourself stuck it’s much easier to find answers than when working with Stitches that’s for sure.
Example. I wanted to use my tokens in CSS properties when setting shadows etc.
boxShadow: '0 0 0 2px $shadow'
At first, I thought that I should be easy and just use token I set, and we are all good to go. But no, You have to use additional $colors before.
boxShadow: '0 0 0 2px $colors$shadow'
To find that out, I had to spend too much time searching for the answers. For my taste, of course. For many of You, many things that I found frustrating are just obvious because You had worked with similar libraries.
Config
The hardest part of working with Stitches is creating a config file. When You jump into a project with it already prepared, It would really be a lifesaver. But making it from scratch for small project is a time-consuming mess with answers that are not easy to find (see point above).
Example. As I said I was using Radix with it to provide some primitives. Radix also offers colors (which Stitches docs recommends, though they are all made by the same people). Theming is quite easy and fun to make with it but setting the colors? Very error prone.
How IT SHOULD work?
You just import color palette from @radix-ui/colors
spread it in colors
property and we are done. And in “light” theme I got it to work just perfect
createStitches({
theme: {
colors: {
...green,
...slate,
...blackA,
// Alias
appBackground: '$slate1',
subtleBackground: '$slate2',
elementBackground: '$slate6',
hoverBackground: '$green6',
border: '$slate5',
highlight: '$green9',
elementGreenBackground: '$green7',
elementHover: '$slate8',
shadow: '$blackA7',
text: '$slate12'
},
)}
The same way should work for dark theme, right? Nope I had to write HSL colors to make it finally work.
const darkModeConfig = {
colors: {
...greenDark,
appBackground: 'hsl(200, 7.0%, 8.8%)', // slateDark1
subtleBackground: 'hsl(195, 7.1%, 11.0%)', //slateDark2
text: 'hsl(210, 6.0%, 93.0%)', // slateDark12
hoverBackground: 'hsl(154, 50.9%, 17.6%)', //greenDark6
elementBackground: 'hsl(199, 6.4%, 17.9%)', //slateDark6,
elementHover: 'hsl(207, 5.6%, 31.6%)', // slateDark8
elementGreenBackground: 'hsl(153, 51.8%, 21.8%)', // greenDark7
border: 'hsl(199, 6.4%, 17.9%)',
highlight: '$greenDark9', // won't work for me,
}
};
Maybe I did something wrong If so please correct me In the comments! It would really want to figure out how it works properly.
Nonetheless I created a Pretty good config file which you can see in the github repo
and use it for yourself.
Would I use it again?
Yes. For me, it’s much better styled components
or emotion
. It has disadvantages, most of them coming from that this framework isn’t that popular, and it’s quite new on the market. But for bigger projects, I would really recommend it. You can separate styles, create variants, tokens and utils. If you don’t like to mix HTML, JS code with CSS this is perfect for you. As you can create one folder and style all the elements there and not worry about it again.
Me
This is my first article not really scientific. Would really appreciate feedback on my writing, code and everything. Use comments, my github https://github.com/szymonSadowski/mapGame or twitter.****
And of course try the project I created to play with Stitches and Radix. https://map-game.vercel.app/
Top comments (0)