diff --git a/apps/web/app/detections/[detectionId]/page.tsx b/apps/web/app/detections/[detectionId]/page.tsx index c8f171d..e767375 100644 --- a/apps/web/app/detections/[detectionId]/page.tsx +++ b/apps/web/app/detections/[detectionId]/page.tsx @@ -216,8 +216,11 @@ function EvidenceTimeline({ evidenceItems }: { evidenceItems: EvidenceItem[] }) title="No evidence available" /> ) : ( -
    - {evidenceItems.map((item) => ( +
      + {evidenceItems.map((item, index) => (
    1. +

      + Evidence item {index + 1} of {evidenceItems.length}, ordered + by timestamp. +

      {item.description}

      diff --git a/apps/web/app/globals.css b/apps/web/app/globals.css index f32a0f0..01c3dfb 100644 --- a/apps/web/app/globals.css +++ b/apps/web/app/globals.css @@ -41,6 +41,25 @@ a { min-height: 100vh; } +.skip-link { + position: absolute; + z-index: 20; + top: 10px; + left: 10px; + transform: translateY(-140%); + border: 2px solid var(--accent-strong); + border-radius: 7px; + background: var(--surface); + color: var(--accent-strong); + font-weight: 760; + padding: 10px 12px; +} + +.skip-link:focus-visible { + transform: translateY(0); + outline: 3px solid #cfe7db; +} + .site-header { border-bottom: 1px solid var(--border); background: rgb(255 255 255 / 92%); @@ -59,6 +78,12 @@ a { .brand { display: grid; gap: 2px; + border-radius: 7px; +} + +.brand:focus-visible { + outline: 3px solid #cfe7db; + outline-offset: 4px; } .brand-name { @@ -93,7 +118,8 @@ a { .nav-link:focus-visible { border-color: var(--border); background: var(--surface-muted); - outline: none; + outline: 3px solid #cfe7db; + outline-offset: 2px; } .page-shell { @@ -102,6 +128,23 @@ a { padding: 28px 0 48px; } +.page-shell:focus-visible { + outline: 3px solid #cfe7db; + outline-offset: 6px; +} + +.sr-only { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; + padding: 0; +} + .hero { display: grid; grid-template-columns: minmax(0, 1.4fr) minmax(280px, 0.8fr); @@ -339,7 +382,8 @@ h3 { .primary-action:hover, .primary-action:focus-visible { background: var(--accent-strong); - outline: none; + outline: 3px solid #cfe7db; + outline-offset: 2px; } .secondary-action { @@ -350,7 +394,8 @@ h3 { .secondary-action:hover, .secondary-action:focus-visible { background: #edf6f1; - outline: none; + outline: 3px solid #cfe7db; + outline-offset: 2px; } .overview-grid { @@ -547,7 +592,8 @@ h3 { .back-link:hover, .back-link:focus-visible { color: var(--accent-strong); - outline: none; + outline: 3px solid #cfe7db; + outline-offset: 3px; } .content-panel { @@ -899,6 +945,13 @@ h3 { gap: 14px; } +.form-help { + margin-bottom: 0; + color: var(--muted); + font-size: 0.9rem; + line-height: 1.5; +} + .review-form div { display: grid; gap: 7px; @@ -926,7 +979,8 @@ h3 { .review-form input:focus, .review-form textarea:focus { border-color: var(--accent); - outline: 3px solid #dceee6; + outline: 3px solid #cfe7db; + outline-offset: 1px; } .review-actions { @@ -963,7 +1017,22 @@ h3 { } .review-actions button:focus-visible { - outline: 3px solid #dceee6; + outline: 3px solid #cfe7db; + outline-offset: 2px; +} + +.submit-status { + min-height: 1.25em; + color: var(--muted); + font-size: 0.9rem; + font-weight: 650; + line-height: 1.4; +} + +.draft-copy button:focus-visible, +.review-actions button:focus-visible { + outline: 3px solid #cfe7db; + outline-offset: 2px; } .decision-result { diff --git a/apps/web/app/layout.tsx b/apps/web/app/layout.tsx index 4252727..653cb5e 100644 --- a/apps/web/app/layout.tsx +++ b/apps/web/app/layout.tsx @@ -22,6 +22,9 @@ export default function RootLayout({ children }: { children: ReactNode }) {
      + + Skip to main content +
      @@ -38,7 +41,9 @@ export default function RootLayout({ children }: { children: ReactNode }) {
      -
      {children}
      +
      + {children} +
      diff --git a/apps/web/app/recommendations/recommendation-review-panel.tsx b/apps/web/app/recommendations/recommendation-review-panel.tsx index ae84b74..c84d229 100644 --- a/apps/web/app/recommendations/recommendation-review-panel.tsx +++ b/apps/web/app/recommendations/recommendation-review-panel.tsx @@ -33,6 +33,7 @@ export function RecommendationReviewPanel({ const canSubmit = reviewer.trim().length > 0 && reason.trim().length > 0 && submittingAction === null; + const isSubmitting = submittingAction !== null; async function submitDecision(action: DecisionAction) { setSubmittingAction(action); @@ -96,10 +97,20 @@ export function RecommendationReviewPanel({
      {formatEvidenceIds(recommendation.evidence_ids)}
      -
      + +

      + Enter a reviewer and decision reason before approving, rejecting, or + deferring this simulator-backed recommendation. +

      setReviewer(event.target.value)} @@ -111,6 +122,7 @@ export function RecommendationReviewPanel({