Brings Shadow DOM CSS encapsulation to React, along with a DSD-compatible template element.
npm install react-shadow-scope
``jsx`
Traditional global CSS risks naming collisions, specificity conflicts, and unwanted style inheritance. Modern tools have been designed to solve these problems by using emulated encapsulation, but nothing can protect from inherited styles except for shadow DOM.
This package does _not_ burden you with all the boilerplate around shadow DOM, nor force you to use web components. Did you know you can attach a shadow root to regular elements, like a
? That's essentially what react-shadow-scope does behind the curtain.> Note This package supports Tailwind in the shadow DOM via the
component. Using Tailwind globally risks naming collisions with other utility classes. This can be especially important for library authors.As a rule of thumb, you should limit your global CSS to little or nothing. The native
@scope rule can get you pretty far, but it still doesn't protect from inherited styles. Shadow DOM encapsulation is the _single best tool we have_.Table of Contents
- Install
- Usage
- Scope
- Composing Styles
- Normalize
- Constructed Style Sheets
- Remote Style Sheets
- Excluding Children From the Scope
- Templates
- Tailwind
- Maintainers
- License
Install
`
npm i react-shadow-scope
`Usage
$3
To create a new CSS scope, import the
Scope component from the package and just pass a string to the stylesheet prop.`jsx
import { Scope } from 'react-shadow-scope';const MyComponent = () => (
<>
{/ Scope gives you protection from inherited styles! /}
This title is blue with underline
h1 { color: red; }}>
This title is red without underline
h1 { font-style: italic; }}>
This title is italicized without underline or color
>
);
`#### Custom Tag Names
By default,
renders a element, but doesn't define it in the custom element registry. The custom tag name just avoids cases where or would break HTML validation.This can be overridden via the
tag prop, in case of conflicts or for better legibility in the dev tools.`jsx
`The above will output:
If you're using TypeScript, you will need to merge with the interface where these element types are declared.
`ts
import { CustomIntrinsicElement } from 'react-shadow-scope';declare global {
namespace ReactShadowScope {
interface CustomElements {
'my-element': CustomIntrinsicElement;
}
}
}
`> Note
>
> In some cases, HTML requires certain nesting rules to be valid. For example,
may only contain tags as direct children. To work around this, you can either render all tags in one parent , or apply your own semantics with role="list" and role="listitem" to your markup instead of using and .---
$3
It's normally a good idea to contain the complexity of your design. In other words, instead of designing the different use cases _outside_ a component, design from the _inside_ by describing the use case, like
usedFor="page" or importance="urgent". The goal should be to eliminate the need for consumers of your component to write any CSS at all.However, sometimes it's necessary to compose styles from the parent scope. In such an event, you may add a class to the
component and select that to style the outer element. Although React applies some unique rules to custom elements, you can just use className as usual and we'll forward it to class internally.This is a gray area that has some indirect impact on the shadow DOM via the cascade. You can also selectively reach into the shadow DOM with shadow parts. It's important to be aware this breaks encapsulation, so it's generally not the recommended approach. Although it's _sometimes_ necessary or beneficial, it often isn't, so be careful. It may require shifting your mental model a bit at first.
---
$3
This package borrows from normalize.css to make style defaults more consistent across browsers. This feature is opted-in by default to hopefully save you some hassle, but you can opt-out any time by setting the
normalize prop to false.`jsx
`All normalized styles are contained inside a
@layer called normalize, which gives them the lowest priority, making them easy to override.> Note
>
> By default,
applies display: contents; to avoid problems with layouts. (This preserves accessibility because it lacks semantics to interfere with anyway.) You may override this with :host { / overrides / }.---
$3
Runtime overhead is a common criticism of CSS-in-JS approaches, but
react-shadow-scope solves these performance concerns using constructed style sheets.Using the
css tagged template literal, you can create a new CSSStyleSheet object and pass it to the stylesheet prop. This allows you to share a single style sheet across different components and between each instance, rather than continuously duplicating the styles at runtime.`jsx
import { css } from 'react-shadow-scope';const stylesheet = css
;const MyComponent = () => {
return (
title here
);
};
const MyOtherComponent = () => {
return (
another title here
);
};
`> NOTE: On the server,
css returns a string which is rendered in a #### Tailwind Props
-
href - This is /tailwind.css if omitted. This will be fetched once and cached.
- customStyles - Pass a string or CSSStyleSheet (the css tagged template function is recommended)
- pendingStyles - Works the same as pendingStyles on the component.
- slottedContent - Works the same as slottedContent on the MIT © 2023 Jonathan DeWitt