HeadlessUIが便利すぎた

## UIを作成する時フレームワークを使ったことはありますか？
私はCSSフレームワークのtailwindを多用していて、そのtailwindからUIコンポーネントが出ていたので使ってみました。
## HeadlessUIとは
Headless Componentと言われ、tailwindから出ているUIの制御や動作だけを行ってくれるものです。なのでデザイン等は自身でしていく必要があるので、CSSやtailwind等のフレームワーク自由に組みわせてUIを作っていきます。

https://headlessui.com/

## 使用感
ReactとVueでの使い方が公式に乗っているのでVueの方を参考にしつつ、私なりの独断と偏見で使用感を書きたいと思います。

https://headlessui.com/v1/vue

:::note warn
インストールの手順については省略しております。
皆様のほうでお調べください
:::

## コンポーネントについて
Vueの方では10個のコンポーネントが用意されていて、特に便利だと感じた3つを紹介したいと思います。
## Tabs
```tabs.vue
<script setup>
    import { TabGroup, TabList, Tab, TabPanels, TabPanel } from "@headlessui/vue";
    import Chat from "@/components/Chat.vue";
    import Todo from "@/components/Todo.vue";
    import Blog from "@/components/Blog.vue";
    import User from "@/components/User.vue";
    import Janken from "@/components/Janken.vue";
    import Test from "@/components/Test.vue";
    
    const menus = [
        { icon: "/src/assets/chat.svg", component: Chat, title: "Chat" },
        { icon: "/src/assets/todo.svg", component: Todo, title: "Todo" },
        { icon: "/src/assets/book.svg", component: Blog, title: "Blog" },
        { icon: "/src/assets/user.svg", component: User, title: "User" },
        { icon: "/src/assets/janken.svg", component: Janken, title: "Janken" },
        { icon: "/src/assets/test.svg", component: Test, title: "Test" },
    ]
</script>

<template>
    <TabGroup>
        <TabList class="w-full sticky flex gap-5 top-10">
            <Tab v-for="menu in menus">
                <img :src="menu.icon" alt="" width="25" height="25">
            </Tab>
        </TabList>
        <TabPanels class="w-full h-full">
            <TabPanel v-for="menu in menus" class="w-full h-full">
                <p class="text-3xl text-center">{{ menu.title }}</p>
                <component :is="menu.component" />
            </TabPanel>
        </TabPanels>
    </TabGroup>
</template>
```
![tabs.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3194285/c2095b9c-f0b2-b972-2367-77ded0f15f53.gif)

:::note
タブを縦にしたり、選択されているタブのスタイルを変更することもできます。
:::

https://headlessui.com/v1/vue/tabs

## Switch(Toggle)
> 公式からの引用です
```switch.vue
<template>
  <Switch
    v-model="enabled"
    :class="enabled ? 'bg-blue-600' : 'bg-gray-200'"
    class="relative inline-flex h-6 w-11 items-center rounded-full"
  >
    <span class="sr-only">Enable notifications</span>
    <span
      :class="enabled ? 'translate-x-6' : 'translate-x-1'"
      class="inline-block h-4 w-4 transform rounded-full bg-white transition"
    />
  </Switch>
</template>

<script setup>
  import { ref } from 'vue'
  import { Switch } from '@headlessui/vue'

  const enabled = ref(false)
</script>
```

![switch3.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3194285/e0f22f3b-fe1c-7ab8-6ac1-9a1691f36427.gif)

:::note
こういうのがサクッと作れるのは良いですね
:::

https://headlessui.com/v1/vue/switch

## Dialog(Modal)
```dialog.vue
    <TransitionRoot appear :show="isOpen" as="template">
        <Dialog as="div" @close="closeModal" class="relative z-10">
            <TransitionChild
                as="template"
                enter="duration-300 ease-out"
                enter-from="opacity-0"
                enter-to="opacity-100"
                leave="duration-200 ease-in"
                leave-from="opacity-100"
                leave-to="opacity-0"
            >
                <div class="fixed inset-0 bg-black/25" />
            </TransitionChild>
            <div class="fixed inset-0 overflow-y-auto">
                <div
                    class="flex min-h-full items-center justify-center p-4 text-center"
                >
                    <TransitionChild
                        as="template"
                        enter="duration-300 ease-out"
                        enter-from="opacity-0 scale-95"
                        enter-to="opacity-100 scale-100"
                        leave="duration-200 ease-in"
                        leave-from="opacity-100 scale-100"
                        leave-to="opacity-0 scale-95"
                    >
                        <DialogPanel
                            class="w-full max-w-md transform overflow-hidden rounded-2xl bg-white p-6 text-left align-middle shadow-xl transition-all"
                        >
                            <DialogTitle
                                as="h3"
                                class="text-lg font-medium leading-6 text-gray-900"
                            >
                            投稿
                            </DialogTitle>
                            <div class="mt-2">
                                <input type="text" v-model="title" class="w-full h-8" placeholder="タイトル">
                            </div>
                            <div class="mt-2">
                                <textarea placeholder="本文" v-model="body" class="w-full h-32 resize-none"></textarea>
                            </div>
                            <div class="mt-4 flex gap-20 justify-center">
                                <button
                                    type="button"
                                    class="inline-flex justify-center rounded-md border border-transparent bg-red-100 px-4 py-2 text-sm font-medium text-red-900 hover:bg-red-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-red-500 focus-visible:ring-offset-2"
                                    @click="closeModal"
                                >
                                キャンセル
                                </button>
                                <button
                                    type="button"
                                    class="inline-flex justify-center rounded-md border border-transparent bg-blue-100 px-4 py-2 text-sm font-medium text-blue-900 hover:bg-blue-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2"
                                    @click="post"
                                >
                                投稿
                                </button>
                            </div>
                        </DialogPanel>
                    </TransitionChild>
                </div>
            </div>
        </Dialog>
    </TransitionRoot>
```
![dialog.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3194285/fb17defa-6a03-8173-1956-37d8f3e53dd2.gif)

:::note
背景の色やダイヤログを開いた時、閉じた時にイベントを発生させることも可能です。
:::

https://headlessui.com/v1/vue/dialog

## まとめ
HeadlessUIを使うことでUIの制御や動作を任せる事ができるので、デザインの方に時間を割くことができ、よりクリエイティブなデザインにすることができると感じました。