テーマ固有のカラースタイルをペースト時のテーブルセルから除去する

basecamp/lexxy

テーブルをコピー&ペーストする際、クリップボードのHTMLにはコピー元テーマ(ダークモード・ライトモード)のインラインカラースタイルが混入する問題がありました。このPRでは、Lexicalがペースト処理を行う前にDOMから該当スタイルを除去することで、ペースト後のテーブルが常に閲覧者のテーマカラーを継承するようになります。

背景

ダークモードのページからテーブルをコピーすると、各セルには background-color: rgb(26, 26, 46) のようなインラインスタイルがクリップボードのHTMLに含まれます。ペーストした本人はダークモード環境で閲覧しているため気づきにくいですが、ライトモードで同じドキュメントを開いた別の閲覧者には、暗い背景色が白いテキストの上に残ったまま表示されてしまいます。逆のケース(ライトモードからダークモードへのペースト)でも同様の問題が発生します。

この問題はブラウザがクリップボードHTMLを生成する仕様に起因しており、コピー元のDOM要素に付与されているインラインスタイルがそのまま text/html 形式でクリップボードに書き込まれます。LexicalはそのクリップボードのHTMLをパースしてノードを生成するため、スタイルがそのまま引き継がれる構造になっています。

技術的な変更

src/editor/contents.jsinsertDOM メソッドに、ペースト操作時のみ実行されるスタイル除去処理が追加されました。

Lexicalが提供する PASTE_TAG 定数を使って、呼び出し元の操作種別を判別しています。insertDOM は汎用的なDOM挿入メソッドであるため、ペースト以外の呼び出し経路(例: プログラムによるコンテンツ挿入)には影響を与えません。

変更前:

importDOM(doc, { tag } = {}) {
  this.#unwrapPlaceholderAnchors(doc)

  this.editor.update(() => {

変更後:

importDOM(doc, { tag } = {}) {
  this.#unwrapPlaceholderAnchors(doc)
  if (tag === PASTE_TAG) this.#stripTableCellColorStyles(doc)

  this.editor.update(() => {

実際の除去処理はプライベートメソッド #stripTableCellColorStyles として分離されています。querySelectorAll("td, th")<td><th> の両方を対象に取り、style.removeProperty()background-colorbackgroundcolor の3プロパティを削除します。

#stripTableCellColorStyles(doc) {
  for (const cell of doc.querySelectorAll("td, th")) {
    cell.style.removeProperty("background-color")
    cell.style.removeProperty("background")
    cell.style.removeProperty("color")
  }
}

removeProperty はプロパティが存在しない場合も例外を投げないため、スタイルが付与されていないセルが含まれていても安全に動作します。

設計判断

DOMをLexical処理前に書き換える方式 が採用されました。

Lexicalのノード変換フックやデコレータで対応する方法も考えられますが、Lexicalがノードを生成する前のDOMレイヤーで介入することで、Lexicalの内部処理に依存しない実装になっています。パース済みのLexicalノードからスタイルを除去するよりも、HTMLのDOM操作として完結するためコードが単純です。

また、PASTE_TAG による判別により、既存の insertDOM メソッドのインターフェースを変えずに動作を限定しています。ペースト以外のコンテンツ挿入でテーブルのスタイルが除去されることを防ぐ、最小限の変更範囲に抑えた判断といえます。

テストは test/browser/tests/paste/table_cell_styles.test.js にPlaywrightのブラウザテストとして追加されています。ダークモード由来(rgb(26, 26, 46))とライトモード由来(rgb(255, 255, 255))のそれぞれのケースで background-color が空文字列になることを検証しており、実際のクリップボードペーストのシナリオを直接テストしています。

まとめ

この変更は、テーマ間のカラー汚染という視覚的には見落としやすいが閲覧環境によって顕在化するバグを、Lexicalの処理パイプラインの入口でDOMを書き換えるという最小限のアプローチで解決しています。PASTE_TAG を軸にした条件分岐により既存の動作を保護しつつ、ペーストされたテーブルが常に閲覧者のテーマに従って表示される一貫したユーザー体験を実現しています。

記事メタデータ

Generated by:
Claude Sonnet 4.6 for DiffDaily
LLM Trace:
ed7dc110

この記事はAIによって自動生成されています。内容の正確性については、必ずソースコードやPRを確認してください。

品質レビュー結果

Review Status:
承認済み
Review Count:
1回
Reviewed by:
Gemini 2.5 Pro for DiffDaily

Review Criteria:

記事構成 ✓ PASS

Title, Context, Technical Detailの存在と明確さ

リード文(総論)→セクション群(各論)→まとめ(結論)の3部構成が明確に適用されており、必須要素(背景、技術的変更)と任意要素(設計判断)がすべて含まれています。

カスタムMarkdown構文 ✓ PASS

シンタックスハイライト・GitHubリンク記法の正確性

ファイル名付きシンタックスハイライト(```javascript:filepath)およびGitHubのPRリンク記法が正しく使用されています。

対象読者への適合性 ✓ PASS

エンジニア向けの適切な技術レベルと表現

Lexicalの内部実装やDOM操作に関する専門的な内容であり、対象読者であるエンジニアに適した技術レベルと表現が用いられています。

パラグラフ・ライティング ✓ PASS

トピックセンテンス・1段落1トピック・段落長

各セクションが総論→各論の構成で、各段落はトピックセンテンスで始まり、1段落1トピックが守られています。可読性が非常に高いです。

Diff内容との照合 ✓ PASS

コードブロックとDiff内容の一致

記事内のコードブロックは、提供されたDiffの内容(追加されたif文、#stripTableCellColorStylesメソッド)と完全に一致しています。ファイル名も正確です。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

「PASTE_TAG」「insertDOM」「removeProperty」など、LexicalやDOM APIに関する技術用語が正確かつ適切に使用されています。

説明の技術的正確性 ✓ PASS

技術的主張の正確性と論理性

「PASTE_TAGによる条件分岐」「DOMレイヤーでの事前処理」といった変更内容の説明は、Diffのコードと整合しており、技術的に正確です。

事実の突合 ✓ PASS

PR情報による主張の裏付け(ハルシネーション検出)

記事内のすべての主張は、PRのDescriptionやDiffのコード内容によって裏付けられています。特に「設計判断」は、コードから読み取れる意図を的確に言語化しており、ハルシネーションはありません。

数値・固有名詞の確認 ✓ PASS

PR番号・コミットID・バージョン等の正確性

PR番号(#893)、テストコード内のRGB値、ファイルパスなど、すべての数値・固有名詞が正確に記載されています。

タイトル・説明との一致 ✓ PASS

記事タイトル・説明とPR内容の一致

記事のタイトルは、PRのタイトル「Strip theme-specific color styles from pasted table cells」の内容を正確に和訳・要約しており、記事全体の主題と一致しています。

外部知識の正確性 ✓ PASS

PRに記載のない外部知識(LTS、サポート状況など)の不使用

PRに記載のないバージョン情報やリリース日程などの外部知識の捏造は見られません。

時間表現の正確性 ✓ PASS

時間表現がPR情報と一致しているか

記事内の時間表現はPRで実装された変更を記述するものであり、時間的な歪曲は見られません。