Closed7

Headless UI を試してみる

odanodan

https://headlessui.dev/
とりあえず Vue で試してみる

https://github.com/odan-sandbox/headless-ui-vue-sandbox

感想
Atomic Design とかやっていると、基礎的なコンポーネントの実装が意外にも実装難易度が高いなあと感じることがあった。例えばうっかり esc キーで閉じないモーダルを作ってしまうとか
その中でスタイルを抜きにした Headless UI はこれを使っていればその心配がないのでとても良いと思った

ただ、Headless UI は Tailwind UI ありきな OSS っぽいので、今後どんな風に発展していくか不安になるところはある

odanodan

一番左上にあった Menu を試す
https://headlessui.dev/vue/menu

<template>
  <Menu>
    <MenuButton>More</MenuButton>
    <MenuItems>
      <MenuItem v-slot="{ active }">
        <a :class='{ "bg-blue-500": active }' href="/account-settings">
          Account settings
        </a>
      </MenuItem>
      <MenuItem v-slot="{ active }">
        <a :class='{ "bg-blue-500": active }' href="/account-settings">
          Documentation
        </a>
      </MenuItem>
      <MenuItem disabled>
        <span class="opacity-75">Invite a friend (coming soon!)</span>
      </MenuItem>
    </MenuItems>
  </Menu>
</template>

装飾のないメニューになる

class は書かれているけど、tailwind css を導入していないので何も装飾がない

odanodan
<template>
  <Menu>
    <MenuButton>More</MenuButton>
    <MenuItems>
      <MenuItem v-slot="{ active }">
        <a :class="[active ? 'active' : 'in-active']" href="/account-settings">
          Account settings: {{ active }}
        </a>
      </MenuItem>
      <MenuItem v-slot="{ active }">
        <a :class="{ 'bg-blue-500': active }" href="/account-settings">
          Documentation
        </a>
      </MenuItem>
      <MenuItem disabled>
        <span class="opacity-75">Invite a friend (coming soon!)</span>
      </MenuItem>
    </MenuItems>
  </Menu>
</template>

<script lang="ts">
import { defineComponent } from "vue";
import { Menu, MenuButton, MenuItems, MenuItem } from "@headlessui/vue";

export default defineComponent({
  components: {
    Menu,
    MenuButton,
    MenuItems,
    MenuItem,
  },
});
</script>

<style>
.active {
  color: red;
}

.in-active {
  color: yellow;
}
</style>

試しに CSS を書いてみる

マウスオーバーすると赤色になる

odanodan

DOM はこんな感じ

aria-* とか role 属性がついていい感じ

odanodan

Accessibility notes
Focus management
Clicking the MenuButton toggles the menu and focuses the MenuItems component. Focus is trapped within the open menu until Escape is pressed or the user clicks outside the menu. Closing the menu returns focus to the MenuButton.

フォーカスがよしなに移動して良さそう

odanodan

List を試してみる

サンプルコード

<template>
  <Listbox v-model="selectedPerson">
    <ListboxButton>{{ selectedPerson.name }}</ListboxButton>
    <ListboxOptions>
      <ListboxOption
        v-for="person in people"
        :key="person"
        :value="person"
        :disabled="person.unavailable"
      >
        {{ person.name }}
      </ListboxOption>
    </ListboxOptions>
  </Listbox>
</template>

<script>
import { ref } from "vue";
import {
  Listbox,
  ListboxButton,
  ListboxOptions,
  ListboxOption,
} from "@headlessui/vue";

export default {
  components: { Listbox, ListboxButton, ListboxOptions, ListboxOption },

  setup() {
    const people = [
      { id: 1, name: "Durward Reynolds", unavailable: false },
      { id: 2, name: "Kenton Towne", unavailable: false },
      { id: 3, name: "Therese Wunsch", unavailable: false },
      { id: 4, name: "Benedict Kessler", unavailable: true },
      { id: 5, name: "Katelyn Rohan", unavailable: false },
    ];
    const selectedPerson = ref(people[0]);

    return {
      people,
      selectedPerson,
    };
  },
};
</script>

Menu と役割被ってない??

ul/li で実装していてえらい

odanodan

https://headlessui.dev/vue/dialog を試す

わかりにくいけど装飾がなにもない dialog が表示されている

DOM の様子

DialogOverlay とか実装は面倒だけど毎回必要になるやつがよしなに用意されている

このスクラップは3ヶ月前にクローズされました