ヘッドレスUIライブラリを使ってみる

PlayGroundAdventCalender2024の15日目

こんにちは、デザインコースのHaruです
PlayGroundではデザインコースですが、個人ではWeb開発を全般的に学んでいます

そして今回はReactで使えるヘッドレスUIライブラおリについて紹介したいと思います

ヘッドレス UI ライブラリ とは

そもそもこれが何か知っていますか?

「スタイル以外の振る舞いを提供するライブラリ」
のことです

従来のUIライブラリでは元々スタイルがついています
ボタンには色がついてて、余白があって、ホバーしたりクリックした時に変化するというような感じです

それらがないのがヘッドレスUIライブラリとなります

そのため完全オリジナルのデザインにすることができるのです

いくつか紹介

  • Radix UI
    • shadcn/uiでも使われているライブラリ
  • HeadlessUI
    • TailwindCSSと同じ開発元なのでTailwindCSSとの相性がいい
  • React aria
    • Adobeが作っており、アクセシビリティに特化している

他にも色々あったり従来のUIライブラリがヘッドレス化していたりもするので、気になった方は調べてみてください!

実際に使ってコンポーネントを作ってみる

紹介した中でもTailwindCSSと相性といいHeadlessUIを使って、ドロップダウンメニューを作っていこうと思います

Headless UI
Completely unstyled, fully accessible UI components, designed to integrate beautifully with Tailwind CSS.

使用する技術構成

  • React v18.3.1
  • Vite v6.0.1
  • Tailwind CSS v3.4.16
  • @headlessui/react v2.2.0

環境構築

このTailwind CSSのドキュメントの手順でやっていきます
(本題とは違うので簡潔に進めます)

Install Tailwind CSS with Vite - Tailwind CSS
Setting up Tailwind CSS in a Vite project.

まずはViteでReactのプロジェクト作成

npm create vite@latest my-project -- --template react
ターミナル

Tailwind CSSをインストール

npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
ターミナル

Tailwind CSSの適用範囲を指定

/** @type {import('tailwindcss').Config} */
export default {
  content: [
    "./index.html",
    "./src/**/*.{js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}
tailwind.config.js

他の不要なCSSは削除し、これだけ加える

@tailwind base;
@tailwind components;
@tailwind utilities;
index.css

これでTailwind CSSのセットアップまでできたので、実行する

npm run dev
ターミナル

HeadlessUIの導入

ではやっと本題のHeadlessUIを入れていきましょう

とは言っても、シンプルにインストールするだけです

npm install @headlessui/react

ドロップダウンメニューを作る

ここから作っていくのですが、ヘッドレスUIライブラリということがわかりやすいようにまずは何もスタイルを指定せず作ります

import { Menu, MenuButton, MenuItem, MenuItems } from "@headlessui/react";

function App() {
	return (
		<Menu>
			<MenuButton>open</MenuButton>
			<MenuItems>
				<MenuItem>
					<a href="#">item1</a>
				</MenuItem>
				<MenuItem>
					<a href="#">item2</a>
				</MenuItem>
				<MenuItem>
					<a href="#">item3</a>
				</MenuItem>
			</MenuItems>
		</Menu>
	);
}

export default App;

App.tsx

これは本当に必要なものをimportしただけですね

では画面はどうなってるでしょうか?

MenuButtonのopenだけだ表示されています
まあ、これはドロップダウンメニューなので、クリックしてみます

中のMenuItemsが出てきました
これがドロップダウンなのかーってくらいスタイルないですよね笑

ただクリックしたら中身が出てくる挙動はこれだけで作れました
あとは好きにスタイリングすれば良いだけです

自作するより圧倒的にですよね
そして、普通に使っている分にはスタイルがないだけに見えますが、ヘッドレスUIライブラリにはアクセシビリティが考慮されてるものが多いです
(スクリーンリーダーやキーボード操作など)

これもメリットの1つになります

でもまあ流石にこのままでは使わないのでスタイリングしていきましょう!

Tailwind CSS でスタイリング

一応HeadlessUIのドキュメントにはすでにスタイリングされたコード例も載っているので参考にしてみてください

アイコンは同じくTailwind CSSの開発者が作ったHeroiconsというものを使います

Heroicons
Beautiful hand-crafted SVG icons, by the makers of Tailwind CSS.

※Tailwind CSSの具体的な記法については解説しません

よくあるユーザーアイコンクリックしたら出てくるメニューにしてみようと思います

なのでまずはMenuButtonをアイコンにします

<MenuButton className={"rounded-full w-12 h-12 border"}>
	<img
		src="/image.png"
		alt="user icon"
		className="rounded-full"
	/>
</MenuButton>
App.tsx

そしてMenuItemsにはありそうな感じのプロフィール・設定・ログアウトの3つを含めてみました
それぞれアイコンもつけています

<MenuItems
anchor={"bottom end"}
transition
className={"rounded-full w-12 h-12 border data-[hover]:brightness-95 data-[open]:brightness-95 data-[focus]:outline-1 data-[focus]:outline-neutral-700"}
>
	<MenuItem>
		<button className="group flex w-full items-center gap-2 rounded-lg py-2 px-3 data-[focus]:bg-neutral-400/10">
			<UserIcon className="size-4 fill-black/50" />
			プロフィール
		</button>
	</MenuItem>
	<MenuItem>
		<button className="group flex w-full items-center gap-2 rounded-lg py-2 px-3 data-[focus]:bg-neutral-400/10">
			<Cog6ToothIcon className="size-4 fill-black/50" />
			設定
		</button>
	</MenuItem>
	<MenuItem>
		<button className="group flex w-full items-center gap-2 rounded-lg py-2 px-3 data-[focus]:bg-neutral-400/10">
			<ArrowRightStartOnRectangleIcon className="size-4 fill-black/50" />
			ログアウト
		</button>
	</MenuItem>
</MenuItems>

App.tsx

このような感じになりました

transitionを使ってアニメーションをつけたり、ホバー時の変化をつけた完成系が以下になります!

0:00
/

まとめ

ヘッドレスUIライブラリの紹介と実際にドロップダウンメニューを作ってみました

MUIのようなUIライブラリと違い、置くだけで良いというのではないのでヘッドレスの方が良いわけではありません
自由度が高く独自のデザインにしたい時に便利なので、選択肢として知っておくのが良いのかと思います!

明日はyuukiさんです。よろしくお願いします!