You can start using Reshaped with the theme we provide, but at some point, you might want to apply custom values to the design tokens and align them with your brand. To solve that, Reshaped has a command-line interface for creating new themes.
You need to create a reshaped.config.js file with the theme definitions next to your project's package.json file to add new themes.
const config = { themes: { productTheme: { color: { foregroundNeutral: { hex: "#1a1a1a", hexDark: "#fff" }, }, }, }, }; module.exports = config;
In this example, we have defined a theme that will only change foregroundNeutral token value. All other values are inherited from the default Reshaped theme. You can check all supported tokens and their format in the last section of this page.
When creating a new themes, you have to override each color token manually. If you don't have a requirement to only use specific color values – check out our documentation on generating the color palette automatically based on a single color value.
Now that you have a config file with theme definitions added, you can use Reshaped CLI to generate these themes. Let's add an NPM script to call the CLI to your package.json:
{ "scripts": { "build:themes": "reshaped theming --output src/themes" } }
Running yarn build:themes or npm run build:themes will take your added theme definitions from reshaped.config.js file and compile them into the src/themes folder. Script will create a folder for each theme and theme fragment with a variables files inside.
src └── themes ├── productTheme │ └── theme.module.css └── fragments └── twitter └── theme.module.css
With the themes built, you can now import them into your code. We can start by picking the productTheme theme we've just built and pass it to the Reshaped provider:
import { Reshaped } from "reshaped"; import "themes/productTheme/theme.css"; const Application = ({ children }) => <Reshaped theme="productTheme">{children}</Reshaped>;
Our product now uses a custom theme and has a new foregroundNeutral token value available. It still uses other tokens from the default Reshaped theme, which means Button component uses a violet color for its background.
Let's create a TwitterButton component with a different button background color with a twitter theme fragment. We can use a Theme utility to define a theme just for the components rendered inside it.
import { Button, Theme } from "reshaped"; import "themes/fragments/twitter/theme.css"; const TwitterButton = (buttonProps) => ( <Theme name="twitter"> <Button {...buttonProps} /> </Theme> );
This concept is called Scoped theming, and you can learn more about it in a separate section.
Even though reshaped.config.js is a Javascript file, you can use comments to enable type autocompletion:
/** @type {import('reshaped').ReshapedConfig} */ const config = { themes: { twitter: { color: { backgroundPrimary: { hex: "#1da1f2" }, backgroundPrimaryHighlighted: { hex: "#1a90da" }, }, }, }, }; module.exports = config;
In addition to themes, reshaped.config.js allows you to create theme fragments. Theme fragment is a subset of a theme values overrides. By using theme fragments, you can save bundle size as your theme output will contain only the tokens you have changed instead of the whole theme.
const config = { themeFragments: { twitter: { color: { backgroundPrimary: { hex: "#1da1f2" }, backgroundPrimaryHighlighted: { hex: "#1a90da" }, }, }, }, }; module.exports = config;
This is quite helpful when you're customizing a specific part of the product but don't need to apply this customization to the whole page. For instance, you can create a Twitter theme fragment to implement a TwitterButton component.
Another benefit is that it's easier to combine themes that way. For example, if your product has two themes and you need to render the TwitterButton in both themes, you won't have to create all combinations of themes yourself. Instead, you can create two main themes and a Twitter theme fragment that will inherit the correct token values from the currently used theme.
Reshaped semantic tokens are aiming to provide a limited number of tokens that should cover most of the use cases for building interfaces. However, there will always be edge cases where semantic tokens might feel limiting. For example, imagine you're building a chart component. You need a few custom colors for it but you also want to make sure they support dark mode the same way all Reshaped components do.
To support that, you can add any custom key for all the tokens in the theme definition and they will be compiled to css alonside all other theme token values.
const config = { themes: { productTheme: { color: { foregroundNeutral: { hex: "#1a1a1a", hexDark: "#fff" }, chartTomato: { hex: "#ff6347", hexDark: "#b8412c" } }, }, }, };
In case you're adding new background colors, you might also want to generate on colors for them, same as we do for the default tokens. You can use themeOptions.generateOnColorsFor option in the config. It will keep all of the default generated on colors and will additionally generate on colors for the tokens you list there, resolving them to black or white value based on the contrast ratio.
const config = { themes: { productTheme: { color: { backgroundChart: { hex: '#ff6347' } }, }, }, themeOptions: { generateOnColorsFor: ['backgroundChart'] } }
When customizing background color token values, Reshaped automatically generates on color values. They get resolved to the white or black color based on background color contrast ratio. You can change these defaults using the themeOptions.onColorValues configuration.
It allows you to pass a resolved color hexLight and hexDark mode values for each of the supported theme colors:
const config = { ..., themeOptions: { onColorValues: { primary: { hexLight: "#...", // Replaces white color hexDark: "#...", // Replaces black color }, } } }
Theme is represented with an object that has token types as keys. Each token type contains a dictionary of token objects with their values.
module.exports = { themes: { [themeName]: { color: { backgroundNeutral: { ... }, ... }, unit: { radiusSmall: { ... }, ... }, fontFamily: { body: { ... }, ... }, fontWeight: { regular: { ... }, ... }, font: { displayLarge: { ... }, ... }, shadow: { raised: { ... }, ... } } }, themeFragments: { [fragmentName]: { ... } }, themeOptions: { generateOnColorsFor: ['backgroundChart'], onColorValues: { primary: { hexLight: "#...", hexDark: "#...", }, critical: { ... }, positive: { ... }, neutral: { ... } }, } }
In addition to the tokens in theme defintion, we also automatically generate dynamic token values. You can find more about them in the Design Tokens section.
Format:
{ color: { foregroundNeutral: { hex: '#000', hexDark: '#fff' } } }
Available token names:
foregroundNeutral foregroundNeutralFaded foregroundDisabled foregroundPrimary foregroundCritical foregroundPositive borderNeutral borderNeutralFaded borderDisabled borderPrimary borderPrimaryFaded borderCritical borderCriticalFaded borderPositive borderPositiveFaded backgroundNeutral backgroundNeutralFaded backgroundDisabled backgroundDisabledFaded backgroundPrimary backgroundPrimaryFaded backgroundCritical backgroundCriticalFaded backgroundPositive backgroundPositiveFaded backgroundNeutralHighlighted backgroundPrimaryHighlighted backgroundCriticalHighlighted backgroundPositiveHighlighted backgroundPage backgroundPageFaded backgroundElevationBase backgroundElevationRaised backgroundElevationOverlay black white
Format:
{ unit: { radiusSmall: { px: 2 } } }
Available token names:
base radiusSmall radiusMedium radiusLarge
Format:
{ fontFamily: { body: { family: 'Arial, sans-serif' } } }
Available token names:
body title
Format:
{ fontWeight: { regular: { weight: 400 } } }
Available token names:
regular medium bold black
Format:
{ font: { title3: { fontSize: { px: 40 }, lineHeight: { px: 44 }, fontWeightToken: 'bold', fontFamilyToken: 'display', } } }
Available token names:
title1 title2 title3 title4 title5 title6 featured1 featured2 featured3 body1 body2 body3 caption1 caption2
Format:
{ shadow: { raised: [ { offsetX: 0, offsetY: 1, blurRadius: 3, colorToken: "black", opacity: 0.08, }, { offsetX: 0, offsetY: 2, blurRadius: 2, colorToken: "black", opacity: 0.06, }, ] } }
Available token names:
raised overlay
Format:
{ viewport: { m: { minPx: 660 } } }
Available token names:
m l xl