Theming

In this guide, we will cover the basics of theming Base Web components. We'll go over the following topics:

Benefits

The theming system that ships with Base Web provides developers with a few useful features:

Centralized Customization

The theme object allows you to configure system-wide styling values in one place. All of our components reference the theme object when assigning style values. This removes the need for certain types of styling overrides and frees you from maintaining custom versions of components simply because your interface requires a different color palette or typography scale.

Light and Dark Themes

Because our theme is provided to components through React’s context system, we can dynamically change the theme at any time. The most common use case is toggling between Base Web’s light and dark themes, but there are many other possibilities. For example, you could introduce controls for modifying layout density or you could let users customize your interface in real-time.

Access to Design Tokens

The theme object is easily accessible when working with Base Web’s various styling utilities (including useStyletron, styled, withStyle, and Block). This makes it easy to use consistent values when extending Base components or styling your components. Using consistent design tokens results in faster development, smaller bundle sizes, and better-looking interfaces.

Setup

Even if you are not interested in creating a custom theme, we require that you select a theme as part of Base Web’s boilerplate setup. Our components require a theme to assign styles.

The theme object itself is nothing special. It is just an object with specific properties (a specific “shape”) that can be passed to our ThemeProvider or BaseProvider. Base Web components will then reference this object when assigning stylistic values such as color or font size.

Default themes

We provide two themes out of the box, LightTheme and DarkTheme, which style components in light & dark variants. If you don’t want to customize Base Web, you can use either of these ready-to-use themes as-is:

import React from 'react';
import {LightTheme} from 'baseui';
export default function App() {
return (
<ThemeProvider theme={LightTheme}>
I can use themed Base Web components here!
</ThemeProvider>
);
}

Toggling light and dark

If you want to allow for toggling between the two themes at runtime, you need to allocate some state for determining which theme is passed to ThemeProvider.

import React from 'react';
import {LightTheme, DarkTheme, ThemeProvider} from 'baseui';
import {Button} from 'baseui/button';
const THEME = {
light: 'light',
dark: 'dark',
};
export default function App() {
const [theme, setTheme] = React.useState(THEME.light);
return (
<ThemeProvider theme={theme === THEME.light ? LightTheme : DarkTheme}>
<Button
onClick={() =>
setTheme(theme === THEME.light ? THEME.dark : THEME.light)
}
>
Toggle light/dark theme!
</Button>
</ThemeProvider>
);
}

BaseProvider

While ThemeProvider will provide your theme object to any descendent Base Web components, we recommend using BaseProvider at the root of your application. BaseProvider combines the functionality of the ThemeProvider with our LayersManager utility. You should only use ThemeProvider directly if you need to provide a separate theme to a subtree of your application.

import React from 'react';
import {BaseProvider, LightTheme} from 'baseui';
import {Provider as StyletronProvider} from 'styletron-react';
import {Client as Styletron} from 'styletron-engine-atomic';
const engine = new Styletron();
export default function App() {
return (
<StyletronProvider value={engine}>
<BaseProvider theme={LightTheme}>
I can use themed Base Web stuff here!
</BaseProvider>
</StyletronProvider>
);
}

This is all the setup required to use our default themes (including all of the Styletron boilerplate). Now let’s look at how you might configure Base Web with a custom theme.

A Custom Theme

You could start writing a theme object from scratch if you were so inclined. We provide Flow and Typescript definitions for the theme object so that you won't miss any of the required theme properties. Another approach might be to take one of our default theme objects and start reassigning properties to your desired values.

However, before you go down either of these roads, you should know that Base Web exports a handy factory function for creating theme objects.

createTheme

As you might expect, createTheme is a factory function for Base Web compliant theme objects. We use createTheme to construct our default light and dark themes.

import {createTheme} from 'baseui';
const theme = createTheme(/* primitives */, /* overrides */);

primitives

Our theme object has a lot of properties. Most of these properties are assigned to a subset of reoccurring values. We call these reoccurring values, theme building primitives. Primitives are made up mostly of colors, (primary, accent, etc), gradients of those colors (primary100, primary200, etc) as well as a primaryFontFamily. To see the exact shape for primitives, reference the Flow or Typescript definitions.

These are passed as the first (and only required) argument for createTheme.

import {createTheme, lightThemePrimitives} from 'baseui';
const primitives = {
...lightThemePrimitives,
accent: "#F127E4", // hot pink
accent50: "#FDEDFC",
accent100: "#FCD3F9",
accent200: "#F89FF3",
accent300: "#F45AEA",
accent400: "#F127E4",
accent500: "#B71DAD",
accent600: "#901788",
accent700: "#600F5B"
};
const theme = createTheme(primitives);

createTheme will use these primitive values to fill out all of the required properties on the theme object. Note, you don't have to assign every primitive from scratch. You can modify our default primitives before passing them into createTheme, as we do in the example above. createTheme then essentially maps this small set of primitives to the much larger set of theme properties in a sensible manner.

But what if the default mapping (of primitives to theme properties) is not quite what you want?

overrides

The second parameter for createTheme is an overrides object.

The overrides object will be deep-merged with the initial result of mapping primitives to all of the theme properties. overrides is just a convenient shortcut for deep-merging assignments onto your theme object.

import {createTheme, lightThemePrimitives} from 'baseui';
const primitives = {
...lightThemePrimitives,
accent: "#F127E4", // hot pink
accent50: "#FDEDFC",
accent100: "#FCD3F9",
accent200: "#F89FF3",
accent300: "#F45AEA",
accent400: "#F127E4",
accent500: "#B71DAD",
accent600: "#901788",
accent700: "#600F5B"
};
const overrides = {
colors: {
buttonSecondaryFill: primitives.accent100,
buttonSecondaryText: primitives.accent,
buttonSecondaryHover: primitives.accent200,
buttonSecondaryActive: primitives.accent300,
buttonSecondarySelectedFill: primitives.accent200,
buttonSecondarySelectedText: primitives.accent,
buttonSecondarySpinnerForeground: primitives.accent700,
buttonSecondarySpinnerBackground: primitives.accent300,
},
};
const theme = createTheme(lightThemePrimitives, overrides);

In this example, we first reassign the accent color on our theme primitives to hot pink. Then we use overrides to re-map the secondary button colors to use our hot pink accent colors rather than the default primary colors.

Between parameters and overrides, you should have a fair amount of control over theme object creation. Ideally, you can use createTheme to quickly construct a Base Web compliant theme object.

Customizing Typography

The primitives object has an optional property, primaryFontFamily, which allows you to set a custom font family string for typography components.

import {createTheme, lightThemePrimitives} from 'baseui';
const primitives = {
...lightThemePrimitives,
primaryFontFamily: 'Verdana',
};
const theme = createTheme(primitives);

In this example, we assign primaryFontFamily to the vastly underrated Verdana font family. With this change, all Base Web components will use Verdana.

But what if we wanted to use Georgia for Display typography? For this purpose, you would need to use overrides.

import {createTheme, lightThemePrimitives} from 'baseui';
const primitives = {
...lightThemePrimitives,
primaryFontFamily: 'Verdana',
};
const overrides = {
typography: {
DisplayLarge: {
fontFamily: 'Georgia',
},
DisplayMedium: {
fontFamily: 'Georgia',
},
DisplaySmall: {
fontFamily: 'Georgia',
},
DisplayXSmall: {
fontFamily: 'Georgia',
},
},
};
const theme = createTheme(primitives, overrides);

Initially, Verdana will be mapped to every typography value as a primitive. By using overrides, we can easily deep-merge some reassignments over the theme object. In this case, we set our Display typography levels to use the Georgia font. We use this same overrides technique for our applications.

Customizing Icons

The theme object is also the source for customizing the icons we use in Base Web components.

You can use any React component as a replacement for an icon, as long as they accept the following properties:

  • size, to set their width and height
  • color, to set their colors
  • title, to set the title of the icon for accessibility purposes

You can find more about the Icon API here and also refer to this list of available icons.

The example below overrides the ChevronLeft icon with the ArrowLeft icon, making the pagination appear slightly different.

All the icons that can be found on the Icon component page can be overridden using this technique.

Pagination icon overrides

of 10

Customizing the Theme Type

There may be a scenario where you want to extend our default theme. We do not recommend removing theme properties, as this may lead to a run-time error when a component references a missing property. However, adding new properties are perfectly acceptable.

// @flow
import type {ThemeT} from 'baseui';
const CustomThemeT = ThemeT & {customColor: string};

You will notice, however, that you can't use createTheme with your CustomThemeT. If you want to use createTheme, we recommend that you first create a normal theme with createTheme and then extend it with your custom properties.

import {createTheme} from 'baseui';
import type {ThemeT} from 'baseui';
const CustomThemeT = ThemeT & {customColor: string};
const theme = createTheme(/*primtives, overrides*/);
const customTheme: CustomThemeT = {
...theme,
customColor: 'pink',
};
function App() {
return <BaseProvider theme={customTheme}>{/* ... */}</BaseProvider>;
}

Note, you can use the custom type theme object with our provider so long as it extends the default ThemeT. Base Web components will essentially ignore the additional properties, but the theme object will be available in component overrides.

If you are using Flow and want to use a custom theme shape with our various styling utilities, you can create new styled, withStyle, and useStyletron methods which respect your custom theme type.

// my-style-utils.js
import {
createThemedStyled,
createThemedWithStyle,
createThemedUseStyletron
} from 'baseui';
export const themedStyled = createThemedStyled<CustomThemeT>();
export const themedWithStyle = createThemedWithStyle<CustomThemeT>();
export const themedUseStyletron = createThemedUseStyletron<CustomThemeT>();

You can import these custom themed utilities wherever you want access to your custom theme.

import {themedUseStyletron as useStyletron} from 'path/to/my-style-utils.js';
function Foo() {
const [css, theme] = useStyletron();
return <div className={css({color: theme.customColor})}>Okay</div>;
}

Using the Theme

The theme object acts as a centralized API for customizing global styling properties, but it also allows developers to use consistent values when extending components or styling new components. Base Web makes access to the theme object a priority across all of our styling utilities and component overrides so that you can always use the right value.

Let's look at how you can use the theme object while extending our components and when building out your interfaces.

Extension

There are two supported methods for styling Base Web components: withStyle and overrides. Both have access to the nearest ancestor theme object.

withStyle

Whenever Base Web exports a Styletron styled component we prefix that component with Styled. You can use withStyle to extend a styled component with your custom styles:

import {withStyle} from 'baseui';
import {StyledBaseButton} from 'baseui/button';
const MyButton = withStyle(StyledBaseButton, ({$theme}) => {
return {
color: $theme.colors.accent,
};
});

Notice how the second parameter is a function that is passed an object with a $theme property. This property provides a reference to your theme object.

overrides

For in-depth information on overrides, check out the official documentation. As it relates to theming, any overrides style function will be passed a reference to the nearest ancestor theme object to make consistent styling easy.

import {Button} from 'baseui/button';
function App() {
return (
<Button
overrides={{
Base: {
style: ({$theme}) => {
return {
marginTop: $theme.sizing.scale500,
};
},
},
}}
/>
);
}

Creation

Theming and extending the built-in Base Web components will get you a long way, but sometimes you need to create something new. This is where out other styling utilities come into play.

useStyletron

import {useStyletron} from 'baseui';
function App() {
const [css, theme] = useStyletron();
return (
<div
className={css({
color: theme.colors.accent,
})}
>
Hello
</div>
);
}

useStyletron lets you generate class names to be passed directly to an element’s classNames prop. useStyletron is a wrapper around the built-in Styletron hook. Our wrapper makes sure you have access to your theme by including it as a second item in the hook’s returned array. This makes it easy to style arbitrary elements without having to create new components or replicate design tokens.

styled

import {styled} from 'baseui';
const Title = styled('div', ({$theme}) => {
return {
color: $theme.colors.accent,
};
});
function App() {
return <Title>Hello</Title>;
}

styled creates a new Styletron styled component. We provide a version that wraps Styletron’s default styled function so that it gets a reference to your theme object. We use this styled function to create all of our styled-components in Base Web.

Block

import {Block} from 'baseui/block';
function App() {
return <Block color="accent" />;
}

Block is a unique tool. It allows you to assign common styling attributes using theme tokens, without having to do theme property lookups yourself. The intention is to let you quickly assign design system tokens to common style attributes, such as font, color, margin, etc.

Theme Properties

The theme object organizes its various properties according to their respective concerns.

Animation

Control animation durations and easing functions.

timing100
0.25s
timing400
0.4s
timing700
0.6s
timing1000
1s
easeOutCurve
cubic-bezier(.2, .8, .4, 1)
easeInCurve
cubic-bezier(.8, .2, .6, 1)
easeInOutCurve
cubic-bezier(0.4, 0, 0.2, 1)

Borders

Control border and border radius styles.

border100
solid
1px
hsla(0, 0%, 0%, 0.04)
border200
solid
1px
hsla(0, 0%, 0%, 0.08)
border300
solid
1px
hsla(0, 0%, 0%, 0.12)
border400
solid
1px
hsla(0, 0%, 0%, 0.16)
border500
solid
1px
hsla(0, 0%, 0%, 0.2)
border600
solid
1px
hsla(0, 0%, 0%, 0.24)
radius100
2px
radius200
4px
radius300
8px
radius400
12px

Breakpoints

Control the media query widths used to establish responsive breakpoints.

small
320px
medium
600px
large
1136px

Colors

Control literal and semantic color values. These differ between light and dark themes.

primaryA
#000000
primaryB
#FFFFFF
accent
#276EF1
negative
#D44333
warning
#FFC043
positive
#21A453
backgroundPrimary
#FFFFFF
backgroundSecondary
#F6F6F6
backgroundTertiary
#EEEEEE
backgroundInversePrimary
#000000
backgroundInverseSecondary
#1F1F1F
contentPrimary
#000000
contentSecondary
#545454
contentTertiary
#757575
contentInversePrimary
#FFFFFF
contentInverseSecondary
#CBCBCB
contentInverseTertiary
#AFAFAF
borderOpaque
#E2E2E2
borderTransparent
rgba(0, 0, 0, 0.08)
borderSelected
#000000
borderInverseOpaque
#333333
borderInverseTransparent
rgba(255, 255, 255, 0.2)
borderInverseSelected
#FFFFFF
backgroundStateDisabled
#F6F6F6
backgroundOverlayDark
rgba(0, 0, 0, 0.3)
backgroundOverlayLight
rgba(0, 0, 0, 0.08)
backgroundAccent
#276EF1
backgroundNegative
#D44333
backgroundWarning
#FFC043
backgroundPositive
#21A453
backgroundLightAccent
#EDF3FD
backgroundLightNegative
#FBEFEE
backgroundLightWarning
#FFF9EF
backgroundLightPositive
#F0FAF3
backgroundAlwaysDark
#000000
backgroundAlwaysLight
#FFFFFF
contentStateDisabled
#AFAFAF
contentAccent
#276EF1
contentNegative
#D44333
contentWarning
#C19132
contentPositive
#21A453
contentOnColor
#FFFFFF
borderStateDisabled
#F6F6F6
borderAccent
#9FBFF8
borderNegative
#ECACA5
borderWarning
#FFE3AC
borderPositive
#9EE2B8

Direction

Control the dir for components. It can be auto, rtl, or ltr.

Grid

Control the columns and gutters for LayoutGrid.

columns
4, 8, 12
gutters
16px, 36px, 36px
margins
16px, 36px, 64px
gaps
0
maxWidth
1280px

Lighting

Control shadows.

shadow400
0 1px 4px hsla(0, 0%, 0%, 0.16)
shadow500
0 2px 8px hsla(0, 0%, 0%, 0.16)
shadow600
0 4px 16px hsla(0, 0%, 0%, 0.16)
shadow700
0 8px 24px hsla(0, 0%, 0%, 0.16)

Media Query

Control media queries added to the theme for convenience.

small
@media screen and (min-width: 320px)px
medium
@media screen and (min-width: 600px)px
large
@media screen and (min-width: 1136px)px

Name

Control the name of your theme. For example, light-theme.

Sizing

Control spacing and sizing.

scale0
2px
scale100
4px
scale200
6px
scale300
8px
scale400
10px
scale500
12px
scale550
14px
scale600
16px
scale650
18px
scale700
20px
scale750
22px
scale800
24px
scale900
32px
scale1000
40px
scale1200
48px
scale1400
56px
scale1600
64px
scale2400
96px
scale3200
128px
scale4800
192px

Typography

Control typography family, size, weight, and height.

ParagraphXSmall
12px
normal
20px
ParagraphSmall
14px
normal
20px
ParagraphMedium
16px
normal
24px
ParagraphLarge
18px
normal
28px
LabelXSmall
12px
500
16px
LabelSmall
14px
500
16px
LabelMedium
16px
500
20px
LabelLarge
18px
500
24px
HeadingXSmall
20px
500
28px
HeadingSmall
24px
500
32px
HeadingMedium
28px
500
36px
HeadingLarge
32px
500
40px
HeadingXLarge
36px
500
44px
HeadingXXLarge
40px
500
52px
DisplayXSmall
36px
500
44px
DisplaySmall
44px
500
52px
DisplayMedium
52px
500
64px
DisplayLarge
96px
500
112px

Z-Index

Control the base z-index for the theme. This should not be used and will be removed in v10 of Base Web.