2026-10-12 訂正:npm installだけではbrowserのimport from 'lit'を解決できません。旧.jsの@property decoratorもcompile設定なしには実行できませんでした。import mapとHTTP配信を明示し、標準JavaScriptのstatic propertiesへ変更します。Shadow DOMの完全なstyle隔離という説明も訂正しました。
module解決とcomponentの定義を分ける
Lit開発環境の公式資料では、browserのbare module specifierを扱うためbundler/開発server等を用意します。この例はimport mapでlitをversion固定のESM CDNへ対応付けます。npmのnode_modulesをbrowserが自動探索するわけではありません。
今回の検証版はLit3.3.3です。2026-10-12にnpm registryで版を確認し、headless ChromiumがESM CDNから読み込んで動作した例です。CDNにはnetwork通信が発生し、利用許可・可用性・CSP・依存の固定方針を確認します。productionではnpmとlockfileを用いたbundling等も選択肢です。このCDN例だけで供給経路の監査が済んだとは言えません。
index.html
次とcomponents.jsを同じfolderに保存します。file://を直接開かず、ローカルのHTTP serverで配信します。
<!doctype html><html lang="ja"><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Litの最小例</title><style>body{font-family:system-ui;color:#222}my-counter{--counter-accent:#005a9c}</style><script type="importmap">{"imports":{"lit":"https://esm.sh/lit@3.3.3"}}</script><script type="module" src="./components.js"></script><h1>Litの最小例</h1><my-greeting name="Lit"></my-greeting><my-counter></my-counter><p id="status" role="status">component読込待ち</p>
<script>document.querySelector('script[src="./components.js"]').addEventListener("error",()=>{document.getElementById("status").textContent="component読込失敗。network設定を確認してください";});</script>
components.js
reactive properties公式のstatic propertiesを使います。constructorで値を設定し、reactive accessorをclass fieldで隠さないようにします。nameは外部から与えるproperty、countは内部stateです。attribute変換はruntimeの全入力検証ではありません。
import {LitElement,html,css}from'lit';
class MyGreeting extends LitElement{
static properties={name:{type:String}};
constructor(){super();this.name='Lit';}
render(){return html`<h2>Hello, ${this.name}!</h2>`;}
}
class MyCounter extends LitElement{
static properties={count:{state:true}};
static styles=css`:host{display:block;padding:1rem}button{font:inherit;color:white;background:var(--counter-accent,#005a9c);padding:.5rem 1rem;border:0}button:focus-visible{outline:3px solid black;outline-offset:4px}button:disabled{opacity:.6}`;
constructor(){super();this.count=0;}
increment(){if(Number.isInteger(this.count)&&this.count>=0&&this.count<10)this.count++;}
reset(){this.count=0;}
render(){return html`<p role="status">Count: ${this.count}</p><button type="button" @click=${this.increment} ?disabled=${this.count>=10}>増やす</button> <button type="button" @click=${this.reset}>リセット</button>`;}
}
customElements.define('my-greeting',MyGreeting);customElements.define('my-counter',MyCounter);
document.getElementById('status').textContent='component定義完了';
counterは0〜10で、上限で増加buttonを無効化します。外部codeが直接propertyを変更することまで禁止するsecurity機構ではありません。renderのtext式を使い、nameの文字列をunsafeHTMLへ渡しません。native buttonとstatus表示、focus枠を使いますが、screen readerを含む適合試験はしていません。
HTTPで配信する
Pythonがある環境では、作成したfolder内で次を実行する方法があります。
python3 -m http.server 8000 --bind 127.0.0.1
http://127.0.0.1:8000/を開き、終了はCtrl+Cです。これは開発確認用のserverで、production向けではありません。今回testでは同等のlocal HTTP配信をNodeで立ち上げ、終了時にcloseしました。CDN読込失敗ではstatusへ失敗を表示し、componentが利用可能だと誤表示しません。
Shadow DOMをstyleの契約として扱う
Lit style公式のとおり、通常の外部selectorはshadow内へ直接届きませんが、継承するfontやcolor、CSS custom properties等の影響は残ります。partを公開した場合は::part等によるstyleも契約になります。今回は--counter-accentをhostから渡す例なので、意図的に外部styleを受けます。
どのframeworkとも無条件に同じ方法で接続できる、Reactと同じ学習負荷、必ず高速・最小bundleという断言も撤回します。property/attribute/event、SSR・hydration、form連携、framework側のwrapper、対象browserを確認し、同じ条件で測定します。
確認範囲
ローカルHTTPとheadless Chromiumで11項目を確認しました。CDN module解決、JavaScript例外なし、greeting、外部nameをtextとして表示してimg elementを生成しないこと、初期count、click増加、上限とdisabled、reset、hostのCSS変数反映、CDNを遮断したときの読込失敗表示です。
npm install/bundling、本番CSP、CDN依存の完全監査、他browser、screen reader、SSR、framework統合、性能は未確認です。旧画像は紹介資料として保持し、今回の検証screen shotとは扱いません。
この記事を書いた人✏️@YushiYamamoto
ITPRODX.com代表 / AIアーキテクト
Next.js / TypeScript / n8nを活用した自律型アーキテクチャ設計を専門としています。
日々の自動化の検証結果や、ビジネス側の視点(ROI等)に関するより深い考察は、以下の公式サイトおよびnoteで発信しています。
