【CSS】htmlとcssでモーダルを作成する方法。（擬似要素と擬似クラスの活用）

擬似要素と擬似クラスを使うとモーダルを簡単に作成することができる。

|![modal.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563526/368ff788-0a1a-b1bc-aa08-285d2a5a6e6f.gif)|
|:--|


擬似要素の`::after`, `::before`,擬似クラスの`:target`を使う。

（参考）[擬似要素と擬似クラスとは？](https://qiita.com/yuta-38/items/e8217fcb7a0a63d55f72#%E6%93%AC%E4%BC%BC%E8%A6%81%E7%B4%A0%E3%81%A8%E6%93%AC%E4%BC%BC%E3%82%AF%E3%83%A9%E3%82%B9%E3%81%AE%E9%81%95%E3%81%84)



##ソースコード

```html:.html
<a href="#modal">モーダルを表示</a>

<div id="modal">
  <div class="message-wrapper">
    <a href="#" class="close"></a>
    <div class="message-box">
      モーダルです。<br>
      元の画面に戻るには閉じるアイコンか、<br>
      枠の外をクリックしてください<br>
    </div>
  </div>
</div>
```

```css:.css
/*1. デオフォルト非表示にする*/
#modal {
  display: none;
}


/*2. 内部リンククリックで適用する*/
#modal:target{
  width: 100%;
  height: 100%;
  position: absolute; 
  top: 0;
  left: 0;
  display: flex;
  justify-content: center;
  align-items: center;
}


/*3. メッセージBOX（テキスト部）*/
#modal .message-box {
  position: relative;
  background-color:  gold;
  padding: 20px;
  height: 80px;
  cursor: default;
}

/*4. 閉じる領域のためのアンカー*/
#modal .close {
  position: relative;
  display: block;
}

/*5. 枠外の閉じる領域*/
#modal .close::before {
  content: "";
  background: rgba(0, 0, 0, 0.6);
  width: 100%;
  height: 100%;
  position: fixed;
  top: 0;
  left: 0;
  cursor: default;
}


/*6. 閉じるボタン*/
#modal .close::after {
  content: "x";
  color: white;
  position: absolute;
  right: -1rem;
  top: -1rem;
  width: 2rem;
  height: 2rem;
  z-index: 1;
  border-radius: 50%;
  background-color: black;
  display: flex;
  justify-content: center;
  align-items: center;
  cursor: pointer;
}
```


<br>
##解説
###1. デオフォルト非表示にする

```css:.css
/*1. デオフォルト非表示にする*/
#modal {
  display: none;
}
```

`id="modal"`を非表示にしている。


<br>
###2. 内部リンククリックでcssを適用する

```css:.css
/*2. 内部リンククリックで適用する*/
#modal:target{
  width: 100%;
  height: 100%;
  position: absolute; 
  top: 0;
  left: 0;
  display: flex;
  justify-content: center;
  align-items: center;
}
```

・`:target`
この擬似クラスを使うと、内部リンクをトリガーとして、リンク先の要素にcssを適用することができる。

なお、`:target`の適用をオフにする（初期状態に戻す）には、ページトップへの内部リンクをクリックする。

・ページトップへの内部リンク：`<a href="#">`


<br>
**▼処理の流れ**

```html:.html
<a href="#modal">モーダルを表示</a>
```

(1)上記リンクで、id="modal"の要素に飛ぶ
　↓
(2)cssの`#modal:target`が発火

ちなみに、`:target`を仕掛ける対象は同一タグ内のクラス属性でも発火する。

>例:`<div id="modal" class="hide">`としてcss`hide:target{処理}`でも発火する。

  　↓
(3)ページトップへの内部リンク：`<a href="#">`クリックで`:target`のcssが外れる。


<br>
**・画面いっぱいに表示**

```
width: 100%;
height: 100%;
```

ただし、このままでは上のdiv要素の下に現在の画面と同じ大きさの要素が生成され、画面全体が下にズレるので以下プロパティを加える。


<br>
**・`absolute`で要素の位置を固定**

```
  position: absolute;
  top: 0;
  left: 0;
```

`absolute`のみだと、当初の位置のままなので、`top`と`left`で場所を指定する。

親要素に`relative`、`fixed`、`absolute`がない場合は`body`タグ（ここでは画面）が基準となる。

これで、元ある要素の上に被せて画面いっぱいに表示することができる。


<br>
**・中身の要素の位置を整える**

```
  display: flex;
  justify-content: center;
  align-items: center;
```

柔軟なレイアウト指定ができるflexboxを指定する（`flex`）これで、`justify-content`と`align-items`プロパティが使えるようになる。


`justify-content: center;`はflexの中の要素を横方向の中央に設置する。

`align-items: center`はflexの中の要素を縦方向の中央に配置する。

以上で、flexの中身を画面中央に配置完了。


<br>
**▼子要素の横幅**
親要素に`flex`を適用すると、子要素の幅は指定がない場合、子要素が内部に持っている幅に自動で調整される。

>flexでない場合、block要素は画面横幅いっぱい（100%）となる。



<br>
###3. メッセージBOX（テキスト部）

```css:.css
/*3. メッセージBOX（テキスト部）*/
#modal .message-box {
  position: relative;
  background-color:  gold;
  padding: 20px;
  height: 80px;
  cursor: default;
}
```

メッセージ部分のレイアウトを調整。

・`position: relative;`
これがないと、兄弟要素の閉じるボタン（aタグ）にpositionを適用した際にaタグ要素よりも裏側に回ってしまう。

positionを設定することで記述の順番通り、こちらがaタグよりも上側に表示される。


```html:.html
    <a href="#" class="close"></a>
    <div class="message-box">
      モーダルです。<br>
      元の画面に戻るには閉じるアイコンか、<br>
      枠の外をクリックしてください<br>
    </div>
```


・`cursor: default;`
矢印マークのポインタが表示される。（環境設定を変更している場合はデフォルトで設定しているもの）

これがないと、テキストの上を通過するときに、テキスト入力アイコンになる。




<br>
###4. モーダルを閉じる領域のためのアンカー


```css:.css
/*4. 閉じる領域のためのアンカー*/
#modal .close {
  display: block;
  position: relative;
}
```

閉じるアクションとして、(1)閉じるボタンを`::after`で(2)閉じる領域`::before`で設定する。

上記処理で、閉じるアクションとなるaタグのデフォルト位置を指定している。


<br>
**・`display: block;`**
aタグはデフォルトinline要素で、兄弟要素のdivの前方に配置されてしまう。

div要素の上に表示させるためblockに変更。


**・`position: relative;`**
親要素を`position: relative;`とすることで、子要素を親要素の位置を基準にして動かすことができるようになる。


<br>
###5. 枠外の閉じる領域

```css:.css
/*5. 枠外の閉じる領域*/
#modal .close::before {
  content: "";
  background: rgba(0, 0, 0, 0.6);
  width: 100%;
  height: 100%;
  position: fixed;
  top: 0;
  left: 0;
  cursor: default;
}
```

擬似要素`::before`でaタグ(class="close")の前方に閉じる領域を作成している。

・`content: "";`
`::before`で使うプロパティ。今回はテキストや画像を挿入しないので空であることを明示しておく。


・`background: rgba(0, 0, 0, 0.6);`
背景色。４つ目のパラメータで透明度を指定している。

>opacity:
透過なし  1 ⇄ 0  完全透過


・画面全体を覆う
この要素が画面全体を覆うようにする。

```
 width: 100%;
 height: 100%;
```

これだけでは要素の領域が確保されていないため変化がない。`fixed`で画面基準として領域を指定する。


・`position: fixed;`
windowを基準点とする。位置を指定しない場合は現在位置をが基点のままとなる。

現在位置を基点とし、windowと同じサイズの半透明の黒枠が表示されるようになる。


・位置の指定

```
  top: 0;
  left: 0;
```

黒枠の設置位置を画面左上に固定する。これで画面全体を覆った黒枠が表示完了。

・`cursor: default;`
ポインタをデフォルトの矢印にする。
これがないと、aタグの延長要素なので指マークのポインタになってしまう。



<br>
###6. 閉じるボタン

```css:.css
/*6. 閉じるボタン*/
#modal .close::after {
  content: "x";
  color: white;
  border-radius: 50%;
  background-color: black;
  position: absolute;
  right: -1rem;
  top: -1rem;
  width: 2rem;
  height: 2rem;
  z-index: 1;
  display: flex;
  justify-content: center;
  align-items: center;
  cursor: pointer;
}
```

`::after`を使ってaタグの後ろに閉じるボタンを設置する。


・ボタンの作成

```
  content: "x";
  color: white;
  border-radius: 50%;
  background-color: black;
  width: 2rem;
  height: 2rem;
```

擬似要素`::after`は`content`プロパティを指定することで、そのテキスト（や画像）を表示できる。

文字色、枠線、背景色を定義。大きさをremで定義。

・`rem`
サイズの単位の一つ。htmlのデフォルトのフォントサイズを１として、大きさを倍数で指定している。


**・ボタンの中身の位置調整**
文字を中央に寄せるため、flexの`justify-content: center;`と`align-items: center;`を使う。

```
  display: flex;
  justify-content: center;
  align-items: center;
  cursor: pointer;
```


**・ボタンの位置を指定**
ボタンをメッセージボックスの右上に配置する。

```
  position: absolute;
  right: -1rem;
  top: -1rem;
```

親要素のaタグが`relative`なので、`absolute`とすることで、aタグの位置を基点に場所を指定できる。

aタグは`dispaly:block`でblock要素にしている。幅を指定しない場合、親要素の100%の幅となる。

親の親要素が`flex`になっているため、親要素の幅は兄弟要素のメッセージボックスの幅と同じになっている。

aタグは現在高さを持っていないので、メッセージボックスの右上からマイナス方向に-1remを指定したことになる。


・`cursor: pointer;`
aタグは通常でもポインタになるが念のためあえて指定。


<br>
以上。


（参考）[mozilla :target](https://developer.mozilla.org/ja/docs/Web/CSS/:target)