Theming

A primer into the (new!) theming architecture behind Evergreen.

Introduction

Evergreen ships with an all-new, extensible theming architecture that lets end-users customize the look and feel of the components in Evergreen as you want. You can target arbitrary styles and states of each component to achieve maximum flexibility and alignment with your brand. This page goes over some of the options and background of the theming architecture, and how you can leverage it in your apps.

Default Theme

Evergreen currently ships with one theme representing our current brand. You can extend it with your own styles or create your own theme from scratch by taking a look at how the defaultTheme object is constructed.

Using Default Theme

Theme API Overview

Most components go by the following API, which takes inspiration from the useStyleConfig API pioneered by the folks over at Chakra UI. There are three parts to the theming system:

  • baseStyle: These are the default "base" styles applied to a component. This includes targeting pseudo-states that some components offer, including _hover, and _active.
  • appearances: These are the custom styles that you can apply to a component based on the appearance prop. Note that some components don't have this.
  • sizes: These are additional props that you can alias via a size property. Again, note that some components don't have this, and you can only configure baseStyle.

An example implementation that leverages some of these props to create a custom button is below:

Custom Appearances

The theming API also supports custom appearances, so you can bring your own verbiage and nomenclature to how you want to define Evergreen styles. We are still working out TypeScript support for this, so use at your own discretion! For the example below, we are going to take those same styles, and apply them to respond to a prop appearance="superdanger".

Theme Utilities

mergeTheme is a utility function that allows you to deeply merge a partial Theme object onto another one, such as the defaultTheme, without having to do multiple object spreads (i.e. ...defaultTheme, ...defaultTheme.colors, ...defaultTheme.components). If you're using TypeScript, it returns a type union of DestinationTheme & SourceTheme, where DestinationTheme is the first argument (such as defaultTheme) and SourceTheme is the type of the second argument (which can be explicitly or implicitly typed).

useTheme is a hook that returns the current theme object from the ThemeProvider. It is generically typed, so you can use it to provide a strongly typed version of your own theme.

Theme Shape

All in all, the shape of the theme object looks as follows. Use this as your baseline direction for piecing together and building theme objects. For TypeScript users, you should be able to use parts of the generic Theme for constructing your own theme, or DefaultTheme which is a more specific version of the interface for defaultTheme.

export default {
  colors,
  fills,
  intents,
  radii,
  shadows,
  fontFamilies: {
    display: '...',
    ui: '...',
    mono: '...',
  },
  fontSizes: {
    /* ... */
  },
  fontWeights: {
    /* ... */
  },
  letterSpacings: {
    /* ... */
  },
  lineHeights: [
    /* ... */
  ],
  zIndices,
  components: {
    Alert: {
      /* ... */
    },
    Avatar: {
      /* ... */
    },
    Badge: {
      /* ... */
    },
    Button: {
      /* ... */
    },
    Card: {
      /* ... */
    },
    Checkbox: {
      /* ... */
    },
    Code: {
      /* ... */
    },
    DialogBody: {
      /* ... */
    },
    DialogFooter: {
      /* ... */
    },
    DialogHeader: {
      /* ... */
    },
    Group: {
      /* ... */
    },
    Heading: {
      /* ... */
    },
    Icon: {
      /* ... */
    },
    InlineAlert: {
      /* ... */
    },
    Input: {
      /* ... */
    },
    Label: {
      /* ... */
    },
    List: {
      /* ... */
    },
    Link: {
      /* ... */
    },
    MenuItem: {
      /* ... */
    },
    Option: {
      /* ... */
    },
    Pane: {
      /* ... */
    },
    Paragraph: {
      /* ... */
    },
    Radio: {
      /* ... */
    },
    Select: {
      /* ... */
    },
    Spinner: {
      /* ... */
    },
    Switch: {
      /* ... */
    },
    Tab: {
      /* ... */
    },
    Table: {
      /* ... */
    },
    TableCell: {
      /* ... */
    },
    TableHead: {
      /* ... */
    },
    TableRow: {
      /* ... */
    },
    TagInput: {
      /* ... */
    },
    Text: {
      /* ... */
    },
    TextDropdownButton: {
      /* ... */
    },
    Tooltip: {
      /* ... */
    },
  },
}