Article
0 comment

Major CSS Bug in SharePoint Framework

I have a bug in my head since SharePoint Framework was first introduced. Why do we need to use CSS Modules, and why is it so bad for responsive web design?

Ok, I am guilty of clickbait.

Despite that, I bet most developers do not know what it is for and unthinkingly follow the rule of always using .module.scss for your style definitions. I would highly recommend reading “What are CSS Modules and why you don’t need them?”.

Another thing I get asked a lot is how to use CSS properly across components. The simple answer always was: You can’t with CSS modules. Please don’t use it or only at the web part root level.

Spoiler: The best solution is at the very end.

The Problem

The road to hell is paved with good intentions.

First, we don’t write entire pages using a SharePoint web part. We write components and want to ensure that our CSS doesn’t negatively affect the rest of the page. This means we need some namespace for the styles of our CSS, too. This namespace is provided by CSS modules by appending a random string to the stylesheet classes.

// myhellwebpart.module.scss
.hell{
   width: 100%;
   height: 100px;
}

Like the above, our web parts style sheet gets translated into this.

// myhellwebpart.module.scss
.hell_abcdef{
   width: 100%;
   height: 100px;
}

Good, so far, we have a random class name instead of a static one. Therefore, we must use a style.hell for our class names.

It works well for a single component, but our web part mostly contains more than one component. So, the next logical step is to create a component.

// hellfirepit.module.scss
.hellFirePit{
   background-color: lime;
}

So what we get now is:

// hellfirepit.module.scss
.hellFirePit_ghijkl{
   background-color: lime;
}

Since both strings that have been attached so far are random, we cannot apply a regular CSS media query at the root of our web part to make changes to any of the sub-components, and there is no way around it.

Sharing components between two web parts is impossible.

Responsive design in the first-party web part, for this reason, heavily relies on all kinds of observers in JavaScript, with the negative downside being performance.

The solutions

Many solutions to this solve the problem. Let’s take a look.

SASS partials

Rename the sub-component files to _.scss instead of filename.scss. To use the components, import them in the main style sheet of your web part.

@use 'sass:meta';
// myhellwebpart.module.scss

// file extension is not needed here.
@include meta.load-css('./hellpit/hellpit');

This way, the classes transform into the following:

.hell_abcdef{
   width: 100%;
   height: 100px;
}

.hellFirePit_abcdef{
   background-color: lime;
}

Every namespace is the same, no matter how many components you use.

The caveat is that you need to reference the style definition from the root of the web part in your react component. So, it works for just one web part per solution. No code sharing is possible.

React Hooks

I attended Andrew Connell’s amazing session on React Hooks. Finally, I got my head around it now. If you are unfamiliar with React Hook – I highly recommend watching Why React Hooks: Enhancing Code Performance and Readability

In his session, he also explained useContext, which is not described in this video Learn React Hooks: useContext – Simply Explained!

With this knowledge, passing all styles objects from the root of the web part down to all the child components is now possible.

import styles from './myhellwebpart.module.scss';

So, useContext only needs to get passed down to the child components, and you can still use styles.hell and styles.hellFirePint wherever you require it.

So you share your stuff around.

CSS modules composition

Learn how composition in CSS modules works.

So it should accomplish the same as before, but I have yet to try it. I’m unsure if someone uses this, at least in the Microsoft 365 development space.

Define namespace in the gulp file

The potential best is a secret kept hidden by Microsoft. You can influence what gets rendered at the end of your class names, share code between components, and, last but not least, apply proper media queries to your web part.

build.sass.setConfig({
   generateScopedName: (name, fileName, css) => {
      return "n8d-" + name + "-webpart";
   }
});

Instead of generating a random string at the end, it used the values provided by generateScopedName. This means the class names transform into this.

.n8d-hell-webpart{
   // ...
}

.n8d-hellFirePit-webpart{
   // ...
}

Not only for one component but for all the components in your webpart. You can still use the same class name anywhere else in your webpart by writing out the class name in your root CSS.

.hell{

   @media screen and (max-width: 768px){
      .hellFirePit{

      }
   }

}

I can use the following above, and it gets converted into this.

.n8d-hell-webpart{
   // ...
}

@media screen and (max-width: 768px){
   .n8d-hell-webpart .n8d-hellFirePit-webpart{
      // ...
   }
}

As I said in the beginning, this is the most useful configuration of your SharePoint Framework component. This feature is so well hidden in the build chain that not even Microsoft uses it to remove all the responsive design JavaScript code.

Thanks to Beau Cameron, who helped me find this shortcut while working together on a solution and a discussion on React Hooks. He confirmed my idea that this problem could be solved this way too.

For other topics around web development and Microsoft 365 full-stack development, you can also subscribe to my new newsletter. I will also include the hottest trends in the web development world.

Leave a Reply

Required fields are marked *.


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