mcpField
S
shadow-dom-overlay-insertion

shadow-dom-overlay-insertion

github.com/editframe開発1971 回インストール2 週間前に更新
リポジトリ

shadow-dom-overlay-insertion について

shadow-dom-overlay-insertionは、Shadow DOM内にスロット配置されるWebコンポーネントへのオーバーレイ挿入に関する技術リファレンスです。挿入先の選び方と、LitのShadow DOM描画前に生じるタイミング差への対処を説明します。

主な機能

  1. 01`assignedSlot`によるスロット配置の検出
  2. 02Shadow DOM内部コンテナへの挿入
  3. 03描画前のスロット割り当てへの対処
  4. 04DOM状態に応じた挿入方法の選択

活用シーン

  1. 01スロット配置されたWebコンポーネントへのオーバーレイ追加
  2. 02オーバーレイがホストのlight DOMに入るのを防止
  3. 03Litの描画後に挿入を再試行

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/editframe/skills --skill shadow-dom-overlay-insertion

インストール後、Claude Code で/shadow-dom-overlay-insertionを入力して読み込まれたか確認します。

詳細

シャドウ DOM へのオーバーレイ挿入

問題

コンポーネント A(例:EFCanvas)が自身の隣にオーバーレイを DOM に挿入しようとすると、通常は this.parentElement.appendChild(overlay) を使用します。A がシャドウ DOM ホスト(例:EFWorkbench)内にスロットとして配置されている場合、これは正常に動作しません。

EFWorkbench (shadow host)
  shadow root
    <div class="canvas-slot-container">   ← the real DOM parent
      <slot name="canvas">                ← slot element
        [EFCanvas is assigned here]       ← light DOM, assigned via slot
  • efCanvas.parentElement は内部コンテナーではなく、シャドウホストを返します
  • shadowHost.appendChild(overlay) を呼び出すと、オーバーレイはホストの ライト DOM に配置されます。シャドウ DOM の CSS と z-index が適用されないため、オーバーレイが表示されません

修正パターン

private insertOverlay(overlay: HTMLElement): void {
  const slot = this.assignedSlot;

  if (slot) {
    // Correctly slotted: insert into the shadow-internal container
    slot.parentElement?.appendChild(overlay);
    return;
  }

  if (this.parentElement?.shadowRoot) {
    // Shadow DOM exists but slot assignment hasn't happened yet
    // (connectedCallback fires before Lit renders shadow DOM).
    // Return early; caller should retry via requestAnimationFrame.
    return;
  }

  // Not slotted — plain DOM, safe to use parentElement
  this.parentElement?.appendChild(overlay);
}

タイミングに関する注意点

HTML の解析中、Lit がシャドウ DOM をレンダリングする前に connectedCallback が実行されます。この時点では:

  • this.assignedSlot は null です(スロットの割り当てがまだ行われていません)
  • this.parentElement は null ではありません(シャドウホストです)
  • this.parentElement.shadowRoot も null です(シャドウ DOM がまだアタッチされていません)

同じマイクロタスクの実行中に後から(Lit の performUpdate の後に):

  • this.assignedSlot は <slot> 要素になります
  • slot.parentElement は内部コンテナーになります

ガードパターン:

connectedCallback() {
  super.connectedCallback();
  this.tryInsertOverlay();
}

private tryInsertOverlay(): void {
  if (!this.assignedSlot && this.parentElement?.shadowRoot) {
    // Too early — shadow host exists but slot not yet assigned.
    // Lit will assign the slot after performUpdate.
    requestAnimationFrame(() => this.tryInsertOverlay());
    return;
  }
  this.insertOverlay(this.overlay);
}

ルールのまとめ

状況assignedSlotparentElement.shadowRoot対応
スロットに配置されていないnullnull/undefinedparentElement.appendChild を使用
正しくスロットに配置されているnon-null(任意)assignedSlot.parentElement.appendChild を使用
早すぎる場合(Lit が未レンダリング)nullnon-null早期に return し、RAF 経由で再試行
スロットに配置され、親にシャドウがないnullnullparentElement.appendChild を使用

親がシャドウ DOM ホストの場合、this.parentElement を直接使用しないでください。

コメント