Google MapsのInfoWindowをCSSでスタイリングする — !importantとCSS詳細度の話

React

こんにちは、白々さじきです。

今回は、Google MapsのInfoWindow(吹き出し)の見た目をCSSでカスタマイズする方法についてまとめます。

「InfoWindowの背景色を変えたい」と思って style を渡してみたのですが、白い吹き出しがそのままで詰まりました。解決するまでの過程を書きます。

結論

InfoWindowの見た目はGoogleが独自のCSSクラスで管理しています。上書きするには、グローバルCSSファイルを作って !important で強制的に上書きします。

なぜ style props だけでは変わらないのか

@vis.gl/react-google-maps<InfoWindow>style を渡しても、変わるのは children(中のコンテンツ)だけです。

// これだと中身のテキスト部分しか変わらない
<InfoWindow anchor={selectedMarker}>
  <div style={{ color: 'white' }}>スターバックス</div>
</InfoWindow>

外側の白い吹き出し(コンテナ)はGoogleのサーバーから読み込まれたCSSで管理されているため、children へのスタイル指定では届きません。

CSS Modulesが使えない理由

「じゃあCSSファイルを作ればいい」と思っても、CSS Modules(.module.css)は使えません

CSS ModulesはビルドのタイミングでクラスX名を変換します。たとえば .gm-style-iw-c と書いても、ビルド後には .gm-style-iw-c__abc123 のような文字列になります。Googleが付けるクラス名とは一致しなくなるため、スタイルが当たりません。

普通のCSSファイルを使う必要があります。

Google Maps InfoWindowのクラス構造

開発者ツール(F12)で確認できる主なクラスは以下のとおりです。

クラス名対応する要素
.gm-style-iw-c吹き出しのメインコンテナ
.gm-style-iw-d内側のスクロール領域
.gm-style-iw-tc::after三角の矢印部分(疑似要素)
.gm-style-iw-chr閉じるボタンの領域

::after は疑似要素なのでHTMLタグとして存在しません。開発者ツールでは親要素を選択して展開すると確認できます。

CSSファイルで上書きする

普通のCSSファイルを作成してViteからimportします。

/* src/styles/map.css */

.gm-style-iw-c,
.gm-style-iw-d,
.gm-style-iw-chr,
.gm-style-iw-chr button {
  background-color: #1a1f3a !important;
  color: #fbbf24 !important;
  border: none !important;
  box-shadow: none !important;
}

.gm-style-iw-d {
  overflow: hidden !important;
}

.gm-style-iw-tc::after {
  background: #1a1f3a !important;
  box-shadow: none !important;
}

.gm-style-iw-chr button img {
  filter: invert(1) !important;
}
// App.tsx
import './styles/map.css';

ViteはCSSファイルのimportを検出して自動で読み込みます。document の操作は不要です。

なぜ !important が必要なのか

通常のCSSでは「詳細度(Specificity)」というスコアが高いほうのルールが優先されます。

GoogleのCSSは詳細度が高く書かれているため、普通にクラスを指定しても負けてしまいます。

/* これはGoogleのCSSに負ける */
.gm-style-iw-d {
  overflow: hidden;
}

/* !important をつけると他のすべてのルールに勝てる */
.gm-style-iw-d {
  overflow: hidden !important;
}

!important は「詳細度の計算を無視して最優先にする」特別な指定です。外部ライブラリのスタイルを上書きするときには有効ですが、自分のコード内では乱用すると管理が難しくなるので注意が必要です。

InfoWindow内のテキストをdivで囲む理由

テキストノード(文字列)には直接 style を渡せません。divで囲むことでスタイルを当てられます。

// NG: テキストノードにstyleは渡せない
<InfoWindow anchor={selectedMarker}>
  {name}
</InfoWindow>

// OK: divで囲む
<InfoWindow anchor={selectedMarker}>
  <div style={{ background: '#1a1f3a', color: '#fbbf24', padding: '4px 8px' }}>
    {name}
  </div>
</InfoWindow>

まとめ

  • InfoWindowの外側のスタイルはGoogleのCSSクラスを上書きする必要がある
  • CSS Modulesはクラス名が変換されるため使えない。普通のCSSファイルを使う
  • Googleの動的CSSは詳細度が高いため !important で上書きする
  • ::after は疑似要素なのでHTMLタグとして見つからない

参考リンク

サポートのお願い

下記リンクからお買い物いただけると、ブログ運営のための費用が増え、有料サービスを利用した記事作成が可能になります。ご協力よろしくお願いします!



コメント

タイトルとURLをコピーしました