docs

Creating Themable User Interfaces

Best practices for developing OpenUI5 applications and controls that properly adapt to different themes while ensuring an excellent and consistent user experience, good performance, and consistent accessibility standards across all themes.


Modern Theming Approach

OpenUI5 supports CSS custom properties as the main theming mechanism. This modern approach provides:


General Principles: Using CSS Custom Properties

Regardless of whether you develop applications, controls, or libraries, use CSS custom properties (also known as CSS variables) for theme-aware styling instead of hard-coded values. For a list of available custom CSS properties and their values, see the Theme Parameter Toolbox.


In-Application Development

For quick customization, you can reference existing CSS custom properties or override existing styles specifically.

/* Application-specific theming */

.myAppNotepadControl {
  /* Making custom control theme-aware */
  background-color: var(--sapBackgroundColor);
  box-shadow: var(--sapContent_Shadow1);
}

/* Application-specific overrides */

/* Given the following XML View definition:
<App class="myApp" xmlns="sap.m">
  <!-- ... -->
    <Button class="myCustomBorderRadius" ... />
  <!-- ... -->
  <dependents>
    <Dialog class="myAwesomeBackground" ...>
      <!-- ... -->
    </Dialog>
  </dependents>
</App>
*/

.myApp .sapMBtn.myCustomBorderRadius {
  border-radius: 8px; /* Custom border radius for specific sap.m.Button within myApp */
}

/* Popup controls (sap.m.Dialog, sap.m.Popover, etc.) render in static area outside myApp. */
.sapMDialog.myAwesomeBackground {
  --sapGroup_ContentBackground: url("..."); /* Custom content background for specific sap.m.Dialog */
}

Runtime Parameter Access (If Required)

For accessing theme parameters at runtime, for example, if CSS custom properties are insufficient, or LESS parameters need to be accessed, use the sap/ui/core/theming/Parameters.get API. For detailed usage patterns, asynchronous handling, and migration guidelines, see the Runtime Parameter Access (Parameters API) section in Enhanced Theming Concepts.


In-Library Development


Organizing Theme Files and Parameters

Theme files and parameters should be organized in dedicated theme files following the established conventions.

com.myorg.mylib/src/themes/
├── base/
│   ├── library.source.less
│   └── shared.less
├── my_theme/
│   ├── library.source.less
│   └── shared.less
└── my_dark_theme/
    ├── library.source.less
    └── shared.less

Where:

Note:

While these files have a .less extension for tooling compatibility, you should primarily use native CSS syntax within them. LESS-specific syntax should only be used when you need advanced features as covered in Enhanced Theming Concepts.

CSS vs LESS File Options

If you use the OpenUI5 CLI for building and provide both a library.css file and a library.source.less file, be aware that the OpenUI5 CLI will always process the library.source.less file during the build process and overwrite the existing library.css file. To avoid this, do the following:

Advanced Organization for Larger Libraries

For libraries with multiple controls, organize files by functionality:

com.myorg.mylib/src/themes/base/
├── library.source.less
├── shared.less              // Common variables and mixins
├── MyButton.less            // Button-specific styles
├── MyInput.less             // Input-specific styles
└── MyTable.less             // Table-specific styles

Take the following file type guidelines into account:


Defining Theme Parameters

Separate Definition of Parameters

Create dedicated parameter files instead of adding CSS directly to library.source.less. Afterwards, import all relevant files in library.source.less as it serves as the build entry point.

/* com.myorg.mylib/src/themes/base/library.source.less */
@import "shared.less";
/* com.myorg.mylib/src/themes/base/shared.less */
:root {
  --_my_lib_control_background: var(--sapBackgroundColor);
  --_my_lib_control_border_color: var(--sapNeutralBorderColor);
  --_my_lib_control_text_color: var(--sapTextColor);
}

Use the Theme Parameter Toolbox to explore available custom CSS properties and their current values.

Theme-Specific Customization

For theme-specific adjustments, create corresponding parameter files in theme folders:

/* com.myorg.mylib/src/themes/my_dark_theme/shared.less */
:root {
  /* Inherit base parameters */
  --_my_lib_control_background: var(--sapBackgroundColor);
  --_my_lib_control_border_color: var(--sapNeutralBorderColor);
  --_my_lib_control_text_color: var(--sapTextColor);

  /* Dark theme specific additions */
  --_my_lib_control_shadow: var(--sapContent_Shadow1);
}

This approach provides clear separation between base styles and theme-specific customization while maintaining proper build integration.

Semantic Naming Convention

Use descriptive names that reflect the parameter’s purpose, not its appearance.

CSS Custom Property Naming Format

When defining your own CSS custom properties for theming, follow these naming conventions to ensure uniqueness and avoid conflicts within OpenUI5 or with other libraries.

For library-specific parameters defined in shared theme files (shared.less), use the pattern --_<library_namespace>_<parameter_name>.

/* Library: my.custom.lib, File: shared.less */
:root {
  --_my_custom_lib_common_spacing: 1rem;
  --_my_custom_lib_error_color: #d32f2f;
}

For control-specific parameters defined in control-specific theme files, use the pattern --_<library_namespace>_<file_name>_<parameter_name>.

/* Library: my.custom.lib, File: MyButton.less */
:root {
  --_my_custom_lib_my_button_primary_background: #0070f2;
  --_my_custom_lib_my_button_hover_background: #005ce6;
}

Usage in CSS:

myCustomLibButton {
  background-color: var(--_my_custom_lib_my_button_primary_background);
  padding: var(--_my_custom_lib_common_spacing);
}

.myCustomLibButton:hover {
  background-color: var(--_my_custom_lib_my_button_hover_background);
}

.myCustomLibInput.error {
  border-color: var(--_my_custom_lib_error_color);
}

Summary:


Defining Control-Specific Theme Parameters

Create parameters that are owned by your library and follow a logical hierarchy when you define them:

/* In your control's theme files */

/* Base level - fundamental properties */
:root {
  --_my_controls_base_background: var(--sapBackgroundColor);
  --_my_controls_base_text: var(--sapTextColor);
}

/* Control level - specific control types */
:root {
  --_my_controls_data_table_header_background: var(--sapList_HeaderBackground);
  --_my_controls_data_table_row_hover_background: var(--sapList_Hover_Background);
  --_my_controls_data_table_border_color: var(--sapList_BorderColor);
  --_my_controls_button_background: var(--_my_controls_base_background);
  --_my_controls_input_background: var(--_my_controls_base_background);
}

/* Variant level - states and variations */
:root {
  --_my_controls_button_primary_background: var(--sapButton_Emphasized_Background);
  --_my_controls_button_secondary_background: var(--_my_controls_button_background);
}

Structure your control CSS to use the defined parameters:

.myControlsDataTable {
  border: 1px solid var(--_my_controls_data_table_border_color);
}

.myControlsDataTable .header {
  background-color: var(--_my_controls_data_table_header_background);
  font-weight: var(--sapButton_Emphasized_FontWeight);
}

.myControlsDataTable .row:hover {
  background-color: var(--_my_controls_data_table_row_hover_background);
}

Accessibility Compliance

Test your parameters for accessibility compliance. Follow the WCAG requirements to ensure your themes meet the accessibility standards. Make sure the following requirements are met:


Testing and Validation


Testing Across Themes

Validate your theming implementation across different themes, for example:

// Theming required from "sap/ui/core/Theming"

// Switch between themes to test adaptation
Theming.setTheme("my_registered_dark_theme");
Theming.attachApplied(() => {
  // Verify your controls adapt correctly
});

For a complete list of built-in themes provided by OpenUI5, see Available Themes.


Responsive Design Integration

Ensure that theming also works with responsive design:

.myResponsiveControl {
  background-color: var(--_my_lib_control_background);
  padding: var(--sapContent_Padding_M);
}

/* Responsive adjustments maintain theming */
@media (max-width: 768px) {
  .myResponsiveControl {
    padding: var(--sapContent_Space_Tiny);
    /* Background color automatically adapts */
  }
}

CSS Coding Guidelines

Follow these essential CSS coding practices when creating themable interfaces:


Syntax and Formatting

/* ✅ Good - Proper syntax and units */
.myCustomLibControl {
  -webkit-transform: scale(1.1);
  transform: scale(1.1);
  padding: 1rem; /* Scales with font size */
  border-width: 1px; /* Fixed size regardless of font */
}

/* ❌ Avoid - Inconsistent syntax */
.myCustomLibControl {
  transform: scale(1.1);
  -webkit-transform: scale(1.1);
  padding: 16px /* Missing semicolon, fixed size instead of scalable */
}

Selector Guidelines

/* ✅ Good - Specific, scoped selectors */
.myCustomLibContainer .myCustomLibButton {
  background-color: var(--_my_custom_lib_button_background);
}

.myCustomLibDialog > .myCustomLibContent {
  padding: var(--_my_custom_lib_content_padding);
}

/* ❌ Avoid - Broad use of the universal selector (*) with properties heavy to the browser's rendering pipeline */
td * {
  width: ...;
  box-shadow: ...;
}

/* ❌ Avoid - Use of !important */
button {
  background-color: blue !important; /* Affects all buttons globally */
}

/* ⚠️ Exception - !important for critical accessibility override */
.myCustomLibHighContrast .myCustomLibButton {
  border: 2px solid currentColor !important; /* Required for high contrast accessibility compliance */
}

Control Structure Guidelines

 /* ✅ Good - No root margins, padding inside */
   .myCustomLibCard {
     padding: var(--_my_custom_lib_card_padding);
     /* No margin on root element */
   }

   .myCustomLibCard .myCustomLibContent {
     margin: var(--_my_custom_lib_content_margin); /* Internal spacing is fine */
   }

   /* ❌ Avoid - Root margins */
   .myCustomLibCard {
     margin: 1rem; /* This should be controlled by parent */
     padding: var(--_my_custom_lib_card_padding);
   }

CSS Class Naming and Container Context


CSS Class Naming Conventions

Follow consistent naming patterns for CSS classes in your custom controls and libraries:

Namespace-Based Naming:

/* ✅ Good - Namespace-based naming */
.myCustomLibButton {
  background-color: var(--_my_custom_lib_button_background);
}

.myCustomLibInput {
  border-color: var(--_my_custom_lib_input_border_color);
}

/* ❌ Avoid - Generic names that might conflict */
.button {
  background-color: var(--_button_background);
}

Container Context Handling *-CTX

If controls need different visuals depending on their container context, use CSS cascades with marker CSS classes.

Implementation Pattern:

  1. Define marker classes

    • Use a semantic name that describes the context, followed by -CTX.

    • Document the purpose in JSDoc.

    • Marker classes themselves contain no CSS rules. They act only as indicators.

  2. Style child controls

    • Use CSS cascades so child controls adjust when a parent context is present.

Example Implementation:

/* Context markers: thatTable-CTX, thatDensity-CTX */
/* Child controls adapt to parent context */

.thatTable-CTX .myCustomLibButton.sapMBtn {
  padding: var(--sapContent_Padding_S);
  font-size: var(--sapFontSmallSize);
}

.thatCondensedDensity-CTX .myCustomLibInput.sapMInputBase {
  height: var(--sapElement_Condensed_Height);
}

CSS Math Functions

CSS math functions can be used together with CSS custom properties, but they should be applied thoughtfully due to potential performance implications. Consider the following recommendations:


Implementation Examples

Basic calc() with custom properties:

myCustomLibDialog {
  /* Calculate dynamic width based on viewport and padding */
  width: calc(100vw - var(--_my_custom_lib_dialog_margin) * 2);
  max-width: calc(800px - var(--_my_custom_lib_padding));
}

Complex calculations with multiple functions:

.myCustomLibResponsiveGrid {
  /* Use min() for responsive sizing with custom properties */
  grid-template-columns: repeat(auto-fit, minmax(min(250px, 100%), 1fr));
  gap: max(var(--_my_custom_lib_min_spacing), 2vw);
}

Performance-conscious approach:

/* ✅ Good - Simple calculation when needed */
.myCustomLibContainer {
  padding: calc(var(--_my_custom_lib_base_spacing) * 2);
}

/* ❌ Avoid - Overly complex calculations */
.myCustomLibOverComplex {
  width: calc(
    100vw -
    var(--_my_custom_lib_sidebar) -
    max(var(--_my_custom_lib_padding), 2rem) * 2 -
    min(var(--_my_custom_lib_margin), 3vw)
  );
}

Best Practices for Math Functions


CSS Selector Performance

When working with large datasets, be aware of CSS selector performance. For detailed guidance on identifying and resolving CSS performance issues with large datasets, see Theming FAQ.