diff --git a/src/components/Layout.tsx b/src/components/Layout.tsx
index 90b02d4..92d570f 100644
--- a/src/components/Layout.tsx
+++ b/src/components/Layout.tsx
@@ -1,6 +1,7 @@
import { Outlet } from "react-router-dom";
import { useIsFetching } from "@tanstack/react-query";
import { Sidebar } from "./Sidebar";
+import { SkipLink } from "@/components/ui/skip-link";
export function Layout() {
// useIsFetching() returns the number of in-flight queries and is 0 on
@@ -11,6 +12,9 @@ export function Layout() {
return (
+ {/* Skip link — first Tab stop, jumps past the sidebar.
+ Hidden until focused (see .skip-link in index.css). */}
+
{/*
Scan element — always in the DOM (no layout shift), 1px tall, full
width. pointer-events-none so it never blocks clicks. The inner
@@ -28,7 +32,11 @@ export function Layout() {
/>
-
+
diff --git a/src/components/ui/badge.tsx b/src/components/ui/badge.tsx
index 7f2526c..f9b0acc 100644
--- a/src/components/ui/badge.tsx
+++ b/src/components/ui/badge.tsx
@@ -3,7 +3,7 @@ import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";
const badgeVariants = cva(
- "inline-flex items-center gap-1.5 rounded-full border px-2.5 py-0.5 text-xs font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:ring-offset-background",
+ "inline-flex items-center gap-1.5 rounded-full border px-2.5 py-0.5 text-xs font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
{
variants: {
variant: {
diff --git a/src/components/ui/claim-state-badge.tsx b/src/components/ui/claim-state-badge.tsx
index e4832f0..4d47314 100644
--- a/src/components/ui/claim-state-badge.tsx
+++ b/src/components/ui/claim-state-badge.tsx
@@ -75,7 +75,7 @@ export function ClaimStateBadge({
className,
)}
>
-
+
{cfg.label}
);
diff --git a/src/components/ui/dialog.tsx b/src/components/ui/dialog.tsx
index 91ffca3..a74d63d 100644
--- a/src/components/ui/dialog.tsx
+++ b/src/components/ui/dialog.tsx
@@ -38,8 +38,11 @@ const DialogContent = React.forwardRef<
{...props}
>
{children}
-
-
+
+
Close
diff --git a/src/components/ui/select.tsx b/src/components/ui/select.tsx
index 124ba90..074bdba 100644
--- a/src/components/ui/select.tsx
+++ b/src/components/ui/select.tsx
@@ -14,7 +14,7 @@ const SelectTrigger = React.forwardRef<
span]:line-clamp-1",
+ "flex h-9 w-full items-center justify-between rounded-md border border-input bg-background/60 px-3 py-1 text-sm shadow-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
className
)}
{...props}
@@ -64,7 +64,7 @@ const SelectItem = React.forwardRef<
& {
+ /** id of the element the link should jump to (target of the focus). */
+ targetId?: string;
+};
+
+/**
+ * "Skip to main content" link.
+ *
+ * Renders off-screen by default so it does not intrude on the visual
+ * design, and slides into view when it receives keyboard focus. Used at
+ * the very top of so keyboard users can bypass the sidebar and
+ * the global scan element on the first Tab.
+ *
+ * Voice: instrument-label eyebrow caps. Same hairline surface as the
+ * sidebar so it feels native when it appears.
+ */
+export function SkipLink({
+ targetId = "main-content",
+ className,
+ children = "Skip to main content",
+ ...props
+}: SkipLinkProps) {
+ return (
+
+ {children}
+
+ );
+}
\ No newline at end of file
diff --git a/src/components/ui/table.tsx b/src/components/ui/table.tsx
index 194f656..4de0695 100644
--- a/src/components/ui/table.tsx
+++ b/src/components/ui/table.tsx
@@ -45,7 +45,7 @@ const TableRow = React.forwardRef<
->(({ className, ...props }, ref) => (
+>(({ className, scope = "col", ...props }, ref) => (
|