Design Tokens for Mesh have just been released!
Skip to content

Design tokens for React

How to work with tokens and react.

Mesh's design tokens are authored in the @nib-group/design-tokens repo using a tool called Style Dictionary. Style Dictionary allows us to have a single source of truth for tokens, and their values, defined in JSON. These token definitions are then used to generate many different formats to support different platforms and tech stacks.

In react, our preferred format for design tokens is CSS Custom Properties.

Using tokens

The design tokens are available directly via the @nib-group/design-tokens npm package. However, within React applications it is easier to access tokens via the @nib-components/theme package and its token function, for custom styling worker, or via provided props on mesh components.

Many mesh components have been updated to consume design tokens for properties such as color, spacing and border radii. By updating to the latest version of @nib-components/theme you can take advantage of these changes with minimal effort.

The Box component

The Box component has been updated to support tokenised values for background, color and borderColor. Padding, margin and border radius values also now pull directly from tokens.

jsx
<Box background="default" color="prominent" padding={4} borderRadius="small">
<Copy>All powered by tokens!</Copy>
</Box>

The old, non-tokenised values are still supported but should be considered deprecated and will be removed in a future release.

jsx
<Box background="brandBase" color="nearWhite">
<Copy>Still works for compatibility reasons</Copy>
</Box>

Other components that are built on top of Box, or use the same prop-mapping logic to allow you to pick a foreground or background value also now have token support. These include:

  • Section
  • Heading
  • Copy

Token function

When creating any custom component, the token function is the recommended way to access tokens. It should be used within your css declarations for a styled-component:

jsx
import styled, {css} from 'styled-components';
import {token, colorDark} from '@nib-components/theme';
export const Wrapper = styled.div`
background-color: ${token('theme.color.bg', '#f2f2f2')}; // string fallback
color: ${token('theme.color.fg', colorDark)}; // selector fallback
padding: ${token('common.dimension.spacing.6')};
`;

The token function is typed to enable autocomplete and typescript errors when invalid token paths are provided.

The token function has a second parameter to provide a fallback value if the token cannot be found for whatever reason. During the initial migration to tokens it is recommended that fallbacks be provided for all token references.

See the token function docs for more information.

ModeProvider

The tokens release brings about a new concept of "modes". Like brands (themes), modes are a way to switch between different sets of tokens. For example, a "feature" mode might have a different color palette to the "default" mode. The ModeProvider component is used to set the mode for a section of your app.

The mesh ThemeProvider component has been updated to have a nested ModeProvider with a mode value of "default".

jsx
import ThemeProvider, { ModeProvider, nib } from '@nib-components/theme';
const App = () => (
<ThemeProvider theme={nib}>
<main>
<Columns space={4} collapseBelow="md">
<Column width="2/3">
Main content here
</Column>
<Column width="1/3">
<ModeProvider mode="feature">
<Box background="default" color="prominent">
<Copy>This box and text is in the feature mode.</Copy>
</Box>
</ModeProvider>
</Column>
</Columns>
</main>
</ThemeProvider>
);

ModeProvider can be nested within other ModeProvider's to allow for different modes to be set at different levels of your component tree. The mode prop can be set to a string to apply for all brands, or an object to apply for specific brands. If you have many brands to support, we have a special key of rest which will apply to any brand not set in the dictionary:

jsx
import { ModeProvider } from '@nib-components/theme';
const App = () => (
<ModeProvider mode={{nib: 'feature', gu: 'alt', rest: 'default'}}>
<Box background="default" color="prominent">
<Copy>This box and text is in the feature mode.</Copy>
</Box>
</ModeProvider>
);

Theme selectors

Up until now the correct approach to styling custom components was to use the selectors exported from our theme package.

jsx
import {colorBrandDark, colorWhite} from '@nib-components/theme';
const Wrapper = styled.div`
background-color: ${colorBrandDark};
color: ${colorWhite};
`;

With the introduction of tokens, this will change. Color selectors have not been updated to reference tokens. This is because these color selectors are not semantic and therefore would be difficult to point to a token as a sensible default value. For example, it may seem reasonable to point the colorBrandBase selector at theme.color.fg.brand but the color selector is not solely used for text color. It is also used for backgrounds, borders and component-level styling. This highlights one of the primary reasons for undergoing the tokens work. There needs to be a level of safety in knowing that foreground color options will have sufficient contrast on background colors.

Other theme selectors that have a 1:1 mapping with tokens have been updated. These include:

  • spacing scale
  • border radii

Spacing utilities like p(4) and m(4) now pull from the spacing scale tokens and do not need to be modified in your code base:

jsx
import {p, mb} from '@nib-components/theme';
const Wrapper = styled.div`
${p(4)};
${mt(2)};
${mb({xs: 4, md: 6})};
`;

Resources