Balance Logo
Balance
Reckon Design System

Theme

The theme is used to share global theme settings throughout the hierarchy of your application. The theme provider is included by the Core component.
Install
pnpm add @reckon-web/theme
Import usage
import {
themeLight,
packs,
makeTheme,
ThemeContext,
ThemeProvider,
useTheme,
usePack,
defaultPaletteResolver
} from '@reckon-web/theme';
  • Code
  • API

Theming is a mechanism by which a consistent look and feel can be applied to all the components within an application.

By using Balance’s theme, developers can easily customize all of their components by changing a set of universal variables, eliminating the need to modify individual components.

Breakpoints

Breakpoints underpin our responsive strategy for the entire system. Breakpoints are used internally by many components and are exposed externally to allow responsive configuration.

Shape

Breakpoints can be defined numerically/unitless for pixels 1024, or as a string to provide a unit as 64em. Below is the default set:

type Breakpoints = {
small: 576;
medium: 768;
large: 992;
xlarge: 1200;
};

These are the only breakpoints that should be used for responsive styling. If your UI requires additional non-standard media queries you should work with your designer to see what solutions are possible working within the system.

Usage

Hook

Import the useMediaQuery hook from the utils package.

import { useMediaQuery } from '@reckon-web/media-query';
Responsive values

To target a specific value that should change across breakpoints use the mq utility function. You can pass in a standard CSS object, and array values will be applied to their respective breakpoints.

Note that if a value is repeated across several breakpoints, a null value can be used to prevent generating unnecessary CSS.

Open in Playroom

This is achieved using Emotion's facepaint library.

Min / Max

For granular control over a group of styles use the minBreak and maxBreak utility functions. They accept a single argument, which must be one of the breakpoint keys supplied to the theme.

Components

Various layout primitives consume the breakpoints as props, similar to the "responsive values".

Padding and Margin

Use the padding and margin properties on the Box component. Each layout primitive extends the Box component:

Padding and Margin
Gap

Use the gap property on components like Stack and Cluster:

Colors

We use a combination of whitespace and color to aid usability and deliver clean experiences to end users. Color helps distinguish our brand, and delivers a consistent user experience across our various digital products.

Note that the colors provided to the theme are only half of the picture. These colours will be operated on to create the palette.

Shape

Because we perform functions on these colours they must be provided as hex values.

type Colors = {
background: string;
text: string;
primary: string;
accent: string;
danger: string;
warning: string;
success: string;
info: string;
decorative: string[];
};

Usage

Import the useTheme hook from the theme package.

import { useTheme } from '@reckon-web/theme';

You may access colors from the theme, though it's not recommended. Prefer using the resolved palette, also available on the theme object.

const { colors } = useTheme();

Core colors

These are the core colors that should be used in all designs and serve to distinguish digital assets associated with the brand.

Key
Default value
Example
background#ffffff
text#20262D
primary#007AFF
accent#3C3391

Functional colors

These colors should be reserved for functional elements or messages, such as indicating the outcome of an action or highlighting a success/failure. They should be used sparingly so as not to risk confusing their meaning.

Key
Default value
Example
danger#E30613
warning#FFB027
success#009754
info#0091EA

Decorative colors

Decorative colors don't have any semantic meaning; they're applied to avatars etc. They should not overlap with the core or functional colors.

Palette

The palette is the complete set of colour values used within the design system components. The default palette is derived from the colours provided to the theme.

Why?

The complete palette contains hundreds of colour declarations, which allow granular control over each component's appearance. Declaring all of them manually would be tedious, and is frankyly unnecessary.

To achieve a given appearance a theme author may only need to change a handful of values, but for any given appearance we can't know which values will need to change, so every value must be individually addressable.

How?

The default palette resolver takes the theme's colors and operates with color functions on them to create the entire palette.

Note that you may return a partial palette because it will be deep merged into the default palette for Core, and deep merged into the ancestral palette for nested ThemeProvider.

Show default palette
{
  "decorative": [
    "#EC4078",
    "#795CD4",
    "#007BC7",
    "#006A80",
    "#00804A"
  ],
  "actions": {
    "passive": "#585c62",
    "active": "#007AFF",
    "critical": "#E30613"
  },
  "status": {
    "default": "#84888c",
    "resolved": "#009754",
    "inprogress": "#0091EA",
    "rejected": "#E30613",
    "pending": "#3C3391",
    "attention": "#FFB027"
  },
  "global": {
    "accent": "#3C3391",
    "border": "#e4e5e6",
    "focusRing": "rgba(0,122,255,0.2)",
    "loading": "#007AFF"
  },
  "background": {
    "base": "#ffffff",
    "muted": "#f8f8f9",
    "dim": "#f2f2f2",
    "dialog": "#ffffff",
    "accent": "#e0deed",
    "active": "#d6eaff",
    "cautious": "#fff2dc",
    "critical": "#fbd7d9",
    "informative": "#d6edfc",
    "positive": "#d6eee4",
    "activeMuted": "#cce4ff",
    "cautiousMuted": "#ffefd4",
    "criticalMuted": "#f9cdd0",
    "informativeMuted": "#cce9fb",
    "positiveMuted": "#cceadd"
  },
  "text": {
    "base": "#20262D",
    "muted": "#585c62",
    "dim": "#909396",
    "accent": "#3C3391",
    "link": "#007AFF",
    "linkHover": "#3689ff",
    "active": "#007AFF",
    "cautious": "#FFB027",
    "critical": "#E30613",
    "informative": "#0091EA",
    "positive": "#009754"
  },
  "formInput": {
    "background": "#f2f2f2",
    "backgroundHovered": "#f2f2f2",
    "backgroundFocused": "#f2f2f2",
    "backgroundDisabled": "#f8f8f9",
    "backgroundInvalid": "#fbd7d9",
    "border": "#f2f2f2",
    "borderHovered": "#e4e5e6",
    "borderFocused": "#007AFF",
    "borderDisabled": "#f8f8f9",
    "borderInvalid": "#fbd7d9",
    "text": "#20262D",
    "textDisabled": "#585c62",
    "textPlaceholder": "#909396",
    "textInvalid": "#E30613"
  },
  "formControl": {
    "background": "#ffffff",
    "backgroundDisabled": "#e4e5e6",
    "border": "#e4e5e6",
    "interaction": "#007AFF",
    "foregroundChecked": "#ffffff",
    "foregroundDisabled": "#909396"
  },
  "segmentedControl": {
    "track": "#f2f2f2",
    "divider": "rgba(32,38,45,0.16)",
    "backgroundSelected": "#ffffff",
    "text": "#585c62",
    "textFocused": "#20262D",
    "textPressed": "#20262D",
    "textSelected": "#20262D"
  },
  "toggle": {
    "track": "#e4e5e6",
    "trackChecked": "#007AFF",
    "handle": "white"
  },
  "listItem": {
    "backgroundFocused": "#edeeee",
    "backgroundPressed": "#e4e5e6",
    "backgroundSelected": "#e4e5e6",
    "divider": "#e4e5e6",
    "text": "#20262D",
    "textFocused": "#20262D",
    "textPressed": "#20262D",
    "textSelected": "#20262D"
  },
  "menuItem": {
    "backgroundFocused": "#f2f2f2",
    "backgroundPressed": "#e4e5e6",
    "backgroundSelected": "#d7d8d9",
    "divider": "#e4e5e6",
    "text": "#20262D",
    "textFocused": "#20262D",
    "textPressed": "#20262D",
    "textSelected": "#20262D"
  },
  "button": {
    "active": {
      "background": "#007AFF",
      "backgroundFocused": "#2381ff",
      "backgroundPressed": "#006ff2"
    },
    "critical": {
      "background": "#E30613",
      "backgroundFocused": "#ed1c1a",
      "backgroundPressed": "#d50007"
    },
    "passive": {
      "background": "#585c62",
      "backgroundFocused": "#5f6369",
      "backgroundPressed": "#4e5258"
    }
  },
  "actionButton": {
    "background": "transparent",
    "backgroundFocused": "#f2f2f2",
    "backgroundPressed": "#e4e5e6",
    "backgroundSelected": "#e4e5e6",
    "border": "#e4e5e6",
    "borderFocused": "#dbdcdd",
    "borderPressed": "#c9cbcd",
    "borderSelected": "#c9cbcd",
    "text": "#585c62",
    "textFocused": "#20262D",
    "textPressed": "#20262D",
    "textSelected": "#20262D"
  }
}
Global

You can provide a paletteResolver to the Core component, which will influence every component within the app:

<Core
paletteResolver={(colors) => ({
/* palette object */
})}
>
App content
</Core>
Nested

If you need to influence only a portion of the app, you can provide a paletteResolver to a nested ThemeProvider component:

<ThemeProvider
paletteResolver={(colors) => ({
/* palette object */
})}
>
Section or portion
</ThemeProvider>

Shadow

Shadows are used to add a sense of elevation to an element, above the surface of the page.

{
S100: `0px 1px 2px rgba(0, 0, 0, 0.2)`,
S200: `0px 2px 4px rgba(0, 0, 0, 0.2)`,
S300: `0px 2px 8px rgba(0, 0, 0, 0.2)`,
S400: `0px 4px 16px rgba(0, 0, 0, 0.2)`,
S500: `-8px 8px 32px rgba(0, 0, 0, 0.2)`,
}

Access shadow from the theme object.

const { shadow } = useTheme();

Radii

Radii values are used to round elements, often in concert with sizing.

{
none: 0,
xsmall: 4,
small: 6,
medium: 8,
large: 12,
}

Access radii from the theme object.

const { radii } = useTheme();

Sizing

We use sizing to apply heights and widths to elements, maintaining a consistent appearance among components. Sizing is flexible based on the theme.

Shape

Sizing can be defined numerically/unitless for pixels (24), or as a string to provide a unit as (2.4rem).

Key
Default value
Example
xxsmall16
xsmall24
small32
base40
medium48
large56
xlarge72

Usage

Hook

Import the useTheme hook from the theme package.

import { useTheme } from '@reckon-web/theme';

Access sizing from the theme object.

Components

Various components consume the sizing keys as props.

Avatars

Avatars accept sizing keys.

LA
Buttons and Inputs

Buttons and inputs accept a limited set of the sizing keys.

Spacing

We use spacing to apply padding and margin to elements. Spacing can be applied uniformly or individually to the sides of an element. Spacing is flexible based on the theme. The default theme implements a base unit of 4px.

Shape

Spacing can be defined numerically/unitless for pixels (4), or as a string to provide a unit as (0.4rem).

Key
Default value
Example
xxsmall2
xsmall4
small8
medium12
large16
xlarge24
xxlarge32

Usage

Hook

Import the useTheme hook from the theme package.

import { useTheme } from '@reckon-web/theme';

Access spacing from the theme object.

Padding "large"

Components

Various layout primitives consume the spacing keys as props.

Padding and Margin

Use the padding and margin properties on the Box component. Each layout primitive extends the Box component:

Padding
Gap

Use spacing keys for the gap property on components like Stack and Cluster:

Typography

Typography helps to create the structure and hierarchy of a page. Typography also helps users read, absorb and comprehend content. Communication should be clear and easy to read. Our font choices are a reflection of this.

Shape

Below is the default typography configuration:

type Typography = {
fontFamily: {
monospace: 'Consolas, Menlo, Monaco, "Andale Mono", "Ubuntu Mono", monospace';
body: 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", sans-serif';
heading: 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", sans-serif';
};
fontSize: {
xsmall: '0.75rem';
small: '0.875rem';
medium: '1rem';
large: '1.125rem';
xlarge: '1.25rem';
xxlarge: '1.5rem';
xxxlarge: '1.875rem';
xxxxlarge: '2.25rem';
xxxxxlarge: '3rem';
xxxxxxlarge: '4rem';
};
fontWeight: {
regular: 400;
medium: 500;
semibold: 600;
bold: 700;
heavy: 800;
};
leading: {
tighter: 1;
tight: 1.2;
base: 1.4;
loose: 1.6;
looser: 1.8;
};
tracking: {
tighter: '-0.02em';
tight: '-0.01em';
base: '0em';
loose: '0.01em';
looser: '0.02em';
};
};

Usage

Hook

Import the useTheme hook from the theme package.

import { useTheme } from '@reckon-web/theme';

Access typography from the theme object.

pre content

Components

Various layout primitives consume the typography keys as props.

Text

The Text component supports leading and tracking.

Text content
Heading

The Heading component supports leading and tracking.

Heading content

Copyright © 2021 Reckon. Designed and developed in partnership with Thinkmill.