コード例
ここで作るのは次のようなUIです。
デモはこちらに用意しています。
aria-expanded
を変更するべきです。このようなシンプルなケースでは、StimulusのValues
ステートを使わずに、aria-expanded
だけで十分に管理できそうです。<% set_breadcrumbs [["DropDown", component_path(:dropdown)]] %> <%= render 'template', title: "DropDown", description: "" do %> <div class="mx-auto w-48"> <!-- Profile dropdown --> <div class="relative ml-3 w-8" data-controller="dropdown" data-action="mouseleave->dropdown#hide"> <button type="button" class="peer relative flex rounded-full bg-white text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2" id="user-menu-button" aria-expanded="false" data-dropdown-target="switch" data-action="mouseenter->dropdown#show" aria-haspopup="true"> <span class="absolute -inset-1.5"></span> <span class="sr-only">Open user menu</span> <%= image_tag("content_images/avatar.webp", class: "h-8 w-8 rounded-full") %> </button> <!-- Dropdown menu, show/hide based on menu state. Entering: "transition ease-out duration-200" From: "transform opacity-0 scale-95" To: "transform opacity-100 scale-100" Leaving: "transition ease-in duration-75" From: "transform opacity-100 scale-100" To: "transform opacity-0 scale-95" --> <div id="user-menu" class="absolute left-0 z-10 w-48 origin-top-right rounded-md bg-white py-1 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none transition-all transform opacity-0 collapse scale-95 ease-in duration-75 peer-aria-[expanded=true]:opacity-100 peer-aria-[expanded=true]:scale-100 peer-aria-[expanded=true]:visible peer-aria-[expanded=true]:ease-out peer-aria-[expanded=true]:duration-200" role="menu" aria-orientation="vertical" aria-labelledby="user-menu-button" tabindex="-1"> <!-- Active: "bg-gray-100", Not Active: "" --> <a href="#" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-300" role="menuitem" tabindex="-1" id="user-menu-item-0">Your Profile</a> <a href="#" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-300" role="menuitem" tabindex="-1" id="user-menu-item-1">Settings</a> <a href="#" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-300" role="menuitem" tabindex="-1" id="user-menu-item-2">Sign out</a> </div> </div> </div> <% end %>
data-controller="dropdown"
でStimulus Controllerと繋げています。ボタン(顔の写真があるところ)とメニューを囲むようにStimulus Controllerを繋げますdata-action="mouseenter->dropdown#show"
とdata-action="mouseleave->dropdown#hide"
のところです。mouseenter
とmouseleave
イベントに反応してStimulus controllerのshow()
とhide()
を呼び出していますaria-expanded="false"
のところをステートとします。これが"false"
になったり"true"
になっているのをCSS擬似セレクタが読み取って、メニューを表示・非表示にしますpeer-aria-[expanded=true]:
となっているところでTailwind CSSがaria-expanded=
のステートを監視します。これに応じてメニューの表示・非表示を切り替えますimport { Controller } from "@hotwired/stimulus" // Connects to data-controller="dropdown" export default class DropdownController extends Controller { static targets = ["switch"] connect() { } show(event) { this.switchTargets.forEach((target) => target.ariaExpanded = "true") } hide(event) { this.switchTargets.forEach((target) => target.ariaExpanded = "false") } }
static targets = ["switch"]
はtargetを指定しています。switch
はボタン(顔のアイコンがあるもの)に指定してありますshow()
, hide()
のイベントでtargetのariaExpanded
の値を切り替えていますaria-expanded
属性で十分と判断しました)aria-expanded
属性を監視するCSS擬似セレクタを使って、実際の表示変更をする方法