Skip to content

Instantly share code, notes, and snippets.

@simianhacker
Last active December 2, 2025 18:52
Show Gist options
  • Select an option

  • Save simianhacker/099f3008d24c421990c3e66960911882 to your computer and use it in GitHub Desktop.

Select an option

Save simianhacker/099f3008d24c421990c3e66960911882 to your computer and use it in GitHub Desktop.
Guide to Creating Customized Themes with Elastic UI (EUI)

Creating a Customized Theme with EUI

User Prompt

/research Explain how to create a customized theme with EUI.

Overview

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

Theme Customization Approaches

There are three main approaches to customizing EUI themes, in order of complexity:

1. Simple Theme Overrides (Recommended for Most Use Cases)

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 LIGHT and DARK color modes
  • Changes cascade throughout the theme system
  • Most themes require fewer than a dozen variable overrides

2. Creating Custom Theme Keys

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>

3. Creating a Complete Custom Theme

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>
);

Consuming Theme Values

Using the useEuiTheme() Hook

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>
  );
};

Using the HOC for Class Components

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);

Available Theme Tokens

Core Token Categories

  1. 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 colorVis arrays
  2. Sizing (euiTheme.size, euiTheme.base)

    • Base unit: base (default 16px)
    • Sizes: xxs, xs, s, m, l, xl, xxl
  3. Typography (euiTheme.font)

    • Font families, weights, scales, and line heights
  4. Borders (euiTheme.border)

    • Colors, widths, and radii
  5. Breakpoints (euiTheme.breakpoint)

    • Responsive breakpoints: xs, s, m, l, xl
  6. Animation (euiTheme.animation)

    • Duration and easing values
  7. Components (euiTheme.components)

    • Component-specific tokens (e.g., buttons, forms)

Color Mode Management

Setting Color Mode

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>
  );
};

Checking Current Color Mode

import { useEuiTheme, useIsDarkMode } from '@elastic/eui';

const MyComponent = () => {
  const { colorMode } = useEuiTheme(); // 'LIGHT' or 'DARK'
  const isDarkMode = useIsDarkMode(); // boolean
  
  return <div>Current mode: {colorMode}</div>;
};

Nested Theme Providers

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>

Computed Values

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'
      ),
    },
  },
};

Best Practices

  1. Minimize Overrides: Touch the least amount of variables possible. Most themes require fewer than a dozen overrides.

  2. Always Define Both Modes: When overriding colors, always define values for both LIGHT and DARK modes.

  3. Use Semantic Tokens: Prefer semantic tokens (like primary, success) over arbitrary values.

  4. Leverage Computed Values: Use computed() to maintain relationships between theme values.

  5. Avoid Overriding Internal Values: Instead of overriding EUI variables at the instance level, create custom keys to avoid cascade effects.

  6. Component-Level Tokens: For fine-grained control, use component-specific tokens from euiTheme.components.

  7. Test Both Color Modes: Always test your theme in both light and dark modes.

  8. Consider High Contrast Mode: EUI supports high contrast mode for accessibility - test your customizations in this mode as well.

Example: Complete Theme Customization

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;

Resources

  • Theme Provider Documentation: See EuiThemeProvider and EuiProvider docs
  • 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/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment