Skip to content

Commit 884037d

Browse files
committed
chore: add aria-expanded correctly
1 parent 608df54 commit 884037d

File tree

1 file changed

+24
-8
lines changed

1 file changed

+24
-8
lines changed

app/pages/about.vue

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -81,12 +81,13 @@ function getAriaLabel(c: GitHubContributor): string {
8181
})
8282
}
8383
84-
// --- Popover Logic (Global Single Instance with Event Delegation + Direct DOM Manipulation) ---
84+
// --- Popover Logic (Global Single Instance with Event Delegation) ---
85+
// We use a single global popover instance for performance (especially in Firefox with many items).
86+
// Event delegation on the list handles interactions, avoiding listeners on every item.
8587
const activeContributor = shallowRef<GitHubContributor>()
8688
const popoverPos = reactive({ top: 0, left: 0, align: 'center' as 'left' | 'center' | 'right' })
87-
const panelRef = ref<HTMLElement>()
89+
const panelRef = useTemplateRef<HTMLElement>('panelRef')
8890
const activeBtnEl = shallowRef<HTMLElement>()
89-
let activeBtnDom: HTMLElement | null = null // Direct DOM reference for performance
9091
let closeTimer: ReturnType<typeof setTimeout> | undefined
9192
let lastOpenTime = 0
9293
@@ -102,8 +103,10 @@ function computePos(btn: HTMLElement) {
102103
const vw = window.innerWidth
103104
const POP_W = 256
104105
const GAP = 8
106+
105107
popoverPos.top = r.bottom + GAP
106108
const center = r.left + r.width / 2
109+
107110
if (center - POP_W / 2 < GAP) {
108111
popoverPos.align = 'left'
109112
popoverPos.left = r.left
@@ -116,6 +119,7 @@ function computePos(btn: HTMLElement) {
116119
}
117120
}
118121
122+
// DON'T MOVE aria-expanded to the template, Firefox performance issues
119123
function setActiveBtnExpanded(btn: HTMLElement | null, value: boolean) {
120124
if (activeBtnDom && activeBtnDom !== btn) {
121125
activeBtnDom.removeAttribute('aria-expanded')
@@ -166,7 +170,7 @@ function onListMouseEnter(e: MouseEvent) {
166170
}
167171
168172
function onListMouseLeave(e: MouseEvent) {
169-
// Solo cerrar si salimos del <ul> completamente
173+
// only close if we exist >ul>
170174
const related = e.relatedTarget as Element | null
171175
if (related?.closest('[data-popover-panel]')) return
172176
if (!related?.closest('button[data-cid]')) scheduleCloseActive()
@@ -187,13 +191,13 @@ function onListClick(e: MouseEvent) {
187191
}
188192
}
189193
190-
// ── Panel mouse events ───────────────────────────────────────────────────────
194+
// Panel mouse events
191195
function onPanelMouseLeave(e: MouseEvent) {
192196
const related = e.relatedTarget as Element | null
193197
if (!related?.closest('button[data-cid]')) scheduleCloseActive()
194198
}
195199
196-
// ── Tab management dentro del panel (foco manual) ───────────────────────────
200+
// Tab management inside the panel (manual focus)
197201
function onPanelKeydown(e: KeyboardEvent) {
198202
if (e.key !== 'Tab' || !panelRef.value) return
199203
const focusables = [...panelRef.value.querySelectorAll<HTMLElement>('a[href]')]
@@ -234,7 +238,7 @@ function onPanelKeydown(e: KeyboardEvent) {
234238
}
235239
}
236240
237-
// ── Document listeners ───────────────────────────────────────────────────────
241+
// Document listeners
238242
function onDocumentPointerDown(e: PointerEvent) {
239243
if (!activeContributor.value) return
240244
const t = e.target as Element
@@ -252,6 +256,8 @@ function onDocumentKeydown(e: KeyboardEvent) {
252256
}
253257
}
254258
259+
let activeBtnDom: HTMLElement | null = null
260+
255261
onMounted(() => {
256262
document.addEventListener('pointerdown', onDocumentPointerDown)
257263
document.addEventListener('keydown', onDocumentKeydown)
@@ -422,7 +428,6 @@ onBeforeUnmount(() => {
422428
</div>
423429
<ul
424430
v-else-if="contributors.length"
425-
ref="listRef"
426431
class="flex flex-wrap justify-center gap-2 list-none p-0 overflow-visible"
427432
@mouseover="onListMouseEnter"
428433
@mouseout="onListMouseLeave"
@@ -615,6 +620,10 @@ onBeforeUnmount(() => {
615620
616621
[data-cid][aria-expanded='true'] img {
617622
@apply ring-2 ring-accent;
623+
transform: scale(1.1);
624+
--un-ring-opacity: 1;
625+
--un-ring-color: color-mix(in srgb, var(--accent) var(--un-ring-opacity), transparent);
626+
box-shadow: 0 0 0 2px var(--un-ring-color);
618627
}
619628
620629
@media (hover: hover) {
@@ -627,6 +636,8 @@ onBeforeUnmount(() => {
627636
}
628637
}
629638
639+
/* Capture tap/click (focus without keyboard navigation => for chrome tap) */
640+
[data-cid]:focus:not(:focus-visible) img,
630641
[data-cid]:focus-visible img {
631642
transform: scale(1.1);
632643
--un-ring-opacity: 1;
@@ -639,6 +650,11 @@ onBeforeUnmount(() => {
639650
z-index: 20;
640651
}
641652
653+
/* FF popup outline */
654+
.contributor-popover:focus {
655+
outline: none;
656+
}
657+
642658
.contributor-popover {
643659
position: fixed;
644660
z-index: 9999;

0 commit comments

Comments
 (0)