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:
@@ -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}
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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 ? (
|
||||||
|
|||||||
Reference in New Issue
Block a user