【Tailwind CSS】擬似クラス・擬似要素の指定方法

## はじめに
TailwindCSSのドキュメントを一読して学んだことをメモした記事です。
多少省略してあるので、全文を確認したい方はドキュメントを確認してください！

https://tailwindcss.com/docs/hover-focus-and-other-states

また、タイトルと一致するドキュメントにリンクを設定しておきました。
ドキュメントを確認したい場合はリンクから確認いただけます。

## [Handling Hover, Focus, and Other States](https://tailwindcss.com/docs/hover-focus-and-other-states)
スタイルの指定方法は、擬似クラス・グループ化・親要素・子孫要素・擬似要素など様々な方法で対応できます。

:::note
利用できる具体的なクラスについては公式ドキュメントに「[Quick reference](https://tailwindcss.com/docs/hover-focus-and-other-states#appendix)」掲載されているのであえて書きません。
:::

TailwindCSSではクラス名の先頭に修飾子を追加することで**条件付きで適用**できますと書かれています！

この**条件付きでというところがポイント**です。


:::note alert
TailwindCSSでは、既存のクラスにホバー状態を追加するのではなく、ホバー時のみ実行するクラス要素を追加されることを留意しなければなりません…
:::

### ホバーやフォーカスなどが「条件付きで適用できる」とは？

通常hoverの状態を指定したい場合は下記のように記述します。

```css
.btn-primary {
  background-color: #0ea5e9;
}
.btn-primary:hover {
  background-color: #0369a1;
}
```

TailwindCSSで記述すると通常の状態のclassとホバーした時の状態のclassが定義されます。
```css
.bg-sky-500 {
  background-color: #0ea5e9;
}
.hover\:bg-sky-700:hover {
  background-color: #0369a1;
}
```

上記の定義のされ方をドキュメントでは**条件付きで適応できる**と表現されています。

### 修飾子は重ねられる
修飾子を複数かさなて定義することが可能です！
```jsx
<button className="dark:md:hover:bg-fuchsia-600">
  Save changes
</butto
```

### [擬似クラス](https://tailwindcss.com/docs/hover-focus-and-other-states#pseudo-classes)
普段スタイリングに利用している擬似クラスを修飾子として付与してスタイルを適用させます。
利用できるクラスについては一覧化されているので詳細はそちらを確認ください！

https://tailwindcss.com/docs/hover-focus-and-other-states#pseudo-class-reference

### [親の状態に基づく設定](https://tailwindcss.com/docs/hover-focus-and-other-states#styling-based-on-parent-state)
親のclassNameに`group`を付与します。
子要素のclassに`group-*`のようなに指定することでスタイルを設定できます。

```jsx
<a href="#" class="group">
  <p className="group-hover:text-white">sample text</p>
</a>
```

#### 特定の親の状態に基づいたスタイルを適用する
グループされたネスト内でスタイルを分けたい時はグループに一意の名前をつけることで特定の親要素の状態に基づいてスタイルを設定できます。

```jsx
<ul role="list">
  <li className="group/item hover:bg-slate-100">
    <a className="group/edit hover:bg-slate-200 group-hover/item:visible" href="">
      <span className="group-hover/edit:text-gray-700">Call</span>
    </a>
  </li>
</ul>
```

任意の値でも設定できます。

```jsx
// classセレクターでの指定
<div className="group is-published">
  <div className="hidden group-[.is-published]:block">
    Published
  </div>
</div>

// &を利用することで詳細な設定できます
<div className="group">
  <div className="group-[:nth-of-type(3)_&]:block">
    Published
  </div>
</div>
```

### [兄弟要素の状態に基づいて要素にスタイルを適用する](https://tailwindcss.com/docs/hover-focus-and-other-states#styling-based-on-sibling-state)
```jsx
<form>
  <label>
    <span>Email</span>
    <input type="email" class="peer"/>
    <p className="peer-invalid:visible">
      Please provide a valid email address.
    </p>
  </label>
</form>
```

:::note alert
`peer`は、`peer`の直後に`peer-*`を付与した要素を設置することで利用できます。
順番を前後させてしまうと動作しないことに注意してください。

```jsx
<p className="peer-invalid:visible">
  Please provide a valid email address.
</p>
<input type="email" className="peer"/>
```
:::

`peer`も一意の名前をつけることで区別できたり、任意の値を指定できたりします！

```jsx:見やすいようにclass属性やpeer以外のclassは排除してます
<fieldset>
  <legend>Published status</legend>
  <input className="peer/draft" />
  <label className="peer-checked/draft:text-sky-500">D</label>

  <input className="peer/published" />
  <label className="peer-checked/published:text-sky-500">P</label>

  <div className="peer-checked/draft:block">Drafts...</div>
  <div className="peer-checked/published:block">Your...</div>
</fieldset>

// 任意の値を設定するパターン
<form>
  <label for="email">Email</label>
  <input className="is-dirty peer" required />
  <div className="peer-[.is-dirty]:peer-required:block">This...</div>
</form>

<div>
  <input type="text" class="peer" />
  <div className="peer-[:nth-of-type(3)_&]:block">
    ....
  </div>
</div>
```

### [直接の子要素にスタイルを適用する](https://tailwindcss.com/docs/hover-focus-and-other-states#styling-direct-children)
**ユーティリティクラスを付与できることが望ましい**とされていますが`*`を利用してスタイルを適用する必要がある場合には下記のように記述できます！

```jsx
<ul className="*:p-4">
  <li>list item</li>
  <li>list item</li>
  <li>list item</li>
</ul>
```

:::note alert
親要素で`*`を利用して子要素に対してスタイルを適用させた場合、子要素でユーティリティクラスを適用してのclassのオーバーライドは機能しないので注意してください…

```jsx
<ul className="*:p-4">
  <li className="p-2">list item</li>
  <li className="pt-6">list item</li>
  <li>list item</li>
</ul>
```
:::

### 子孫要素に基づきスタイルを適用する
`has-*`を使うことで子孫要素の状態やコンテンツに基づいてスタイルを適用できます！

```jsx
<label className="has-[:checked]:bg-indigo-50">
  Google Pay
  <input type="radio" />
</label>
```

`[:checked]`以外にも`[:focus]`、`has-[img]`や`has-[a]`で要素セレクタを使用してコンテンツの有無に基づいて要素のスタイルを設定できます。

#### `group`や`peer`の親や兄弟要素の子孫要素に基づいてスタイルを適用させることも可能！

'group'を利用する場合は親を`group`を付与した上でスタイルを適用させたい要素に`group-has-[a]:bg-indigo-50`のように指定します。
親要素の中にaタグがある場合、バックグラウンドが設定した色になります。

`peer`も同様で、状態をウォッチしたい兄弟要素に対して'peer'を付与します。
スタイルを適用させたい要素に`peer-has-[:checked]:bg-indigo-50`のように利用します。

:::note
ドキュメントにも書かれてますがformなどで利用するのが便利だと感じました！

```jsx
<div>
  <label className="peer">
    <input type="checkbox" checked />
      Create a to do list
  </label>
  <p className="peer-has-[:checked]:bg-indigo-50">hoge</p>
</div>
```
:::

### [擬似要素`::before`と`::after`](https://tailwindcss.com/docs/hover-focus-and-other-states#before-and-after)

下記のように記述すると`::before`と`::after`のスタイルを適用できます。

```jsx
<p className="before:ml-4 before:text-red-500">hoge</p>
<p className="after:mr-4 after:text-red-500">hoge</p>
```

:::note
TailwindCSSではデフォルトで`content: ''`が設定されているため別の値が必要な場合の除いて指定する必要がありません！
:::

`content: ''`がデフォルトで設定されているのは、TailwindCSSで用意されている[preflight](https://tailwindcss.com/docs/preflight)で設定されているからです。
もし、無効にしていた場合は`content-['']`を設定する必要があります。

#### 擬似要素よりもHTMLを利用する方が簡単
ドキュメントには`::before`や`::after`を利用するよりもHTMLを利用した方が可読性も上がり,
コードが短くなります。

:::note
DOMに表示させたくないなどのSEO的な要件があるかもしれませんが、空の要素が配置されることにSEO的なマイナスはありません。そのほかに`::before`や`::after`を必ず利用しなければならない理由がないのであればHTMLで実装した方が無難かと思います！
:::

### [プレースホルダーテキストにスタイル適用](https://tailwindcss.com/docs/hover-focus-and-other-states#placeholder-text)

```jsx
<input className="placeholder:text-slate-400" />
```

### [ファイル入力ボタンにスタイル適用](https://tailwindcss.com/docs/hover-focus-and-other-states#file-input-buttons)

```jsx
<input className="file:p-2 "/>
```

:::note alert
TailwindCSSのpreflightでファイル入力ボタンのborderはリセットされません…。
リセットや追加には明示的に指定する必要があります。
:::

### [リストマーカーにスタイル適用](https://tailwindcss.com/docs/hover-focus-and-other-states#list-markers)

```jsx
<ul role="list" className="marker:text-red-700">
  <li>list item</li>
</ul>
```

:::note
`marker`は継承可能な設計になっているため、`<li>`に直接指定できるだけでなく繰り返しを避けるために親で指定することもできます！
:::

### [選択されたテキストへのスタイル適用](https://tailwindcss.com/docs/hover-focus-and-other-states#highlighted-text)
`selection:`は継承されるので設定した箇所の子要素全てに適用されます。
```jsx
<div className="selection:bg-fuchsia-300">
  <p>hoge</p>
</div>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146612/503def2b-6865-4441-53e4-a8e75e6489ab.png)

### [最初の行と最初の文字へスタイル適用](https://tailwindcss.com/docs/hover-focus-and-other-states#first-line-and-first-letter)
最初の行は`first-line`、最初の文字は`first-letter`を使う

```jsx
<p className="first-line:uppercase"></p>
<p className="first-letter:text-xl"></p>
```

### [ダイアログの背景にスタイルを適用](https://tailwindcss.com/docs/hover-focus-and-other-states#dialog-backdrops)
`backdrop:`を利用すると`dialog`要素の背景スタイルを設定できます。

### [レスポンシブブレークポイントに対するスタイル適用](https://tailwindcss.com/docs/hover-focus-and-other-states#responsive-breakpoints)
設定するスタイルの前に`md:`や`lg:`を付与することで実現できます。

```jsx
<div className="p-4 md:p-6 lg:p-8"></div>
```

### [ダークモードへのスタイル適用](https://tailwindcss.com/docs/hover-focus-and-other-states#prefers-color-scheme)
ライトモードは通常のclassをしようし、ダークモードのみ`dark:`を利用します。

```jsx
<div className="text-black dark:text-white"></div>
```

### [アニメーションの無効設定に対してスタイルを適用](https://tailwindcss.com/docs/hover-focus-and-other-states#prefers-reduced-motion)
ユーザーがアニメーション削減を要求した場合のスタイルを設定できます。

```jsx
<svg className="motion-reduce:hidden" />
```

開発者ツールで「prefers-reduced-motion:reduce」をエミュレートするとスピナーが非表示になります。

:::note
エミュレートをするには開発者ツールを開いて、レンダリングタブを探してください。
スクロールしていくと「CSSメディア特製 prefers-contentをエミュレートする」が見つかるはずです！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146612/dd432c0e-6555-51eb-abd0-ef2398ccbd81.png)
:::

`motion-safe`を利用するとモーションの削減が要求されなかった場合のスタイルを定義できます。

### [コントラストの増減のスタイルを適用](https://tailwindcss.com/docs/hover-focus-and-other-states#prefers-contrast)
高いコントラストを要求された場合のスタイルは`contrast-more:`、低いコントラストの要求に対してのスタイル適用は`contrast-less`で対応できます。

```jsx
<p className="contrast-more:opacity-100">hoge</p>
<p className="contrast-less:opacity-100">hoge</p>
```

### [強制カラーモードへスタイルを適用](https://tailwindcss.com/docs/hover-focus-and-other-states#forced-colors-mode)

https://developer.mozilla.org/ja/docs/Web/CSS/@media/forced-colors

`forced-colors:`を利用してスタイルを適用させます。

```jsx
<p className="forced-colors:black">hoge</p>
```

### [ビューポートの方向でスタイルを適用](https://tailwindcss.com/docs/hover-focus-and-other-states#viewport-orientation)
画面が立て向きの場合と横向きの場合でスタイルを分けられます。

```jsx
<p className="portrait:hidden">hoge</p>
<p className="landscape:hidden">hoge2</p>
```

### [ドキュメントが印刷されている時のみスタイルを適用](https://tailwindcss.com/docs/hover-focus-and-other-states#print-styles)
```jsx
<p className="print:block">hoge</p>
```

### [ブラウザのサポート状況に基づいてスタイルを適用](https://tailwindcss.com/docs/hover-focus-and-other-states#supports-rules)
プロパティがブラウザでサポートされているかどうかをチェックします。

```jsx
<p className="supports-[display:grid]:grid">hoge</p>
```

また、`tailwind.config`でショートカットを設定することが可能です。

```diff_jsx
module.exports = {
  theme: {
    supports: {
      grid: 'display: grid',
    },
  },
}

- <p className="supports-[display:grid]:grid">hoge</p>
+ <p className="supports-grid:grid">hoge</p>
```

## まとめ
「そんな指定方法があるのか！」とかなり勉強になりました！

:::note
細かな仕様を理解しておくことで誤った記述がなくなったり、スタイルが当たらなかったりを防ぐことになると思うので一度確認してみることをオススメします！
:::

<!--
TailwindCSSのドキュメント全体をまとめた記事を書いております。
他の仕様を確認したい場合はそちらをご一読いただけると嬉しいです！
-->