diff --git a/README.md b/README.md
index af10bf3..6a3de02 100644
--- a/README.md
+++ b/README.md
@@ -258,6 +258,26 @@ following features:
containing block. To work around this, the polyfill strips any non-`auto`
inset from the target (setting `inset: auto`) and re-applies it as padding
on the wrapper, so the wrapper continues to drive positioning.
+ - Moving the target into the wrapper disconnects and reconnects it. If the
+ target is a custom element, its `connectedCallback` therefore runs more
+ than once, and any setup that can only happen once must be guarded — for
+ example, calling `attachShadow()` a second time throws. This applies to
+ any custom element the polyfill positions with `position-area`, including
+ a host positioned by a `position-area` in its own `:host` rule:
+
+ ```js
+ class MyElement extends HTMLElement {
+ connectedCallback() {
+ if (this.shadowRoot) return;
+ this.attachShadow({ mode: 'open' });
+ // ...
+ }
+ }
+ ```
+
+ Setting [`positionAreaContainingBlock`](#positionareacontainingblock) to
+ `false` (or `'auto'`, for targets that don't need the wrapper) avoids the
+ wrapper, and with it the reconnection.
- When the wrapper is not added, styles that resolve against the containing
block — percentage sizes, `auto` or percentage margins, percentage padding,
or `stretch`/`anchor-center` self-alignment — will not match native
diff --git a/shadow-dom.html b/shadow-dom.html
index b835fb9..8ad0cb0 100644
--- a/shadow-dom.html
+++ b/shadow-dom.html
@@ -97,6 +97,39 @@
}
}
customElements.define('position-anchor-on-host', PositionAnchorOnHost);
+
+ // `position-area` in a `:host` rule positions the shadow *host*, which
+ // lives in the outer tree rather than in the shadow root the rule came
+ // from. The styles the polyfill generates to map the computed insets
+ // onto the target have to be inserted into that outer tree to match it.
+ const positionAreaOnHostSheet = new CSSStyleSheet();
+ positionAreaOnHostSheet.replaceSync(`
+ :host {
+ --element-color: var(--target, var(--outer-anchored));
+ background: var(--element-color);
+ border: thin solid var(--border);
+ border-radius: var(--radius-1);
+ color: white;
+ font-weight: bold;
+ padding: 0.5em;
+ white-space: nowrap;
+ position: absolute;
+ position-area: top;
+ }
+ `);
+
+ class PositionAreaOnHost extends HTMLElement {
+ connectedCallback() {
+ // Moving the host into the `position-area` wrapper disconnects and
+ // reconnects it, so this runs more than once.
+ if (this.shadowRoot) return;
+
+ this.attachShadow({ mode: 'open' });
+ this.shadowRoot.adoptedStyleSheets = [positionAreaOnHostSheet];
+ this.shadowRoot.innerHTML = '
position-area
+ With polyfill applied: Target sits directly above the Anchor.
+
+ The position-area is declared in a
+ :host rule, so the element it positions is the host
+ (<position-area-on-host>), which lives in the outer
+ tree rather than in the shadow root the rule came from. The styles the
+ polyfill generates to map the computed insets onto the target are
+ inserted into the host's own tree; a <style> inside
+ the shadow root would never match the host.
+
<div class="anchor" style="anchor-name: --position-area-on-host">Anchor</div>
+<position-area-on-host style="position-anchor: --position-area-on-host">Target</position-area-on-host>
+<script>
+<!-- Load the shadow entrypoint before defining custom elements,
+so the replaceSync and adoptedStyleSheets patches are installed
+before any connectedCallback runs. -->
+import { patchAndPolyfillConstructedStylesheets } from '@oddbird/css-anchor-positioning/fn';
+patchAndPolyfillConstructedStylesheets();
+
+class PositionAreaOnHost extends HTMLElement {
+ connectedCallback() {
+ // Moving the host into the position-area wrapper reconnects it.
+ if (this.shadowRoot) return;
+
+ this.attachShadow({ mode: "open" });
+
+ const sheet = new CSSStyleSheet();
+ sheet.replaceSync(`
+ :host {
+ position: absolute;
+ position-area: top;
+ }
+ `);
+ this.shadowRoot.adoptedStyleSheets = [sheet];
+ this.shadowRoot.innerHTML = "<slot></slot>";
+ }
+}
+customElements.define("position-area-on-host", PositionAreaOnHost);
+</script>