Joe Bruno
Joe Bruno

Reputation: 427

CSS Modules, React and Overriding CSS Classes

I am using a React component library (React Toolbox), which is outputting this class in their Tab component using CSS Modules and Webpack: label___1nGy active___1Jur tab___Le7N The tab is a className prop I am passing down. The label and active classes are coming from the library. I want to apply a different set of styles on active, something like .tab.active where tab is referring to the styles I have created and active matches the generated selector the library has created but cannot figure out how to do this with css-modules. I need to override this dynamically selector: .label___1nGy.active___1Jur.

Browser

[CSS]]2 [React]3

Upvotes: 32

Views: 102751

Answers (6)

Vikrant Thete
Vikrant Thete

Reputation: 141

Using :global(.classname) you can override the external classnames.

Even 3rd party library css can be override.

:global(.active) {
  color: hotpink;
}

Upvotes: 7

David Minaya
David Minaya

Reputation: 127

Group style loader

You can use the group-style-lader to override the style of the components. This loader gives you an easy and intuitive way of override the style of the components.

Configure the loader

  1. Install the loader

    npm install --save-dev group-style-loader
    
  2. Configure the loader in your webpack settings

    module.exports = {
      module: {
        rules: [
          {
            test: /\.css$/i,
            use: [
              'group-style-loader', 
              'style-loader', 
              {
                loader: "css-loader",
                options: {
                  modules: true
                }
              }
            ]
          }
        ]
      }
    };
    

You only need to put it, before the style-loader or the mini-css-extract-plugin loader.

Override the style of the components

The next example show as you can to override the style of the Card component from the App component.

You can define the style of your component as you are used to.

card.css

.container {
  width: 300px;
  height: 400px;
  border-radius: 8px;
}

.title {
  font-weight: bold;
  font-size: 24px;
}

.summary {
  margin-top: 24px;
  font-size: 20px;
}

The unique difference is that in the Card component, you going to use the mergeStyle function to merge the custom style with the default style of the component.

card.jsx

import React from 'react';
import { mergeStyle } from './card.css';

export function Card({ customStyle }) {

  const style = mergeStyle(customStyle);

  return (
    <div className={style.container}>
      <div className={style.title}>Title</div>
      <div className={style.summary}>
        Lorem ipsum dolor sit amet, consectetur            
        adipiscing elit, sed do eiusmod tempor.
      </div>
    </div>
  )
}

Then, in the App component, to override the style of the Card component you need to define the custom style of the Card component using the separator _. All the classes using this separator going to be grouped in the card property of the style object.

app.jsx

.title {
  font-size: 32px;
  font-weight: bold;                
  margin: 44px;
}

.list {
  display: flex;
}

.card_title {
  color: blue;
}

.card_summary {
  color: green;
}

Finally, you only need to pass the custom style of the Card component through the customStyle property of it.

app.jsx

import React from 'react';
import { Card } from './card';
import { style } from './app.css';

export function App() {
  return (
    <div>
      <h1 className={style.title}>Group style</h1>         
      <div className={style.list}>
        <Card/>
        <Card customStyle={style.card}/>
      </div>
    </div>
  );
}

In the previous example, you have two Cards components, the first uses its default style, and the second uses the customs tyle that we have defined.

You can see a full explanation of how to use the group-style-loader in its repository.

Upvotes: 3

Maxy max
Maxy max

Reputation: 1

trick in fileName.css

add className-active after declaring className

.className{
  background: white;
}

.className-active{
  background: black;
}

<div className={'className className-active'} />
<div className={'className-active className'} />

divs always will be black

Upvotes: -1

Richard Scarrott
Richard Scarrott

Reputation: 7033

CSS modules won't allow you to safely override the active className (largely by design). Really it should be exposed via an API, e.g. 'activeClassName'.

If the maintainers disagree or you need this quick then you can quite easily add your own active className because your implementing component is managing the index state:

import {Tab, Tabs} from 'react-toolbox';
import styles from './MyTabs.css';


class MyTabs extends React.Component {
  state = {
    index: 1
  };

  handleTabChange(index) {
    this.setState({ index });
  }

  render() {
    const { index } = this.state;
    return (
      <Tabs index={index} onChange={this.handleTabChange}>
        <Tab label="Tab0" className={index === 0 ? styles.active : null}>
            Tab 0 content
        </Tab>
        <Tab label="Tab1" className={index === 1 ? styles.active : null}>
            Tab 1 content
        </Tab>
      </Tabs>
    );
  }
}

Disclaimer: Code is untested.

Upvotes: 5

alechill
alechill

Reputation: 4502

Old post but still relevant, so adding an answer to help those with similar issue

While not inherently possible in CSS modules alone, the author of the react-toolbox library has actually solved this particular problem very nicely

Read their github docs which are more in depth on the subject at https://github.com/react-toolbox/react-toolbox#customizing-components

A list of the themeable classes for a particular component is given on the component demo page on their site too

In your case as well as passing a className for tab, you would also create a theme with classes that overrides that desired parts of the default theme and pass that as the theme prop. For example something alog the lines of...

MyComponentWithTabs.css

.tab {
  color: white;
}

MyTabTheme.css

.active {
  color: hotpink;
}

MyComponentWithTabs.js

import styles from './MyComponentWithTabs.css'
import tabTheme from './MyTabTheme.css'

// blah blah...

return <Tab key={index} className={styles.tab} theme={tabTheme} />

Under the surface this uses a decorator they have abstracted into separate library react-css-themr, I recommend giving that a read too as it explains how the defaults are composed with your overrides in much greater depth, including the different merging strategies they use

Upvotes: 16

Patrick
Patrick

Reputation: 1428

I had a similar case, and I solved it like so:

import classNames from 'classnames';

...

const activeClassName = {};
activeClassName[`${styles.active}`] = this.props.isActive;
const elementClassNames = classNames(styles.element, activeClassName);

return <div className={elementClassNames} />

I'm using the classnames package to dynamically add the active class based off of the isActive prop. Instead of an isActive prop you can provide any boolean value.

A more terse way of doing this may be:

const elementClassNames = classNames(styles.element, {[styles.active]: this.props.isActive});

Upvotes: 5

Related Questions