2

** For simplicity's sake I am not going to include all the Typescript stuff in my code examples here, so please just roll with it.

I'm using Semantic UI React as a UI component/styling framework for my SPFx webpart.

I have a button component that uses the Semantic UI Button component. Semantic UI adds its own classes ui button to its button component. I want to apply some extra styling to my button, so I added my own custom class.

MyButton.module.scss

.myButton {
  margin-top: 1em;
}

MyButton.tsx

import * as React from 'react';
import { Button } from 'semantic-ui-react';
import styles from './MyButton.module.scss';

export default class MyButton extends React.Component {
  public render() {
    return (
      <Button className={styles.myButton} />
    );
  }
}

What ends up getting rendered in the HTML is

<button class="ui button myButton_123456"></button>

however, the extra styling I want added does not get applied because the Semantic UI CSS uses a selector of .ui.button, and while I can see the that the browser recognizes that my class/style is there, it only recognizes the style as applying to the selector .myButton_123456, so the default Semantic CSS has greater specificity, and my style is ignored.

Now if I were dealing with regular CSS, I know that I could define my custom style like this:

.ui.button.myButton {
  margin-top: 1em;
}

and my style would have more specificity and override the default Semantic styling.

However, if I put that in the SCSS module file, what I end up seeing is that those classes all get split up and are available as separate properties on the style object imported into my component.

If I just use my custom style alone, as I did originally, the HTML gets rendered the same, but in the browser CSS tools, I no longer see my class as a single class that's getting overridden by Semantics double class selector. It's just not there anymore.

And if I try something like

<Button className={`${styles.ui} ${styles.button} ${styles.myButton}`} />

then the HTML ends up like

<button class="ui button ui_123456 button_654321 myButton_987654"></button>

Now, in that case I win the specificity battle by having a triple-class selector, but it happens by inadvertently creating two new classes, which seems unnecessary.

So how can I structure things so that the browser ends up seeing

.ui.button.myButton_123456 {
  margin-top: 1em;
}

and correctly overrides the default Semantic styling by using Semantics own classes and one single new custom class?


Thanks to @theChrisKent for the answer, I've now learned a bit about :global and :local when using CSS modules.

Just wanted to add that the solution was not exactly as in the posted answer. Trying to do

.myButton {
  :global {
    &.ui.button {
      margin-top: 1em;
    }
  }
}

gave me build errors saying "missing whitespace after :global", and looked like it was trying to construct the CSS like this:

.myButton :global.ui.button {
  margin-top: 1em;
}

The way I was able to get it to work was by constructing the SASS like this:

.myButton:global(.ui.button) {
  margin-top: 1em;
}

which comes across in the browser like

.myButton_123456.ui.button {
  margin-top: 1em;
}

which is exactly what I was looking for.

1 Answer 1

2

You can mix your SCSS classes with known classes by using the :global selector. So you should be able to modify your SCSS like this:

.myButton {
  :global {
    &.ui.button {
      margin-top: 1em;
    }
  }
}

There's also the :local selector if you need to switch back while in the global scope.

1
  • 1
    :global worked, but not exactly the way you put as an example. I've updated my question with the format of how it ended up working. Feb 13, 2023 at 17:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.