Home > Back-end >  Is there a way to keep linked stylesheets localized to single components?
Is there a way to keep linked stylesheets localized to single components?

Time:11-24

I have a component that relies on external stylesheets. I'm bringing the stylesheet into the component like this:

Child component

export default class Child extends Component {
  render() {
    return (
      <div>
        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css" />
        ...my code here...
      </div>
    );
  }
}

But what's happening is this is forcing those styles onto the parent component as well.

Parent Component

export default class Parent extends Component {
  render() {
    return (
      <div>
        ...code here...
        <Child />
        ... more code here...
      </div>
    );
  }
}

Is anyone aware of a way that I can keep that stylesheet link localized to just that child component so the styles aren't applied to the parent component as well?

Edit 2

Currently trying the shadow dom route, trying to pass down some children. Getting an error after the initial render saying Cannot read properties of undefined (reading 'children'). It does render the this.props.children initially...

import React, { Component } from 'react';

class MyComponent extends HTMLElement {
  constructor() {
    super();
    const shadow = this.attachShadow({ mode: 'open' });
    shadow.innerHTML = `
      ${this.props.children}
    `;
  }
};

export default class Child extends Component {
  render() {
    return (
      <div>
        <script>
          {!customElements.get("my-component") && customElements.define('my-component', MyComponent)}
        </script>
        <my-component>
          <h1>Hello from shadow</h1>
        </my-component>
        <h1>Hello</h1>
      </div>
    );
  }
}

CodePudding user response:

You can try CSS Modules. Add :local(.className) to the class you want to use in your code which is in the font-awesome-min.css file. Then import the styles to your component. For example import styles from './font-awesome-min.css' then use the module in your code. The styles will only apply to specific element and won't affect other elements in the document. So let's say you have a class called .usericon in your css you do this in the css file.

CSS

:local(.usericon){
    fill: red;
}

React Code

import styles from './font-awesome-min.css'

export default function Profile(){
    return (
        <i className={styles.usericon}>User Icon</i>
    )
}

CodePudding user response:

One way to truly isolate your CSS is with Web Components. Web Components are a browser API that allows defining custom elements with their own "shadow DOM". If a style is defined inside the shadow DOM, it is truly sandboxed with no styles going in or out. You can use whatever selectors you like:

class FancyBox extends HTMLElement {
  constructor() {
    super();
    const shadow = this.attachShadow({ mode: 'open' });
    shadow.innerHTML = `
      <style>
        .fancy-box {
          border: solid 3px darkblue;
          background: dodgerblue;
          padding: 10px;
          color: white;
          font: 16px sans-serif;
        }
      </style>
      <div >
        <slot></slot>
      </div>
    `;
  }
}

customElements.define('fancy-box', FancyBox);
.fancy-box {
  border: dashed 3px darkred !important;
  background: crimson !important;
  padding: 10px !important;
  color: white !important;
  font: 16px sans-serif;
}
<fancy-box>Safe in my shadow DOM</fancy-box>       
<div class="fancy-box">I am affected by outside stylesheets</div>
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

Note the use of <slot></slot>. This is a placeholder for child elements of the component.

If I wanted to use this custom element from React, it needs to be defined separately so it only runs once.

class FancyBox extends HTMLElement { /*...*/ };
customElements.define('fancy-box', FancyBox);

class ReactFancyBox extends React.Component {
  constructor() {
    super();
    this.state = { value: 'hello world!' }
  }

  handleChange(e) {
    this.setState({ value: e.currentTarget.value });
  }

  render() {
    return (
      <div>
        <fancy-box>
           <strong>{this.state.value}</strong>
        </fancy-box>
        <input value={this.state.value} onChange={e => this.handleChange(e)} />
      </div>
    );
  }
};

  • Related