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.
<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.
<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:
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 fallbackcolor: ${token('theme.color.fg', colorDark)}; // selector fallbackpadding: ${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"
.
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:
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.
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:
import {p, mb} from '@nib-components/theme';const Wrapper = styled.div`${p(4)};${mt(2)};${mb({xs: 4, md: 6})};`;