/researchExplain how to create a customized theme with EUI.
EUI uses a sophisticated JavaScript-based theming system that provides:
- Proxy-based theme construction with cascading and computed values
- Color mode support (light and dark themes)
- Override and extension capabilities for all theme tokens
- Component-specific tokens for granular customization
- CSS-in-JS integration with Emotion
There are three main approaches to customizing EUI themes, in order of complexity:
Override specific theme tokens using the modify prop on EuiProvider or EuiThemeProvider.
import { EuiProvider } from '@elastic/eui';
const overrides = {
colors: {
LIGHT: {
lightShade: '#d3e6df',
primary: '#006bb4'
},
DARK: {
lightShade: '#394c4b',
primary: '#1ba9f5'
},
},
size: {
base: 18, // Changes the base size from 16px to 18px
},
};
const App = () => (
<EuiProvider modify={overrides}>
<YourComponents />
</EuiProvider>
);Key Points:
- Override the minimum number of tokens needed
- Always provide values for both
LIGHTandDARKcolor modes - Changes cascade throughout the theme system
- Most themes require fewer than a dozen variable overrides
Extend the theme with your own custom tokens using computed values:
import {
EuiThemeProvider,
useEuiTheme,
computed,
shade,
tint
} from '@elastic/eui';
// Define TypeScript interface for your custom keys
interface ThemeExtensions {
colors: {
customColorPrimary: string;
customColorPrimaryHighlight: string;
customColorPrimaryText: string;
};
}
const primaryOverrides = {
colors: {
LIGHT: {
customColorPrimary: 'rgb(29, 222, 204)',
customColorPrimaryHighlight: computed(
(customColorPrimary) => tint(customColorPrimary, 0.8),
'colors.customColorPrimary'
),
customColorPrimaryText: computed(
(customColorPrimary) => shade(customColorPrimary, 0.8),
'colors.customColorPrimary'
),
},
DARK: {
customColorPrimary: 'rgb(29, 222, 204)',
customColorPrimaryHighlight: computed(
([customColorPrimary]) => shade(customColorPrimary, 0.8),
['colors.customColorPrimary']
),
customColorPrimaryText: computed(
([customColorPrimary]) => tint(customColorPrimary, 0.8),
['colors.customColorPrimary']
),
},
},
};
// Use your custom theme keys
const MyComponent = () => {
const { euiTheme } = useEuiTheme<ThemeExtensions>();
return (
<div
css={{
background: euiTheme.colors.customColorPrimaryHighlight,
color: euiTheme.colors.customColorPrimaryText,
}}
>
Custom themed content
</div>
);
};
<EuiThemeProvider modify={primaryOverrides}>
<MyComponent />
</EuiThemeProvider>Build an entirely new theme from scratch (advanced):
import { buildTheme, EuiThemeShape } from '@elastic/eui-theme-common';
import { EuiProvider } from '@elastic/eui';
// Define your complete theme structure
const myCustomTheme: EuiThemeShape = {
colors: {
LIGHT: {
primary: '#006bb4',
accent: '#dd0a73',
success: '#017d73',
warning: '#f5a700',
danger: '#bd271e',
// ... all other color tokens
},
DARK: {
primary: '#1ba9f5',
// ... all other color tokens
},
},
base: 16,
size: {
xxs: '2px',
xs: '4px',
s: '8px',
m: '12px',
base: '16px',
l: '24px',
xl: '32px',
xxl: '40px',
},
border: {
color: {
LIGHT: '#d3dae6',
DARK: '#343741',
},
radius: {
medium: '6px',
small: '4px',
},
width: {
thin: '1px',
thick: '2px',
},
},
font: {
family: 'Inter, sans-serif',
// ... font configurations
},
animation: {
// ... animation settings
},
breakpoint: {
// ... responsive breakpoints
},
levels: {
// ... z-index levels
},
focus: {
// ... focus states
},
components: {
// ... component-specific tokens
},
flags: {
hasGlobalFocusColor: true,
hasVisColorAdjustment: false,
},
};
const MyCustomTheme = buildTheme(myCustomTheme, 'MY_CUSTOM_THEME');
const App = () => (
<EuiProvider theme={MyCustomTheme}>
<YourComponents />
</EuiProvider>
);import { useEuiTheme } from '@elastic/eui';
import { css } from '@emotion/react';
const MyComponent = () => {
const { euiTheme, colorMode, highContrastMode } = useEuiTheme();
return (
<div
css={css`
background: ${euiTheme.colors.lightShade};
padding: ${euiTheme.size.xl};
border-radius: ${euiTheme.border.radius.medium};
color: ${euiTheme.colors.text};
`}
>
Themed content
</div>
);
};import { withEuiTheme, WithEuiThemeProps } from '@elastic/eui';
import { css } from '@emotion/react';
class MyComponent extends React.Component<WithEuiThemeProps> {
render() {
const { theme } = this.props;
return (
<div
css={css`
background: ${theme.euiTheme.colors.lightShade};
padding: ${theme.euiTheme.size.xl};
`}
>
Themed content
</div>
);
}
}
export default withEuiTheme(MyComponent);-
Colors (
euiTheme.colors)- Brand colors:
primary,accent,success,warning,danger - Text colors:
text,textPrimary,textAccent, etc. - Background colors:
body,backgroundBasePlain,emptyShade - Shade colors:
lightestShade,lightShade,mediumShade,darkShade - Data visualization colors: Via
colorVisarrays
- Brand colors:
-
Sizing (
euiTheme.size,euiTheme.base)- Base unit:
base(default 16px) - Sizes:
xxs,xs,s,m,l,xl,xxl
- Base unit:
-
Typography (
euiTheme.font)- Font families, weights, scales, and line heights
-
Borders (
euiTheme.border)- Colors, widths, and radii
-
Breakpoints (
euiTheme.breakpoint)- Responsive breakpoints:
xs,s,m,l,xl
- Responsive breakpoints:
-
Animation (
euiTheme.animation)- Duration and easing values
-
Components (
euiTheme.components)- Component-specific tokens (e.g.,
buttons,forms)
- Component-specific tokens (e.g.,
import { useState } from 'react';
import { EuiProvider } from '@elastic/eui';
const App = () => {
const [colorMode, setColorMode] = useState<'light' | 'dark'>('light');
return (
<EuiProvider colorMode={colorMode}>
<button onClick={() => setColorMode(colorMode === 'light' ? 'dark' : 'light')}>
Toggle theme
</button>
<YourComponents />
</EuiProvider>
);
};import { useEuiTheme, useIsDarkMode } from '@elastic/eui';
const MyComponent = () => {
const { colorMode } = useEuiTheme(); // 'LIGHT' or 'DARK'
const isDarkMode = useIsDarkMode(); // boolean
return <div>Current mode: {colorMode}</div>;
};Use nested EuiThemeProvider for section-specific overrides:
<EuiProvider colorMode="light">
<MainContent />
<EuiThemeProvider colorMode="dark">
<Sidebar />
</EuiThemeProvider>
<EuiThemeProvider colorMode="inverse">
<Modal /> {/* Will be dark if parent is light */}
</EuiThemeProvider>
</EuiProvider>Use computed() to create dynamic theme values based on other tokens:
import { computed, shade, tint } from '@elastic/eui';
const modifications = {
colors: {
LIGHT: {
myColor: '#007bff',
myColorDarker: computed(
(myColor) => shade(myColor, 0.3),
'colors.myColor'
),
myColorLighter: computed(
(myColor) => tint(myColor, 0.3),
'colors.myColor'
),
},
},
};-
Minimize Overrides: Touch the least amount of variables possible. Most themes require fewer than a dozen overrides.
-
Always Define Both Modes: When overriding colors, always define values for both
LIGHTandDARKmodes. -
Use Semantic Tokens: Prefer semantic tokens (like
primary,success) over arbitrary values. -
Leverage Computed Values: Use
computed()to maintain relationships between theme values. -
Avoid Overriding Internal Values: Instead of overriding EUI variables at the instance level, create custom keys to avoid cascade effects.
-
Component-Level Tokens: For fine-grained control, use component-specific tokens from
euiTheme.components. -
Test Both Color Modes: Always test your theme in both light and dark modes.
-
Consider High Contrast Mode: EUI supports high contrast mode for accessibility - test your customizations in this mode as well.
import React, { useState } from 'react';
import { EuiProvider, useEuiTheme, computed, shade, tint } from '@elastic/eui';
import { css } from '@emotion/react';
const themeOverrides = {
colors: {
LIGHT: {
primary: '#0066cc',
accent: '#f66a0a',
lightShade: '#f5f7fa',
},
DARK: {
primary: '#36a2ef',
accent: '#f66a0a',
lightShade: '#25262e',
},
},
size: {
base: '18px', // Larger base size
},
border: {
radius: {
medium: '8px', // More rounded
},
},
};
const MyThemedComponent = () => {
const { euiTheme, colorMode } = useEuiTheme();
return (
<div
css={css`
background: ${euiTheme.colors.lightShade};
padding: ${euiTheme.size.xl};
border-radius: ${euiTheme.border.radius.medium};
border: ${euiTheme.border.width.thin} solid ${euiTheme.colors.primary};
`}
>
<h1 css={{ color: euiTheme.colors.primary }}>
Custom Themed Component
</h1>
<p css={{ color: euiTheme.colors.text }}>
Currently in {colorMode.toLowerCase()} mode
</p>
</div>
);
};
const App = () => {
const [colorMode, setColorMode] = useState<'light' | 'dark'>('light');
return (
<EuiProvider colorMode={colorMode} modify={themeOverrides}>
<button onClick={() => setColorMode(colorMode === 'light' ? 'dark' : 'light')}>
Toggle Theme
</button>
<MyThemedComponent />
</EuiProvider>
);
};
export default App;- Theme Provider Documentation: See
EuiThemeProviderandEuiProviderdocs - Token Reference: Explore all available tokens in the theme documentation
- Component Tokens: View component-specific tokens in
@elastic/eui-theme-common - Color Utilities: Use
shade(),tint(), and other color manipulation functions - Existing Themes: Reference the Borealis and Amsterdam themes for complete examples
- Official Documentation: https://eui.elastic.co/