If you are a SharePoint Framework developer, you're most likely aware that SPFx allows you to refer to the theme
colors of the context site. As a result, if your web part is placed on a site that uses a red theme, it uses the red
palette as well, and if it's placed on a site that uses the blue theme, it automatically adjusts itself to use the blue
palette. All of this is done automatically without any changes to the web part code in between. But what if you
expose your web part to Microsoft Teams? And selected theme in Teams is Dark or Contrast? Unfortunately, SharePoint
Framework doesn't handle MS Teams themes for you, you should do that by yourself. And it's not a big deal if you
have few controls in the web part. But what if your project contains tens of different components? In this
post I want to share the approach I used to support MS Teams themes in the web part that has about 50 different
components. Note: I use React for production SPFx development, so all the thoughts below may not be applicable to
other frameworks, especially if the components are not so encapsulated as in React.
Initial State
Let's say, you were developing your web part for SharePoint. And as a good developer, you have a
separate folder with a separate CSS (SCSS) for each component. And, of course, you use SPFx theme colors. As a
result, you have a project structure like:
And inside your SCSS you can have
something like:
And it will look always good in SharePoint:
But in MS Teams, especially with
dark theme, it may look not so good:
It happens because your web part
is still "lives" in the context of the underlying site. And, in my case, it has "green" theme. And if you use
Office UI Fabric components, all of them will either use their own default styles or the theme from SharePoint site as
well:
So, to handle Teams themes
correctly we'll need:
Handle our own components' styles
Correctly override Office UI Fabric styles
Let's see how we can achieve that.
1. Handle Theme change in MS Teams
The first step is to handle theme change in Teams. And Teams JavaScript
SDK contains a handler registerOnThemeChangeHandler that we can use to be informed when the
theme is changed. Team's context also contains theme property
that shows the current theme: default, dark, or contrast. Knowing that, we can use the next code to handle the
theming:
So, during the initialization, and whenever the theme is changed we are setting data-theme
attribute of document's body to the selected theme.
Why data attribute on body?
You
may ask why are we setting data attribute? And why on body, not the web part's root DOM element? There are multiple
reasons for that:
All "layer" components of Office UI Fabric (Dialogs, Panels, etc.) are rendered outside of the web part's DOM.
If our attribute is set on the web part's root DOM element we won't be able to process these "layer" components.
We can't use scoped CSS class names (className_<hash>). The reason for that is the styles are scope for
each component separately. And if we assign, for example, styles.dark class in
the web part's root component it will be trasformed to something like dark_6e6e1386. But in our FirstComponent it will be dark_32a77d9d. As a result we won't be able to use nested CSS rules like:
You can use global class name instead of data attribute. I just prefer data attribute.
2. Design Your Components for Teams Themes
Next step is to select what colors to use for each of 3 Teams
themes. In ideal world designer should help you with that... But in real world we can use some helper tools to
achieve it by ourselves. For example, we can use SharePoint
Theme Generator. Good thing in using it - it will generate a set of the same variables that we used for
SharePoint. For example, you'll have white, primaryText and themePrimary colors. And as a result, you could use these
generated colors in the same place where the default variable was used. So, for default Teams theme let's set the
next values in the Generator:
Primary theme color: #6264a7
Body text color: #252423
Body background color: #F3F2F1
And now in the Designer we have all the theme variables assigned:
Store generated variables for
future use. And let's do the same for dark and contrast.
Dark:
Primary theme color: #6264a7
Body text color: #ffffff
Body background color: #2d2c2c
Contrast:
Primary theme color: #6264a7
Body text color: #ffffff
Body background color: #0000000
Of course, SharePoint Theme Generator can't give us the ideal result. Especially for contrast theme. And it's
still recommended to either tweak the colors a bit if needed or beg a designer to help you :)
3. Define Variables for Each Color and Each Theme
Next step is to create SASS variables. For example,
instead of direct
We can use variable:
Moreover, these variables can be define in a separate scss file and shared between different components. I would
also recommend to provide pretty specific names for the variables. For example, if you want to use some color as FirstComponent background, name the variable $firstComponent-background. Let's create variables for all our custom color:
And let's define all these variables in the separate module _colors.module.scss in common.
Now we can reference this file in
any of our components.
4. Override Styles for Your Custom Components
Now, when we have global data attribute set and variables for all
the themes we can override styles for our custom components for each theme. And they will be automatically applied as
this or that value of data attribute is set. Again, using example of the FirstComponent
we'll have:
So, here we still use site theme if the web part is rendered in SharePoint. But we also have different colors for
different themes in Microsoft Teams. And now our component looks much better in Teams:
5. Override Global Office UI Fabric Styles
So, custom components now look good in Teams. But if you use Office
UI Fabric (OUIFR) components - they still don't respect Teams themes.
To override all the styles
correctly, you'll need to figure out what OUIFR components are used and what classes they have. For example, we use
Panel component. This component use such classes as ms-Layer, ms-Panel,
ms-Overlay and so on. Next step is to analyze DOM element with which class sets applies color styles
(backgrounds, borders, font colors, shadows, etc.) Doing that for the Panel in our
example we'll figure out that we need to override:
.ms-Fabric
.ms-Button-icon
.ms-Overlay
.ms-Panel-main
.ms-Panel-headerText
You can do all the global overrides in the root component's CSS, but to make it more manageable for large projects
I would recommend to create 3 separate files in common folder for all 3 themes and import them
in the root component. But first, let's add variables that will be used in the overrides into _colors.module.scss. Again, you can get most of the colors from SharePoint Theme Generator. If
some values do exist in window.__themeState__.theme but not in the Generator then just switch
SharePoint site theme to the one that is close enough to Teams theme and get values from there.
Global.default.module.scss
Global.dark.module.scss
Global.contrast.module.scss
And in the root component:
Now the Panel has correct colors as well:
6. Don't Forget About Web Part Property Pane!
One component that still looks ugly is Web Part Property
Pane
And, unfortunately, it doesn't
have any global classes we can override. Only .spPropertyPaneContainer. But of course we
can use other CSS selectors. Again, let's define all the colors first in our _colors.module.scss:
And now let's add overrides to Global.default.module.scss, Global.dark.module.scss,
Global.contrast.module.scss (Note: the CSS below should be added inside [data-theme] {
:global {): Global.default.module.scss
Global.dark.module.scss
Global.contrast.module.scss
Yay! Now all the parts of our web part look amazing:
Conclusion
As you can see it takes time to support MS Teams team in your SharePoint Framework web part. Even if
you have a single component in there. Imagine how long it will take to add support for 50 components. So, I would
recommend to add support in the moment when you develop each component. It will be much easier and not so painful.
Hopefully, this post will also reduce the time you spend to support Teams themes in your SPFx solutions. The
code sample for this post can be found here.
Comments