Adapted from and inspired by react-shadow-root.
A tiny 1.3 kB minified library for rendering a Preact component inside a ShadowRoot. Supports constructable stylesheets and slotted elements.
Add the registry to .npmrc
:
@alorel:registry=https://npm.pkg.github.com
Then install it:
npm install @alorel/preact-shadow-root
The library uses hooks depends on preact@^10.0.0
interface Props {
adoptedStyleSheets?: CSSStyleSheet[];
/** Shadow root content */
children: ComponentChildren;
/**
* @see {@link ShadowRootInit#delegatesFocus}
* @default false
*/
delegatesFocus?: boolean;
/** Attach the shadow root to this host element instead of creating one */
host?: Element;
/**
* Shadow root mode. Changes to this property are ignored.
* @default open
*/
mode?: ShadowRootMode;
/** Slot elements to render */
slots?(): ComponentChildren;
}
import {ShadowHost} from '@alorel/preact-shadow-root';
import {h} from 'preact';
function MyShadowComponent() {
return (
<div>
{/* The div will be the shadow root host; most elements would do */}
<ShadowHost>
<span>I am inside a shadow root</span>
</ShadowHost>
</div>
);
}
Slotted content is provided through a render function. Both named and default slots are supported.
import {ShadowHost} from '@alorel/preact-shadow-root';
import {Fragment, h} from 'preact';
import {useCallback} from 'preact/hooks';
function MyNamedSlottedComponent({children}) {
const slotFn = useCallback(() => children, [children]);
return (
<header>
{/* The div will be the shadow root host; most elements would do */}
<ShadowHost slots={slotFn}>
<slot name={'named'}/>
</ShadowHost>
</header>
);
}
function MySlottedShadowComponent({children}) {
const slotFn = useCallback(() => children, [children]);
return (
<section data-foo={'bar'}>
{/* The div will be the shadow root host; most elements would do */}
<ShadowHost slots={slotFn}>
<slot>Default content</slot>
</ShadowHost>
</section>
);
}
function App() {
return (
<Fragment>
<MySlottedShadowComponent/>
<hr/>
<MySlottedShadowComponent>Provided content</MySlottedShadowComponent>
<hr/>
<MyNamedSlottedComponent>
<span slot={'named'}>I'm slotted!</span>
<span slot={'other'}>I'm skipped</span>
</MyNamedSlottedComponent>
</Fragment>
);
}
import {AdoptedStylesheets, ShadowHost} from '@alorel/preact-shadow-root';
import {h} from 'preact';
const sheet = new CSSStyleSheet();
sheet.replaceSync(':host{background:yellow;color:blue}');
const stylesheets = [sheet];
function AdoptedStylesheetsExample() {
return (
<div>
<ShadowHost adoptedStyleSheets={stylesheets}>
{/* A standard non-adopted stylesheet */}
<style>{':host{display:inline-block}'}</style>
<AdoptedStylesheets/>
<span>Top tier colour combo</span>
</ShadowHost>
</div>
);
}
function App() {
return <AdoptedStylesheetsExample/>;
}
When constructable stylesheets are supported, this results in the following:
When they aren't, <AdoptedStylesheets/>
will render them as regular styles:
Notes:
- Adopted stylesheets are applied after all the
<style>
attributes - position<AdoptedStylesheets/>
accordingly - Maintain the same reference to the stylesheets array unless a sheet gets added/removed, otherwise they'll keep getting reapplied
The library is packaged in the following variants:
module | ES version | file | main fields |
---|---|---|---|
commonjs | ES6 | index.js | main |
es2015 | ES5 | index.esm5.js | esm5, module |
es2015 | ES6 | index.esm2015.js | es2015, esm2015 |
umd | ES5 | index.umd.js | browser |
You can configure your build system accordingly.
If you use Typescript you won't need proptype definitions as the compiler will just throw an error.
If you use Javascript you'll need to install the following:
npm install prop-types@>=15.6.0 @alorel/preact-shadow-root-proptypes
Then, make sure the proptype definitions are imported before you make use of the library:
import '@alorel/preact-shadow-root-proptypes';
// Your app component etc
module | ES version | file | main fields |
---|---|---|---|
commonjs | ES5 | index.js | main |
es2015 | ES5 | index.esm5.js | esm5, module |
umd | ES5 | index.umd.js | browser |