A Theme is a structured collection of design decisions that change the appearance of a UI library. An Amplify UI theme is a structured object of design tokens, breakpoints, and overrides. The goals of the Amplify UI theme are:
- Leverage platform technologies as much as possible for performance and broad support. This means plain CSS and CSS variables. You can always fall back to writing CSS (or a pre-processer to CSS like Sass).
- Use framework-specific patterns to provide an easier developer experience. This means providing an extendable theme which generates CSS and CSS variables for your application.
Getting started
Step 1: Wrap your App with ThemeProvider
import { ThemeProvider } from '@aws-amplify/ui-react';
const App = (
<ThemeProvider>
<MyApp>/* AmplifyUI */</MyApp>
</ThemeProvider>
);
Step 2: Use the theme to style components
// Option 1: Use the theme through component variations
import { Text } from '@aws-amplify/ui-react';
const MyComponent = ({ children }) => {
return <Text variation="primary">{children}</Text>;
};
// Option 2: Get the theme object through the useTheme hook and style components with it
import { Text, useTheme } from '@aws-amplify/ui-react';
const MyComponent = ({ children }) => {
const { tokens } = useTheme();
return <Text color={tokens.colors.font.tertiary}>{children}</Text>;
};
Optional: To extend or override a token in the default theme, create a custom theme:
import { ThemeProvider, Theme } from '@aws-amplify/ui-react';
// Step 1: Create a new Theme with your custom values
const theme = {
name: 'my-theme',
tokens: {
colors: {
font: {
primary: { value: '#008080' },
// ...
},
},
},
};
// Step 2: Pass the new theme to `ThemeProvider`
// this will apply the theme to all Amplify UI components
const App = (
<ThemeProvider theme={theme}>
<MyApp>/* AmplifyUI */</MyApp>
</ThemeProvider>
);
import { ThemeProvider, Theme } from '@aws-amplify/ui-react';
// Step 1: Create a new Theme with your custom values
const theme: Theme = {
name: 'my-theme',
tokens: {
colors: {
font: {
primary: { value: '#008080' },
// ...
},
},
},
};
// Step 2: Pass the new theme to `ThemeProvider`
// this will apply the theme to all Amplify UI components
const App = (
<ThemeProvider theme={theme}>
<MyApp>/* AmplifyUI */</MyApp>
</ThemeProvider>
);
Theme object
The theme object is where you define tokens for color palette, font stacks, spacing, and more. By default it will extend from the defaultTheme
Amplify UI provides.
export const myTheme = {
name: 'my-theme',
tokens: {
colors: {
font: {
primary: { value: 'red' },
},
},
},
};
CSS
You can theme Amplify UI using CSS and CSS variables if you do not want to use the theme object structure. Amplify UI components use plain CSS so styling components can be done with CSS (or a pre-processor like Sass). All of the design tokens defined in the Amplify theme are CSS variables which can be overridden:
:root, [data-amplify-theme] {
--amplify-colors-font-primary: #333;
/* you can also use references: */
--amplify-colors-font-secondary: var(--amplify-colors-neutral-60);
}
If you want more customization than the design tokens provide, you can also override the CSS for components:
/* All components have a class name starting with `amplify` */
.amplify-button {
font-size: 2rem;
padding: 1rem 2rem;
background: none;
border: 2px solid black;
}
.amplify-button:hover {
background: gray;
}
Or if you prefer you can use alternative styling with a styling libraries
Unstyled
Amplify UI components can be use unstyled if you want full control over the look-and-feel. To use the components unstyled, import them as you normally would and do not import the CSS.
import { Button, Card } from '@aws-amplify/ui-react';
// don't import the CSS:
// import '@aws-amplify/ui-react/styles.css';
export const App = () => {
// ...
};
Theme Structure
Design Tokens
Amplify UI uses Design Tokens for storing design decisions and is the primary way to theme the components. Design tokens are categorized by type under namespaces; for example, colors go under the colors
namespace. Stitches, Chakra-UI, and Evergreen use a similar convention for organizing their design tokens.
*/
interface BaseTokens<Output extends OutputVariantKey = unknown> {
borderWidths?: BorderWidths<Output>;
colors?: Colors<Output>;
fonts?: Fonts<Output>;
fontSizes?: FontSizes<Output>;
fontWeights?: FontWeights<Output>;
lineHeights?: LineHeights<Output>;
opacities?: Opacities<Output>;
outlineOffsets?: OutlineOffsets<Output>;
outlineWidths?: OutlineWidths<Output>;
radii?: Radii<Output>;
shadows?: Shadows<Output>;
space?: Space<Output>;
time?: Time<Output>;
transforms?: Transforms<Output>;
}
References
One import thing about design tokens is they can reference other design tokens. The default theme tokens use references a lot to make a robust system where you can modify a few tokens to have a large effect. The syntax for design token references follows the draft W3C Design Tokens Community Group specification
const myTheme = {
name: 'my-theme',
tokens: {
colors: {
font: {
// references colors.neutral.100
// because the default theme defines that color already
// we don't need to re-define it here
primary: { value: '{colors.neutral.100.value}' },
},
},
},
};
Component token definitions
Amplify UI follows a consistent pattern when defining tokens for a component's states and variations. This is helpful for discovering what tokens are available for theming different aspects of a component. Amplify UI uses the following pattern:
component[modifier][_state][child];
A modifier
could be a distinct style variation, like the primary or link variant for the Button component. A modifier could also be a variation based on size, such as small
, medium
, or large
.
State typically refers to a change in the component due to an interaction from the user or application itself, such as hover, focus, loading or disabled. Note: Amplify UI prefixes states with an underscore, _
, to help distinguish state names from modifier names.
The Button component is a good example of a token definition that includes multiple states and modifiers and follows this pattern:
export const button = {
// ... default tokens
// states
_hover: {},
_focus: {},
_loading: {},
_disabled: {},
// variations with states
primary: {
_hover: {},
_focus: {},
_loading: {},
_disabled: {},
},
// size modifiers
small: {},
large: {},
};
Compiled, this would create the following CSS custom properties:
--amplify-component-button-hover-token: value,
--amplify-component-button-focus-token: value,
--amplify-component-button-hover-loading: value,
--amplify-component-button-focus-disabled: value,
--amplify-component-button-primary-hover-token: value,
--amplify-component-button-primary-focus-token: value,
--amplify-component-button-primary-hover-loading: value,
--amplify-component-button-primary-focus-disabled: value,
--amplify-component-button-small-token: value,
--amplify-component-button-large-token: value,
Fonts
Amplify UI allows custom fonts to be used in the theme. The font tokens are defined in the fonts
namespace. You can define your primary font stack and fallback font stack values the same way you would do in a CSS font-family
rule.
const myTheme = {
name: 'my-theme',
tokens: {
fonts: {
default: {
variable: { value: 'Raleway, sans-serif' },
static: { value: 'Raleway, sans-serif' },
},
},
},
};
Breakpoints
Breakpoints allow you to set media query breakpoints for responsive design. You can then define breakpoint-specific token overrides or use the breakpoints for different layouts in Javascript.
type BreakpointKey = 'base' | 'small' | 'medium' | 'large' | 'xl' | 'xxl';
export interface Breakpoints {
values: Record<BreakpointKey, number>;
defaultBreakpoint: BreakpointKey;
}
// Breakpoint unit is in pixels
export const breakpoints: Breakpoints = {
values: {
base: 0,
small: 480, // breakpoint unit is px
medium: 768,
large: 992,
xl: 1280,
xxl: 1536,
},
defaultBreakpoint: 'base',
};
You can modify default breakpoints in your theme's breakpoints
definition:
const myTheme = {
name: 'my-theme',
breakpoints: {
// Will be deep merged with the default theme
// so you don't have to override all the breakpoint values
values: {
// default unit is 'em'
medium: 50,
},
},
//...
};
Note: Unfortunately right now CSS media queries do not support CSS variables so there is no way to customize the breakpoints using only CSS.
Overrides
An override
is a collection of design tokens that should take precedence in certain situations, like dark mode. Overrides are built into the theme configuration, but kept separate, so that Amplify UI can use CSS for overriding parts of the theme.
import { defaultTheme } from '@aws-amplify/ui-react';
export const theme = {
name: 'my-theme',
overrides: [
{
colorMode: 'dark',
tokens: {
colors: {
neutral: {
10: { value: defaultTheme.tokens.colors.neutral[100].value },
20: { value: defaultTheme.tokens.colors.neutral[90].value },
40: { value: defaultTheme.tokens.colors.neutral[80].value },
80: { value: defaultTheme.tokens.colors.neutral[40].value },
90: { value: defaultTheme.tokens.colors.neutral[20].value },
100: { value: defaultTheme.tokens.colors.neutral[10].value },
},
black: { value: '#fff' },
white: { value: '#000' },
},
},
},
{
breakpoint: 'large',
tokens: {
space: {
small: { value: '1rem' },
medium: { value: '2rem' },
large: { value: '3rem' },
},
},
},
],
};
You can override design tokens in CSS by using a media query or adding extra selectors to [data-amplify-theme="{theme.name}"]
.
@media (prefers-color-scheme: dark) {
[data-amplify-theme='my-theme'] {
--amplify-colors-black: #fff;
--amplify-colors-white: #fff;
}
}
[data-amplify-theme='my-theme'].disco {
--amplify-colors-font-primary: pink;
}
Merging multiple themes
If you have multiple themes, you can extend your base theme using the createTheme
function.
import { createTheme, defaultTheme } from '@aws-amplify/ui';
// by default, createTheme extends the defaultTheme.
export const baseBrandTheme = createTheme({
name: 'base-brand-theme',
tokens: {
colors: {
font: {
primary: { value: 'red' },
},
},
},
});
export const otherBrandTheme = createTheme(
{
name: 'other-brand-theme',
tokens: {
colors: {
font: {
primary: { value: 'blue' },
},
},
},
},
baseBrandTheme
);
// The 2nd argument is the base theme to be extended
// if it is omitted, it will use the defaultTheme