🌼

【React】daisyUIを触ってみた!かなり楽しいw

2022/03/08に公開

daisyUIとは

The most popular, free and open-source Tailwind CSS component library

Tailwind CSSをベースとしたCSSライブラリで、No JavaScriptでかわいくて機能的な見た目を実現できるスグレモノ。
https://daisyui.com/

Bootstrapのような使い勝手でいろんなコンポーネントを実現できる。実態はTailwind CSSなので拡張も簡単。

今回はこちらのライブラリをReact(Next.js)で色々触ってみましたので、感想などをお伝えします。

なぜ興味を持ったか

最近プロジェクトでChakra UIを触っている。かなり便利なのだが、機能もスタイリングも提供されたものを使っているゆえにロックインされている感じが少し怖い。
https://chakra-ui.com/

特に機能(ロジック)面の実装においては思うところが色々あって、

  • 慣れていないときにあらゆるコンポーネントでいちいち公式ドキュメントを見に行くのが煩わしい
  • 提供されていない機能があったときに、Chakra UIが大きいだけにそれ以外のライブラリを使うのはどこか違和感がある
  • なんだかんだfrom scratchで自作するかそれに近い薄さのライブラリを使うくらいが、柔軟ゆえに長期的には楽だったりする

などなど。恩恵が大きいだけに馴染んでしまえば気にならないのだが、Themingの方もややこしかったりするので技術選定するには勇気がいるかもなーという感じ。

で、「こんなものがあったらなあ」というので

  • 柔軟で簡潔なスタイリングが提供されていて
  • 実装が面倒なUIはサクッと書けて
  • 機能面についてはほぼノータッチ

みたいなのを思い浮かべており、「daisyUIがぴったりじゃないか!」となった。

基本的な使い方

一定程度HTML(JSX)の書き方に注意しつつ、必要なclassNameを書くだけ。
たとえばパンくずリストなんかはこれだけでそれなりの見た目になってしまう。

Breadcrumbs.tsx
<div className="text-sm breadcrumbs">
  <ul>
    <li><a>Home</a></li> 
    <li><a>Documents</a></li> 
    <li>Add Document</li>
  </ul>
</div>

結果

既に多くのコンポーネントが提供されており、探求するのがかなり楽しかったw

https://daisyui.com/components/
個人的に使いたいなーと思ったコンポーネントについてはのちほど紹介します。

Themeが楽しい

https://daisyui.com/docs/themes/

公式ですでに29のThemeが提供されており、好みの色合いを選びつつほんとに何も考えずにいい感じの見た目を演出することができる。

使い方は至ってシンプルで、configで使いたいthemeを設定したりしなかったりした後[1]className="bg-primary"と言うふうにprimarysecondaryに割り当てられた色を使用するだけ。

Button.tsx
<button className="btn">BUTTON</button>
<button className="btn btn-primary">PRIMARY</button>
<button className="btn btn-secondary">SECONDARY</button>
<button className="btn btn-accent">ACCENT</button>
<button className="btn btn-ghost">GHOST</button>
<button className="btn btn-link">LINK</button>

下の画像にあるように既存のThemeを一部上書きすることも簡単にできてしまう。

また、Themeを自作することもかなり容易で、公式サイトにはTheme Generatorなるものも。コンポーネントを見ながら色を決められるのでかなり作りやすそう。

https://daisyui.com/theme-generator/

もちろんthemeを外して0からスタイリングすることも可能。

Slackモックを作ってみた

このTheming、なんかSlackのThemesに似ているなーと思ったので、daisyUIを使って簡単なSlackのモックを作ってみた。

https://daisy-slack-ui.vercel.app/

デスクトップ版のみだしハリボテもいいところだが、Themingの切り替えや多くのDropdownなどを盛り込んでUI自体はそこそこリッチなものができた。今デプロイされているものはセットアップ含め合計6時間ほどで出来上がっており、個人的な感覚としては「爆速」だ(特に整備していないがソースコードはこちら)。


Themeの切り替え


リンクボタン付きDropdown


MenuのDropdownを二重に実装


Tooltipもチラリ


簡単なモーダル。JS抜きなんだぜ?

実装の所感としては、変なJSとか書いてないのにサクサクいい感じの見た目が出来上がるのがすっごい楽しかったw

個人的に「いいね!」なコンポーネント

触ってみた中で良さげだったコンポーネントをご紹介。

Slackモック作成時に多用したコンポーネント。Stateとか書かずに、しかも位置を制御しながら、こんなにも簡単に実装できるなんて本当に嬉しい。Dropdownの中にMenuを使うことで見た目もキレイに。

<div className="dropdown">
   <label tabIndex={0} className="m-1 btn">
     Click
   </label>
   <ul
     tabIndex={0}
     className="p-2 w-52 shadow dropdown-content menu bg-base-100 rounded-box"
   >
     <li>
       <a>Item 1</a>
     </li>
     <li>
       <a>Item 2</a>
    </li>
  </ul>
</div>

https://daisyui.com/components/dropdown/
https://daisyui.com/components/menu/

ちょくちょく使用する上に、on/offのロジックやアニメーションとセットで用意しなければいけないので手間のかかるイメージUI。だけどJSナシでできちゃうんです。そう、daisyUIならね。

export const Modal: VFC = () => {
  return (
    <>
      <label htmlFor="my-modal-4" className="btn modal-button">
        open modal
      </label>

      <input type="checkbox" id="my-modal-4" className="modal-toggle" />
      <label htmlFor="my-modal-4" className="cursor-pointer modal">
        <label className="relative modal-box" htmlFor="">
          <h3 className="text-lg font-bold">
            Congratulations random Interner user!
          </h3>
          <p className="py-4">
            Youve been selected for a chance to get one year of subscription to
            use Wikipedia for free!
          </p>
        </label>
      </label>
    </>
  )
}

https://daisyui.com/components/modal/

Collapse

よく「アコーディオン」という名前で実装していたやつ。こういうのにState使うのばかばかしいなーと思っていたので、CSSで済むのは嬉しい。

export const Collapse: VFC = () => {
  return (
    <>
      <div
        tabIndex={0}
        className="max-w-sm border collapse collapse-arrow border-base-300 rounded-box"
      >
        <input type="checkbox" className="peer" />
        <div className="bg-white collapse-title text-primary-content">
          Click me to show/hide content
        </div>
        <div className="bg-white collapse-content text-primary-content">
          <p>tabindex={0} attribute is necessary to make the div focusable</p>
        </div>
      </div>
    </>
  )
}


https://daisyui.com/components/collapse/

Card

細かい角丸の調整やレスポンシブが数個のclassNameで全部済む。こういう「簡単に見えてちょっと面倒なやつ」が本当に簡単に片付くのはとても良い。

export const Card: VFC = () => {
  return (
    <>
      <div className="shadow-xl card bg-base-100 lg:card-side">
        <figure>
          <img
            src="https://api.lorem.space/image/album?w=400&h=400"
            alt="Album"
          />
        </figure>
        <div className="card-body">
          <h2 className="card-title">New album is released!</h2>
          <p>Click the button to listen on Spotiwhy app.</p>
          <div className="justify-end card-actions">
            <button className="btn btn-primary">Listen</button>
          </div>
        </div>
      </div>
    </>
  )
}


コンテンツに接する部分は画像の角丸が取れているの嬉しい😆
https://daisyui.com/components/card/

form関連

InputやSelect、Textareaなど。form関連の部品は、React Hook Form等で機能面は十分に充実しており、あとは見た目だけちゃちゃっとできたら嬉しいなーと前から思っていた。これで見た目も簡単に作れる。嬉しい。

export const Select: VFC = () => {
  const [value, setValue] = useState("未選択")

  const handleChange = (e: ChangeEvent<HTMLSelectElement>) => {
    setValue(e.target.value)
  }

  return (
    <>
      <select
        className="w-full max-w-xs select select-bordered"
        onChange={handleChange}
      >
        <option disabled selected>
          Who shot first?
        </option>
        <option>Han Solo</option>
        <option>Greedo</option>
      </select>
      <div className="p-2">選択中の値も別で取れます:{value}</div>
    </>
  )
}


https://daisyui.com/components/select/

Table

これは本当に嬉しい。素のTailwindでTableを書こうとすると「各セルに同じスタイリングをいちいち当てる」みたいなことが起きがちだが、daisyUIなら一番上にclassName="table"を指定するだけで大体OK。モットーの”Clean HTML”をひしひしと感じる。

以下は、大体同じテーブルを書いた時の素のTailwindとdaisyUIの実装比較。どちらも自分のテンプレートリポジトリから持ってきました(後述)。

素のTailwind CSS
const UserTableItem: VFC<UserProps> = ({ user }) => {
  const { id, name, gitHubUserName } = user
  const gitHubUserPage = `https://github.com/${gitHubUserName}`

  return (
    <tr className="whitespace-nowrap">
      <td className="py-4 px-6 text-sm text-gray-500">{id}</td>
      <td className="py-4 px-6">{name}</td>
      <td className="py-4 px-6">
        {gitHubUserName ? (
          <ExLink href={gitHubUserPage}>{gitHubUserName}</ExLink>
        ) : (
          <span className="text-gray-300">{"none"}</span>
        )}
      </td>
    </tr>
  )
}

export const UserTable: VFC<UserListProps> = ({ users }) => {
  return (
    <div className="w-min border-b border-gray-200 shadow">
      <table className="divide-y divide-gray-300 table-auto">
        <thead className="bg-gray-50">
          <tr>
            <th className="py-2 px-6 text-xs text-gray-500">ID</th>
            <th className="py-2 px-6 text-xs text-gray-500">Name</th>
            <th className="py-2 px-6 text-xs text-gray-500">GitHub</th>
          </tr>
        </thead>
        <tbody className="bg-white divide-y divide-gray-300">
          {users.map((user) => (
            <UserTableItem user={user} key={user.id} />
          ))}
        </tbody>
      </table>
    </div>
  )
}

daisyUI
const UserTableItem: VFC<UserProps> = ({ user }) => {
  const { id, name, gitHubUserName } = user
  const gitHubUserPage = `https://github.com/${gitHubUserName}`

  return (
    <tr>
      <td>{id}</td>
      <td>{name}</td>
      <td>
        {gitHubUserName ? (
          <a
            href={gitHubUserPage}
            target="_blank"
            rel="noreferrer"
            className="link link-secondary"
          >
            {gitHubUserName}
          </a>
        ) : (
          <span className="text-error">{"none"}</span>
        )}
      </td>
    </tr>
  )
}

export const UserTable: VFC<UserListProps> = ({ users }) => {
  return (
    <table className="table shadow">
      <thead>
        <tr>
          <th>ID</th>
          <th>Name</th>
          <th>GitHub</th>
        </tr>
      </thead>
      <tbody>
        {users.map((user) => (
          <UserTableItem user={user} key={user.id} />
        ))}
      </tbody>
    </table>
  )
}

タイトルがUpperCaseになってしまうなど若干のクセはあるが、ほとんどclassNameを書いていないのにそれなりの見た目になってくれている。
https://daisyui.com/components/table/

Tooltip

嬉しい〜〜〜〜。変なライブラリを使わずに、そして頭を使わずにこれを実装できるのほんとうに嬉しい〜〜〜〜〜〜。

export const ToolTip: VFC = () => {
  return (
    <div className="tooltip tooltip-bottom" data-tip="hello">
      <button className="btn">Hover me</button>
    </div>
  )
}

https://daisyui.com/components/tooltip/

Code

CLI画面のかわいいUI。他にもPhoneやWindowがあって、チュートリアル系のUIなどで表現の幅がぐっと広がる予感。

export const Code: VFC = () => {
  return (
    <div className="mockup-code">
      <pre data-prefix="$">
        <code>npx scaffdog generate uc</code>
      </pre>
    </div>
  )
}

https://daisyui.com/components/mockup-code/

今の所いまいちに感じているところ

まだ10時間も触れてないけど、すでにちょいちょい気になるところもありまして、

わりとハック的な(?)ところもある

https://future-architect.github.io/articles/20211124a/
上のブログが詳しいのですが、ModalなどJS抜きでUIの状態遷移を表現しているコンポーネントは書いてて「なんで動くんだ!?」と思うことが少々。解読もけっこう難しい。

ただ慣れてしまえばそこまで抵抗も無く、JSを使わないことによる恩恵もあるかも?

単なるお手軽部品かと思いきや、C++やRustで実装されているブラウザネイティブな機能のみをつかっているということは、ある意味、最速のUIコンポーネントなのでは?

また、HTMLのセマンティクスからも少し離れた使い方になることもあり、そこらへんは知識と注意が必要そう。

公式のサンプルコードがHTMLのみ(多分)

自分が観測している範囲内だとコンポーネント実装の公式サンプルはHTMLのみで、JSXを書いているときはそのままコピペできないときが多い(それでもエディタが無理やり直してくれたりするのだが)。

サンプルの種類自体は豊富でありがたいのだけれど、せっかくフレームワーク選ばず使えるのが魅力的な技術なので、今後さらなる充実に期待(というかコントリビュートしたいくらいだ)。

ESLintルールでちょっと困る

eslint-plugin-tailwindcssのおすすめ設定をそのまま使っていると、daisyUIのクラスを使ったときに「そんなのTailwindのにありませんから!」と怒られてしまう。


Classname 〇〇 is not a Tailwind CSS class!

自分はこの設定(tailwindcss/no-custom-classname)を"off"にすることで対処しましたが、もしdaisyUI用のプラグイン等ありましたら教えて下さい🙏🏻

.eslint.json
{
"rules": {
    "tailwindcss/no-custom-classname": "off",
   }
}

まとめ

もともとの要求であった

  • 柔軟で簡潔なスタイリングが提供されていて
  • 実装が面倒なUIはサクッと書けて
  • 機能面についてはほぼノータッチ

に関しては(個人的には)オールクリア!とにかく低コストでそれなりのUIを実装したい個人開発やプロトタイピングのときに活躍しそうな技術でした。

テンプレートリポジトリを用意しました

自分としてはかなり気に入りまして今後もちょくちょく使いそうなので、もともとTailwindのみで用意していたボイラープレートをチューニングしてdaisyUI×Next.jsのテンプレートリポジトリを用意しました。daisyUIとdaisyUIおすすめの@tailwind/typograpyを入れた上で、先述のESLintチューニングも施してあるやつです。

yarnするだけでStorybook込みで始められるので、これを機にdaisyUI入門したい人はぜひこちらからどうぞ。
https://github.com/HajimexxxNakagawa/Next-daisy-Boilerplate

デモはこちら。テーマの切り替えも実装済みなので、そこらへんの把握にも良いかもしれません。

以上です!最後までお読みいただきありがとうございました!

完全なる余談

Chakra UIやらRadix UIやらdaisyUIやらを触っていると、「ただデータをマップしていい感じの見た目を作る仕事」としてのフロントエンドエンジニアの価値はほぼほぼ0に近いと強く実感します。だって誰でもできちゃうんだもの。

高度なアクセシビリティを意識した実装ができるようになるか、バックエンドまで見れるようになるか、デザインやビジネス面など実装以前の部分で力を発揮するか...何かしらの+αが無いとフロントエンドエンジニアをやっていけない時代がすぐそこに来ているんでしょうね。そんなの自分がプログラミングの勉強を始めた頃(2019年半ば)からわかってたことなのですけれど。

ざっくり言うと自分は「高速でプロトタイプを作りビジネス面に貢献する」というのを目指していますが、今のところ本当に「ただのフロントエンドエンジニア」なので、なんかもう、みなさん一緒にがんばりましょ〜〜〜〜〜〜〜〜〜〜〜。

脚注
  1. zero configですべてのThemeが使える ↩︎

Discussion