fix: sidebar accessibility

This commit is contained in:
Simon 2026-01-16 14:17:15 +01:00
parent c3515b4105
commit c86ae6aa9d
Signed by: simon
GPG key ID: 40E7A58C4AA1EDB2
3 changed files with 33 additions and 25 deletions

View file

@ -186,14 +186,14 @@
============================================ */ ============================================ */
/* Disable all interactive effects on expanded-menu-group header (no href, not clickable) */ /* Disable all interactive effects on expanded-menu-group header (no href, not clickable) */
.sidebar .menu > li.menu-group > .expanded-menu-group > div:not(a) { .sidebar .menu > li.expanded-menu-group > div:not(a) {
pointer-events: none; pointer-events: none;
cursor: default; cursor: default;
} }
.sidebar .menu > li.menu-group > .expanded-menu-group > div:not(a):hover, .sidebar .menu > li.expanded-menu-group > div:not(a):hover,
.sidebar .menu > li.menu-group > .expanded-menu-group > div:not(a):active, .sidebar .menu > li.expanded-menu-group > div:not(a):active,
.sidebar .menu > li.menu-group > .expanded-menu-group > div:not(a):focus { .sidebar .menu > li.expanded-menu-group > div:not(a):focus {
background-color: transparent !important; background-color: transparent !important;
box-shadow: none !important; box-shadow: none !important;
cursor: default !important; cursor: default !important;

View file

@ -84,14 +84,13 @@ defmodule MvWeb.Layouts.Sidebar do
<!-- Nested Admin Menu --> <!-- Nested Admin Menu -->
<.menu_group icon="hero-cog-6-tooth" label={gettext("Administration")}> <.menu_group icon="hero-cog-6-tooth" label={gettext("Administration")}>
<.menu_subitem href={~p"/users"} icon="hero-user-circle" label={gettext("Users")} /> <.menu_subitem href={~p"/users"} label={gettext("Users")} />
<.menu_subitem href={~p"/admin/roles"} icon="hero-shield-check" label={gettext("Roles")} /> <.menu_subitem href={~p"/admin/roles"} label={gettext("Roles")} />
<.menu_subitem <.menu_subitem
href={~p"/membership_fee_settings"} href={~p"/membership_fee_settings"}
icon="hero-currency-euro"
label={gettext("Fee Settings")} label={gettext("Fee Settings")}
/> />
<.menu_subitem href={~p"/settings"} icon="hero-cog-6-tooth" label={gettext("Settings")} /> <.menu_subitem href={~p"/settings"} label={gettext("Settings")} />
</.menu_group> </.menu_group>
</ul> </ul>
""" """
@ -124,8 +123,12 @@ defmodule MvWeb.Layouts.Sidebar do
defp menu_group(assigns) do defp menu_group(assigns) do
~H""" ~H"""
<!-- Expanded Mode: Always open div structure --> <!-- Expanded Mode: Always open div structure -->
<li class="expanded-menu-group"> <li role="none" class="expanded-menu-group">
<div class="flex items-center gap-3"> <div
class="flex items-center gap-3"
role="group"
aria-label={@label}
>
<.icon name={@icon} class="size-5 shrink-0" aria-hidden="true" /> <.icon name={@icon} class="size-5 shrink-0" aria-hidden="true" />
<span class="menu-label">{@label}</span> <span class="menu-label">{@label}</span>
</div> </div>
@ -147,7 +150,6 @@ defmodule MvWeb.Layouts.Sidebar do
<.icon name={@icon} class="size-5 shrink-0" aria-hidden="true" /> <.icon name={@icon} class="size-5 shrink-0" aria-hidden="true" />
</.link> </.link>
<ul <ul
tabindex="0"
class="dropdown-content menu bg-base-100 rounded-box shadow-lg z-50 min-w-48 p-2 focus:outline-none" class="dropdown-content menu bg-base-100 rounded-box shadow-lg z-50 min-w-48 p-2 focus:outline-none"
role="menu" role="menu"
> >

View file

@ -144,7 +144,8 @@ defmodule MvWeb.Layouts.SidebarTest do
assert menu_item_count > 0, "Should have at least one top-level menu item" assert menu_item_count > 0, "Should have at least one top-level menu item"
# Check that nested menu groups exist # Check that nested menu groups exist
assert html =~ "<details" assert html =~ ~s(<li role="none" class="expanded-menu-group">)
assert html =~ ~s(role="group")
assert has_class?(html, "expanded-menu-group") assert has_class?(html, "expanded-menu-group")
# Check that nested menu items exist # Check that nested menu items exist
@ -192,8 +193,9 @@ defmodule MvWeb.Layouts.SidebarTest do
html = render_sidebar(authenticated_assigns()) html = render_sidebar(authenticated_assigns())
# Check for nested menu structure # Check for nested menu structure
assert html =~ "<details" assert html =~ ~s(<li role="none" class="expanded-menu-group">)
assert html =~ "<summary" assert html =~ ~s(role="group")
assert html =~ ~s(aria-label="Administration")
assert has_class?(html, "expanded-menu-group") assert has_class?(html, "expanded-menu-group")
# Check that nested menu has subitems # Check that nested menu has subitems
@ -208,7 +210,7 @@ defmodule MvWeb.Layouts.SidebarTest do
# There should be more menuitems than data-tips (nested items don't have data-tip) # There should be more menuitems than data-tips (nested items don't have data-tip)
assert menuitem_count > data_tip_count, assert menuitem_count > data_tip_count,
"Should have nested menu items (menuitems without data-tip)" "Should have nested menu items (menuitems without data-tip)"
end end
test "T3.3: renders nested menu with dropdown for collapsed state" do test "T3.3: renders nested menu with dropdown for collapsed state" do
@ -351,8 +353,9 @@ defmodule MvWeb.Layouts.SidebarTest do
test "T5.4: nested menu has correct ARIA attributes" do test "T5.4: nested menu has correct ARIA attributes" do
html = render_sidebar(authenticated_assigns()) html = render_sidebar(authenticated_assigns())
# Details summary should have haspopup # Expanded mode should have role="group" with aria-label
assert html =~ ~s(aria-haspopup="true") assert html =~ ~s(role="group")
assert html =~ ~s(aria-label="Administration")
# Dropdown button should have haspopup # Dropdown button should have haspopup
assert html =~ ~s(aria-haspopup="menu") assert html =~ ~s(aria-haspopup="menu")
@ -518,7 +521,8 @@ defmodule MvWeb.Layouts.SidebarTest do
assert html =~ ~s(role="menuitem") assert html =~ ~s(role="menuitem")
# Check that nested menus exist # Check that nested menus exist
assert html =~ "<details" assert html =~ ~s(<li role="none" class="expanded-menu-group">)
assert html =~ ~s(role="group")
# Footer section # Footer section
assert html =~ "theme-controller" assert html =~ "theme-controller"
@ -624,9 +628,10 @@ defmodule MvWeb.Layouts.SidebarTest do
test "renders expanded menu group" do test "renders expanded menu group" do
html = render_sidebar(authenticated_assigns()) html = render_sidebar(authenticated_assigns())
# details/summary present # expanded-menu-group structure present
assert html =~ "<details" assert html =~ ~s(<li role="none" class="expanded-menu-group">)
assert html =~ "<summary" assert html =~ ~s(role="group")
assert html =~ ~s(aria-label="Administration")
assert has_class?(html, "expanded-menu-group") assert has_class?(html, "expanded-menu-group")
end end
@ -654,7 +659,7 @@ defmodule MvWeb.Layouts.SidebarTest do
# There should be more menuitems than data-tips (nested items don't have data-tip) # There should be more menuitems than data-tips (nested items don't have data-tip)
assert menuitem_count > data_tip_count, assert menuitem_count > data_tip_count,
"Should have nested menu items (menuitems without data-tip)" "Should have nested menu items (menuitems without data-tip)"
# Verify nested menu structure exists # Verify nested menu structure exists
assert html =~ ~s(role="menu") assert html =~ ~s(role="menu")
@ -836,9 +841,10 @@ defmodule MvWeb.Layouts.SidebarTest do
assert has_class?(html, "expanded-menu-group") assert has_class?(html, "expanded-menu-group")
assert has_class?(html, "collapsed-menu-group") assert has_class?(html, "collapsed-menu-group")
# Details element should not have duplicate hover classes # Expanded menu group should have correct structure
# (CSS handles this, but we verify structure) # (CSS handles hover effects, but we verify structure)
assert html =~ "<details" assert html =~ ~s(<li role="none" class="expanded-menu-group">)
assert html =~ ~s(role="group")
end end
test "tooltips only visible when collapsed" do test "tooltips only visible when collapsed" do