|
| 1 | +--- |
| 2 | +name: web-fragments |
| 3 | +description: >- |
| 4 | + Build, embed, debug, and deploy micro-frontends with the Web Fragments framework |
| 5 | + (the `web-fragments` npm package by web-fragments.dev / Cloudflare). Use whenever the user |
| 6 | + works with web-fragments, the `<web-fragment>` element, `FragmentGateway`, |
| 7 | + `registerFragment`, `getWebMiddleware` / `getNodeMiddleware`, fragment "piercing", or the |
| 8 | + "reframed" iframe isolation — and more broadly when they want to incrementally migrate, |
| 9 | + decompose, or compose a web frontend from independently deployed micro-frontends that |
| 10 | + share one DOM, even if they don't name the library. Covers authoring a fragment, wiring it |
| 11 | + into a host app, gateway/route-pattern config, styling across the shadow boundary, and |
| 12 | + troubleshooting hydration/asset/type errors. Prefer this skill over guessing the API — the |
| 13 | + framework is beta (v0.8.x) and several public docs are stale. |
| 14 | +--- |
| 15 | + |
| 16 | +# Web Fragments |
| 17 | + |
| 18 | +Framework-/platform-agnostic micro-frontend architecture. Each micro-frontend's client JS |
| 19 | +runs in an isolated context (a hidden iframe = *reframed*) while all fragments **share one |
| 20 | +DOM, navigation, and history** — "containers for web frontends." Beta v0.8.x, in production |
| 21 | +at Cloudflare. |
| 22 | + |
| 23 | +**Don't read `node_modules/web-fragments/` source for the API — it's captured here and in |
| 24 | +`references/`.** web-fragments.dev docs are partly stale; when they disagree with this skill, |
| 25 | +trust the skill (verify via the package's `exports` map in `node_modules/web-fragments/package.json`). |
| 26 | + |
| 27 | +## Mental model: 3 pieces |
| 28 | + |
| 29 | +1. **Fragment app** — a *standalone* app (any stack) at an HTTP endpoint serving its own |
| 30 | + HTML + assets; doesn't know it's a fragment. → `references/fragment-authoring.md` |
| 31 | +2. **Host (shell)** — the existing app that embeds fragments: calls `initializeWebFragments()` |
| 32 | + once + places `<web-fragment fragment-id="…">`. → `references/host-integration.md` |
| 33 | +3. **Gateway** — **mandatory** middleware on the host server (issue #278): proxies fragment |
| 34 | + requests onto the host's single origin and *pierces* SSR markup into the shell. |
| 35 | + |
| 36 | +Flow: browser → host origin → gateway matches `routePatterns` → proxies to fragment |
| 37 | +`endpoint` → response composed into the host page. |
| 38 | + |
| 39 | +## Public API (v0.8.x) — exactly three entry points |
| 40 | + |
| 41 | +There is **no** `web-fragments/middleware` entry point (a stale doc claims one). |
| 42 | + |
| 43 | +| Import | Exports | |
| 44 | +|---|---| |
| 45 | +| `web-fragments` | `initializeWebFragments()`, `WebFragment`, `WebFragmentHost` | |
| 46 | +| `web-fragments/gateway` | `FragmentGateway`, `getWebMiddleware`, types `FragmentConfig`, `FragmentMiddlewareOptions` | |
| 47 | +| `web-fragments/gateway/node` | `getNodeMiddleware` | |
| 48 | + |
| 49 | +**Client (host bootstrap):** |
| 50 | +```ts |
| 51 | +import { initializeWebFragments } from 'web-fragments'; |
| 52 | +initializeWebFragments(); // defines the custom elements; call as early as possible |
| 53 | +``` |
| 54 | +```html |
| 55 | +<web-fragment fragment-id="party-button"></web-fragment> |
| 56 | +``` |
| 57 | +`fragment-id` is required (throws without it); `src` is optional. `<web-fragment-host>` is |
| 58 | +internal — you rarely author it. |
| 59 | + |
| 60 | +**Gateway (host server):** |
| 61 | +```ts |
| 62 | +import { FragmentGateway } from 'web-fragments/gateway'; |
| 63 | +const gateway = new FragmentGateway(/* { piercingStyles?: string } */); |
| 64 | +gateway.registerFragment({ // method is registerFragment, NOT register |
| 65 | + fragmentId: 'party-button', // required; must match the element's fragment-id |
| 66 | + endpoint: 'https://party-button.example.dev', // required; URL string OR a fetch-compatible fn |
| 67 | + routePatterns: ['/__wf/party-button/:_*', '/'], // required (path-to-regexp v6): assets + host route(s) |
| 68 | + piercing: true, // optional, default true; false = client-only |
| 69 | + // piercingClassNames, forwardFragmentHeaders, iframeHeaders, onSsrFetchError — see api-reference |
| 70 | +}); |
| 71 | +``` |
| 72 | +Full `FragmentConfig` + deprecated aliases (`upstream`→`endpoint`, |
| 73 | +`prePiercingClassNames`→`piercingClassNames`): `references/api-reference.md`. |
| 74 | + |
| 75 | +**Middleware (pick ONE per host runtime):** |
| 76 | +```ts |
| 77 | +import { getWebMiddleware } from 'web-fragments/gateway'; // Web/Fetch: CF, Vercel, Netlify, Hono, SW |
| 78 | +const mw = getWebMiddleware(gateway, { mode: 'development' }); // 'production' | 'development' |
| 79 | + |
| 80 | +import { getNodeMiddleware } from 'web-fragments/gateway/node'; // Node: Express / Connect |
| 81 | +app.use(getNodeMiddleware(gateway)); |
| 82 | +``` |
| 83 | +Per-framework wiring: `references/host-integration.md`. |
| 84 | + |
| 85 | +## Workflow (add a fragment to an app) |
| 86 | + |
| 87 | +1. **Fragment app** serves its own HTML+assets, emitting assets under a **unique** path |
| 88 | + (e.g. Vite `build.assetsDir: "__wf/<id>/"`) that lines up with the asset `routePattern`. |
| 89 | +2. **Bootstrap** the host client: `initializeWebFragments()` at the earliest entry. |
| 90 | +3. **Place** `<web-fragment fragment-id="…">` at the chosen host route. |
| 91 | +4. **Register**: `new FragmentGateway()` + `registerFragment(...)` with two patterns (assets + |
| 92 | + host route). The host-route pattern MUST match where the element lives (step 3). |
| 93 | +5. **Install** the middleware matching the runtime (Web vs Node). |
| 94 | +6. **Verify** (below), then deploy — fragment and host ship independently. |
| 95 | + |
| 96 | +## Verify |
| 97 | + |
| 98 | +Fragment renders as **nested shadow roots**: `<web-fragment>` ▸ shadowRoot ▸ |
| 99 | +`<web-fragment-host>` ▸ shadowRoot ▸ `<wf-document>`, plus a hidden `<iframe name="wf:<id>">` |
| 100 | +running its scripts. In dev tools: fragment DOM is in the main document but inside a shadow |
| 101 | +root (style isolation); a `wf:<id>` context exists; removing the element tears it down; |
| 102 | +asset requests hit `/__wf/<id>/…` on the **host** origin (proxied). |
| 103 | + |
| 104 | +Automated: `node scripts/doctor.mjs <project-dir>` flags the common wiring mistakes (wrong |
| 105 | +import path, `register` vs `registerFragment`, asset/routePattern mismatch, missing |
| 106 | +middleware). Use it whenever a fragment "doesn't show up" or assets 404. |
| 107 | + |
| 108 | +## Bundled resources |
| 109 | + |
| 110 | +Load only the reference matching the task: |
| 111 | +- `references/api-reference.md` — every export, full `FragmentConfig`/options, deprecations. Writing/reviewing gateway/element code. |
| 112 | +- `references/host-integration.md` — middleware wiring per framework (Express, CF Pages/Workers, Vercel, Netlify, Hono, Angular, Next.js, Vite SPA). |
| 113 | +- `references/fragment-authoring.md` — build a fragment from scratch: structure, Vite `assetsDir`, route patterns, deploy. |
| 114 | +- `references/troubleshooting.md` — known issues/gotchas (hydration, asset truncation, nodenext types, Angular htmlrewriter, style accumulation). Read FIRST when broken. |
| 115 | +- `references/css-and-styling.md` — **verified** style behavior across the shadow boundary (`:root` vs `:host`, `@layer`, what inherits/leaks). |
| 116 | +- `references/csp-and-iframe.md` — CSP / `X-Frame-Options` / `frame-ancestors` so the gateway's hidden iframe isn't blocked. Read when a fragment silently fails to load or you're setting security headers. |
| 117 | +- `references/piercing-and-performance.md` — piercing internals (`sec-fetch-dest` hard-nav, `data-piercing` styling, HTMLRewriter), caching (`Vary`, stub TTL, `forwardFragmentHeaders`), `mode:production`, route specificity. Read for perf/piercing/CLS work. |
| 118 | +- `references/frameworks-astro.md` — **verified** Astro recipe: embedding, `<ClientRouter />`, the head-accumulation bug (#297), and smooth transitions without ClientRouter. |
| 119 | + |
| 120 | +Scripts (run, don't read): `scripts/scaffold-fragment.mjs` (generate a Vite fragment + |
| 121 | +gateway snippet); `scripts/doctor.mjs` (static-check a project). |
| 122 | + |
| 123 | +**Official slash commands** (PR #294, ship with the package under `.claude/commands/`): |
| 124 | +`/web-fragments:init-fragment | gateway-config | debug-fragment | fragment-test | |
| 125 | +migrate-to-fragments | fragment-csp | fragment-perf` — explicit task runners. They complement |
| 126 | +this skill: the skill auto-triggers and supplies verified knowledge; the commands run a |
| 127 | +specific job on request. Use them when the user invokes one or wants a guided scaffold/audit. |
| 128 | + |
| 129 | +## Top gotchas (full list: references/troubleshooting.md) |
| 130 | + |
| 131 | +- **Wrong middleware import.** No `web-fragments/middleware`. Web = `web-fragments/gateway`; Node = `web-fragments/gateway/node`. |
| 132 | +- **`registerFragment`, not `register`.** |
| 133 | +- **Asset path ↔ routePattern mismatch** — #1 cause of 404'd assets / blank fragments. Fragment asset dir and gateway asset `routePattern` must share the prefix. |
| 134 | +- **Gateway is mandatory** — no server-less mode yet (#278). |
| 135 | +- **`nodenext`** type resolution may fail (#284); **Angular host** needs `outputMode:"server"` + `externalDependencies` (#280). |
| 136 | +- **`:root` is dead inside a fragment; use `:host`.** Host `:root` vars + inherited props (font/color) flow in; selector rules don't. → `css-and-styling.md`. |
| 137 | +- **`X-Frame-Options: DENY` on the fragment endpoint silently kills it** — it blocks the gateway's hidden iframe. Allow framing (`frame-ancestors 'self' <gateway-origin>`). → `csp-and-iframe.md`. |
| 138 | +- **Piercing fires only on hard navigation** (`sec-fetch-dest: document`); set `mode:'production'` when deployed; register specific routePatterns before broad ones (first-match-wins). → `piercing-and-performance.md`. |
0 commit comments