Article
0 comment

How to make CSS Variables work in every web part context

In the recent blog post on how to make a web part works with different section backgrounds using CSS variables, I already covered. At the same time, this works perfectly for just regular web parts. There are specific scenarios, where this approach fails.

Time to wrap up the web part scenario and extend it with two additional use cases. Single part app pages and Microsoft Teams integrated web part doesn’t have a definition for supporting section background. Simply because there are no section backgrounds and the code won’t use any of the CSS variable definitions.

Theming on single part app pages

What works great on a web part to have CSS variables aka CSS custom properties, renders when the web part used on a single par app page renders in the following way.

Webpart rendered using CSS variable and theme variant

The web part used on single part app page renders without any background or colour changes, only with the defined values in the theme.

Web part as as single page app part

The issue lies her in the code of the web part.

protected onInit(): Promise<void> {

  // Consume the new ThemeProvider service
  this._themeProvider = this.context.serviceScope.consume(ThemeProvider.serviceKey);

  // If it exists, get the theme variant
  this._themeVariant = this._themeProvider.tryGetTheme();

  // If there is a theme variant
  if (this._themeVariant) {

    // we set transfer semanticColors into CSS variables
    this.setCSSVariables(this._themeVariant.semanticColors);

  }
  //...

If the method ‘tryGetTheme()’ fails and can not get executed the ‘this._themeVariant’ will be ‘undefined’ and the CSS variable won’t get generated. In other words, what works perfectly for a regular web part will fail in this scenario.

At least not yet (hopeful changes in future), to cover this scenario as well. There is no supported way to convert the current applied SharePoint theme into CSS variables.

At least, for example, button background and other properties are only accessible in CSS/SCSS in the following way.

.mywebpart{
  background-color: "[theme:primaryButtonBackground]";
  color: "[theme:primaryButtonText]";
}

This way, the web part works, but it leads to another issue.

.mywebpart{
  background-color: var(--primaryButtonBackground);
  background-color: "[theme:primaryButtonBackground]";
  color: var(--primaryButtonText);
  color: "[theme:primaryButtonText]";
}

The problem is this also works only in one of both scenarios. This definition ruins the section awareness of the web part, but make it work on the single app part page. So we need to have a method that helps in both scenarios.

Luckily there is a way to create the matching CSS variables without using the theme provider. Every SPFx page comes with a theme state object defined on the Window object.

// Consume the new ThemeProvider service
this._themeProvider = this.context.serviceScope.consume(ThemeProvider.serviceKey);

// If it exists, get the theme variant
this._themeVariant = this._themeProvider.tryGetTheme();

console.debug('Theme variant ::: ', this._themeVariant);

// If there is a theme variant
if (this._themeVariant) {

  // we set transfer semanticColors into CSS variables
  this.setCSSVariables(this._themeVariant.semanticColors);

} else if (window["__themeState__"].theme) {

  // FALLBACK TO App Page

  // we set transfer semanticColors into CSS variables
  this.setCSSVariables(window["__themeState__"].theme);

}

If the theme provider returns a theme, everything works fine. Suppose no theme provider is available, like on single app part pages, fallback to the theme state object defined for the overall page. The result of this is that theme colours work here finally.

Correct rendered web part using __themeState__

The downside is that it is not yet currently officially supported and properties might change in future. For now, it is the only way to have experiences consistent this way.

BONUS: Teams and theming?

The _themeProvider (officially supported way) might support Microsoft Teams? Sadly it doesn’t work this way, but the ‘themeState’, the fallback works here too.

SharePoint Framework web part in Microsoft Teams using Dark Theme

So the ‘plurple’ button background, gets applied to the web part, as well as the correct text colour, on a dark-themed Teams as well on a regular Teams instance.

The only thing that does not work is the High Contrast option, but that is supported when the overall user interface is set as high contrast on Windows anyway.

SharePoint Framework web part in Microsoft Teams using High Contrast Theme

Hope for the future

The themeState on Window is not officially supported but widely used in SharePoint Framework development.

From my perspective, there are two paths. Adding the themeState supported, or which might is the better option provide a consistent object using the themeProvider.

The themeProvider object should never return ‘undefined’; it should always return the currently used theme. In the case of a single part app pages or the usage in Microsoft Teams, it should return the overall page theme and the section theme for the use as a regular web part.

Another bonus would be, to return High Contrast, when Microsoft Teams set to high contrast.

If you like to see how this web part is built more into detail on Github