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;
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
);
}
}
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);
}
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";
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();
}
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();
}
Inside the render
method the _themeVariant
variable 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);
}
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>
);
}
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)