SAPUI5 supports CSS custom properties as the main theming mechanism. This modern approach provides:
Native browser support with optimal performance
Runtime flexibility for dynamic theme changes
Simplified development without build-time compilation
Better maintainability with standard CSS syntax
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.
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 */
}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.
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.lessWhere:
com.myorg.mylib is your library's namespace
base/ contains theme-independent styles applied to all themes
Theme folders (my_theme/, my_dark_theme/) contain theme-specific customization
library.source.less serves as the main entry point for the LESS build process
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
Option 1 (recommended): Using .less files with SAPUI5 CLI
Use .less files, organized as shown above, with native CSS syntax for maximum compatibility. For build setup and
configuration, see SAPUI5 CLI.
Option 2: Direct CSS files without tooling
Alternatively, you can provide a library.css file directly in the theme folder if you prefer to work
without build tooling.
Remember that without tooling, you must provide a library.css file for each theme you want to support:
com.myorg.mylib/src/themes/
├── base/
│ └── library.css // Base theme styles
├── sap_horizon/
│ └── library.css // Horizon theme styles
├── sap_horizon_dark/
│ └── library.css // Horizon dark theme styles
├── sap_horizon_hcb/
│ └── library.css // High contrast black theme
└── sap_horizon_hcw/
└── library.css // High contrast white themeIf you use the SAPUI5 CLI for building and provide both a
library.css file and a library.source.less file, be aware that the SAPUI5 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:
Use only library.source.less files when working with SAPUI5 CLI (recommended)
Use only library.css files when not using SAPUI5 CLI build tooling
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:
Main file (library.source.less): Imports all other files and serves as build entry
point.
/* library.source.less - Import structure */ @import "shared.less"; // Import shared variables first @import "MyButton.less"; // Import control-specific files @import "MyInput.less"; @import "MyTable.less";
Shared files (shared.less): Variables, mixins, and CSS used across multiple
controls.
/* shared.less - Common variables */
:root {
--_my_lib_base_spacing: var(--sapContent_Padding_M);
--_my_lib_border_radius: 4px;
--_my_lib_focus_color: var(--sapContent_FocusColor);
}Control-specific files (MyButton.less): Properties that are specific to individual
controls.
/* MyButton.less - Button-specific properties */
:root {
--_my_lib_button_height: 2.25rem;
--_my_lib_button_background: var(--sapButton_Background);
}
.myLibButton {
height: var(--_my_lib_button_height);
background-color: var(--_my_lib_button_background);
border-radius: var(--_my_lib_border_radius); /* from shared.less */
}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";
```
```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.
✅
Correct: --_my_lib_button_primary_background (semantic
naming)
❌ Incorrect: --_my_lib_blue_color (appearance-based naming)
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 SAPUI5 or with other libraries.
Start with underscore: All custom CSS properties must begin with --_
Use snake_case: Use underscores to separate all parts including parameter names
Library namespace with underscores: Convert dots to underscores (my.custom.lib →
my_custom_lib)
Lowercase naming: Use lowercase letters throughout
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:
✅
Correct: --_my_custom_lib_button_primary_background
❌ Incorrect: --myCustomLibButtonPrimaryBackground (no underscore prefix, wrong case)
❌ Incorrect: --_my_custom_lib_button_blue_background (appearance-based naming)
❌ Incorrect: --_my.custom.lib_button_background (dots instead of underscores)
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);
}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:
--sapBackgroundColor and --sapErrorBorderColor are used together, which should fulfill the
targeted WCAG success criteria related to non-text contrasts. Reusing such existing base parameters can also help align with
the accessibility standards even if users switch to high contrast themes from SAPUI5 at runtime.
The existing focus indicator is fully visible. For more information, see, for example, WCAG 2.2 "Failure of Success Criterion 1.4.11, 2.4.7 and 2.4.13".
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 SAPUI5, see Available Themes.
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 */
}
}Follow these essential CSS coding practices when creating themable interfaces:
Use semicolons, even if optional, for better consistency and maintainability
Use rem for dimensions that should scale with font size; use px only
for dimensions that must remain fixed regardless of the font size.
Put browser-prefixed properties before the un-prefixed variant to ensure proper fallback behavior
/* ✅ 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 */
}Do not style HTML elements directly without namespace prefixes; all selectors must include library-specific CSS classes to avoid duplicate names
Avoid broad use of the universal selector (*), especially without a direct child
selector (>), due to performance implications
Do not use !important, as it makes custom adaptations more difficult. Use more
specific selectors instead. If you have to deviate from these guidelines (in exceptional cases only), document the
reasoning behind this.
/* ✅ 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 */
}Only use inline CSS for control-instance specific styling, such as dynamic widths or positioning, that cannot be achieved through CSS classes
Root elements should not have outer margins; add any required spacing as padding inside the control. Root margins are owned by the parent control or layout.
/* ✅ 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);
}Follow consistent naming patterns for CSS classes in your custom controls and libraries:
Namespace-Based Naming:
Use your library namespace in camel case letters as the prefix.
Example: my.custom.lib → myCustomLibButton,
myCustomLibInput
/* ✅ 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);
}*-CTXIf controls need different visuals depending on their container context, use CSS cascades with marker CSS classes.
Implementation Pattern:
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.
Style child controls
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 can be used together with CSS custom properties, but they should be applied thoughtfully due to potential performance implications. Consider the following recommendations:
Use static values or existing solutions, such as CSS
Grid or FlexBox, instead of CSS math functions as they can be
expensive.
Use CSS math functions only sparingly and if necessary, for example, for the following scenarios:
Complex calculations that cannot be achieved with standard CSS
Dynamic sizing that depends on multiple custom properties
Maintaining mathematical relationships between theme parameters
When using CSS math functions, evaluate the performance impact, especially on lower-end devices.
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)
);
}Consider alternatives first: Evaluate if CSS Grid,
FlexBox,
or static values might work instead.
Keep calculations simple: Avoid nested or overly complex expressions.
Use meaningful custom properties: Make calculations readable and maintainable.
Test across devices: Verify performance on various device types.
Document complex calculations: Explain the purpose and expected behavior.
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.