appview/layouts/fragments: topbar dropdown accessibility updates and minor design updates #912

open
opened by besaid.zone targeting master from besaid.zone/core: topbar-dropdown

I've made some minor accessibility and design changes to the topbar dropdown. Mainly just adding icons and adding some accessibility enhancements by putting the dropdown links in an unordered list instead of a details/summary combo and adding aria attributes where needed

wasn't able to test the dropdown with VoiceOver since for some reason I can't log in locally with safari. I did run axe devtools though to make sure I've marked things up correctly and it didn't report any issues with my changes. I went over the Authoring Practicing Guide for menu's and read some relevant articles when working on this:

This dropdown does use a newer browser API but it is baseline in all of the major browsers so there shouldn't be any issues: https://caniuse.com/?search=popover

It also uses some very new css properties for positioning the dropdown but I tested in the latest safari, chrome and Firefox and it worked in all 3

Changed files
+82 -22
appview
pages
templates
layouts
fragments
+63 -22
appview/pages/templates/layouts/fragments/topbar.html
··· 47 47 {{ end }} 48 48 49 49 {{ define "profileDropdown" }} 50 - <details class="relative inline-block text-left nav-dropdown"> 51 - <summary class="cursor-pointer list-none flex items-center gap-1"> 52 - {{ $user := .Did }} 53 - <img 54 - src="{{ tinyAvatar $user }}" 55 - alt="" 56 - class="rounded-full h-6 w-6 border border-gray-300 dark:border-gray-700" 57 - /> 58 - <span class="hidden md:inline">{{ $user | resolve | truncateAt30 }}</span> 59 - </summary> 60 - <div class="absolute flex flex-col right-0 mt-4 p-4 rounded w-48 bg-white dark:bg-gray-800 dark:text-white border border-gray-200 dark:border-gray-700"> 61 - <a href="/{{ $user }}">profile</a> 62 - <a href="/{{ $user }}?tab=repos">repositories</a> 63 - <a href="/{{ $user }}?tab=strings">strings</a> 64 - <a href="/settings">settings</a> 50 + {{ $user := .Did }} 51 + <button type="button" popovertarget="navigation-popover" class="site-navigation-dropdown-trigger" aria-label="Open site navigation dropdown"> 52 + <img 53 + src="{{ tinyAvatar $user }}" 54 + alt="" 55 + class="rounded-full h-6 w-6 border border-gray-300 dark:border-gray-700" 56 + /> 57 + </button> 58 + <div popover="auto" id="navigation-popover" class="site-navigation-popover shadow-md border border-gray-100 rounded-sm p-2"> 59 + <div class="flex gap-2 py-2"> 60 + <img 61 + src="{{ tinyAvatar $user }}" 62 + alt="" 63 + class="rounded-full h-6 w-6 border border-gray-300 dark:border-gray-700" 64 + /> 65 + <p>{{ $user | resolve | truncateAt30 }}</p> 66 + </div> 67 + <hr class="h-1 w-full mb-1 mt-2" /> 68 + <ul id="navigation-menu-popover"> 69 + <li> 70 + <a href="/{{ $user }}"> 71 + {{ i "user" "w-4 h-4" }} 72 + <span>profile</span> 73 + </a> 74 + </li> 75 + <li> 76 + <a href="/{{ $user }}?tab=repos"> 77 + {{ i "book-marked" "w-4 h-4" }} 78 + <span>repositories</span> 79 + </a> 80 + </li> 81 + <li> 82 + <a href="/{{ $user }}?tab=strings"> 83 + {{ i "spool" "w-4 h-4" }} 84 + <span>strings</span> 85 + </a> 86 + </li> 87 + <li> 88 + <a href="/settings"> 89 + {{ i "settings" "w-4 h-4" }} 90 + <span>settings</span> 91 + </a> 92 + </li> 93 + <hr class="h-1 w-full mb-1 mt-2" /> 94 + <li> 65 95 <a href="#" 66 - hx-post="/logout" 67 - hx-swap="none" 68 - class="text-red-400 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300"> 69 - logout 70 - </a> 71 - </div> 72 - </details> 96 + hx-post="/logout" 97 + hx-swap="none" 98 + class="text-red-600 flex gap-2 items-center hover:bg-red-50 hover:text-red-700 px-2 py-2 rounded-sm"> 99 + {{ i "arrow-right-from-line" "w-4 h-4" }} 100 + <span>logout</span> 101 + </a> 102 + </li> 103 + </ul> 104 + </div> 73 105 74 106 <script> 75 107 document.addEventListener('click', function(event) { ··· 80 112 } 81 113 }); 82 114 }); 115 + 116 + const navigationPopoverLinks = document.querySelectorAll("#navigation-menu-popover li a"); 117 + const currentPageURL = window.location.href 118 + navigationPopoverLinks.forEach(link => { 119 + const navigationPopoverLinkURL = link.href 120 + if (navigationPopoverLinkURL === currentPageURL) { 121 + link.ariaCurrent = "page" 122 + } 123 + }) 83 124 </script> 84 125 {{ end }}
+19
input.css
··· 89 89 @apply no-underline text-black hover:underline hover:text-gray-800 dark:text-white dark:hover:text-gray-300; 90 90 } 91 91 92 + #navigation-menu-popover li:not(:last-of-type) a { 93 + @apply flex gap-2 items-center px-2 pb-2 pt-1.5 rounded-sm hover:bg-green-50 hover:text-green-700 no-underline 94 + } 95 + 96 + a[hx-post="/logout"] { 97 + @apply no-underline; 98 + } 99 + 92 100 label { 93 101 @apply block text-gray-900 text-sm font-bold py-2 uppercase dark:text-gray-100; 94 102 } ··· 962 970 color: #f9fafb; 963 971 } 964 972 } 973 + 974 + .site-navigation-dropdown-trigger { 975 + anchor-name: --dropdown-trigger; 976 + } 977 + 978 + .site-navigation-popover { 979 + margin: 0; 980 + inset: auto; 981 + position-anchor: --dropdown-trigger; 982 + position-area: bottom left; 983 + }