feat(web): harden shell keyboard navigation
This commit is contained in:
parent
756714e67c
commit
c3831176a6
@ -35,7 +35,7 @@ Stack: Next.js 16 + React 19 + TypeScript
|
||||
- [x] Artifact/attachment UI
|
||||
- [x] Task extraction/review UI
|
||||
- [x] Workspace filters and saved views
|
||||
- [ ] Keyboard navigation improvements
|
||||
- [x] Keyboard navigation improvements
|
||||
- [ ] Dense knowledge UX polish
|
||||
|
||||
# Phase W3 — Agent UX
|
||||
@ -103,6 +103,11 @@ Stack: Next.js 16 + React 19 + TypeScript
|
||||
- denser search result rows with status/owner/workspace metadata
|
||||
- operator workflow summary cards on dashboard and reviews
|
||||
- workspace owner visibility for denser knowledge navigation
|
||||
- Hardened shared accessibility/keyboard affordances with:
|
||||
- skip-to-content support in the shared shell
|
||||
- stronger focus-visible treatment for interactive controls
|
||||
- clearer active-nav semantics via `aria-current`
|
||||
- keyboard/accessibility guidance surfaced in navigation/settings
|
||||
|
||||
# Open Questions
|
||||
|
||||
@ -133,7 +138,7 @@ Stack: Next.js 16 + React 19 + TypeScript
|
||||
- Artifact upload/download UX
|
||||
- Extraction-backed task review flows
|
||||
- Backend-backed agent activity timeline, approval queue, proposal diff review, and audit filtering
|
||||
- Dense keyboard navigation, accessibility hardening, and performance polish
|
||||
- Remaining dense/accessibility polish and performance hardening
|
||||
- Remaining verification:
|
||||
- run `npm test`
|
||||
|
||||
|
||||
@ -14,15 +14,26 @@ export function AppShell({
|
||||
}) {
|
||||
return (
|
||||
<div className="app-shell">
|
||||
<a href="#main-content" className="skip-link">
|
||||
Skip to main content
|
||||
</a>
|
||||
<Sidebar />
|
||||
<main className="main-panel">
|
||||
<main id="main-content" className="main-panel" tabIndex={-1} aria-labelledby="page-title">
|
||||
<div className="page-grid">
|
||||
<header className="surface-card" style={{ padding: "var(--ml-space-6)", display: "flex", justifyContent: "space-between", gap: "var(--ml-space-4)", alignItems: "start", flexWrap: "wrap" }}>
|
||||
<header
|
||||
className="surface-card"
|
||||
style={{ padding: "var(--ml-space-6)", display: "flex", justifyContent: "space-between", gap: "var(--ml-space-4)", alignItems: "start", flexWrap: "wrap" }}
|
||||
>
|
||||
<div style={{ display: "grid", gap: "var(--ml-space-2)" }}>
|
||||
<div style={{ fontFamily: "var(--ml-font-display)", fontSize: "var(--ml-fs-2xl)", fontWeight: 700 }}>{title}</div>
|
||||
<h1
|
||||
id="page-title"
|
||||
style={{ margin: 0, fontFamily: "var(--ml-font-display)", fontSize: "var(--ml-fs-2xl)", fontWeight: 700 }}
|
||||
>
|
||||
{title}
|
||||
</h1>
|
||||
<div style={{ color: "var(--ml-text-secondary)", maxWidth: 720 }}>{description}</div>
|
||||
</div>
|
||||
{actions ? <div>{actions}</div> : null}
|
||||
{actions ? <div aria-label="Page actions">{actions}</div> : null}
|
||||
</header>
|
||||
{children}
|
||||
</div>
|
||||
|
||||
@ -18,7 +18,7 @@ export function Sidebar() {
|
||||
const pathname = usePathname();
|
||||
|
||||
return (
|
||||
<aside className="sidebar" style={{ padding: "var(--ml-space-6)" }}>
|
||||
<aside className="sidebar" style={{ padding: "var(--ml-space-6)" }} aria-label="Primary">
|
||||
<div style={{ display: "grid", gap: "var(--ml-space-6)" }}>
|
||||
<div className="surface-card" style={{ padding: "var(--ml-space-5)", display: "grid", gap: "var(--ml-space-3)" }}>
|
||||
<div className="badge" style={{ width: "fit-content" }}>
|
||||
@ -33,7 +33,7 @@ export function Sidebar() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav style={{ display: "grid", gap: "var(--ml-space-2)" }}>
|
||||
<nav aria-label="Primary navigation" style={{ display: "grid", gap: "var(--ml-space-2)" }}>
|
||||
{navItems.map((item) => {
|
||||
const Icon = item.icon;
|
||||
const active = pathname === item.href || pathname.startsWith(`${item.href}/`);
|
||||
@ -41,7 +41,8 @@ export function Sidebar() {
|
||||
<Link
|
||||
key={item.href}
|
||||
href={item.href}
|
||||
className="surface-muted"
|
||||
className="surface-muted nav-link"
|
||||
aria-current={active ? "page" : undefined}
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
@ -57,6 +58,12 @@ export function Sidebar() {
|
||||
);
|
||||
})}
|
||||
</nav>
|
||||
|
||||
<div className="surface-card" style={{ padding: "var(--ml-space-5)", display: "grid", gap: "var(--ml-space-2)" }}>
|
||||
<strong>Keyboard flow</strong>
|
||||
<span style={{ color: "var(--ml-text-secondary)" }}>Use Tab to move between navigation, filters, and dense result surfaces.</span>
|
||||
<span style={{ color: "var(--ml-text-secondary)" }}>Use the skip link to jump directly into page content.</span>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user