Technology Blog Posts by SAP
cancel
Showing results for 
Search instead for 
Did you mean: 
dominikschreiber
Product and Topic Expert
Product and Topic Expert
1,910

Do you have self-developed applications built with SAPUI5, UI5 WebComponents, Fundamental NGX or even one of SAPs classic UIs? Or e.g. your own in-house SAPUI5 component library? Then, it is likely that you've felt the need for theme-specific CSS.

How it was: JavaScript and FOUC

In the past, this was barely possible. In the SAPUI5 world, you had to resort to the Parameters.get() API and create your CSS in JavaScript. In general, you had to inspect the applied theme at runtime, using JavaScript, and serve your theme-specific CSS based on the inspection result. This leads to flashes-of-unstyled-content (FOUC), which, again, has to be treated.

What we have now: sapSapThemeId

But not anymore! [1]

With theming base content 11.23.0, we introduced the new theming parameter sapSapThemeId. This parameter always contains the technical id of the SAP-provided theme the applied theme is based on (in case of a custom-theme) or of the applied theme itself. I.e. --sapSapThemeId of sap_horizon is sap_horizon, and sapSapThemeId of my_horizon (a custom theme based on sap_horizon) is also sap_horizon.

Disclaimer: use existing parameters

The technique I'm about to describe, is a "last resort" solution. Always check first

  1. if there is already a theming parameter you could use instead – Use the parameter explorer of the theming base content to get an overview of what's already there;
  2. if it would make sense to add a theming parameter to the theming base content – Write a comment here, create an issue for the theming base content or contact the development team directly.

Only if both checks are answered negatively, you should use sapSapThemeId to bring your own "delta" css.

The idea: container style queries

The idea is to craft a container style query against --sapSapThemeId (the CSS custom property version of sapSapThemeId), and define your own custom properties based on the result. In the example, I'll use relative colors to darken a background in light themes and lighten it in dark themes; and I'll use CSS nesting for a more concise example; but both are not strictly necessary.

Example: a custom list item

As an example, imagine the background color of a custom list item (which is identified by the CSS class myListItem). It should depend on sapList_Background, but be 10% darker than that in light themes, 10% lighter than that in dark themes, with 30% instead of 10% for high-contrast themes.

For that, we'll create our own CSS custom property, --myList_Background, that defaults to --sapList_Background:

 

:root {
  --myList_Background: var(--sapList_Background);
}

 

Next, we'll use that custom property in the .myListItem selector:

 

.myListItem {
  background-color: var(--myList_Background);
}

 

And now, we'll overwrite the value of --myList_Background in the body selector based on the container style query value of --sapSapThemeId:

 

body {
  /* light low-contrast themes */
  @container style(--sapSapThemeId: sap_horizon)
          or style(--sapSapThemeId: sap_fiori_3) {
    /* darken by 10% */
    --myList_Background: hsl(from var(--sapList_Background) h s calc(l - 10));
  }
  /* dark low-contrast themes */
  @container style(--sapSapThemeId: sap_horizon_dark)
          or style(--sapSapThemeId: sap_fiori_3_dark) {
    /* lighten by 10% */
    --myList_Background: hsl(from var(--sapList_Background) h s calc(l + 10));
  }
  /* light high-contrast themes */
  @container style(--sapSapThemeId: sap_horizon_hcw)
          or style(--sapSapThemeId: sap_fiori_3_hcw) {
    /* darken by 30% */
    --myList_Background: hsl(from var(--sapList_Background) h s calc(l - 30));
  }
  /* dark high-contrast themes */
  @container style(--sapSapThemeId: sap_horizon_hcb)
          or style(--sapSapThemeId: sap_fiori_3_hcb) {
    /* lighten by 30% */
    --myList_Background: hsl(from var(--sapList_Background) h s calc(l + 30));
  }
}

 

That's it, now our custom list item has a custom background that is still connected to the official theming variables, and that depends on the actually applied theme.

A bit of background: container style queries

So, why does that work?

MDN does a good job explaining container style queries, so this is just a small gist.

The --sapSapThemeId custom property is, like all custom properties of the theming base content, defined at :root, which usually is the <html> element. It is crucial that this also is a containment context (the outermost containment context even).

The <body> is inside :root (the <html>), so container styles applied to the <html> containment context have an effect on the <body>.

The CSS

 

body {
  @container style(--sapSapThemeId: sap_horizon) {
    --myList_Background: #c0ffee;
  }
}
/* which is a more concise version of */
@container style(--sapSapThemeId: sap_horizon) {
  body {
    --myList_Background: #c0ffee;
  }
}

 

could be read as

  • In any container (@container without a <container-name>),
  • on which the custom property --sapSapThemeId has the value sap_horizon,
  • in body elements (which is only the one <body> that contains all visible parts of an html page)
  • the custom property --myList_Background
  • has the value #c0ffee

In the example, we also used relative colors, which could be the topic of a separate blog post.

Recap: theme-specific CSS without JavaScript

We have seen that since theming base content 11.23.0, which will soon be available in SAPUI5 (likely version 1.132, but I can't promise anything) and classic UIs (UR/c2 2413), you can write a container style query against --sapSapThemeId to change values of your own CSS custom properties, or even add theme-specific selectors.

Please try that out and leave a comment how it went.


Footnotes

[1] The described technique relies on container style queries for custom properties, actively implemented but not yet supported in Firefox (see tracking bug 1795622)