Article
2 comments

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: Demo

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

2 Comments

  1. Hi Stefan, the article is good, and more to learn the tricks on how to leverage the CSS variables. The link in the first paragraph is broken just to make sure that everyone learns from your article.
    Can you help me answer the below question?
    Since we are setting the CSS variables on every web part, what happens when there are multiple web parts on the same page implemented with the above method. Will, there be any caveats? Is there any common way of handling the CSS variables so that all the web parts in the solution can make use of it?
    Thanks again for the information.

    Reply

    • When there are multiple web part on the same page nothing happens to the other web parts. CSS Variables only work in a scoped context. So when you add the CSS variables on the style attribute of your web part then only this can use the values of the vars, while another might use others.

      This is especially handy when you have different colored section on one page together. The web parts totally behave different depending on which section they are in. Even if you would have global `:root` scoped variable the might get overwritten by the web part but will be the previous value after the web part.

      See it more like local vs global variables. Both have the same name one global one local but neither of both overwrite each other.

      Reply

Leave a Reply

Required fields are marked *.


This site uses Akismet to reduce spam. Learn how your comment data is processed.