DEV Community

Guido Zambarda
Guido Zambarda

Posted on • Originally published at iamguidozam.blog on

Theming your SPFx solution

One thing that I like and that I think it’s a great addition to a SharePoint Framework solution is the support of the current theme, especially when the solution will be deployed in Teams.

In a nutshell there are a couple of possible ways discussed in this article to achieve the theming support: one way is that you can retrieve the current theme styles and reuse the values in the SCSS as variables; another way is to use the Theme Provider and set the colors directly on the elements.

I’ve created a sample solution to demonstrate the two ways to achieve this, if you want to check the code you will find it here.

First thing first let’s see what is the result of the sample solution! Following there are some screenshots for my web part inside Teams with different themes applied.

Light theme

Dark theme

Classic theme

High contrast theme

Show me the code

The screenshots above are all from the same web part without the need to update any kind of code or CSS. To achieve this I will explain the two methods I was talking about before.

Method 1: use the onThemeChanged method and SCSS

You can use the onThemeChanged method from the BaseClientSideWebPart class to retrieve the current theme, this means that the implementation will be inside the web part class. The signature of the method is the following:

protected onThemeChanged(theme: IReadonlyTheme | undefined): void;
Enter fullscreen mode Exit fullscreen mode

As you can see this method accepts a theme argument which contains the colors used by the current theme. In the sample I retrieved the bodyText color from the theme variable and set it as a property in the style property of the domElement of the web part.

The full implementation that I used is:

protected onThemeChanged(currentTheme: IReadonlyTheme | undefined): void {

 if (!currentTheme) {
  return;
 }

 const { semanticColors } = currentTheme;

 if (semanticColors) {
  this.domElement.style.setProperty(
    "--bodyText",
    semanticColors.bodyText || null
  );
 }
}
Enter fullscreen mode Exit fullscreen mode

As you can see the semanticColors retrieved from the currentTheme argument contains the bodyText color and the value retrieved, if present, will be assigned to the --bodyText property set on the style of the current domElement.

Once that the property is set it can be used straight from the SCSS file using the var method to specify the target variable, sticking to the sample solution, that would be something like:

.themeAware {
  overflow: hidden;
  padding: 1em;
  margin: 20px;
  color: var(--bodyText);
}
Enter fullscreen mode Exit fullscreen mode

There are many more properties that can be used this way, to have an idea of some of the possibilities check out the list at the end of this article.

Method 2: use the ThemeProvider

It is possible to use the ThemeProvider to retrieve the current theme, to use the provider is necessary to import it from the @microsoft/sp-component-base inside the web part class:

import { 
 ThemeProvider
} from "@microsoft/sp-component-base";
Enter fullscreen mode Exit fullscreen mode

Next, in the onInit method, it’s possible to consume the provider to get the current theme:

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();

 this._themeProvider.themeChangedEvent.add(
  this,
  this._handleThemeChangedEvent
 );

 return super.onInit();
}
Enter fullscreen mode Exit fullscreen mode

The variable _themeVariant will be passed to the web part component to allow the access to the colors, I will cover that in a second.

In the onInit method there is also the registration of the _handleThemeChangedEvent which, as the name suggests, handles what happens when the theme is changed, for example in this sample is in charge to update the _themeVariant variable and re-render the web part:

private _handleThemeChangedEvent(args: ThemeChangedEventArgs): void {
    this._themeVariant = args.theme;
    this.render();
}
Enter fullscreen mode Exit fullscreen mode

Inside the render method the _themeVariantvariable will be passed to the component of the web part:

public render(): void {

 const element: React.ReactElement<IThemeAwareProps> = React.createElement(ThemeAware, { 
  themeVariant: this._themeVariant
 });

 ReactDom.render(element, this.domElement);
}
Enter fullscreen mode Exit fullscreen mode

Finally in the component class the themeVariant will be used to retrieve the colors and directly set it to the style attribute of the target elements, for example:

public render(): React.ReactElement<IThemeAwareProps> {
  const { semanticColors }: IReadonlyTheme = this.props.themeVariant!;

  return (
    <div className={styles.themeAware} style={{ backgroundColor: semanticColors?.bodyBackground }}>
      <span className={styles.title}>Welcome to SharePoint!</span>
      <p className={styles.subTitle}>This web part is theme aware.</p>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

In the next block you can have a little more in depth of the properties that can be found in the semanticColors variable.

A couple more info

I want to point out that there are many properties that can be used to retrieve the theme colors, here is a partial list of the available ones:

  • disabledText
  • errorText
  • inputText
  • buttonText
  • buttonTextDisabled
  • primaryButtonText
  • accentButtonText
  • link
  • bodyBackground
  • disabledBackground
  • disabledBorder
  • focusBorder
  • errorBackground
  • successBackground

If you want to check out all the available properties you can check out the following interfaces from the Fluent UI package:

Wrap up

I think that theme handling should be taken in consideration when developing a modern SPFx solution to deliver a more coherent UI to the user, it’s not a complicated process once you get used to it and it’s a great addition to your SPFx solutions!

Hope this helps!

Top comments (0)