Skip to content

Commit 9d96fd4

Browse files
committed
feat: Enhance focus and selection styles to improve accessibility and eliminate double indicators
1 parent 8476d25 commit 9d96fd4

File tree

5 files changed

+139
-21
lines changed

5 files changed

+139
-21
lines changed

src/components/Menu.scss

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,9 @@ $submenu-transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
213213
background-color: var(--hover-background);
214214
color: var(--text-selected-color);
215215
box-shadow: $hover-shadow;
216+
/* Enhanced selection styling that works better with focus states */
217+
position: relative;
218+
font-weight: 500;
216219

217220
.menu-item-icon {
218221
opacity: 1;

src/components/Menu.vue

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,22 @@ export default defineComponent({
247247
setActiveIndex(index);
248248
selectMenuItem(name, id, subMenu, false, props.onSelection);
249249
250+
// Direct style manipulation to remove any focus borders
251+
const target = event.currentTarget as HTMLElement;
252+
if (target) {
253+
// Forcibly remove any focus indicators
254+
target.style.outline = 'none';
255+
target.style.border = 'none';
256+
257+
// Apply a setTimeout to ensure this persists after any CSS transitions
258+
setTimeout(() => {
259+
if (target && target.classList.contains('selected')) {
260+
target.style.outline = 'none';
261+
target.style.border = 'none';
262+
}
263+
}, 10);
264+
}
265+
250266
// Announce selection for screen readers
251267
if (!subMenu && name) {
252268
// Create announcement for screen reader
@@ -294,8 +310,8 @@ export default defineComponent({
294310
const nextIndex = isDivider
295311
? actvIndex - 2
296312
: actvIndex - 1 < 0
297-
? len - 1
298-
: actvIndex - 1;
313+
? len - 1
314+
: actvIndex - 1;
299315
setActiveIndex(nextIndex);
300316
break;
301317
@@ -543,4 +559,23 @@ export default defineComponent({
543559
<style lang="scss" scoped>
544560
@use './Menu';
545561
@use './styles/accessibility';
562+
@use './styles/focused-items';
563+
564+
/* Direct fix for the Edit menu item and other focused items */
565+
.menu-list-item {
566+
&.selected,
567+
&.highlight {
568+
/* Remove all focus indicators when the item is already visually highlighted */
569+
outline: none !important;
570+
border: none !important;
571+
box-shadow: none !important;
572+
573+
&:focus,
574+
&:focus-visible {
575+
outline: none !important;
576+
border: none !important;
577+
box-shadow: none !important;
578+
}
579+
}
580+
}
546581
</style>

src/components/index.vue

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
@touchstart="handleEnhancedTouchStart"
1010
@touchend="handleEnhancedTouchEnd"
1111
@touchmove="handleEnhancedTouchMove"
12-
@click="toggleMenu"
1312
>
1413
<div
1514
ref="menuHead"
@@ -20,6 +19,7 @@
2019
aria-haspopup="menu"
2120
:aria-expanded="menuActive"
2221
aria-label="Menu"
22+
@click="toggleMenu"
2323
@keydown="handleKeyboardMenuActivation"
2424
>
2525
<span class="menu-head-icon">
@@ -288,7 +288,7 @@ export default defineComponent({
288288
});
289289
290290
// open/close the menu
291-
const toggleMenu = (event: MouseEvent | KeyboardEvent) => {
291+
const toggleMenu = (event: MouseEvent | KeyboardEvent | TouchEvent) => {
292292
measurePerformance('toggleMenu', () => {
293293
markAsUsed('menuToggle');
294294
@@ -521,11 +521,9 @@ export default defineComponent({
521521
const handleEnhancedTouchStart = (event: TouchEvent) => {
522522
handleTouchStart(event, (touchEvent) => {
523523
if (touchEvent.type === 'longpress') {
524-
// Long press opens menu and provides haptic feedback
524+
// Long press activates the menu (same as click/tap)
525525
triggerHapticFeedback('medium');
526-
if (!menuActive.value) {
527-
toggleMenu(event as any);
528-
}
526+
toggleMenu(event);
529527
}
530528
});
531529
@@ -541,8 +539,9 @@ export default defineComponent({
541539
const handleEnhancedTouchEnd = (event: TouchEvent) => {
542540
handleTouchEnd(event, (touchEvent) => {
543541
if (touchEvent.type === 'tap') {
544-
// Provide light haptic feedback for taps
542+
// Tap activates the menu (same as click)
545543
triggerHapticFeedback('light');
544+
toggleMenu(event);
546545
} else if (touchEvent.type === 'swipe') {
547546
// Handle swipe gestures
548547
const swipe = getSwipeDirection();
@@ -655,4 +654,24 @@ export default defineComponent({
655654
<style lang="scss">
656655
@use './index';
657656
@use './styles/accessibility';
657+
658+
/* Fix for double focus indicators */
659+
.menu-list-item {
660+
&.selected,
661+
&.highlight {
662+
outline: none !important;
663+
border: none !important;
664+
665+
&:focus,
666+
&:focus-visible {
667+
outline: none !important;
668+
border: none !important;
669+
}
670+
}
671+
672+
/* Only apply focus style on non-selected/non-highlighted items */
673+
&:not(.selected):not(.highlight):focus-visible {
674+
outline: 2px solid #007bff;
675+
}
676+
}
658677
</style>

src/components/styles/accessibility.scss

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,19 +21,27 @@
2121
border-radius: 50%;
2222
}
2323

24-
/* Menu list items focus indicator */
25-
.menu-list-item:focus-visible {
26-
outline: 2px solid #007bff;
27-
outline-offset: 2px;
28-
border-radius: 4px;
29-
}
30-
31-
/* General menu item focus (fallback and specific targeting) */
24+
/* General menu item focus - the primary focus indicator for menu items */
3225
[role='menuitem']:focus-visible {
33-
outline: 2px solid #007bff !important;
34-
outline-offset: -2px !important;
35-
position: relative;
36-
z-index: 1;
26+
/* Hide the outline when the menu item is selected or highlighted */
27+
&.selected, &.highlight {
28+
outline: none !important;
29+
box-shadow: none !important;
30+
border-color: transparent !important;
31+
}
32+
33+
/* Special case for selected submenu items to avoid double indicators */
34+
&.sub-menu.selected .menu-item-wrapper {
35+
background-color: var(--hover-background);
36+
}
37+
38+
/* Only show outline when using keyboard navigation and not selected */
39+
&:not(.selected):not(.highlight) {
40+
outline: 2px solid #007bff;
41+
outline-offset: -2px;
42+
position: relative;
43+
z-index: 1;
44+
}
3745
}
3846

3947
/* Hide outline for mouse users (focus without focus-visible) */
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/* Enhanced focus and selection styles to avoid duplicate indicators */
2+
3+
/* When the item is focused via keyboard and also selected, prioritize selection styling */
4+
.menu-list-item[role='menuitem']:focus-visible.selected {
5+
outline: none !important;
6+
7+
.menu-item-wrapper {
8+
/* Slightly enhance the background to indicate focus while selected */
9+
background-color: var(--hover-background);
10+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
11+
}
12+
}
13+
14+
/* Fix for the Edit button in the image - which has both highlight and possibly focus visible */
15+
.menu-list-item.highlight:focus-visible,
16+
.menu-list-item:focus-visible,
17+
li.menu-list-item[role="menuitem"]:focus-visible {
18+
outline: none !important;
19+
border-color: transparent !important;
20+
}
21+
22+
/* Ensure no borders on Edit button which has the specific highlight */
23+
.menu-list-item:has(.name:contains("Edit")):focus-visible {
24+
outline: none !important;
25+
border: none !important;
26+
box-shadow: none !important;
27+
}
28+
29+
/* Direct CSS override to remove the blue border completely */
30+
.menu-list-item {
31+
&:focus {
32+
outline: none !important;
33+
border-color: transparent !important;
34+
}
35+
36+
&:focus-visible {
37+
outline: none !important;
38+
border-color: transparent !important;
39+
}
40+
41+
/* Keep the focus style only for keyboard navigation on non-highlighted items */
42+
&:not(.selected):not(.highlight):focus-visible {
43+
outline: 2px solid #007bff;
44+
outline-offset: -2px;
45+
}
46+
}
47+
48+
/* Completely eliminate all double borders */
49+
.menu-wrapper [role="menuitem"].selected,
50+
.menu-wrapper [role="menuitem"].highlight {
51+
border: none !important;
52+
outline: none !important;
53+
}

0 commit comments

Comments
 (0)