Skip to content

In ArcGIS Experience Builder, most theme customizations can be achieved by modifying theme options in the theme/variables.json. However, if you want to create a more unique and differentiated theme, customizing theme options may not be sufficient; you can customize each component's style to meet your design needs. Component styles in the Experience Builder theme are built using Emotion styled API, allowing you to create dynamic styles based on the runtime theme and component props.

1. Override component or global styles

To customize styles in an Experience Builder theme module, create or edit the style.ts file located at path to theme/style.ts. Depending on your needs, you can choose to:

  • Override component styles: Export style objects for specific base components you want to customize.
  • Add global styles: Use the special CssBaseline component to set global CSS rules, such as font faces or resets, that affect all components in your application.

You can use either approach or both, depending on what you want to customize.

2. Validate and optimize

After customizing component styles and adding global styles, it is important to validate and optimize your theme to ensure it meets accessibility standards and maintains a consistent user experience.

Customizing base component styles

Base components refer to components that can be directly imported from jimu-ui module and whose styles can be overridden in ArcGIS Experience Builder theme module. You can directly customize these base component styles by creating and exporting style objects in path to theme/style.ts.

Code example: Customize styles for a Button component:

Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
9
10
// style.ts
import type { ThemeComponentStyleOptions } from 'jimu-theme'

export const Button: ThemeComponentStyleOptions['Button'] = {
  root: ({ theme }) => {
    return {
      // Your custom styles here
    }
  }
}

Style function parameters (root):

ParameterTypeDescription
themeThemeResolved runtime theme object (tokens + computed values).
styleStateanyComponent props/state (variant, color, size, tag, etc.).
propsanyOriginal React props (when exposed).
refanyReserved (internal use).

Return value can be:

  • Plain object (recommended).
  • Template string (for raw CSS with selectors).

Here are some key points to consider:

  1. The exported object name must match the component name, the object key is fixed to "root".
  2. Use ThemeComponentStyleOptions to get the proper type definitions for the component style object.
  3. The style value is a function that returns an Emotion-compatible style object:
    • theme: the runtime theme object, containing theme variables.
    • styleState: the component's runtime state, which equals the props of the component. You can refer to the props of each component under Components/jimu-ui/index.

Code example: Dynamically change Button styles based on variant and color

Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// style.ts
import type { ThemeComponentStyleOptions } from 'jimu-theme'

export const Button: ThemeComponentStyleOptions['Button'] = {
  root: ({ styleState, theme }) => {
    const isPrimaryContained = styleState.variant === 'contained' && styleState.color === 'primary'
    const styles = isPrimaryContained
      ? {
          backgroundColor: theme.sys.color.secondary.main,
          color: theme.sys.color.secondary.text,
          '&:hover': {
            backgroundColor: theme.sys.color.secondary.dark
          },
          '&:focus-visible': {
            outline: `2px solid ${theme.sys.color.primary.main}`,
            outlineOffset: '2px'
          }
        }
      : {}
    return styleState.tag === 'a' ? { '&[role="button"]': styles } : styles
  }
}

The ternary conditional statement is required to handle the button component that uses an <a> tag internally. To ensure sufficient style specificity, the [role="button"] selector is applied when the tag is <a>. Most other components do not require this pattern.

Use dark colors for code blocksCopy
1
return styleState.tag === 'a' ? { '&[role="button"]': styles } : styles

Global styles

Global styles affect the entire application and can be defined using the CssBaseline component in your style.ts file. This is useful for setting global CSS rules, such as font faces or resets.

Here is an example of using the CssBaseline component to set a global font size:

Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
9
10
// style.ts
import type { ThemeComponentStyleOptions } from 'jimu-theme'

export const CssBaseline: ThemeComponentStyleOptions['CssBaseline'] = {
  root: () => ({
    html: {
      fontSize: '80%'
    }
  })
}

Additionally, the styleState from CssBaseline includes a rootUrl property that represents the app’s root URL. This allows you to load fonts or assets using absolute paths:

Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// style.ts
export const CssBaseline = {
  root: ({ styleState }) => {
    const rootUrl = styleState.rootUrl || ''
    return `
      @font-face {
        font-family: 'Family name';
        src: url('${rootUrl}themes/<theme name>/assets/<font file>') format('truetype');
        font-weight: 400;
        font-style: normal;
      }
    `
  }
}

Limitations

Typically, a custom theme can still be edited through the Experience Builder Theme Settings panel. However, once component styles are overridden in the style.ts or style.scss, the corresponding components will no longer respond to Theme Settings adjustments.

If you are certain that your custom styles do not conflict with Theme Settings, you can explicitly allow theme editing by setting themeCustomizable to true in the manifest file:

Use dark colors for code blocksCopy
1
"themeCustomizable": true

Best practices

Below are some best practices to follow when customizing component styles to ensure maintainability and accessibility:

  • Prefer theme options before component overrides (maintains configurability).
  • Keep overrides scoped; avoid redefining universal elements (body, html) unless required.
  • Use semantic tokens (theme.sys.color.*) instead of hardcoded hex values for consistency.
  • Use object style form for better merging and fewer parsing errors.
  • Limit use of global resets; they can impact third-party widgets.

What's next?

Learn about the different theme development options and how to create and customize themes:

Your browser is no longer supported. Please upgrade your browser for the best experience. See our browser deprecation post for more details.