Plasmo CSUI + Tailwind CSSで他サイトの影響を受けない方法
Plasmo CSUI + Tailwind CSSで他サイトの影響を受けない方法
はじめに
Chrome拡張機能の開発でPlasmoとTailwind CSSを組み合わせて使っていると、特定のサイトで拡張機能のUIが意図しない表示になってしまうことがあります。
「CSUIを使ってるからShadow DOMでスタイルは完全に分離されているはず...」と思っていたのに、なぜかあるサイトではheader
の高さが40pxではなく25pxになってしまう。そんな経験はありませんか?
この記事では、Chrome拡張機能のContent ScriptsでTailwind CSSを使用する際に発生するCSS継承問題と、その根本的な解決方法を解説します。PlasmoのCSUIを例に説明しますが、他のContent Scripts実装でも同様の問題と解決策が適用できます。
問題の発生状況
期待する動作
// PlasmoOverlay.tsx
const PlasmoOverlay = () => {
return (
<div className="fixed top-4 left-4 bg-white shadow-md">
<header className="h-10 border-b flex items-center px-4">
<h1>拡張機能</h1>
</header>
</div>
)
}
Tailwindのh-10
クラスはheight: 2.5rem
、つまり40pxになるはずです。
実際の問題
しかし、あるサイトで上記のコンポーネントを表示すると、header
の高さが25pxになってしまいます。
開発者ツールで確認すると:
- Tailwindの
h-10
はheight: 2.5rem
として適用されている - しかし実際の高さは25px
原因の究明
Content Scriptsの制約
まず、なぜこの問題が発生するのかを理解するために、Content Scriptsとiframeの違いを確認しましょう。
iframe拡張機能の場合:
<!-- 完全に独立したドキュメント -->
<iframe src="chrome-extension://abc/popup.html">
<html> <!-- 独自のhtml要素 -->
<head><title>拡張機能</title></head>
<body>拡張機能のUI</body>
</html>
</iframe>
- ✅ 完全に分離されたhtml
- ✅ サイトのCSS影響なし
- ❌ 開発が複雑(postMessage通信が必要)
Content Scripts(CSUI)の場合:
<!-- サイトと同じhtmlドキュメント内 -->
<html style="font-size: 62.5%;"> <!-- Yahooのhtml -->
<body>
<div>サイトのコンテンツ</div>
<plasmo-csui> <!-- ここに注入される -->
#shadow-root <!-- 部分的な分離 -->
<div class="h-10">拡張機能のUI</div>
#shadow-root
</plasmo-csui>
</body>
</html>
- ❌ サイトのhtmlに依存
- ❌ 継承プロパティの影響あり
- ✅ 開発が簡単(直接DOM操作可能)
Content Scriptsはサイトと同じhtmlドキュメント内で動作するため、拡張機能側でサイトのhtml要素を変更することはできません。
なぜCSUIを選ぶのか
Reactらしい開発体験:
// CSUI - 普通のReact開発と同じ
const PlasmoOverlay = () => {
const [count, setCount] = useState(0);
const handleClick = () => {
// 直接ページ操作
const element = document.querySelector('.target');
element.style.border = '2px solid red';
setCount(count + 1);
};
return (
<button onClick={handleClick}>
クリック数: {count}
</button>
);
};
iframeだと:
// iframe - 通信処理が必要
const IframeComponent = () => {
const [count, setCount] = useState(0);
const handleClick = () => {
// 複雑な通信処理
parent.postMessage({
type: 'STYLE_ELEMENT',
selector: '.target',
styles: { border: '2px solid red' }
}, '*');
setCount(count + 1);
};
return <button onClick={handleClick}>クリック数: {count}</button>;
};
PlasmoがCSUIをデフォルト選択している理由は、Reactらしい直感的な開発を可能にするためです。軽微なCSS問題と引き換えに、大幅な開発効率向上を実現しています。
Shadow DOMでも継承される理由
PlasmoのCSUIはShadow DOMを使用してスタイルを分離していますが、CSS仕様上、継承可能なプロパティはShadow DOM境界を越えて継承されるという特性があります。
特にfont-size
は継承プロパティのため、以下のような影響を受けます:
Yahooサイトの設定:
html {
font-size: 62.5%; /* 16px × 62.5% = 10px */
}
remの計算への影響:
- 通常:
2.5rem = 2.5 × 16px = 40px
- Yahoo:
2.5rem = 2.5 × 10px = 25px
Shadow DOM内でも影響を受ける継承プロパティ
color
font-family
-
font-size
← 今回の問題 line-height
visibility
- など
継承されないプロパティ
background
border
margin
padding
width
-
height
(直接指定の場合)
解決策:postcss-rem-to-responsive-pixel
この問題を根本的に解決するには、Tailwind CSSが生成するrem
単位をpx
単位に変換します。
パッケージのインストール
pnpm add -D postcss-rem-to-responsive-pixel
PostCSS設定の変更
// postcss.config.js
/**
* @type {import('postcss').ProcessOptions}
*/
module.exports = {
plugins: {
tailwindcss: {},
'postcss-rem-to-responsive-pixel': {
rootValue: 16, // 1rem = 16px
propList: ['*'], // 全プロパティ変換
transformUnit: 'px', // px出力
replace: true, // remを置き換え
mediaQuery: true // メディアクエリ内も変換
},
autoprefixer: {}
}
}
変換結果
変換前(Tailwind生成CSS):
.h-10 {
height: 2.5rem;
}
変換後(postcss-rem-to-responsive-pixel適用後):
.h-10 {
height: 40px;
}
設定オプションの解説
重要な設定項目
-
propList: ['*']
: デフォルトは['font', 'font-size', 'line-height', 'letter-spacing']
のみですが、['*']
で全プロパティを変換 -
rootValue: 16
: 1remの基準値(通常は16px) -
transformUnit: 'px'
: 出力単位をpxに指定 -
replace: true
: remを完全に置き換える
他の解決策との比較
1. CSS上書きによる対処
:host {
font-size: 16px !important;
}
:host .h-10 {
height: 40px !important;
}
デメリット:
- 個別対応が必要
- メンテナンスが困難
- すべてのTailwindクラスに対応できない
2. Tailwind設定でpx化
// tailwind.config.js
module.exports = {
theme: {
spacing: {
'10': '40px', // remの代わりにpx
// 全サイズを個別定義...
}
}
}
デメリット:
- 設定が膨大になる
- Tailwindのアップデートに追従困難
3. PostCSSプラグインによる変換(推奨)
メリット:
- 設定が簡単
- 全自動変換
- メンテナンス不要
- Tailwindのアップデートにも対応
検証:実際の効果
変換前の問題
/* Yahooサイトでの実際の値 */
.h-10 { height: 2.5rem; } /* = 25px (10px × 2.5) */
.p-4 { padding: 1rem; } /* = 10px (10px × 1) */
変換後の解決
/* どのサイトでも一定 */
.h-10 { height: 40px; } /* = 40px */
.p-4 { padding: 16px; } /* = 16px */
まとめ
PlasmoのCSUIは優れたスタイル分離機能を提供しますが、CSS仕様上の継承プロパティについては完全な分離ができません。特にfont-size
の継承によるrem計算への影響は、拡張機能の表示に予期しない問題を引き起こします。
postcss-rem-to-responsive-pixel
を使用してrem単位をpx単位に変換することで:
- ✅ サイト依存のCSS問題を根本解決
- ✅ 設定が簡単で保守性が高い
- ✅ Tailwind CSSの利便性を維持
- ✅ どのサイトでも一貫した表示
Chrome拡張機能開発でPlasmo + Tailwind CSSを使用している方は、ぜひこの対策を導入してみてください。
Discussion