【Headless UI入門】もうスタイルに悩まない！Headless UIで自由なデザインを実現する方法

# はじめに
今回ですが、最近トレンドになっている`Headless UI`について概要と、魅力について以下の記事を参考に、記事を書いていきます。
Headless UIについて、最近よく耳にするという方も、初めての方も、本記事を見たら、その魅力が伝わると思いますし、標準で使っていく気になると思います。

https://www.subframe.com/blog/how-headless-components-became-the-future-for-building-ui-libraries

# Headless UI ライブラリとは
Headless UI ライブラリとは、その名の通り、**デザインのないUIライブラリ**のことです。
デザインがない以外の特徴としては、アクセシビリティ対応が組み込まれていたり、`Tailwind CSS`などの他のスタリングツールと併用して使用することができることが挙げられます。

つまり、非HeadlessなUIライブラリとは異なり、Headlessではスタイルと機能の柔軟性が高くなるということです。さらには、Headless ライブラリではデフォルトでスタイルなしのコンポーネントが提供されるため、開発者がカスタムしたスタイルとの競合が発生しないという大きなメリットがあります。

この柔軟性により、`Radix UI`や`HeadlessUI`などのHeadless ライブラリが新しいプロジェクトの標準になりました。

さらに、最近では、`shadcn/ui`や`Justd`などHeadless UIライブラリ上に構築されたコンポーネントライブラリを使用することが標準になりつつあります。あのMaterial UIでさえ独自のHeadless コンポーネントライブラリ として`Base UI`を開発しています。
（`shadcn/ui`はRadix UI上に、`Justd`はReact Aria上に構築されたコンポーネントライブラリです）

https://ui.shadcn.com/

https://getjustd.com/

https://mui.com/base-ui/getting-started/


# Headless コンポーネントライブラリが標準となる理由
では、なぜHeadless コンポーネントライブラリが標準になるのでしょうか。
（私は今後、ますますHeadless UIは伸びると思っています）

いくら、柔軟性が高いとはいえ、Material UIのように予めスタイリングされたコンポーネントを使ったほうが、開発スピードは速いはずですし、実際、予めスタイリングされたコンポーネントでデザインを構築してUIを開発する手法は賢い戦略と言えます。

しかしながら、このような戦略は大きな欠点をはらんでいます。
それは**独自のスタイル設定が非常に難しい**という点です。

どういうことか解説します。
Material UIなど従来の非Headless コンポーネントライブラリはデフォルトスタイルを持っています。
これはパット見は便利ですが、**カスタマイズ性が低く、設定が難しい**という側面も持っています。

Material UIを例にとりましょう。
あるアプリケーション開発において、完全にMaterial UIのコンポーネントが提供するスタイリングのみで構築するという前提であれば、先程の戦略は功を奏します。

ただ、そのようなアプリケーションはほとんどなく、たいていのアプリケーション開発では、スタイリングのカスタマイズが必要です。

実際にコードにして比較してみましょう。

### Material UI
Material UIの場合、大抵のスタイルは`sx`propsでスタイリングできますが、より細かなスタイルは以下のような煩雑な手順を踏む必要があります。

1. Material UIの特定のクラスをoverrideすること
2. 素のCSSをあてること


また、これらの設定が**Material UIの特定のクラスの子孫クラス・セレクターにも及ぶ可能性があり、その都度、クラス名やセレクター名をdevtools等で特定し、素のCSSでoverride**する必要性もあります。
（ちなみに、Material UIはドキュメントも見づらいので、ドキュメントから探そうものなら、それだけで日が暮れる覚悟で労力を投下する必要があります）

どうでしょうか。
この説明と以下のコード及び、このスタイリングコードがさらに肥大化する可能性を考えると、Headless UI上に構築されていないコンポーネントライブラリを使用するのは、かなりしんどいと感じるのではないでしょうか。

```tsx
import Switch from '@mui/material/Switch'

const MuiSwitch = () => {
  return (
    <Switch
      sx={{
        "&.MuiSwitch-root .MuiSwitch-thumb": {
            backgroundColor: "red"
         }
      }}
    />
  )
}
```

ここまでで、細かいスタイルの適用は非Headlessのコンポーネントライブラリではかなり難しいということがわかったと思います。

次にHeadlessUIの方を見てみましょう。

### Radix UI + Tailwind CSS
```tsx
import * as Switch from "@radix-ui/react-switch"

const HeadlessSwitch = () => {
  return (
    <Switch.Root>
      <Switch.Thumb className="bg-red-600" />
    </Switch.Root>
 )
}
```

明らかにシンプルになっていることがわかります。

つまり、Headless vs 非Headlessの結果は言うまでもなく、Headlessに軍配が上がります。

非Headlessでは**UI構築という本質的な部分を行うよりコンポーネントライブラリとの格闘という非本質的な部分に多くの時間を割かれることになります。**

ところが、Headless UIのコンポーネントライブラリを使用するとUI構築の部分に真に集中することができるようになります。

だからこそ、Headless UIで構築されたコンポーネントライブラリが、今後ますます伸びるし標準になると予想されているのです。

そして、実際に、`Radix UI`と`Tailwind CSS`で構築された、コピペ可能なコンポーネントライブラリの`shadcn/ui`が流行っており、コピペ可能なコンポーネントライブラリとしてトレンドとなっています。
`shadcn/ui`に関しては、昨年のGitHub獲得Star数（JSライジング・スター2023）は1位を獲得したのは記憶に新しいところと思います。

https://risingstars.js.org/2023/ja

また、最近では先程紹介した、`React Aria`と`Tailwind CSS`で構築されたコピペ可能なコンポーネントライブラリである`Justd`もでてきています。

https://getjustd.com/

私は最近の個人開発や学習では`Justd`をメインに使っているのですが、自動レスポンシブ対応されていたり、開発者がこれは使うだろうなという機能をコンポーネント + propsとして提供してくれているので、こちらもメンテが盛んになれば、流行ると思っています。

# おわりに
どうでしょうか？
Headless コンポーネントライブラリに対するモチベーションが湧いたことを願っていますが、最後に参考記事の一文を紹介したいと思います。

> 今後も多くのheadless UIライブラリがでてくるでしょうし、既存のものはさらに改良が進んでReactのエコシステムにより良い影響を及ぼす

私も著者の意見に賛同しますし、上記のようになることを信じています。ただ、日本ではまだまだ、主流とは言えないのが残念としか言えないので、もっとトレンドになってほしいところではあります。

Headless UIライブラリの使用実績の有無に囚われず、本質的な部分であるUIの構築に集中できることを満たしてくれるかをベースに考えていきたいところですね。

# おまけ
実際に非Headless コンポーネントライブラリのMaterial UIとHeadless コンポーネントライブラリの細かなスタイリングをしたものを作成してみたので、比較してみましょう。

## 非Headless コンポーネントライブラリの場合
以下はMantineでいうところのoutlineのTabになります。

https://mantine.dev/core/tabs/

```tsx
const MuiTabs = () => {
  return (
    <Tabs
      TabIndicatorProps={{
        style: {
          display: 'none',
        },
      }}
      sx={{
        px: 8,
        position: 'relative',
        '& .MuiTabs-flexContainer': {
          gap: 0.1,
        },
        '&::before': {
          content: '""',
          position: 'absolute',
          width: '100%',
          height: '1px',
          bottom: 0,
          bgcolor: 'red',
          zIndex: 0,
        },
        '&::after': {
          content: '""',
          position: 'absolute',
          height: '1px',
          width: '100%',
          left: 0,
          bottom: 0,
          bgcolor: 'red',
          zIndex: 0,
        },
      }}
    />
  )
}

const MantineTab = () => {
  return (
    <Tabs variant="outline" />
  )
}
```

率直に言って「やってられない」というニュアンスが伝わったのではないかと思います。

実際、かなりライブラリと格闘しました。
特定のpropsを探し出し、デフォルトスタイルを打ち消したり、特定クラスを探し出し、素のCSSでスタリング及びoverrideしないとoutlineのTabはできなかったです。

逆に素のCSSだけでやっているMantineは神ライブラリと言えるでしょう。

## Headless UIコンポーネントの場合
では、上記のMaterial UIとMantineのTabsと同様のスタリングをHeadless コンポーネントライブラリの`Justd`のTabsでやってみましょう。
実際に、`Justd`は`Tailwind CSS`で少しカスタマイズするだけでできそうです。
（`shadcn/ui`も同じことが言えそうです）

https://getjustd.com/docs/components/navigation/tabs

以下の画像が、デフォルトのTabsインストール時と変更時の比較です。

`Tailwind CSS`だけで行えるので、かなり楽ですし、特定のクラスやセレクタを調査する必要なく、簡単に実装できました。

また実際のメインのページコンポーネントでは、Tabsを使用していますが、スタリングの肥大化はしていないことを確認できます。

とはいえ、そこそこ変更しているので、やはりMantineがすごいと言わざるを得ないという検証結果となりそうです。

### タブ呼び出し部分
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/879778/0da736e3-69fa-7e3f-7cd3-b17eea112517.png)


### 実際にインストールしたコンポーネント部分
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/879778/624b9cf5-2e69-98d7-1933-acbc19ce12c8.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/879778/04e6437f-6e1b-c8c4-3029-22c2c3e804c5.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/879778/4cc23058-e984-bb58-9cfb-1753e8ab2f69.png)



# 参考文献

https://www.subframe.com/blog/how-headless-components-became-the-future-for-building-ui-libraries

https://ui.shadcn.com/

https://getjustd.com/

https://mui.com/base-ui/getting-started/

https://risingstars.js.org/2023/ja