feat(web): NavDrawer 补 aria-controls + ForeshadowCard 语义化 dl/dt/dd(a11y)

NavDrawer 汉堡按钮补 aria-controls 指向抽屉面板(Drawer 新增可选 id prop,默认 nav-drawer);ForeshadowCard 的 <dl> 裸 <div> 行改真 <dt>/<dd> 结构,可视文案不变,importance 用 sr-only 标签。行为不变。
This commit is contained in:
Yaojia Wang
2026-07-08 13:27:32 +02:00
parent 1bda024141
commit 47d5558e4a
3 changed files with 25 additions and 3 deletions

View File

@@ -16,6 +16,8 @@ interface DrawerProps {
side?: "left" | "right"; side?: "left" | "right";
// 无障碍标签role=dialog // 无障碍标签role=dialog
label: string; label: string;
// 抽屉面板 id供触发按钮 aria-controls 指向(无障碍关联)。
id?: string;
triggerRef?: RefObject<HTMLElement | null>; triggerRef?: RefObject<HTMLElement | null>;
children: ReactNode; children: ReactNode;
} }
@@ -27,6 +29,7 @@ export function Drawer({
onClose, onClose,
side = "left", side = "left",
label, label,
id,
triggerRef, triggerRef,
children, children,
}: DrawerProps) { }: DrawerProps) {
@@ -58,6 +61,7 @@ export function Drawer({
<div className={`${overlayScrim} lg:hidden`} onClick={onClose}> <div className={`${overlayScrim} lg:hidden`} onClick={onClose}>
<div <div
ref={panelRef} ref={panelRef}
id={id}
role="dialog" role="dialog"
aria-modal="true" aria-modal="true"
aria-label={label} aria-label={label}

View File

@@ -15,6 +15,9 @@ interface NavDrawerProps {
activeNav?: ActiveNav; activeNav?: ActiveNav;
} }
// 抽屉面板 id供汉堡按钮 aria-controls 关联(无障碍)。
const DRAWER_ID = "nav-drawer";
// 移动端导航:汉堡按钮 + 左侧抽屉(仅 <lg桌面用 LeftNav 静态侧栏。UX §5.1。 // 移动端导航:汉堡按钮 + 左侧抽屉(仅 <lg桌面用 LeftNav 静态侧栏。UX §5.1。
export function NavDrawer({ projectId, activeNav }: NavDrawerProps) { export function NavDrawer({ projectId, activeNav }: NavDrawerProps) {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
@@ -32,6 +35,7 @@ export function NavDrawer({ projectId, activeNav }: NavDrawerProps) {
onClick={() => setOpen(true)} onClick={() => setOpen(true)}
aria-label="打开导航" aria-label="打开导航"
aria-expanded={open} aria-expanded={open}
aria-controls={DRAWER_ID}
variant="ghost" variant="ghost"
size="icon" size="icon"
className="-ml-2 lg:hidden" className="-ml-2 lg:hidden"
@@ -43,6 +47,7 @@ export function NavDrawer({ projectId, activeNav }: NavDrawerProps) {
onClose={() => setOpen(false)} onClose={() => setOpen(false)}
side="left" side="left"
label="主导航" label="主导航"
id={DRAWER_ID}
triggerRef={triggerRef} triggerRef={triggerRef}
> >
<div className="px-2 pb-2"> <div className="px-2 pb-2">

View File

@@ -58,10 +58,23 @@ export function ForeshadowCard({
<dl className="mt-2 space-y-0.5 text-xs text-ink-soft"> <dl className="mt-2 space-y-0.5 text-xs text-ink-soft">
{typeof item.planted_at === "number" ? ( {typeof item.planted_at === "number" ? (
<div> {item.planted_at} </div> <div className="flex gap-1">
<dt></dt>
<dd>{item.planted_at} </dd>
</div>
) : null}
{windowText ? (
<div className="flex gap-1">
<dt></dt>
<dd>{windowText}</dd>
</div>
) : null}
{item.importance ? (
<div className="flex gap-1">
<dt className="sr-only"></dt>
<dd>{item.importance}</dd>
</div>
) : null} ) : null}
{windowText ? <div> {windowText}</div> : null}
{item.importance ? <div>{item.importance}</div> : null}
</dl> </dl>
{item.progress && item.progress.length > 0 ? ( {item.progress && item.progress.length > 0 ? (