ふわっとメニュー

Web

Nuxt3とTailwind CSSでふわっとフェードでメニューを表示する

Nuxt3でTailwind CSSとHeadless UIのDialogを使って、ハンバーガーメニューをクリックしたら、ふわっとフェードでメニューを表示しようとしたが、ページ遷移するとエラーを返し、うまくいかなかったので、Tailwind CSSだけで実装することに。案外簡単にできたので、メモ。

Tailwind CSSとHeadless UIのDialogで実装するもうまくいかない

下記サイトを参考に、docker上で認証を試していた時の話。認証系はうまくいったが、構成を改造していじっくっているうちに、エラーに遭遇しました。

Nuxt3でセッションを使用したログイン認証機能を作る

基本はlayoutsを使い、headerには複数ページで使い回すcomponentsのheader.vueがあり、参考ページのようにHeadless UIのDialogを使っています。クリックすると、スライドやドロワーではなく、画面を覆うふわっとメニューにしたかったので、そのようにTailwind CSSを記述。しかし、メニューから遷移すると、Error: There are no focusable elements inside the <FocusTrap />がブラウザのコンソールに表示され、うまくいきません。Dialogなんで、1つのページでの制御で完結しているのかもと思いつつ、ページをまたがるやり方を探して色々あがいたのですが、やっぱりダメ。そこで、無理にHeadless UIを使わなくてもいいかと思い、Tailwind CSSだけで実装する道を模索しました。

クリックとCSSの連動で解決

最初はscriptに、addEventListnerとclassListを使ったjsを書いていましたが、onMountedで記述する際、ややこしくなったので、もっと楽できるやり方がありそうな気がしてcopilotに尋ねると、ズバリな解答をするではないですか。へぇ~、:classで値を渡してあげると変更できるのね。そうそう、コレコレと実装したのが以下の抜粋です。flagはfalseを渡したいものが複数あったので、こうなっていますが、ひっくり返るやつでも問題ありません。

<script lang="ts" setup>
import {
  Bars3Icon,
  XMarkIcon,
  UserCircleIcon,
  Cog6ToothIcon,
  PencilIcon,
  ExclamationCircleIcon,
  HomeIcon,
} from '@heroicons/vue/24/outline';

const mobileMenuOpen = ref(false);
const menuBtnAction = (flag: boolean) => {
  if (flag === true) {
    mobileMenuOpen.value = true;
  } else {
    mobileMenuOpen.value = false;
  }
};
</script>
<template>
 <header class="inset-x-0 top-0 z-50">
  <div>
   <button
    type="button"
    class="naviWrap__open -m-2.5 inline-flex items-center justify-center rounded-md p-2.5 text-gray-700"
    @click="menuBtnAction(true)"
   >
    <Bars3Icon class="h-6 w-6" aria-hidden="true" />
   </button>
  </div>

  <div
   class="naviWrap fixed inset-0 px-6 lg:px-8 py-10 bg-white transition duration-200 ease-in-out"
   :class="{
    '-z-50 opacity-0': !mobileMenuOpen,
    'z-50 opacity-100': mobileMenuOpen,
   }"
  >
  <div>
   <button
    type="button"
    class="menu__btn naviWrap__close -m-2.5 rounded-md p-2.5 text-gray-700"
    @click="menuBtnAction(false)"
  >
   <XMarkIcon class="h-6 w-6" aria-hidden="true" />
  </button>
 </div>
 <div class="mt-6 flow-root">
  <div class="-my-6 divide-y divide-dashed">
   <NuxtLink
    class="naviWrap__close flex items-center py-4"
    @click="menuBtnAction(false)"
    to="/dashboard/"
   >
    <HomeIcon class="w-6 h-6 mr-4 text-grey-500" />
     ダッシュボード
   </NuxtLink>
   <NuxtLink
    class="naviWrap__close flex items-center py-4"
    @click="menuBtnAction(false)"
    to="/dashboard/settings"
   >
    <PencilIcon class="w-6 h-6 mr-4 text-grey-500" />
     記事作成
    </NuxtLink>

    <NuxtLink
     class="naviWrap__close flex items-center py-4"
     @click="menuBtnAction(false)"
     to="/dashboard/settings"
    >
     <Cog6ToothIcon class="w-6 h-6 mr-4 text-grey-500" />
      設定変更
    </NuxtLink>
    <NuxtLink
     class="naviWrap__close flex items-center py-4"
     @click="menuBtnAction(false)"
     to="/dashboard/cancel"
    >
     <ExclamationCircleIcon class="w-6 h-6 mr-4 text-grey-500" />
      退会
    </NuxtLink>
    <div class="border-b-1 border-dashed"></div>
   </div>
  </div>
 </header>
</template>

コメント