Skip to content

Commit 0c8a1ac

Browse files
committed
fix(webapp): keep collapsed side menu section items out of the tab order
When a side menu section is collapsed its items are visually hidden (height 0) but stayed in the DOM, so keyboard and screen-reader users could still focus them. Mark the collapsed content inert so it is removed from both the tab order and the accessibility tree, restoring it when the section expands.
1 parent fc60f13 commit 0c8a1ac

1 file changed

Lines changed: 14 additions & 1 deletion

File tree

apps/webapp/app/components/navigation/SideMenuSection.tsx

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { AnimatePresence, motion } from "framer-motion";
2-
import React, { useCallback, useState } from "react";
2+
import React, { useCallback, useEffect, useRef, useState } from "react";
33
import { ToggleArrowIcon } from "~/assets/icons/ToggleArrowIcon";
44

55
type Props = {
@@ -27,13 +27,25 @@ export function SideMenuSection({
2727
headerAction,
2828
}: Props) {
2929
const [isCollapsed, setIsCollapsed] = useState(initialCollapsed);
30+
const contentRef = useRef<HTMLDivElement>(null);
3031

3132
const handleToggle = useCallback(() => {
3233
const newIsCollapsed = !isCollapsed;
3334
setIsCollapsed(newIsCollapsed);
3435
onCollapseToggle?.(newIsCollapsed);
3536
}, [isCollapsed, onCollapseToggle]);
3637

38+
// When the section is collapsed its items are visually hidden (height 0) but stay in the DOM for
39+
// the animation, so remove them from the tab order and the accessibility tree with `inert` —
40+
// otherwise keyboard/screen-reader users can focus invisible items. `inert` doesn't affect
41+
// layout, so the height animation is unchanged. Set the DOM property directly since React 18's
42+
// handling of the `inert` prop is unreliable.
43+
useEffect(() => {
44+
if (contentRef.current) {
45+
contentRef.current.inert = isCollapsed;
46+
}
47+
}, [isCollapsed]);
48+
3749
return (
3850
<div className="w-full overflow-hidden">
3951
{/* Header container - stays in DOM to preserve height */}
@@ -81,6 +93,7 @@ export function SideMenuSection({
8193
</div>
8294
<AnimatePresence initial={false}>
8395
<motion.div
96+
ref={contentRef}
8497
className="w-full"
8598
initial={isCollapsed ? "collapsed" : "expanded"}
8699
animate={isCollapsed ? "collapsed" : "expanded"}

0 commit comments

Comments
 (0)