Design Tokens Generator: design-tokens.dev Design Tokens with Chakra UI

Design Tokens integration with Chakra UI: Tutorial and Example Application for Frontend Developers

It is recommended to check the intro article before diving in specifics of the current guide. Source code for this and other example applications can be found on Github.

Overview and Prerequisites

Chakra UI is a simple, modular and accessible component library that gives you the building blocks you need to build your React applications. The closest analogue (competitor) is Material UI. Another alternative might be Mantine UI.

As Chakra UI is all-in-one solution for UI development needs, installation, but more importantly setup, takes several steps. Let’s begin with installation, following official documentation:

npm i @chakra-ui/react @emotion/react @emotion/styled framer-motion

Next step is to wrap our application in ChakraProvider component.

Alternative approach is to use ChakraBaseProvider, that offers more headless solution. It might be useful if your project requires more flexibility. For the sake of development velocity the primary option is more reasonable.

With additional provider in place our main.tsx will be updated accordingly:

// excerpt from main.tsx

ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
  <React.StrictMode>
    <ThemeProvider>
      <ChakraProvider resetCSS={false} disableGlobalStyle={true} theme={theme}>
        <App />
      </ChakraProvider>
    </ThemeProvider>
  </React.StrictMode>
);

Couple of notes here:

  • resetCSS is set to false, since we normalize CSS in our core styles
  • disableGlobalStyle is true for the same reason
  • finally, custom theme allows us to customize Chakra UI according to our needs

In our case theme is located in a dedicated file and structured like this:

// excerpt from theme.ts

import { extendTheme } from '@chakra-ui/react';

const theme = extendTheme({
  colors: {},
  fonts: {},
  fontSizes: {},
  lineHeights: {},
});

export { theme };

We’ll be looking in detail into theme in the following section. For now, let’s dive in and create a couple of components!

Explore the source code in DTG Examples repository.
Current guide refers to the following dependencies versions, latest at the moment of writing:

"@dtg-examples/common-tokens": "1.0.0",

"@chakra-ui/cli": "2.4.1",
"@chakra-ui/react": "2.7.1",
"@chakra-ui/styled-system": "2.9.1",
"@emotion/react": "11.11.1",
"@emotion/styled": "11.11.0",
"framer-motion": "10.12.18",
"vite": "^4.3.9"

Typical Component

To start with Chakra UI let’s create a simple presentational component:

// excerpt form Header.tsx

import { Flex, Heading } from '@chakra-ui/react';

const Header = (props: HeaderProps) => {
  const { children } = props;

  return (
    <Flex as="header">
      <Heading as="h1">Dystopian Weather</Heading>
      <div>{children}</div>
    </Flex>
  );
};

That works, but does not look right… What’s missing is the styling. Check out the same component with all styling in place:

// excerpt form Header.tsx

import { Flex, Heading } from '@chakra-ui/react';

const Header = (props: HeaderProps) => {
  const { children } = props;

  return (
    <Flex
      as="header"
      alignItems="center"
      pos="relative"
      zIndex={20}
      px={4}
      py={8}
      bg="base.dark"
      color="contrast.dark"
      borderBottom={2}
      borderColor="primary.alpha50"
    >
      <Heading as="h1" fontSize="2xl" lineHeight="short" m={0} flex="1 1 auto">
        Dystopian Weather
      </Heading>
      <div>{children}</div>
    </Flex>
  );
};

Styling in Chakra UI is implemented via CSS facade (aka style props) in the form of component properties. It’s fairly straightforward and only requires initial documentation study. Practically, vast majority of your CSS needs are covered.

Additionally, welcome to explore the dedicated components API for Flex and Heading in the respective docs.


Let’s have a look at something more intricate and interactive. To create our List (cities list) component we’ll be using Tabs from Chakra UI collection. Original component offers panel layout, however according to design we don’t need those.

Using Tabs is not a very first choice for a component like List, but for the sake of experiment it’s a curious exercise.

// excerpt from List.tsx

import { Box, Tab, TabList, Tabs } from '@chakra-ui/react';

const List = (props: ListProps) => {
  const { clsx, value, items, onSelectValue } = props;

  const index = items.findIndex((item) => item.uid === value);

  return (
    <Box __css={clsx}>
      <Tabs
        orientation="vertical"
        variant="unstyled"
        index={index}
        onChange={(idx) => {
          const uid = items[idx].uid;
          onSelectValue(uid);
        }}
        flexGrow={1}
      >
        <TabList
          display="flex"
          flexFlow="column"
          justifyItems="stretch"
          justifyContent="stretch"
          w="100%"
          p={4}
          bg="gamma.300"
        >
          {items.map(({ uid, city, code, temp }) => (
            <Tab
              flexGrow={1}
              flexShrink={1}
              key={uid}
              value={uid}
              display="block"
              px={4}
              py={3}
              border={0}
              bg="secondary.default"
              color="secondary.contrast"
              cursor="pointer"
              lineHeight="base"
              textAlign="initial"
              fontSize="md"
              transitionProperty="all"
              transitionTimingFunction="ease-out"
              transitionDuration="faster"
              _hover={{
                bg: 'secondary.tint',
              }}
              _selected={{
                zIndex: 1,
                color: 'secondary.default',
                bg: 'secondary.contrast',
              }}
              _focusVisible={{
                outline: theme.focus.style,
                outlineOffset: theme.focus.outline,
              }}
              sx={{ '--focus-color': theme.colors.primary.default }}
            >
              <Box fontSize="lg">{city}</Box>
              <Box>
                {weather[code]}: {temp}°C
              </Box>
            </Tab>
          ))}
        </TabList>
      </Tabs>
    </Box>
  );
};

Apart from the basic, yet customized tabs change logic, there are several points worth mentioning:

  • Notice __css prop on the generic Box component. It’s an analogue of the sx prop with lower priority, which is perfect for this composition case, as we need to pass styles from the parent component
  • In addition to the basic style props we also have _hover, _selected and other pseudo props - very handy, and there’s quite a bunch of those - default and proprietary, depending on the component
  • Along with the numeric and string values for the style props we directly use our theme object as well
  • Finally the sx prop is used for tweaking (or simply passing, if needed) the CSS variable for focus-color

Interestingly enough, some props are very straightforward and map 1:1 to CSS attributes, like this one - flexFlow="column".
However for many other properties values are not so transparent yet. So now it’s time to dive deeper into theme setup and explore semantic values for said props.

Design Tokens Integration

Chakra UI theme configuration and Design Tokens integration consist of two related parts.

First part is quite obvious, we need to customize (or better say, extend) the theme with our values. We’ll be using generated Design Tokens in the form of CSS variables:

// excerpt from theme.ts

import { extendTheme } from '@chakra-ui/react';

const theme = extendTheme({
  colors: {
    primary: {
      default: 'var(--awsm-color-primary)',
      tint: 'var(--awsm-color-primary-tint)',
      shade: 'var(--awsm-color-primary-shade)',
      tone: 'var(--awsm-color-primary-tone)',
      contrast: 'var(--awsm-color-primary-contrast)',
      alpha50: 'rgba(var(--awsm-color-primary-rgb), 0.5)',
    },
  },
  radii: {
    none: '0',
    base: 'var(--awsm-radius-medium)',
    sm: 'var(--awsm-radius-small)',
    md: 'var(--awsm-radius-medium)',
    lg: 'var(--awsm-radius-large)',
    xl: 'var(--awsm-radius-round)',
    full: 'var(--awsm-radius-pill)',
    round: 'var(--awsm-radius-round)',
  },
});

It’s important to mention that Chakra UI allows both values and keys change. If you wish to have radii.huge and radii.tiny - this would be totally possible. You can also null-ify unused keys if needed. In my configuration example you can find a mixed approach to theme customization, which overrides colors, preserving most of other properties. Find more information about these options in the official docs.

Second part is responsible for easy integration of you customized theme into the UI code. Practically, what you need to do when theme is updated, make sure that typings are updated respectively, so your IDE would be helpful and not limiting.

This can be achieved with setting up an automated process, following official documentation.

Install Chakra UI CLI:

npm i -D @chakra-ui/cli

And generate typings (--watch flag allows to automate this process, while you update the theme file):

chakra-cli tokens src/theme.ts --watch

Now, it won’t be difficult to match semantic style props with the respective theme values:

// excerpt from List.tsx

<Tab
  bg="secondary.default"
  color="secondary.contrast"
  _hover={{
    bg: 'secondary.tint',
  }}
>...</Tab>

By default out List item (represented by Tab in this example) would have secondary color for background and secondary contrast color for text. On :hover, the background would change to secondary tint color. Similar record in CSS would look like that:

.list-item {
  background-color: var(--awsm-color-secondary);
  color: var(--awsm-color-secondary-contrast);
}

.list-item:hover {
  background-color: var(--awsm-color-secondary-tint);
}

Conclusion

Using Chakra UI with tokens created by Design Tokens Generator takes couple of steps and yields great results.

Chakra UI is the powerful all-in-one solution for UI development in React. Supercharged with Design Tokens it gets way better! Not only you can modify and extend the default theme, but you can completely replace the abstraction layer with the custom semantic tokens. Taking everything into account, this approach can be recommended for small- and mid-size applications without a doubt.

To get started even faster, feel free to experiment with the example theme file or use it as reference for future projects.

To compare Chakra UI with alternatives, more suitable your project needs, welcome to explore the guides on Headless UI, Radix UI and of course Material UI.