Theming

A theme is a coherent style applied on UI components. Themes make it easier to customize your app's design for a specific brand or app state (for example, 'light mode' and 'dark mode').

Head to this demo scope to see the full example of the theming solution.

Create a theme provider component

A theme needs a theme provider to apply the same style values on multiple UI components. Run the following to create a new component that will later be used as a theme-provider for your components:

$bit
Copiedcopy

Create design tokens with default values

To create your basic or default theme, start by composing a list of design tokens. These are pairs of style attributes and values which represent all design decisions.

Add a design-tokens.ts file to your component, to place your design tokens:

/** @filename: my-theme/design-tokens.ts */

export const defaultDesignTokenValues = {
  primaryColorBackground: '#568cb0',
  primaryColorText: '#ebebeb',
  mediumFontSize: 25,
  buttonPaddingMedium: '1rem',
  buttonFontWeight: 700,
};
CopiedCopy

Design tokens are usually maintained in a JSON format, to make them compatible across a wide range of technologies.

Implement a theme provider

Run the following to install Bit's theme-provider generator:

$bit
Copiedcopy

Use the createTheme function, to generate a new theme-provider that uses your design tokens:

import { createTheme } from '@teambit/base-react.theme.theme-provider';
/* import the default tokens from the file generated in the previous step */
import { defaultDesignTokenValues } from './design-tokens';

/* generate a theme using the design token default values */
export const MyTheme = createTheme({
  theme: defaultDesignTokenValues,
});

/* create a theme schema to standardize future theme extensions */
export type MyThemeSchema = typeof defaultDesignTokenValues;
CopiedCopy

Make MyTheme and the MyThemeSchema type alias available to other components by exporting them from your component's index.ts file:

export { MyTheme } from './theme-provider';
export type { MyThemeSchema } from './theme-provider';
CopiedCopy

Use your theme provider

The theme provider provides two methods of theming:

  • Theming components using a React Context that provides the right values to the components' JS variables that are used for styling
  • Theming components using CSS custom props (in this case, it only has to make the custom CSS props available in the page)

Theme React components using Context

Use the useTheme hook provided by your newly-generated theme-provider, to set the right values for your component styling. For example:

import type { CSSProperties } from 'react';
import { MyTheme } from '@my-org.my-scope/my-theme';

const Button = ({ children }) => {
  const {
    primaryColorBackground,
    primaryColorText,
    buttonPaddingMedium,
    buttonFontWeight,
  } = MyTheme.useTheme();
  const style: CSSProperties = {
    backgroundColor: primaryColorBackground,
    color: primaryColorText,
    padding: buttonPaddingMedium,
    fontWeight: buttonFontWeight,
  };
  return <button style={style}>{children}</button>;
};
CopiedCopy

Wrap you components with the Theme context (provided by your theme-provider):

import { MyTheme } from '@my-org.my-scope/my-theme';
import { Button } from '@my-org.my-scope/button';

() => (
  <MyTheme.ThemeProvider>
    <Button>I'm a themed button!</Button>
  </MyTheme.ThemeProvider>
)
CopiedCopy

Theme any component using CSS props

Use CSS props to style your component. The props will be set with the values defined (and included in the page, at the root of your theamable DOM elements) by your theme-provider:

const Button = ({ children }) => {
  const style: CSSProperties = {
    backgroundColor: 'var(--primary-color-background)',
    color: 'var(--primary-color-text)',
    padding: 'var(--button-padding-medium)',
    fontWeight: 'var(--button-font-weight)',
  };
  return <button style={style}>{children}</button>;
};
CopiedCopy
import { MyTheme } from '@my-org.my-scope/my-theme';
import { Button } from '@my-org.my-scope/button';

() => (
  <MyTheme.ThemeProvider>
    <Button>I'm a themed button!</Button>
  </MyTheme.ThemeProvider>
)
CopiedCopy

Create a custom theme

Create a new component to serve as the darker theme for your apps' 'dark mode':

$bit
Copiedcopy

Create an object with new design token values. Use the previously generated MyThemeScheme type to ensure a proper use of design tokens.

/** @filename: my-dark-theme.ts */

import type { MyThemeSchema } from '@my-org.my-scope/my-theme';

export const darkThemeOverrides: Partial<MyThemeSchema> = {
  primaryColorBackground: '#151d33',
  primaryColorText: '#c9cdd6',
};
CopiedCopy

Make this object available to other components by exporting it from its index.ts file:

/** @filename: index.ts */
export { darkThemeOverrides } from './dark-theme';
CopiedCopy

Use the custom theme

Pass the overrides object to your default theme component to override or extend its design token values:

import { MyTheme } from '@my-org.my-scope/my-theme';
import { darkThemeOverrides } from '@my-org.my-scope/my-dark-theme';
import { Button } from '@my-org.my-scope/button';

() => (
  <MyTheme.ThemeProvider overrides={darkThemeOverrides}>
    <Button>I'm a themed button!</Button>
  </MyTheme.ThemeProvider>
)
CopiedCopy