🕌

次にくる?!Radix UI触ってみた(Radix UI × Tailwind CSS)

2024/02/27に公開

はじめに

この記事はQiitaに投稿した2022年アドベントカレンダーの記事を移管したものです。

Radix UI 概要

https://www.radix-ui.com/

Why wastetime reinventing UI components?

Radixとは2022年7月22日にメジャーリリースされたUI Component Library
その特徴はHeadlessであること、そしてコンポーネントの種類の豊富さです。

ここで言うHeadlessとはスタイルがないことで、
UI Componentを「機能」と「見た目」に分けそのうち機能のみを提供するというもの
つまりHeadlessなUI Component LibraryはスタイルがないアクセシブルなUIコンポーネントを提供してくれます。

Headless な UI Component Library、Radixの他ではその名の通り”Headless UI”というTailwind CSSの開発元としてもお馴染みTailwind Labsの提供するUI Component Libraryもありますが、2022年12月時点でもコンポーネントの種類は少し寂しい…

一方、RadixはDialogやPopoverなどメジャーなコンポーネントはもちろん、TooltipやToastなど比較的豊富な種類のコンポーネントを提供しています。

本記事では導入方法からRadixでコンポーネントを実装するまで一通り触れてみたいと思います!

最終系

switch_demo.gif

導入方法

Radixは使いたいコンポーネントごとにinstallします。
今回はswitchをinstall

tarminal
% yarn add @radix-ui/react-switch

とりあえず最低限の実装してみる

今回はRadix UIとTailwind CSSでコンポーネントを実装したいとおもいます。
とりあえずJSXで実装

index.tsx
import React from "react";
import * as Switch from "@radix-ui/react-switch";

const MySwitch = () => {
  return (
    <form>
      <div>
        <label htmlFor="notification">通知を受け取る</label>
        <Switch.Root id="notification">
          <Switch.Thumb />
        </Switch.Root>
      </div>
    </form>
  );
};

MUI等のスタイルを包括したUI Componentと違い現時点ではスタイルがなくlabelしか見えない状態に...

1.jpg

Styling

さて、このままtailwindCSSでStylingしていくのですが1つ問題が...

状態によるStyling、例えばswitchがonの状態、offの状態でStylingを分ける必要があります。
Radixではコンポーネントの状態がdata-state で管理されています。

Data attributes

When components are stateful, their state will be exposed in a data-state attribute. For example, when an Accordion Item is opened, it includes a data-state="open" attribute.

switch onの時 switch offの時
3.jpg 4.jpg

ですので単にCSSでStylingする場合は下記のような方法でswitchがonの状態、offの状態で背景色を変更することができます。

.hogehoge {
	background-color: white;
}
.hogehoge[data-state='checked'] {
  background-color: black;
}

しかしTailwindでは任意のHTML attributesでのStylingを直接サポートしていないため新たにtailwindcss-radixというライブラリを使用することにします。

https://www.npmjs.com/package/tailwindcss-radix

Install

terminal
% yarn add tailwindcss-radix

tailwind configのpluginに設定

tailwind.config.cjs
module.exports = {
  content: ["./src/**/*.{js,jsx,ts,tsx}"],
  theme: {
    extend: {},
  },
  plugins: [require("tailwindcss-radix")()], // 追記
}

これによりtailwindで簡単にdata-stateのchecked uncheckedをベースにStylingできるようになりました。
下記のようにradix-state-というPrefixによってRadixのdata-stateにアクセスできます。
(Prefixを任意のものに設定もできるが今回は割愛)

const MySwitch = () => {
  return (
    <form>
      <div>
        <label htmlFor="notification">通知を受け取る</label>
        <Switch.Root className="radix-state-checked:bg-black radix-state-unchecked:bg-gray-600" id="notification">  // tailwind追加
          <Switch.Thumb />
        </Switch.Root>
      </div>
    </form>
  );
};

最終的な実装

あとは最終系を目指してコツコツとTailwind CSSでStylingしていきます....

const MySwitch = () => {
  return (
    <form>
      <div className={clsx("flex", "items-center")}>
        <label
          className={clsx("text-base", "pr-3", "select-none")}
          htmlFor="notification"
        >
          通知を受け取る
        </label>
        <Switch.Root
          className={clsx(
            "group",
            ["w-[42px]", "h-[25px]"],
            ["inline-flex", "flex-shrink-0"],
            ["bg-gradient-to-r", "from-[#EC008C]", "to-[#FC6767]"],
            "relative",
            "cursor-pointer",
            "rounded-full",
            "shadow-md",
            [
              "radix-state-checked:bg-black",
              "radix-state-unchecked:bg-gray-600",
              "radix-state-checked:focus:ring",
              "radix-state-unchecked:focus:ring-1",
            ],
            ["focus:outline-none", "focus:ring-white"],
            ["transition-colors", "duration-200", "ease-in-out"],
            ["border-2", "border-transparent"]
          )}
          id="notification"
        >
          <Switch.Thumb
            className={clsx(
              ["inline-block", "h-[21px]", "w-[21px]"],
              "transform",
              "rounded-full",
              "group-radix-state-checked:translate-x-5",
              "bg-white",
              "shadow-lg",
              "ring-0",
              "pointer-events-none",
              ["transition", "duration-200", "ease-in-out"],
              "group-radix-state-unchecked:translate-x-0"
            )}
          />
        </Switch.Root>
      </div>
    </form>
  );
};

export default MySwitch;

まとめ & 感想

MUI等のドキュメントのコピペだけである程度綺麗に実装できるUI Componentに慣れていたので、0からStylingするのは少し大変でしたが、

プロダクトをより洗練されたものにしたい!
UI/UXにとことん拘りたい!

という方には強くお勧めできると思います!

個人的にもHeadlessなUI ComponentはHeadless UIしか選択肢になく、その種類の乏しさから導入には消極的でしたが、Radix UIの豊富さを受け、今後個人開発等でも積極的に取り入れていきたいと思います。

Let's Radix !

Discussion