dialog の背景色が変わらないときに読んで下さい (React サンプルコード付き)

## CSS 書いても背景色が変わらない！

モダールを作成するのに超便利な dialog 要素ですが、ハマりがちな罠があります。

```css
dialog::backdrop {
  background-color: rgba(0, 0, 0, 0.5);
}
```

それが上記のような CSS を書いていても dialog の背景色が変わらない！というものです。
これを CSS やブラウザーのせいにして諦めかけていませんか？


### 確認事項
* dialog の **open に boolean 値を入れない**
* dialog の **開閉は `showModal()` と `close()` で行う**
* クラスの付け替えで表示・非表示を行わない

これらを守れば `::backdrop` で背景色がつくはずです。

:::note alert
**ダメな例: 一番やりがちなやつ**
```tsx
// モーダルの開閉状態を管理するフラグ
const [hasModalOpened, setHasModalOpened] = useState(false);

return(
  {/* 背景色を変えたいときに open に boolean 値を入れるな!!! */}
  <dialog open={hasModalOpened}>
    {/* ...ここにモーダルの内容が入る... */}
  </dialog>
);
```
:::

## サンプルコード
モーダルを使用する際、大抵は下記のような要件になると思います。

* 上位のコンポーネントで何かクリックするとモーダルを開く
* モーダル内には閉じるためのボタンが付いている

これを簡単に実装してみます。

```tsx:App.tsx
import React, { useState } from "react";

import SampleModal from "./components/SampleModal";

import "./styles.css";

const App = () => {
  // Modal の開閉状態は下記の useState で持つ
  const [hasModalOpened, setHasModalOpened] = useState(false);

  return (
    <div className="App">
      {/* このボタンを押すと dialog が開く */}
      <button type="button" onClick={() => setHasModalOpened(true)}>
        Modal Open
      </button>

      <SampleModal
        {/* ここで Modal の開閉状態を渡す */}
        hasModalOpened={hasModalOpened}
        {/* SampleModal 内で onClose が呼び出されると hasModalOpened に false がセットされる */}
        onClose={() => setHasModalOpened(false)}
      />
    </div>
  );
};

export default App;
```
```tsx:SampleModal.tsx
import React, { useEffect, useRef } from "react";

interface Props {
  hasModalOpened: boolean;
  onClose: () => void;
}

// モーダル本体
const SampleModal = ({ hasModalOpened, onClose }: Props) => {
  // dialog 要素が関連付けされると参照される
  const modalRef = useRef<HTMLDialogElement>(null);

  // hasModalOpened に反応して showModal() か close() が実行される
  useEffect(() => {
    if (hasModalOpened) {
      // showModal() を使うとモーダルが表示されて ::backdrop が有効になる
      modalRef.current?.showModal();
    } else {
      // close() でモーダルが閉じられる
      modalRef.current?.close();
    }
  // hasModalOpened を監視している
  }, [hasModalOpened]);

  return (
    {/* dialog 要素と modalRef を関連付けている */}
    <dialog ref={modalRef}>
      {/* このボタンを押すと onClose が呼ばれモーダルが閉じる */}
      <button type="button" className="close-button" onClick={onClose}>
        x
      </button>
      <p>サンプルモーダル</p>
    </dialog>
  );
};

export default SampleModal;
```
```css:style.css
.App {
  font-family: sans-serif;
  text-align: center;
}

dialog::backdrop {
  background-color: rgba(0, 0, 0, 0.5);
}
```

`SampleModal.tsx` では `useRef` フックを使用して `dialog` 要素を参照しています。
そして `useEffect` フックを使用して `hasModalOpened` の変化を監視し、変化に応じて取得した `dialog` 要素の `showModal()` または `close()` を呼び出しています。

### サンプルコード実行結果
![SampleModal.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2038389/5d3a9d1f-0b1b-0bc5-3934-55bccb05164b.gif)


## 解説
そもそも `::backdrop` という疑似要素は `showModal()` とともに呼び出されます。
そのため `open` 属性に値を代入しても `::backdrop` は呼び出されません。

https://developer.mozilla.org/ja/docs/Web/API/HTMLDialogElement/showModal

その点を踏まえて先の確認事項であげたリストがなぜダメなのか軽く解説をつけます。



<dl>

<dt>dialog の open に boolean 値を入れない</dt>
<dd><code>open</code> 属性はあくまでモーダルが表示されているかどうかの状態を示すものなので <code>open</code> 属性に直接 boolean 値を入れても <code>::backdrop</code> は機能しません。</dd>

<br />

<dt>dialog の開閉は <code>showModal()</code> と <code>close()</code> で行う</dt>
<dd><code>showModal()</code> メソッドを使うことで <code>::backdrop</code> が初めて有効になります。<code>close()</code> メソッドも同様にモーダルを閉じる際に使用します。</dd>

<br />

<dt>クラスの付け替えで表示・非表示を行わない</dt>
<dd>CSS クラスの付け替えでモーダルの表示・非表示を管理すると <code>::backdrop</code> が適切に動作しません。</dd>

</dl>

## 関連情報

### ブラウザーのサポート状況
`dialog` 要素は 2022 年 3 月以降のバージョンであれば、IE などを除くほとんどのブラウザーで動作します。
上記の変更を行なっても背景色が変わらない場合はブラウザーの更新を試してみて下さい。

https://developer.mozilla.org/ja/docs/Web/HTML/Element/dialog#%E3%83%96%E3%83%A9%E3%82%A6%E3%82%B6%E3%83%BC%E3%81%AE%E4%BA%92%E6%8F%9B%E6%80%A7

https://developer.mozilla.org/ja/docs/Web/CSS/::backdrop#%E3%83%96%E3%83%A9%E3%82%A6%E3%82%B6%E3%83%BC%E3%81%AE%E4%BA%92%E6%8F%9B%E6%80%A7