T-CREATOR

Web Components と Declarative Shadow DOM の現在地:HTML だけで描くサーバー発 UI

Web Components と Declarative Shadow DOM の現在地:HTML だけで描くサーバー発 UI

近年、Web フロントエンド開発では JavaScript フレームワークが主流となっていますが、サーバーサイドレンダリング(SSR)の重要性が再認識される中で、新たな選択肢が注目を集めています。それが Declarative Shadow DOM です。

Web Components は長年「JavaScript が必要」という課題を抱えていましたが、Declarative Shadow DOM の登場により、サーバー側で完結する UI コンポーネントの実装が可能になりました。この技術は、ハイドレーションの複雑さを回避しながら、カプセル化されたスタイルと構造を持つコンポーネントを HTML だけで表現できるという革新的なアプローチをもたらします。

本記事では、Web Components の基礎から Declarative Shadow DOM の仕組み、そして実際のサーバー発 UI の実装方法まで、初心者の方にもわかりやすく解説していきますね。

背景

Web Components とは何か

Web Components は、再利用可能なカスタム要素を作成するための標準技術群です。主に以下の 3 つの技術で構成されています。

mermaidflowchart TB
  wc["Web Components"] --> ce["Custom Elements<br/>独自タグの定義"]
  wc --> sd["Shadow DOM<br/>スタイルのカプセル化"]
  wc --> ht["HTML Templates<br/>再利用可能なマークアップ"]

  ce --> ex1["例: &lt;my-button&gt;"]
  sd --> ex2["スコープ付き CSS"]
  ht --> ex3["&lt;template&gt; タグ"]

この図が示すように、Web Components は標準化された 3 つの要素技術を組み合わせることで、フレームワークに依存しない再利用可能なコンポーネントを実現します。

従来の Web Components の課題

従来の Web Components(Imperative Shadow DOM)には、いくつかの制約がありました。

mermaidflowchart LR
  server["サーバー"] -->|HTML 送信| browser["ブラウザ"]
  browser -->|JS 読み込み| js["JavaScript"]
  js -->|attachShadow 呼び出し| shadow["Shadow DOM 構築"]
  shadow -->|レンダリング| ui["UI 表示"]

  style js fill:#ffcccc
  style shadow fill:#ffcccc

上記のフローで示されるように、従来の方式では JavaScript の実行が必須でした。

従来方式の主な課題

#課題説明
1JavaScript 必須Shadow DOM の構築には必ず JavaScript が必要
2レンダリング遅延JS のダウンロード・パース・実行まで UI が表示されない
3SSR 非対応サーバー側でレンダリングした内容が Shadow DOM に反映されない
4ハイドレーション複雑化クライアント側での再構築が必要で実装が複雑

これらの課題により、パフォーマンスを重視するサイトや、JavaScript が無効な環境では、Web Components の採用が困難でした。

サーバーサイドレンダリングの重要性

現代の Web 開発では、初期表示速度や SEO の観点から SSR が重要視されています。

SSR が重要視される理由

  • 初期表示速度の向上: ユーザーは JavaScript の実行を待たずにコンテンツを閲覧できます
  • SEO 最適化: 検索エンジンのクローラーが完全な HTML を取得可能です
  • アクセシビリティ: JavaScript が無効な環境でもコンテンツが利用可能になります
  • Core Web Vitals 改善: LCP(Largest Contentful Paint)などの指標が向上します

しかし、従来の Web Components は SSR との相性が悪く、この問題を解決する必要がありました。

課題

Web Components と SSR の不整合

従来の Web Components を SSR で利用する場合、以下のような問題が発生していました。

mermaidsequenceDiagram
  participant Server as サーバー
  participant Browser as ブラウザ
  participant JS as JavaScript
  participant DOM as Shadow DOM

  Server->>Browser: HTML 送信
  Note over Browser: Light DOM のみ表示<br/>(不完全な UI)
  Browser->>JS: スクリプト読み込み
  JS->>DOM: attachShadow() 実行
  DOM->>Browser: Shadow DOM 構築
  Note over Browser: 完全な UI 表示<br/>(フラッシュ発生)

このシーケンス図から、サーバーから送信された HTML と最終的な UI の間にギャップがあることがわかります。

具体的な問題点

  1. 二重レンダリング: サーバー側でレンダリングした内容が、クライアント側で再構築される
  2. FOUC(Flash of Unstyled Content): スタイルが適用される前の状態が一瞬表示される
  3. パフォーマンス低下: JavaScript の実行を待つ必要があり、Time to Interactive が遅延する
  4. 重複コード: サーバー側とクライアント側で同じロジックを実装する必要がある

ハイドレーションの複雑さ

React や Vue などのフレームワークでは、SSR で生成された HTML に対してクライアント側でイベントリスナーなどを付与する「ハイドレーション」という処理が行われます。

typescript// 従来の Imperative Shadow DOM の例
class MyComponent extends HTMLElement {
  connectedCallback() {
    // JavaScript で Shadow DOM を構築
    const shadowRoot = this.attachShadow({ mode: 'open' });

    // サーバー側でレンダリングされた内容は使えない
    shadowRoot.innerHTML = `
      <style>
        button { background: blue; color: white; }
      </style>
      <button>クリック</button>
    `;
  }
}

上記のコードでは、connectedCallback が実行されるまで Shadow DOM が構築されません。

この処理には以下の課題がありました。

#課題影響
1状態の同期サーバー側の状態とクライアント側の状態を一致させる必要がある
2タイミング制御ハイドレーション中にユーザー操作が発生すると不整合が起きる
3バンドルサイズ増加ハイドレーション用のコードが必要になる
4デバッグの困難さサーバーとクライアントの両方を考慮する必要がある

パフォーマンスへの影響

JavaScript 必須の Web Components は、特にモバイル環境やネットワークが不安定な環境で、パフォーマンス上の問題を引き起こしていました。

パフォーマンスボトルネック

  • JavaScript ダウンロード時間: ネットワーク速度に依存します
  • パース・コンパイル時間: デバイスの CPU 性能に依存します
  • 実行時間: DOM 操作は特にコストが高い処理です
  • メモリ使用量: Shadow DOM の構築にはメモリが必要となります

これらの課題を解決するために登場したのが、Declarative Shadow DOM なのです。

解決策

Declarative Shadow DOM の登場

Declarative Shadow DOM は、HTML のみで Shadow DOM を宣言的に定義できる新しい仕様です。2020 年に Chrome 90 で初めて実装され、現在では主要ブラウザでサポートが進んでいます。

mermaidflowchart LR
  server["サーバー"] -->|DSD 付き HTML| browser["ブラウザ"]
  browser -->|パース| shadow["Shadow DOM 自動構築"]
  shadow -->|即座にレンダリング| ui["UI 表示"]

  style shadow fill:#ccffcc
  style ui fill:#ccffcc

この図が示すように、JavaScript を必要とせず、ブラウザが HTML をパースする段階で Shadow DOM が自動的に構築されます。

Declarative Shadow DOM の特徴

#特徴メリット
1HTML のみで定義JavaScript 不要でサーバー側で完結
2即座にレンダリングJS の実行を待たずに表示される
3SSR ネイティブ対応サーバー側でレンダリングした内容がそのまま使える
4プログレッシブ・エンハンスメントJS が無効でも基本機能が動作する

基本的な構文

Declarative Shadow DOM は、<template> タグに shadowrootmode 属性を付与することで定義します。

html<!-- Declarative Shadow DOM の基本構文 -->
<my-component>
  <template shadowrootmode="open">
    <style>
      button {
        background: #0070f3;
        color: white;
        padding: 12px 24px;
        border: none;
        border-radius: 6px;
        cursor: pointer;
      }
    </style>
    <button>
      <slot></slot>
    </button>
  </template>
  クリックしてください
</my-component>

このコードは、サーバー側で生成され、ブラウザに送信されると自動的に Shadow DOM として解釈されます。

shadowrootmode 属性の値

  • open: JavaScript から element.shadowRoot でアクセス可能
  • closed: JavaScript からのアクセスを制限(推奨されない場合が多い)

スタイルのカプセル化

Declarative Shadow DOM の最大の利点の一つが、スタイルのカプセル化です。

html<!-- スタイルのカプセル化の例 -->
<style>
  /* グローバルスタイル */
  button {
    background: red; /* これは影響しない */
  }
</style>

<my-card>
  <template shadowrootmode="open">
    <style>
      /* Shadow DOM 内のスタイル */
      .card {
        border: 1px solid #e0e0e0;
        border-radius: 8px;
        padding: 16px;
        box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
      }

      button {
        background: #0070f3; /* こちらが適用される */
        color: white;
      }
    </style>

    <div class="card">
      <slot name="title"></slot>
      <slot></slot>
      <button>詳細を見る</button>
    </div>
  </template>

  <h2 slot="title">カードタイトル</h2>
  <p>カードの内容がここに入ります。</p>
</my-card>

上記のコードでは、グローバルの button スタイルは Shadow DOM 内の button に影響せず、Shadow DOM 内で定義されたスタイルのみが適用されます。

カプセル化のメリット

  • CSS の命名衝突を回避できます
  • コンポーネント単位でスタイルを管理できます
  • グローバル CSS の影響を受けません
  • リファクタリングが安全に行えます

スロットによるコンテンツ配置

<slot> 要素を使用することで、コンポーネントの利用者が提供するコンテンツを柔軟に配置できます。

html<!-- 名前付きスロットの活用 -->
<user-profile>
  <template shadowrootmode="open">
    <style>
      .profile {
        display: grid;
        grid-template-columns: 80px 1fr;
        gap: 16px;
        padding: 20px;
        background: #f5f5f5;
        border-radius: 8px;
      }

      .avatar {
        grid-row: 1 / 3;
      }

      .name {
        font-size: 24px;
        font-weight: bold;
        margin: 0;
      }

      .bio {
        color: #666;
        margin: 0;
      }
    </style>

    <div class="profile">
      <div class="avatar">
        <slot name="avatar"></slot>
      </div>
      <h2 class="name">
        <slot name="name">名前未設定</slot>
      </h2>
      <p class="bio">
        <slot name="bio">自己紹介文がありません</slot>
      </p>
    </div>
  </template>

  <img
    slot="avatar"
    src="/user.jpg"
    alt="ユーザー画像"
    width="80"
    height="80"
  />
  <span slot="name">山田太郎</span>
  <span slot="bio"
    >Web 開発者です。TypeScript と Next.js
    が好きです。</span
  >
</user-profile>

このコードでは、slot 属性を使って各コンテンツを適切な位置に配置しています。

スロットの種類と使い分け

#種類用途記法
1デフォルトスロット名前なしのコンテンツを受け取る<slot><​/​slot>
2名前付きスロット特定の位置にコンテンツを配置<slot name="xxx"><​/​slot>
3フォールバックコンテンツスロットが空の場合のデフォルト<slot>デフォルト<​/​slot>

サーバーサイドでの生成

Declarative Shadow DOM は、サーバーサイドで HTML を生成する際に最も力を発揮します。

Node.js での生成例

typescript// サーバーサイドでのコンポーネント生成関数
function generateButton(
  text: string,
  variant: 'primary' | 'secondary' = 'primary'
) {
  const colors = {
    primary: { bg: '#0070f3', color: 'white' },
    secondary: { bg: '#f5f5f5', color: '#333' },
  };

  const style = colors[variant];

  return `
    <custom-button>
      <template shadowrootmode="open">
        <style>
          button {
            background: ${style.bg};
            color: ${style.color};
            padding: 12px 24px;
            border: none;
            border-radius: 6px;
            font-size: 16px;
            cursor: pointer;
            transition: opacity 0.2s;
          }
          button:hover {
            opacity: 0.8;
          }
        </style>
        <button>
          <slot></slot>
        </button>
      </template>
      ${text}
    </custom-button>
  `;
}

この関数をサーバー側で呼び出すことで、動的にコンポーネントを生成できます。

typescript// Express での利用例
import express from 'express';

const app = express();

app.get('/', (req, res) => {
  const html = `
    <!DOCTYPE html>
    <html lang="ja">
    <head>
      <meta charset="UTF-8">
      <title>Declarative Shadow DOM サンプル</title>
    </head>
    <body>
      <h1>ボタンサンプル</h1>
      ${generateButton('送信する', 'primary')}
      ${generateButton('キャンセル', 'secondary')}
    </body>
    </html>
  `;

  res.send(html);
});

このコードでは、サーバー側で完全な HTML を生成し、クライアントに送信しています。JavaScript は一切必要ありません。

具体例

Next.js での実装

Next.js を使用して、Declarative Shadow DOM を活用したサーバーコンポーネントを実装してみましょう。

プロジェクトのセットアップ

bash# Next.js プロジェクトの作成
yarn create next-app my-dsd-app --typescript
cd my-dsd-app

# 必要なパッケージのインストール
yarn add react react-dom next

コンポーネントの作成

typescript// components/Card.tsx
// カードコンポーネントの型定義
interface CardProps {
  title: string;
  description: string;
  imageUrl?: string;
  href?: string;
}

型定義を行った後、コンポーネントのロジックを実装します。

typescript// components/Card.tsx(続き)
// Declarative Shadow DOM を使用したカードコンポーネント
export function Card({
  title,
  description,
  imageUrl,
  href,
}: CardProps) {
  return (
    <web-card>
      <template shadowrootmode='open'>
        <style>{`
          .card {
            display: flex;
            flex-direction: column;
            border: 1px solid #e0e0e0;
            border-radius: 12px;
            overflow: hidden;
            transition: box-shadow 0.3s ease;
            background: white;
          }

          .card:hover {
            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
          }

          .card-image {
            width: 100%;
            height: 200px;
            object-fit: cover;
          }

          .card-content {
            padding: 20px;
          }

          .card-title {
            font-size: 20px;
            font-weight: bold;
            margin: 0 0 12px 0;
            color: #333;
          }

          .card-description {
            font-size: 14px;
            color: #666;
            line-height: 1.6;
            margin: 0;
          }

          .card-link {
            display: block;
            text-decoration: none;
            color: inherit;
          }
        `}</style>

        <a className='card-link' href={href || '#'}>
          <div className='card'>
            {imageUrl && (
              <img
                className='card-image'
                src={imageUrl}
                alt={title}
                loading='lazy'
              />
            )}
            <div className='card-content'>
              <h3 className='card-title'>{title}</h3>
              <p className='card-description'>
                {description}
              </p>
            </div>
          </div>
        </a>
      </template>
    </web-card>
  );
}

このコンポーネントは、サーバー側でレンダリングされ、完全なスタイル付きの HTML としてクライアントに配信されます。

データフェッチとの組み合わせ

Next.js の Server Components を活用して、データフェッチと組み合わせた実装を見てみましょう。

typescript// app/page.tsx
// サーバーコンポーネントでのデータフェッチ
interface Article {
  id: number;
  title: string;
  excerpt: string;
  thumbnail: string;
  slug: string;
}

データの型を定義した後、フェッチ関数を実装します。

typescript// app/page.tsx(続き)
// 記事データの取得関数
async function getArticles(): Promise<Article[]> {
  // 実際の API エンドポイントからデータを取得
  const res = await fetch(
    'https://api.example.com/articles',
    {
      next: { revalidate: 3600 }, // 1時間キャッシュ
    }
  );

  if (!res.ok) {
    throw new Error('記事の取得に失敗しました');
  }

  return res.json();
}

取得したデータをコンポーネントで使用します。

typescript// app/page.tsx(続き)
import { Card } from '@/components/Card';

// メインページコンポーネント
export default async function Home() {
  // サーバー側でデータフェッチ
  const articles = await getArticles();

  return (
    <main style={{ padding: '40px' }}>
      <h1>最新記事</h1>
      <div
        style={{
          display: 'grid',
          gridTemplateColumns:
            'repeat(auto-fill, minmax(300px, 1fr))',
          gap: '24px',
          marginTop: '24px',
        }}
      >
        {articles.map((article) => (
          <Card
            key={article.id}
            title={article.title}
            description={article.excerpt}
            imageUrl={article.thumbnail}
            href={`/articles/${article.slug}`}
          />
        ))}
      </div>
    </main>
  );
}

このコードにより、サーバー側で記事データを取得し、Declarative Shadow DOM を使用したカードコンポーネントとしてレンダリングされます。JavaScript の実行を待たずに、完全なスタイル付きのコンテンツが表示されます。

このアプローチのメリット

mermaidflowchart TB
  req["ユーザーリクエスト"] --> server["Next.js サーバー"]
  server --> fetch["API データフェッチ"]
  fetch --> render["HTML 生成<br/>(DSD 含む)"]
  render --> send["完全な HTML 送信"]
  send --> browser["ブラウザ"]
  browser --> instant["即座に表示"]

  style instant fill:#ccffcc

上の図が示すように、完全にレンダリングされた HTML がクライアントに送信されるため、ユーザーは即座にコンテンツを閲覧できます。

インタラクティブ性の追加

Declarative Shadow DOM で構築した UI に、必要に応じて JavaScript でインタラクティブ性を追加することもできます。これをプログレッシブ・エンハンスメントと呼びます。

typescript// components/InteractiveCard.tsx
'use client'; // クライアントコンポーネントとして宣言

import { useEffect, useRef } from 'react';

interface InteractiveCardProps {
  title: string;
  description: string;
  onCardClick?: () => void;
}

型定義の後、インタラクティブなコンポーネントを実装します。

typescript// components/InteractiveCard.tsx(続き)
export function InteractiveCard({
  title,
  description,
  onCardClick,
}: InteractiveCardProps) {
  const cardRef = useRef<HTMLElement>(null);

  // クライアント側でのイベントリスナー追加
  useEffect(() => {
    const card = cardRef.current;
    if (!card) return;

    const handleClick = () => {
      console.log(`カードがクリックされました: ${title}`);
      onCardClick?.();
    };

    // Shadow Root 内の要素にアクセス
    const shadowRoot = card.shadowRoot;
    if (shadowRoot) {
      const cardElement = shadowRoot.querySelector('.card');
      cardElement?.addEventListener('click', handleClick);

      // クリーンアップ関数
      return () => {
        cardElement?.removeEventListener(
          'click',
          handleClick
        );
      };
    }
  }, [title, onCardClick]);

  return (
    <interactive-card ref={cardRef}>
      <template shadowrootmode='open'>
        <style>{`
          .card {
            border: 1px solid #e0e0e0;
            border-radius: 12px;
            padding: 20px;
            cursor: pointer;
            transition: all 0.3s ease;
            background: white;
          }

          .card:hover {
            transform: translateY(-4px);
            box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1);
          }

          .card:active {
            transform: translateY(-2px);
          }
        `}</style>

        <div className='card'>
          <h3>{title}</h3>
          <p>{description}</p>
        </div>
      </template>
    </interactive-card>
  );
}

このアプローチにより、基本的な UI は JavaScript なしで表示され、JavaScript が利用可能な環境では追加のインタラクティブ性が提供されます。

Express での実装例

Node.js と Express を使用したシンプルなサーバー実装も見てみましょう。

typescript// server.ts
// 必要なモジュールのインポート
import express from 'express';
import type { Request, Response } from 'express';

基本的な設定とヘルパー関数を実装します。

typescript// server.ts(続き)
// Express アプリケーションの初期化
const app = express();
const PORT = 3000;

// DSD コンポーネント生成ヘルパー
function createAlert(
  message: string,
  type: 'info' | 'warning' | 'error'
) {
  const colors = {
    info: {
      bg: '#e3f2fd',
      border: '#2196f3',
      text: '#0d47a1',
    },
    warning: {
      bg: '#fff3e0',
      border: '#ff9800',
      text: '#e65100',
    },
    error: {
      bg: '#ffebee',
      border: '#f44336',
      text: '#b71c1c',
    },
  };

  const style = colors[type];

  return `
    <custom-alert>
      <template shadowrootmode="open">
        <style>
          .alert {
            background: ${style.bg};
            border-left: 4px solid ${style.border};
            color: ${style.text};
            padding: 16px;
            border-radius: 4px;
            margin: 16px 0;
            font-family: system-ui, -apple-system, sans-serif;
          }

          .alert-icon {
            display: inline-block;
            margin-right: 8px;
            font-weight: bold;
          }
        </style>

        <div class="alert">
          <span class="alert-icon">ℹ️</span>
          <slot>${message}</slot>
        </div>
      </template>
    </custom-alert>
  `;
}

ルートハンドラーを実装します。

typescript// server.ts(続き)
// ルートエンドポイント
app.get('/', (req: Request, res: Response) => {
  const html = `
    <!DOCTYPE html>
    <html lang="ja">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Declarative Shadow DOM サーバーサンプル</title>
      <style>
        body {
          font-family: system-ui, -apple-system, sans-serif;
          max-width: 800px;
          margin: 0 auto;
          padding: 40px 20px;
          background: #f5f5f5;
        }
        h1 {
          color: #333;
        }
      </style>
    </head>
    <body>
      <h1>システム通知</h1>
      ${createAlert('正常に保存されました', 'info')}
      ${createAlert('この操作は取り消せません', 'warning')}
      ${createAlert('エラーが発生しました', 'error')}
    </body>
    </html>
  `;

  res.send(html);
});

// サーバーの起動
app.listen(PORT, () => {
  console.log(
    `サーバーが http://localhost:${PORT} で起動しました`
  );
});

このサーバーは、完全にスタイル付けされたアラートコンポーネントを、JavaScript なしで配信します。

実行方法

bash# TypeScript のコンパイルと実行
yarn add express @types/express typescript ts-node
yarn ts-node server.ts

ブラウザで http:​/​​/​localhost:3000 にアクセスすると、JavaScript の実行を待たずに、完全にスタイル付けされたアラートが表示されます。

ブラウザサポートの確認

現在の Declarative Shadow DOM のブラウザサポート状況を確認しましょう。

主要ブラウザのサポート状況(2025 年 1 月時点)

#ブラウザバージョンサポート状況
1Chrome90+★★★ 完全サポート
2Edge91+★★★ 完全サポート
3Safari16.4+★★★ 完全サポート
4Firefox123+★★★ 完全サポート
5Opera76+★★★ 完全サポート

主要ブラウザでのサポートが進んでおり、本番環境での利用も十分に可能な状況です。

フォールバック戦略

古いブラウザのサポートが必要な場合は、ポリフィルを使用できます。

typescript// polyfill.ts
// Declarative Shadow DOM のポリフィル
(function () {
  // すでにサポートされている場合は何もしない
  if (
    document.createElement('template').shadowRootMode !==
    undefined
  ) {
    return;
  }

  // ページ読み込み時にポリフィルを実行
  function polyfillDeclarativeShadowDOM() {
    // shadowrootmode 属性を持つ template 要素を検索
    document
      .querySelectorAll('template[shadowrootmode]')
      .forEach((template) => {
        const mode = template.getAttribute(
          'shadowrootmode'
        ) as 'open' | 'closed';
        const parent = template.parentElement;

        if (!parent) return;

        // Shadow Root を作成
        const shadowRoot = parent.attachShadow({ mode });

        // template の内容を Shadow Root に移動
        shadowRoot.appendChild(template.content);

        // template 要素を削除
        template.remove();
      });
  }

  // DOM 読み込み完了時に実行
  if (document.readyState === 'loading') {
    document.addEventListener(
      'DOMContentLoaded',
      polyfillDeclarativeShadowDOM
    );
  } else {
    polyfillDeclarativeShadowDOM();
  }
})();

このポリフィルをページの <head> 内で読み込むことで、古いブラウザでも Declarative Shadow DOM が動作するようになります。

html<head>
  <script src="/polyfill.js"></script>
</head>

ただし、ポリフィルを使用する場合は JavaScript が必須となるため、本来の「JavaScript 不要」というメリットは失われる点に注意が必要です。

まとめ

Declarative Shadow DOM は、Web Components の長年の課題であった「JavaScript 必須」という制約を解決し、サーバーサイドレンダリングとの完全な統合を実現した画期的な技術です。

本記事のポイント

  • HTML だけでコンポーネント化: JavaScript なしでカプセル化された UI を実装できます
  • SSR ネイティブ対応: サーバー側で生成した HTML がそのままブラウザで動作します
  • パフォーマンス向上: JavaScript の実行を待たずに UI が表示されます
  • プログレッシブ・エンハンスメント: 必要に応じて JavaScript で機能を追加できます
  • ブラウザサポート拡大: 主要ブラウザで利用可能になっています

この技術は、特に以下のようなケースで威力を発揮するでしょう。

  • コンテンツ中心の Web サイト(ブログ、ニュースサイトなど)
  • SEO が重要なマーケティングサイト
  • 初期表示速度を重視するサービス
  • JavaScript のバンドルサイズを削減したい場合
  • シンプルなコンポーネントライブラリの構築

React や Vue などのフレームワークが主流の現在ですが、Declarative Shadow DOM は「すべてを JavaScript で実装する」という従来のアプローチに対する重要な選択肢を提供しています。

プロジェクトの要件に応じて、フレームワークと Declarative Shadow DOM を組み合わせることで、より効率的で保守性の高い Web アプリケーションを構築できるようになりました。

今後、この技術がさらに普及し、Web 標準としてより多くの開発者に活用されることが期待されますね。

関連リンク