How Palette Works

When we first began building Artsy's Design System we wanted something that could serve as a good foundation for low and high-level components, while also being intuitive for our development team. Some of us were facinated with functional css but having worked with React, styled-components and various CSS-in-JS libraries, we wanted something that was less vanilla CSS than React props, something that we could easly use with TypeScript. Pretty quickly we stumbled upon a brilliant library by Brent Jackson called styled-system that combined all of these things. Fans of Tailwind CSS should feel right at home here.

Styled System

Styled System bills itself as a way to "build custom UI components with constraint-based style props based on scales defined in your theme." There's a lot here, but in short what it means is: Take some values defined in a theme, define some function mixins to consume values in the theme, and map the output to React component props.

For example:

import { space } from "styled-system"
import { ThemeProvider } from "styled-components"

const Box = styled.div`
  ${space};
`

const theme = {
  space: {
    1: "10px",
    2: "20px",
    3: "30px",
    4: "40px",
  },
}

const App = () => {
  return (
    <ThemeProvider theme={theme}>
      <Box p={2}>This is a box with 20px padding</Box>
    </ThemeProvider>
  )
}

What we're doing is creating a new primative component called Box that mixes-in a space function from styled-system. This function then "decorates" the component with a handful of props that then read in the theme and allow the develper quick ways to access the values. One of those props is p (shorthand for padding), and by passing in 2 we're saying "look at theme.space, find the key for p={2} and return the value" - which equals 20px. And with TypeScript, we get really nice intellisense as well as type-checking of the values passed into the prop.

At scale and across a big dev team, this works well after the basics are understood. Our UI becomes like lego blocks, where the theme ("constraints") defines the shape of the pieces that are available. Design drift no more.

A fuller example of Box from the Palette codebase:

/**
 * All the system functions for Box
 */
export const boxMixin = compose(
  background,
  border,
  color,
  layout,
  position,
  space,
  textAlign
)

export const Box = styled.div`
  ${boxMixin}
`

You can see that not only is there the space mixin, but also background and position and others, each providing their own set of props that we can then tap into directly on the Box component.

Similarly, lets define a Flex component:

import { flexbox } from "styled-system"

const Flex = styled.div`
  ${flexbox};
`

Now with this defined, we can then create something more complex:

const Banner = ({ message, isPriority }) => {
  return (
    <Flex flexDirection="row" justifyContent="space-between">
      <Box px={2} bgColor="brand">
        {message}
      </Box>

      {isPriority && <PriorityIcon />}
    </Flex>
  )
}

Under the hood we're laying out the component into a container that displays a message and, if isPriority is set, optionally shows an icon. The contents are distributed evenly across the containing space thanks to the justifyContent prop being set to space-between on the <Flex> component container. No long-hand css required, and everything is type-checked. In fact, unless you're building something unconventionally complex, you should never need to use vanilla css or conventional styled components again; everything you could need is defined within our primatives in Palette.

Full API

As styled-system can do much more, check out the API docs for a full list of functionality.

On this page
Styled System
Full API