Isolet Widget Isolation

reason-machines/trending-skills/skills/isolet-widget-isolation

作者 reason-machines2384a003145a無授權條款83 個星標收錄於 2026年10月8日更新於 2026年10月8日儲存庫3 個月前更新

Package any component into a self-contained, isolated widget with shadow DOM, scoped styles, and multi-framework support

AI 產生的概覽

將任意前端元件打包成具備 shadow DOM 樣式隔離的獨立小工具,並支援多種輸出格式。

功能
此技能介紹 isolet-js,這是一套函式庫與 CLI,可將 React、Solid、Svelte 或原生 JS 元件封裝成彼此隔離的小工具。內容涵蓋 createIsolet API、各框架轉接器、隔離模式(shadow-dom、scoped、none)、建置設定、CSS 與資源內嵌,以及 IIFE/ESM/CommonJS 輸出。另提供樣式外洩、清理與全域名稱等問題的疑難排解說明。
適用情境
當你需要把元件嵌入任意頁面且不希望發生樣式衝突,或需要將元件打包成可直接以 script 標籤引入的小工具時使用。也適合建置具備作用域 CSS、支援多框架的自包含小工具套件。
執行需求
需要 Node.js 與 npm 才能安裝 isolet-js;使用 CLI(npx isolet-js)進行範本初始化與建置。框架轉接器需要對應框架(React、Solid、Svelte)可用,外部相依套件須由宿主頁面提供。此技能本身不附帶指令碼,只有說明文件。

isolet Widget Isolation

Skill by ara.so — Daily 2026 Skills collection.

isolet-js packages any component (React, Solid, Svelte, vanilla JS, etc.) into a self-contained, isolated widget. Widgets render inside shadow DOM by default, so styles are fully scoped. Output formats include IIFE (script tag), ESM, and CommonJS.

Install

sh
npm install isolet-js

Core API: createIsolet

ts
import { createIsolet } from "isolet-js";
const widget = createIsolet({  name: "my-widget",          // required: unique identifier  mount: myMountFn,           // required: (container, props) => cleanup | void  css: `h1 { color: red; }`,  // optional: scoped CSS  isolation: "shadow-dom",    // "shadow-dom" | "scoped" | "none"  shadowMode: "open",         // "open" | "closed"  hostAttributes: { "data-widget": "true" },  zIndex: 9999,});
widget.mount(document.body, { title: "Hello" }); // mount into targetwidget.update({ title: "Updated" });             // update propswidget.unmount();                                // tear down
// Instance propertieswidget.container;   // HTMLElement — the render containerwidget.shadowRoot;  // ShadowRoot | nullwidget.mounted;     // boolean

Framework Adapters

React

tsx
import { createIsolet } from "isolet-js";import { react } from "isolet-js/react";
function Greeting({ name }: { name: string }) {  return <h1>Hello, {name}!</h1>;}
const widget = createIsolet({  name: "greeting",  mount: react(Greeting),  css: `h1 { color: tomato; font-family: sans-serif; }`,});
widget.mount(document.body, { name: "World" });widget.update({ name: "Isolet" });widget.unmount();

Vanilla JS

ts
import { createIsolet } from "isolet-js";import { vanilla } from "isolet-js/vanilla";
const widget = createIsolet({  name: "counter",  mount: vanilla((container, props) => {    let count = props.initial ?? 0;    const btn = document.createElement("button");    btn.textContent = `Count: ${count}`;    btn.onclick = () => {      btn.textContent = `Count: ${++count}`;    };    container.appendChild(btn);
    // Return cleanup function    return () => container.removeChild(btn);  }),});
widget.mount(document.getElementById("app"), { initial: 5 });

Solid

tsx
import { createIsolet } from "isolet-js";import { render } from "solid-js/web";import App from "./App";
const widget = createIsolet({  name: "solid-widget",  mount(container, props) {    const dispose = render(() => <App {...props} />, container);    return dispose; // dispose is the cleanup function  },});

Svelte

ts
import { createIsolet } from "isolet-js";import App from "./App.svelte";
const widget = createIsolet({  name: "svelte-widget",  mount(container, props) {    const app = new App({ target: container, props });    return () => app.$destroy();  },});

Isolation Modes

ts
// Full CSS isolation — shadow DOM (default)createIsolet({ name: "w", mount: fn, isolation: "shadow-dom" });
// Scoped — plain div wrapper, styles injected globallycreateIsolet({ name: "w", mount: fn, isolation: "scoped" });
// No isolation — mounts directly into target elementcreateIsolet({ name: "w", mount: fn, isolation: "none" });

CLI

sh
npx isolet-js init            # scaffold isolet.config.tsnpx isolet-js build           # bundle widget(s) from confignpx isolet-js build --watch   # rebuild on file changesnpx isolet-js build --minify  # minified production build

Config File

ts
// isolet.config.tsimport { defineConfig } from "isolet-js";
export default defineConfig({  name: "my-widget",  entry: "./src/index.ts",  styles: "./src/widget.css",       // CSS to inline; url() assets become data URIs  format: ["iife", "esm"],          // output formats  outDir: "./dist",                 // default: "dist"  globalName: "MyWidget",           // global name for IIFE builds  external: ["react"],              // don't bundle these deps  dts: true,                        // emit .d.ts files  minify: true,                     // minify output  platform: "browser",              // target platform});

Multiple Widgets

ts
export default defineConfig([  { name: "widget-a", entry: "./src/a.ts", styles: "./src/a.css" },  { name: "widget-b", entry: "./src/b.ts", format: ["esm"] },]);

CSS & Asset Handling

The build pipeline handles everything automatically:

  • styles in config → CSS is read, url() references (fonts, images) inlined as data URIs, result available as __ISOLET_CSS__ in your entry
  • .css imports → converted to JS string exports (shadow DOM safe)
  • Asset imports (.png, .woff2, .mp3, etc.) → inlined as data URIs
  • styles: "./path.css" in createIsolet → resolved and inlined at build time
ts
// Entry file using __ISOLET_CSS__ injected by CLIimport { createIsolet } from "isolet-js";import { react } from "isolet-js/react";import MyComponent from "./MyComponent";
declare const __ISOLET_CSS__: string;
export const widget = createIsolet({  name: "my-widget",  css: __ISOLET_CSS__,   // populated from config.styles at build time  mount: react(MyComponent),});

Or reference the CSS path directly (auto-resolved at build time):

ts
createIsolet({  name: "my-widget",  styles: "./widget.css",  // isolet build resolves this  mount: react(MyComponent),});

Manual Vite Plugin Setup

If using Vite directly instead of the CLI:

ts
// vite.config.tsimport { defineConfig } from "vite";import {  cssTextPlugin,  inlineAssetsPlugin,  autoStylesPlugin,} from "isolet-js/plugins";
export default defineConfig({  plugins: [cssTextPlugin(), inlineAssetsPlugin(), autoStylesPlugin()],});

Script Tag (IIFE) Usage

html
<script src="https://unpkg.com/isolet-js/dist/index.iife.js"></script><script>  const { createIsolet } = __ISOLET__;
  const widget = createIsolet({    name: "inline-widget",    mount(container, props) {      container.innerHTML = `<p>Hello, ${props.name ?? "World"}!</p>`;    },    css: `p { font-family: sans-serif; color: navy; }`,  });
  widget.mount(document.body, { name: "Visitor" });</script>

For a bundled custom widget via IIFE:

ts
// isolet.config.tsexport default defineConfig({  name: "my-widget",  entry: "./src/index.ts",  format: ["iife"],  globalName: "MyWidget",  minify: true,});
html
<!-- Resulting script tag distribution --><script src="./dist/my-widget.iife.js"></script><script>  MyWidget.widget.mount(document.getElementById("root"), { title: "Hi" });</script>

Common Patterns

Lazy-mount on demand

ts
const widget = createIsolet({ name: "chat", mount: react(ChatApp), css: styles });
document.getElementById("open-chat").addEventListener("click", () => {  if (!widget.mounted) {    widget.mount(document.body, { userId: currentUserId });  }});
document.getElementById("close-chat").addEventListener("click", () => {  widget.unmount();});

z-index overlay widget

ts
const modal = createIsolet({  name: "modal",  mount: react(ModalComponent),  css: modalStyles,  zIndex: 10000,  hostAttributes: { role: "dialog", "aria-modal": "true" },});

Reactive props updates

ts
const widget = createIsolet({ name: "status", mount: react(StatusBar), css });widget.mount(document.body, { status: "idle" });
// Later, update without remounting:widget.update({ status: "loading" });widget.update({ status: "done" });

Troubleshooting

Styles leaking in or out Use isolation: "shadow-dom" (default). Verify your css option or styles path is correctly set — without CSS in the shadow root, the host page styles will not apply inside.

__ISOLET_CSS__ is undefined This variable is only injected by the isolet build CLI when styles is set in config. For manual Vite builds, add autoStylesPlugin() to your Vite config.

Component not rendering Ensure the mount function appends to container, not to document.body. In shadow DOM mode, the container is inside the shadow root.

Cleanup not running Return a cleanup function from your mount callback. Without it, widget.unmount() cannot tear down framework internals (timers, subscriptions, etc.).

IIFE global not found Check globalName in config matches what you reference in HTML. The runtime core exposes globalThis.__ISOLET__ when no globalName is set.

External deps not found at runtime If you set external: ["react"], the host page must provide React globally or via module federation before your widget script loads.

來源與署名

來源:reason-machines/trending-skills位於skills/isolet-widget-isolation提交2384a00

授權條款: 無授權條款

內容歸原作者所有。SourceWeft 從公開儲存庫中收錄這些內容。

檢舉或申請下架