Pass tokens to components

Interactively use your tokens to theme your code components

Last updated on August 26, 2022

Overview

By passing your live design tokens into your code components you can interactively see the impact of token edits as you create and maintain themes for your design system in Interplay.

For this to work, you need to configure Interplay to dynamically pass your design tokens to your code components. There are a couple of different ways you can do this, depending on what your component code requires:

  1. Pass your design tokens dynamically as a theme object to your components
  1. Set your design tokens as CSS variables expected by your components in the browser

Both of these approaches make use of an extension script to read the live tokens and output them in the format required - detailed steps below.

If using the live tokens is not needed, an alternative approach to switching the theme for your components is to create a custom wrapper component that controls component styling based on the active theme name e.g. by switching a prebuilt theme, classname or stylesheet. For more details see wrapper components. (The active theme is passed to the wrapper component as a prop called '_theme').

Please contact us if these approaches do not suit your components or if you would like any help!

Passing tokens as a theme object

Use this approach if your code components use a theme object for styling. You can pass your design tokens to your components as a dynamically-created theme object (usually into a ThemeProvider wrapper component or similar). Switching themes in Interplay passes a theme object containing different token values.

In these steps we'll extend Interplay to read your design tokens, format them into a theme object, and pass the object to your components. To set this up:

1. Create a wrapper component

If you don't already have one, create a wrapper component that receives the theme object as a component property and passes it to your components. Usually this will involve using a ThemeProvider or similar from the styling library you are using and setting a prop with the theme object received.

Here is an example wrapper component that we use for the Material UI components:

import React from 'react';
import { createTheme, ThemeProvider } from '@mui/material/styles';
import AdapterDateFns from '@mui/lab/AdapterDateFns';
import LocalizationProvider from '@mui/lab/LocalizationProvider';

export const ThemeWrapper = ({children, themeData, mode = 'light'}) => {

  //mode can be light or dark - will base theme on mode if no themeData is passed
  const modeTheme = createTheme({ palette: { mode }})

  //if themeData passed, create theme from it
  //otherwise use default themeData from mode prop, defaulting to 'light'
  const theme =  themeData ? createTheme(themeData) : modeTheme;

  return (
        <ThemeProvider theme={theme}>
          <LocalizationProvider dateAdapter={AdapterDateFns}>
              {children}
          </LocalizationProvider>
        </ThemeProvider>
  );
}

Once you have created the wrapper component in your repo:

  • Add it to your index of components and run the CLI to import it with your other code components.
  • In Interplay, go to Settings > Includes tab and set the component as the wrapper for your design system

2. Create a computedValue extension

Create an  extension of type computedValue in your project settings that scripts the conversion of your live tokens data into a theme object. Here is an example extension that does this:

interplay.addExtension({
    id: 'themeData',
    name: 'themeData',
    type: 'ComputedValue',
    value: (context) => {
       //read tokens from context provided by Interplay
       const { tokens } = context;
       const tokenNames = Object.keys(tokens);
        if(tokenNames.length === 0){
            return;
        }

        //format tokens into theme data format
        let themeData = {};
        tokenNames.forEach((tokenName) => {
            const token = tokens[tokenName];
            setValue(themeData, tokenName, token.value);
        }, {});

        //make any other formatting changes required to themeData object here

        console.log({themeData});
        return themeData;
    },
});

const setValue = (object, path, value) => {
  var fullPath = path.split('.'),
      way = fullPath.slice(),
      last = way.pop();

  way.reduce(function (r, a) {
      return r[a] = r[a] || {};
  }, object)[last] = value;
}

Register this extension in the project settings for your design system:

 

3. Pass the computedValue to the wrapper

Edit the properties of your wrapper component to use the computedValue property. The wrapper is set as the root node when rendering your components and will receive the object returned by your computedValue extension each time.

Passing tokens as CSS variables

Use this approach if your code components expect CSS variables for styling.

Interplay will iterate through your design tokens, setting them as CSS variables in the browser for your components to pick up. Switching themes in Interplay will set a different set of CSS variables. (Sometimes this approach requires adapting the built CSS to expect CSS variables.)

1. Create a computedValue extension

Create an  extension of type computedValue in your project settings that reads your design tokens and sets them as CSS variables. Here is an example extension that does this:

interplay.addExtension({
  id: 'setCSSVars',
  name: 'setCSSVars',
  type: 'ComputedValue',
  value: (context) => {
    const { tokens = {} } = context;
    const tokenNames = Object.keys(tokens);
    if(tokenNames.length === 0){
      return;
    }

    tokenNames.forEach((tokenName) => {
      const token = tokens[tokenName] || {};

      //format the tokens into the CSS var names that your code expects
      const cssVarname = "--" + tokenName.split(".").join("-");

      //the browser console is useful for debugging extension scripts
      console.log(`Setting css var:${cssVarname} value:${token.value}`)

      //call setCSSProperty provided to sandbox to set css var on the window
      setCSSProperty(cssVarname, token.value);
    }, {});

    return;
  },
});

To register this extension, go to the project settings of your design system:

2. Create or configure a wrapper or global styles component

This computedValue needs to execute once on every page where your components appear, to set the CSS variables they need.It is not yet possible to configure this directly on the extension itself, but we can make this happen by attaching the computedValue to a component that appears once on every page. This is true for any component that is registered as a wrapper component or include component.

  • Identify an existing component that can be used as an include component on every page, or create a placeholder component in your repo that renders no visual UI to use as an include component.
  • Ensure this component is in the index of components that is imported by the CLI and run the import again if this component is not already imported
  • Set the component as a wrapper or include component in your project settings.
    • Set as a wrapper component if your component needs to act as the root for all your other components
    • Set as an include component if it just needs to appear on the page with your components.

3. Pass the computedValue to the include/wrapper component.

Navigate to the component you identified in step 2, add a new property and set the extension as the input type.

Now whenever your components are rendered, the include/wrapper component will be present and the extension prop will set your design tokens as CSS variables for your components to find in the browser.

Did this answer your question?
😞
😐
🤩