Spec covers the --c-focus-ring token definition, full audit of all 19 affected files, WCAG 2.4.11 analysis, element-by-element mockups (light and dark), and exact CSS/Tailwind diffs ready for implementation. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1153 lines
64 KiB
HTML
1153 lines
64 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Focus Rings — Design Spec · Familienarchiv</title>
|
||
<style>
|
||
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
|
||
body{font-family:'Helvetica Neue',Arial,sans-serif;background:#ECEAE4;color:#1A1A1A;line-height:1.5}
|
||
.doc{max-width:1440px;margin:0 auto;padding:48px 32px}
|
||
|
||
/* ── Masthead ─── */
|
||
.mast{background:#012851;border-radius:10px;padding:32px 40px;margin-bottom:48px}
|
||
.mast-top{display:flex;align-items:flex-start;justify-content:space-between;gap:24px;margin-bottom:16px}
|
||
.mast h1{font-size:22px;font-weight:900;color:#fff;letter-spacing:-.4px;margin-bottom:6px}
|
||
.mast p{font-size:12px;color:rgba(255,255,255,.55);max-width:680px;line-height:1.7}
|
||
.mast-badge{font-size:9px;font-weight:800;padding:3px 9px;border-radius:20px;text-transform:uppercase;letter-spacing:.8px;white-space:nowrap;flex-shrink:0;margin-top:4px}
|
||
.mb-spec{background:#a1dcd8;color:#012851}
|
||
.decisions{display:grid;grid-template-columns:repeat(4,1fr);gap:10px;margin-top:20px;border-top:1px solid rgba(255,255,255,.12);padding-top:16px}
|
||
.dec{background:rgba(255,255,255,.07);border-radius:6px;padding:10px 12px}
|
||
.dec-label{font-size:7px;font-weight:800;text-transform:uppercase;letter-spacing:.8px;color:rgba(255,255,255,.35);margin-bottom:5px}
|
||
.dec-value{font-size:9.5px;font-weight:700;color:#fff;line-height:1.5}
|
||
|
||
/* ── Section headings ─── */
|
||
.sec{margin-bottom:64px}
|
||
.sec+.sec{border-top:2px dashed #C8C4BE;padding-top:56px}
|
||
.sec-h{font-size:11px;font-weight:800;text-transform:uppercase;letter-spacing:1.2px;color:#888;margin-bottom:20px;display:flex;align-items:center;gap:10px}
|
||
.sec-h::after{content:'';flex:1;height:1px;background:#D8D4CE}
|
||
.sec-num{background:#012851;color:#fff;font-size:9px;font-weight:900;padding:2px 7px;border-radius:10px}
|
||
|
||
/* ── Layout helpers ─── */
|
||
.sg{display:grid;gap:20px;align-items:start}
|
||
.sg-2{grid-template-columns:1fr 1fr}
|
||
.sg-3{grid-template-columns:1fr 1fr 1fr}
|
||
.sb{display:flex;flex-direction:column}
|
||
.sl{font-size:9px;font-weight:800;color:#888;text-transform:uppercase;letter-spacing:1.5px;margin-bottom:6px;display:flex;align-items:center;gap:6px}
|
||
.sc{font-size:8.5px;color:#888;margin-top:6px;font-style:italic;line-height:1.5}
|
||
.mode-label{display:inline-block;font-size:7.5px;font-weight:800;text-transform:uppercase;letter-spacing:.8px;padding:2px 8px;border-radius:10px;margin-bottom:8px}
|
||
.lm{background:#E8F4F8;color:#012851;border:1px solid #a1dcd8}
|
||
.dm{background:#012851;color:#a1dcd8;border:1px solid #0d3358}
|
||
|
||
/* ── Issue callouts ─── */
|
||
.issue{background:#FFF7ED;border:1px solid #FDBA74;border-radius:8px;padding:16px 20px;margin-bottom:12px}
|
||
.issue-id{font-size:8px;font-weight:800;text-transform:uppercase;letter-spacing:.8px;color:#C2410C;margin-bottom:4px}
|
||
.issue-title{font-size:13px;font-weight:700;color:#1A1A1A;margin-bottom:6px}
|
||
.issue-body{font-size:11px;color:#7C2D12;line-height:1.6}
|
||
.issue-body code{background:rgba(0,0,0,.06);border-radius:3px;padding:1px 5px;font-size:10px;font-family:monospace}
|
||
.issue-fix{background:#F0FDF4;border:1px solid #86EFAC;border-radius:6px;padding:10px 14px;margin-top:10px;font-size:11px;color:#14532D;line-height:1.6}
|
||
.issue-fix strong{font-weight:800}
|
||
.issue-fix code{background:rgba(0,0,0,.06);border-radius:3px;padding:1px 5px;font-size:10px;font-family:monospace}
|
||
|
||
/* ── Token table ─── */
|
||
.tok-table{width:100%;border-collapse:collapse;font-size:10px}
|
||
.tok-table th{text-align:left;font-size:8px;font-weight:800;text-transform:uppercase;letter-spacing:.8px;color:#888;padding:6px 8px;border-bottom:2px solid #D8D4CE}
|
||
.tok-table td{padding:7px 8px;border-bottom:1px solid #E8E4DF;vertical-align:middle}
|
||
.tok-table tr:hover td{background:rgba(0,0,0,.025)}
|
||
.swatch{display:inline-block;width:20px;height:20px;border-radius:4px;border:1px solid rgba(0,0,0,.12);vertical-align:middle;margin-right:6px;flex-shrink:0}
|
||
.swatch-pair{display:flex;align-items:center;gap:6px}
|
||
.hex{font-family:monospace;font-size:10px}
|
||
.tag-bad{display:inline-block;font-size:7.5px;font-weight:800;text-transform:uppercase;letter-spacing:.6px;padding:1px 5px;border-radius:3px;background:#FEE2E2;color:#991B1B}
|
||
.tag-ok{display:inline-block;font-size:7.5px;font-weight:800;text-transform:uppercase;letter-spacing:.6px;padding:1px 5px;border-radius:3px;background:#D1FAE5;color:#065F46}
|
||
.tag-new{display:inline-block;font-size:7.5px;font-weight:800;text-transform:uppercase;letter-spacing:.6px;padding:1px 5px;border-radius:3px;background:#DBEAFE;color:#1E3A5F}
|
||
|
||
/* ── Spec disclaimer ─── */
|
||
.spec-disclaimer{background:#FFF8E1;border:1.5px solid #FFC107;border-radius:6px;padding:11px 16px;font-size:11px;color:#6D4C00;margin-bottom:32px;line-height:1.6}
|
||
.spec-disclaimer strong{font-weight:800}
|
||
|
||
/* ── Agent Implementation Reference ─── */
|
||
.impl-ref{background:#0d1117;border-radius:8px;margin-top:20px;overflow:hidden;border:1px solid #30363d}
|
||
.impl-ref-hdr{background:#161b22;padding:9px 16px;font-size:9.5px;font-weight:800;color:#f0883e;border-bottom:1px solid #30363d;display:flex;align-items:center;gap:8px;letter-spacing:.4px;text-transform:uppercase}
|
||
.impl-ref-hdr::before{content:'⚙';font-size:12px}
|
||
.impl-ref-hdr span{color:rgba(240,136,62,.55);font-weight:400;margin-left:auto;font-size:9px;text-transform:none;letter-spacing:0}
|
||
.impl-ref table{width:100%;border-collapse:collapse;font-size:10px}
|
||
.impl-ref th{text-align:left;font-size:8px;font-weight:800;text-transform:uppercase;letter-spacing:.8px;color:#8b949e;padding:8px 14px;border-bottom:1px solid #21262d}
|
||
.impl-ref td{padding:6px 14px;border-bottom:1px solid #161b22;vertical-align:top;line-height:1.6;color:#c9d1d9}
|
||
.impl-ref tr:last-child td{border-bottom:none}
|
||
.impl-ref td:first-child{color:#79c0ff;font-weight:700;white-space:nowrap;width:200px}
|
||
.impl-ref td code{font-family:'SFMono-Regular',Consolas,monospace;font-size:9.5px;background:#161b22;color:#a5d6ff;padding:1px 5px;border-radius:3px;white-space:nowrap}
|
||
.impl-ref .ir-px{color:#7ee787;font-family:monospace;font-size:9.5px}
|
||
|
||
/* ── CSS diff block ─── */
|
||
.diff-block{background:#0d1117;border-radius:8px;overflow:hidden;border:1px solid #30363d;margin-top:20px}
|
||
.diff-block-hdr{background:#161b22;padding:8px 14px;font-size:9px;font-weight:800;color:#8b949e;border-bottom:1px solid #30363d;text-transform:uppercase;letter-spacing:.6px;font-family:'SFMono-Regular',Consolas,monospace}
|
||
.diff-block pre{padding:14px;font-size:10px;line-height:1.7;color:#c9d1d9;font-family:'SFMono-Regular',Consolas,monospace;overflow-x:auto;white-space:pre}
|
||
.diff-add{color:#7ee787}
|
||
.diff-remove{color:#f85149}
|
||
.diff-context{color:#8b949e}
|
||
.diff-file{color:#f0883e;font-weight:700}
|
||
|
||
/* ── Visual mockup frames ─── */
|
||
.mock-frame{border-radius:8px;overflow:hidden;border:1.5px solid #B8B4AE}
|
||
.mock-lm{background:#f0efe9}
|
||
.mock-dm{background:#010e1e;border-color:#0d3358}
|
||
.mock-body{padding:16px 18px;display:flex;flex-direction:column;gap:12px}
|
||
.mock-label{font-size:8px;font-weight:800;text-transform:uppercase;letter-spacing:.8px;margin-bottom:4px}
|
||
.mock-lm .mock-label{color:#6b7280}
|
||
.mock-dm .mock-label{color:#8b97a5}
|
||
|
||
/* ── Light mode focus demos ─── */
|
||
.demo-input-lm{
|
||
width:100%;height:24px;border-radius:3px;border:1.5px solid #012851;
|
||
background:#fff;padding:0 8px;font-size:9px;font-family:inherit;color:#012851;
|
||
outline:none;
|
||
box-shadow:0 0 0 2px #f0efe9, 0 0 0 4px #012851;
|
||
}
|
||
.demo-input-lm-idle{
|
||
width:100%;height:24px;border-radius:3px;border:1.5px solid #e4e2d7;
|
||
background:#fff;padding:0 8px;font-size:9px;font-family:inherit;color:#012851;
|
||
outline:none;
|
||
}
|
||
.demo-input-lm-error{
|
||
width:100%;height:24px;border-radius:3px;border:1.5px solid #dc2626;
|
||
background:#fff;padding:0 8px;font-size:9px;font-family:inherit;color:#012851;
|
||
outline:none;
|
||
box-shadow:0 0 0 2px #f0efe9, 0 0 0 4px #012851;
|
||
}
|
||
.demo-btn-lm-primary{
|
||
height:24px;padding:0 12px;background:#012851;color:#fff;border:none;
|
||
border-radius:3px;font-size:9px;font-weight:700;font-family:inherit;cursor:default;
|
||
outline:none;
|
||
box-shadow:0 0 0 2px #f0efe9, 0 0 0 4px #012851;
|
||
}
|
||
.demo-btn-lm-ghost{
|
||
height:24px;padding:0 12px;background:transparent;color:#012851;
|
||
border:1.5px solid #012851;border-radius:3px;font-size:9px;font-weight:700;font-family:inherit;cursor:default;
|
||
outline:none;
|
||
box-shadow:0 0 0 2px #f0efe9, 0 0 0 4px #012851;
|
||
}
|
||
.demo-btn-lm-idle{
|
||
height:24px;padding:0 12px;background:#012851;color:#fff;border:none;
|
||
border-radius:3px;font-size:9px;font-weight:700;font-family:inherit;cursor:default;
|
||
outline:none;
|
||
}
|
||
.demo-icon-lm{
|
||
width:22px;height:22px;border-radius:3px;background:rgba(1,40,81,.08);
|
||
display:inline-flex;align-items:center;justify-content:center;
|
||
outline:none;
|
||
box-shadow:0 0 0 2px #f0efe9, 0 0 0 4px #012851;
|
||
font-size:11px;
|
||
}
|
||
.demo-icon-lm-idle{
|
||
width:22px;height:22px;border-radius:3px;background:rgba(1,40,81,.08);
|
||
display:inline-flex;align-items:center;justify-content:center;
|
||
font-size:11px;
|
||
}
|
||
.demo-chip-lm{
|
||
display:inline-flex;align-items:center;gap:4px;padding:2px 4px 2px 7px;
|
||
background:rgba(161,220,216,.2);border:1px solid #a1dcd8;border-radius:2px;
|
||
font-size:8px;color:#012851;font-weight:600;
|
||
}
|
||
.demo-chip-lm-close{
|
||
width:14px;height:14px;background:none;border:none;cursor:default;
|
||
border-radius:2px;font-size:9px;color:#012851;display:inline-flex;align-items:center;justify-content:center;
|
||
outline:none;
|
||
box-shadow:0 0 0 1.5px #f0efe9, 0 0 0 3px #012851;
|
||
}
|
||
.demo-chip-lm-close-idle{
|
||
width:14px;height:14px;background:none;border:none;cursor:default;
|
||
border-radius:2px;font-size:9px;color:#012851;display:inline-flex;align-items:center;justify-content:center;
|
||
}
|
||
.demo-checkbox-lm{
|
||
width:13px;height:13px;border-radius:2px;border:1.5px solid #012851;
|
||
background:#fff;flex-shrink:0;
|
||
outline:none;
|
||
box-shadow:0 0 0 2px #f0efe9, 0 0 0 4px #012851;
|
||
}
|
||
.demo-link-lm{
|
||
font-size:9px;font-weight:700;color:#012851;text-decoration:none;
|
||
outline:none;
|
||
box-shadow:0 0 0 2px #f0efe9, 0 0 0 4px #012851;
|
||
border-radius:2px;padding:1px 2px;
|
||
}
|
||
.demo-nav-lm{
|
||
display:inline-block;font-size:7.5px;font-weight:800;text-transform:uppercase;
|
||
letter-spacing:.5px;color:#fff;padding:4px 7px;border-radius:3px;
|
||
outline:none;
|
||
box-shadow:0 0 0 2px #012851, 0 0 0 4px #a1dcd8;
|
||
background:rgba(255,255,255,.08);
|
||
}
|
||
.demo-nav-lm-idle{
|
||
display:inline-block;font-size:7.5px;font-weight:800;text-transform:uppercase;
|
||
letter-spacing:.5px;color:rgba(255,255,255,.6);padding:4px 7px;border-radius:3px;
|
||
}
|
||
|
||
/* ── Dark mode focus demos ─── */
|
||
.demo-input-dm{
|
||
width:100%;height:24px;border-radius:3px;border:1.5px solid #a1dcd8;
|
||
background:#011526;padding:0 8px;font-size:9px;font-family:inherit;color:#f0efe9;
|
||
outline:none;
|
||
box-shadow:0 0 0 2px #010e1e, 0 0 0 4px #a1dcd8;
|
||
}
|
||
.demo-input-dm-idle{
|
||
width:100%;height:24px;border-radius:3px;border:1.5px solid #0d3358;
|
||
background:#011526;padding:0 8px;font-size:9px;font-family:inherit;color:#f0efe9;
|
||
outline:none;
|
||
}
|
||
.demo-btn-dm-primary{
|
||
height:24px;padding:0 12px;background:#a1dcd8;color:#012851;border:none;
|
||
border-radius:3px;font-size:9px;font-weight:700;font-family:inherit;cursor:default;
|
||
outline:none;
|
||
box-shadow:0 0 0 2px #010e1e, 0 0 0 4px #a1dcd8;
|
||
}
|
||
.demo-btn-dm-ghost{
|
||
height:24px;padding:0 12px;background:transparent;color:#a1dcd8;
|
||
border:1.5px solid #a1dcd8;border-radius:3px;font-size:9px;font-weight:700;font-family:inherit;cursor:default;
|
||
outline:none;
|
||
box-shadow:0 0 0 2px #010e1e, 0 0 0 4px #a1dcd8;
|
||
}
|
||
.demo-icon-dm{
|
||
width:22px;height:22px;border-radius:3px;background:rgba(255,255,255,.08);
|
||
display:inline-flex;align-items:center;justify-content:center;
|
||
outline:none;
|
||
box-shadow:0 0 0 2px #010e1e, 0 0 0 4px #a1dcd8;
|
||
font-size:11px;color:#f0efe9;
|
||
}
|
||
.demo-chip-dm{
|
||
display:inline-flex;align-items:center;gap:4px;padding:2px 4px 2px 7px;
|
||
background:rgba(0,199,177,.15);border:1px solid #00c7b1;border-radius:2px;
|
||
font-size:8px;color:#a1dcd8;font-weight:600;
|
||
}
|
||
.demo-chip-dm-close{
|
||
width:14px;height:14px;background:none;border:none;cursor:default;
|
||
border-radius:2px;font-size:9px;color:#a1dcd8;display:inline-flex;align-items:center;justify-content:center;
|
||
outline:none;
|
||
box-shadow:0 0 0 1.5px #010e1e, 0 0 0 3px #a1dcd8;
|
||
}
|
||
.demo-checkbox-dm{
|
||
width:13px;height:13px;border-radius:2px;border:1.5px solid #a1dcd8;
|
||
background:#011526;flex-shrink:0;
|
||
outline:none;
|
||
box-shadow:0 0 0 2px #010e1e, 0 0 0 4px #a1dcd8;
|
||
}
|
||
.demo-nav-dm{
|
||
display:inline-block;font-size:7.5px;font-weight:800;text-transform:uppercase;
|
||
letter-spacing:.5px;color:#fff;padding:4px 7px;border-radius:3px;
|
||
outline:none;
|
||
/* On navy header: use narrower 1px gap so ring is distinct from header bg */
|
||
box-shadow:0 0 0 1px #01335e, 0 0 0 3px #a1dcd8;
|
||
background:rgba(255,255,255,.1);
|
||
}
|
||
.demo-row{display:flex;align-items:center;gap:14px;flex-wrap:wrap}
|
||
.demo-pair{display:flex;align-items:center;gap:6px}
|
||
|
||
/* ── Note / info boxes ─── */
|
||
.note{background:#EFF6FF;border:1px solid #BFDBFE;border-radius:6px;padding:12px 16px;font-size:11px;color:#1E3A5F;line-height:1.6;margin-top:16px}
|
||
.note strong{font-weight:800}
|
||
.warn-note{background:#FFF7ED;border:1px solid #FED7AA;border-radius:6px;padding:12px 16px;font-size:11px;color:#7C2D12;line-height:1.6;margin-top:16px}
|
||
.warn-note strong{font-weight:800}
|
||
.warn-note code{background:rgba(0,0,0,.06);border-radius:3px;padding:1px 5px;font-size:10px;font-family:monospace}
|
||
|
||
/* ── Audit table ─── */
|
||
.audit-table{width:100%;border-collapse:collapse;font-size:10.5px;margin-top:12px}
|
||
.audit-table th{text-align:left;font-size:8px;font-weight:800;text-transform:uppercase;letter-spacing:.8px;color:#888;padding:7px 10px;border-bottom:2px solid #D8D4CE}
|
||
.audit-table td{padding:8px 10px;border-bottom:1px solid #E8E4DF;vertical-align:top;line-height:1.6}
|
||
.audit-table tr:last-child td{border-bottom:none}
|
||
.audit-table td:first-child{font-family:monospace;font-size:10px;color:#012851;font-weight:700}
|
||
.audit-table td code{font-family:monospace;font-size:9.5px;background:rgba(1,40,81,.07);color:#012851;padding:1px 4px;border-radius:3px}
|
||
|
||
/* ── Header bar for nav demo ─── */
|
||
.hdr-demo{background:#012851;padding:8px 14px;display:flex;align-items:center;gap:10px;border-radius:6px}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="doc">
|
||
|
||
<!-- ═══════════════════════════════════════════════════════════
|
||
MASTHEAD
|
||
════════════════════════════════════════════════════════════ -->
|
||
<div class="mast">
|
||
<div class="mast-top">
|
||
<div>
|
||
<h1>Focus Rings — Design Spec</h1>
|
||
<p>Most interactive elements currently use browser-default focus rings. There is no <code style="color:rgba(255,255,255,.7);background:rgba(255,255,255,.1);padding:1px 5px;border-radius:3px;font-size:11px">--c-focus-ring</code> semantic token, leading to 5 different ad-hoc approaches across the codebase. This spec defines a single token, proves WCAG 2.4.11 compliance in both modes, and gives exact Tailwind classes for every element type. Relates to issue #167.</p>
|
||
</div>
|
||
<span class="mast-badge mb-spec">Design Spec</span>
|
||
</div>
|
||
<div class="decisions">
|
||
<div class="dec">
|
||
<div class="dec-label">Root cause</div>
|
||
<div class="dec-value">No --c-focus-ring token — 5 different ring colors across the codebase</div>
|
||
</div>
|
||
<div class="dec">
|
||
<div class="dec-label">WCAG 2.4.11 failure</div>
|
||
<div class="dec-value">ring-accent in light mode (#a1dcd8 on white) = 1.52:1 — fails 3:1 minimum</div>
|
||
</div>
|
||
<div class="dec">
|
||
<div class="dec-label">Light token</div>
|
||
<div class="dec-value">#012851 brand-navy — 14:1 on white, 13:1 on sand — WCAG AAA ✓</div>
|
||
</div>
|
||
<div class="dec">
|
||
<div class="dec-label">Dark token</div>
|
||
<div class="dec-value">#a1dcd8 brand-mint — 14.3:1 on canvas (#010e1e) — WCAG AAA ✓</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="spec-disclaimer">
|
||
<strong>📐 Mockup scale notice —</strong> all font-size, height, and padding values
|
||
in the mockup CSS are scaled to ~55% of actual implementation values.
|
||
<strong>Do not copy sizes from mockup CSS.</strong> Use the ⚙ Implementation
|
||
Reference tables after each section.
|
||
</div>
|
||
|
||
|
||
<!-- ═══════════════════════════════════════════════════════════
|
||
1. ISSUE CATALOG
|
||
════════════════════════════════════════════════════════════ -->
|
||
<div class="sec">
|
||
<div class="sec-h"><span class="sec-num">1</span> Issue Catalog</div>
|
||
|
||
<div class="issue">
|
||
<div class="issue-id">Issue 01 · Critical — WCAG 2.4.11 Failure</div>
|
||
<div class="issue-title">ring-accent in light mode = 1.52:1 contrast — fails the focus indicator minimum</div>
|
||
<div class="issue-body">
|
||
The header (AppNav, UserMenu, LanguageSwitcher, ThemeToggle, NotificationBell) uses <code>focus-visible:ring-accent</code>.
|
||
In light mode <code>--c-accent: #a1dcd8</code>. That mint ring on the white/sand background has a contrast ratio of <strong>1.52:1</strong> — the WCAG 2.4.11 minimum is 3:1 for the focus indicator against adjacent colors.<br><br>
|
||
Notifications page filter pills (<code>focus-visible:ring-accent focus-visible:ring-offset-2</code>) have the same failure — the ring-offset is white (#fff), so the contrast is #a1dcd8 vs #fff = 1.52:1.
|
||
</div>
|
||
<div class="issue-fix">
|
||
<strong>Fix:</strong> Replace <code>ring-accent</code> with <code>ring-focus-ring</code> throughout. In light mode <code>--c-focus-ring: #012851</code> gives 14:1 on white and 13:1 on sand — WCAG AAA.
|
||
</div>
|
||
</div>
|
||
|
||
<div class="issue">
|
||
<div class="issue-id">Issue 02 · Critical — WCAG 2.4.11 Failure</div>
|
||
<div class="issue-title">Chip close buttons: focus:outline-none with no ring replacement</div>
|
||
<div class="issue-body">
|
||
<code>PersonMultiSelect.svelte:94</code> and <code>TagInput.svelte:98</code> apply <code>focus:outline-none</code> to the chip × buttons with <strong>no</strong> replacement focus indicator. These are keyboard-operable interactive controls. Removing the browser outline without a replacement is a hard WCAG 2.4.11 fail — keyboard users cannot see which chip they are about to remove.<br><br>
|
||
Additionally, <code>TagInput.svelte:125</code> sets <code>outline-none focus:ring-0</code> on the inner text input, also leaving it with zero focus indicator.
|
||
</div>
|
||
<div class="issue-fix">
|
||
<strong>Fix:</strong> Remove the bare <code>focus:outline-none</code> from both chip close buttons and replace with <code>focus:outline-none focus-visible:ring-2 focus-visible:ring-focus-ring</code>. For the TagInput inner input: remove <code>focus:ring-0</code> and add <code>focus:ring-2 focus:ring-focus-ring</code> (show on focus, not just focus-visible, since this is a text field).
|
||
</div>
|
||
</div>
|
||
|
||
<div class="issue">
|
||
<div class="issue-id">Issue 03 · High</div>
|
||
<div class="issue-title">No --c-focus-ring token — 5 different ring colors across the codebase</div>
|
||
<div class="issue-body">
|
||
Current audit of <code>focus</code> / <code>focus-visible</code> classes across all Svelte files reveals five distinct ring-color decisions:<br><br>
|
||
• <code>ring-accent</code> — header elements, PanelHistory, MentionEditor, AppNav, UserGroupsSection<br>
|
||
• <code>ring-ink</code> — form inputs, textareas, selects (WhoWhenSection, DescriptionSection, TranscriptionSection, SearchFilterBar, ConversationFilterBar, ForgotPassword, profile forms)<br>
|
||
• <code>ring-primary</code> — PersonTypeahead compact mode<br>
|
||
• <code>ring-black</code> — PersonTypeahead dropdown (Headless UI default)<br>
|
||
• nothing — TagInput inner input, chip close buttons<br><br>
|
||
With no single token to update, switching focus color for dark mode requires editing every file independently — and future components will continue to drift.
|
||
</div>
|
||
<div class="issue-fix">
|
||
<strong>Fix:</strong> Add <code>--c-focus-ring</code> to <code>layout.css</code> (light + dark blocks) and <code>--color-focus-ring: var(--c-focus-ring)</code> in <code>@theme inline</code>. All components then use <code>ring-focus-ring</code> — one token to retheme all focus rings at once.
|
||
</div>
|
||
</div>
|
||
|
||
<div class="issue">
|
||
<div class="issue-id">Issue 04 · Medium</div>
|
||
<div class="issue-title">Inconsistent ring width and missing ring-offset</div>
|
||
<div class="issue-body">
|
||
Ring widths vary: some elements use <code>ring-1</code> (1px, barely visible), some <code>ring-2</code> (2px), and form inputs omit the width class entirely (Tailwind 4 default ring is 1px). No element outside the notifications page uses <code>ring-offset</code>, so the ring is drawn on top of the element border rather than floating outside it — making it hard to distinguish the focus ring from the border color change.<br><br>
|
||
The correct visual: <strong>2px ring + 2px offset</strong> on elements sitting on surface/canvas backgrounds. On the header (dark background), the ring draws directly without offset (ring appears clearly against navy).
|
||
</div>
|
||
<div class="issue-fix">
|
||
<strong>Fix:</strong> Standardize on <code>focus-visible:ring-2 focus-visible:ring-focus-ring focus-visible:ring-offset-2</code> for all buttons, links, and icon buttons on light backgrounds. For form inputs: <code>focus:ring-2 focus:ring-focus-ring focus:ring-offset-0</code> (inputs need the ring to show on click, not just keyboard nav; offset-0 because the ring replaces the border-color signal). For header elements: <code>focus-visible:ring-2 focus-visible:ring-focus-ring</code> (no offset — ring reads clearly on navy bg).
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<!-- ═══════════════════════════════════════════════════════════
|
||
2. TOKEN DEFINITION
|
||
════════════════════════════════════════════════════════════ -->
|
||
<div class="sec">
|
||
<div class="sec-h"><span class="sec-num">2</span> Token Definition</div>
|
||
|
||
<div class="sg sg-2">
|
||
<div>
|
||
<div class="sl">Semantic Token Values</div>
|
||
<table class="tok-table">
|
||
<thead>
|
||
<tr>
|
||
<th>CSS Variable</th>
|
||
<th>Mode</th>
|
||
<th>Value</th>
|
||
<th>Contrast (typical bg)</th>
|
||
<th>WCAG</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code style="font-size:10px">--c-focus-ring</code></td>
|
||
<td>Light</td>
|
||
<td>
|
||
<div class="swatch-pair">
|
||
<span class="swatch" style="background:#012851"></span>
|
||
<span class="hex">#012851</span>
|
||
</div>
|
||
</td>
|
||
<td>14:1 on white · 13:1 on sand (#f0efe9)</td>
|
||
<td><span class="tag-ok">AAA ✓</span></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code style="font-size:10px">--c-focus-ring</code></td>
|
||
<td>Dark</td>
|
||
<td>
|
||
<div class="swatch-pair">
|
||
<span class="swatch" style="background:#a1dcd8;border-color:#0d3358"></span>
|
||
<span class="hex">#a1dcd8</span>
|
||
</div>
|
||
</td>
|
||
<td>14.3:1 on #010e1e · 9.2:1 on #011526</td>
|
||
<td><span class="tag-ok">AAA ✓</span></td>
|
||
</tr>
|
||
<tr>
|
||
<td colspan="2" style="color:#888;font-size:9px">Tailwind utility class</td>
|
||
<td colspan="3"><code style="font-size:10px">ring-focus-ring</code> (via <code>--color-focus-ring: var(--c-focus-ring)</code>)</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<div class="note" style="margin-top:14px">
|
||
<strong>Why these values?</strong> Light mode reuses <code>--c-primary</code> (#012851), which already scores 14:1 on white for text — a free contrast win. Dark mode reuses <code>--c-primary</code> dark value (#a1dcd8 brand-mint), which scores 14.3:1 on the darkest canvas. Both exceed WCAG AAA (7:1) and comfortably pass WCAG 2.4.11's 3:1 minimum.
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<div class="sl">Why not reuse --c-primary?</div>
|
||
<div style="background:#fff;border:1px solid #E8E4DF;border-radius:6px;padding:14px;font-size:11px;line-height:1.7;color:#555">
|
||
<p style="margin-bottom:10px"><code style="font-size:10px;background:#f0efe9;padding:1px 4px;border-radius:3px">--c-primary</code> is used for button backgrounds and interactive text. If we map <code>--c-focus-ring</code> to the same value as <code>--c-primary</code>, the token works identically — and that is exactly the right choice here.</p>
|
||
<p style="margin-bottom:10px">The distinction matters for clarity: <code>--c-focus-ring</code> is a semantic token with a specific purpose (focus indicators). Even if it resolves to the same hex today, a future redesign can update one without touching the other.</p>
|
||
<p>Having an explicit <code>--c-focus-ring</code> also makes it immediately clear in component code that a focus style is intentional, not an accidental color reuse.</p>
|
||
</div>
|
||
|
||
<div class="warn-note" style="margin-top:14px">
|
||
<strong>Do not use <code>--c-accent</code> as a focus ring in light mode.</strong> <code>--c-accent</code> in light mode is #a1dcd8, a mint that scores only 1.52:1 on white backgrounds — decorative use only. This is the root cause of the current WCAG 2.4.11 failures in the header.
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<!-- ═══════════════════════════════════════════════════════════
|
||
3. ELEMENT GALLERY
|
||
════════════════════════════════════════════════════════════ -->
|
||
<div class="sec">
|
||
<div class="sec-h"><span class="sec-num">3</span> Element Gallery — Focus States</div>
|
||
<p style="font-size:11px;color:#666;margin-bottom:24px">Each element shown idle (no focus) and focused. Left panel = light mode. Right panel = dark mode. Mockup values are ~55% scale.</p>
|
||
|
||
<!-- 3A: Text Inputs -->
|
||
<div class="sg sg-2" style="margin-bottom:32px">
|
||
<div class="sb">
|
||
<div class="sl">3A — Text Inputs & Textareas <span class="mode-label lm">Light</span></div>
|
||
<div class="mock-frame mock-lm">
|
||
<div class="mock-body">
|
||
<div>
|
||
<div class="mock-label">Idle</div>
|
||
<input class="demo-input-lm-idle" type="text" value="Hans Müller" readonly>
|
||
</div>
|
||
<div>
|
||
<div class="mock-label">Focused (keyboard or click)</div>
|
||
<input class="demo-input-lm" type="text" value="Hans Müller" readonly>
|
||
</div>
|
||
<div>
|
||
<div class="mock-label">Focused + error border</div>
|
||
<input class="demo-input-lm-error" type="text" value="" placeholder="Pflichtfeld" readonly>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sc">Ring: 2px navy #012851 · Offset: 0px (ring sits flush against border) · Border also changes to focus-ring color on focus</div>
|
||
</div>
|
||
<div class="sb">
|
||
<div class="sl">3A — Text Inputs & Textareas <span class="mode-label dm">Dark</span></div>
|
||
<div class="mock-frame mock-dm">
|
||
<div class="mock-body">
|
||
<div>
|
||
<div class="mock-label">Idle</div>
|
||
<input class="demo-input-dm-idle" type="text" value="Hans Müller" readonly>
|
||
</div>
|
||
<div>
|
||
<div class="mock-label">Focused</div>
|
||
<input class="demo-input-dm" type="text" value="Hans Müller" readonly>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sc">Ring: 2px mint #a1dcd8 · Offset: 0px · Border also changes to ring-focus-ring</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="impl-ref">
|
||
<div class="impl-ref-hdr">Implementation Reference — Text Inputs / Textareas / Selects
|
||
<span>Real values · mockup above is ~55% scale</span>
|
||
</div>
|
||
<table>
|
||
<thead><tr><th>Element</th><th>Tailwind classes to ADD / CHANGE</th><th>Real size</th><th>Notes</th></tr></thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>Any text input, textarea, select</td>
|
||
<td><code>focus:outline-none focus:ring-2 focus:ring-focus-ring focus:border-focus-ring</code></td>
|
||
<td><span class="ir-px">ring 2px</span></td>
|
||
<td>Use <code>focus:</code> not <code>focus-visible:</code> — user must see which text field is active even on mouse click. Remove any <code>focus:ring-ink</code>, <code>focus:ring-accent</code>, <code>focus:ring-primary</code>, <code>focus:ring-black</code>. Do NOT add ring-offset — offset-0 is correct for inputs.</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Error-state input (focused)</td>
|
||
<td><code>focus:ring-focus-ring</code> (ring color stays navy/mint)</td>
|
||
<td><span class="ir-px">ring 2px</span></td>
|
||
<td>The error border color (red-400) is the border — the focus ring is always ring-focus-ring. Two separate visual signals: border = validation state, ring = keyboard position.</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Files / components to update</td>
|
||
<td colspan="3"><code>WhoWhenSection.svelte</code> · <code>DescriptionSection.svelte</code> · <code>TranscriptionSection.svelte</code> · <code>SearchFilterBar.svelte</code> · <code>ConversationFilterBar.svelte</code> (both instances) · <code>forgot-password/+page.svelte</code> · <code>PanelHistory.svelte</code> · <code>MentionEditor.svelte</code> · <code>UserPasswordSection.svelte</code> · <code>UserProfileSection.svelte</code> · <code>PersonalInfoForm.svelte</code> · <code>PasswordChangeForm.svelte</code> · <code>PersonTypeahead.svelte</code></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<!-- 3B: Buttons -->
|
||
<div class="sg sg-2" style="margin-bottom:32px;margin-top:36px">
|
||
<div class="sb">
|
||
<div class="sl">3B — Buttons (Primary & Ghost) <span class="mode-label lm">Light</span></div>
|
||
<div class="mock-frame mock-lm">
|
||
<div class="mock-body">
|
||
<div class="demo-row">
|
||
<div>
|
||
<div class="mock-label">Primary idle</div>
|
||
<button class="demo-btn-lm-idle">Speichern</button>
|
||
</div>
|
||
<div>
|
||
<div class="mock-label">Primary focused</div>
|
||
<button class="demo-btn-lm-primary">Speichern</button>
|
||
</div>
|
||
</div>
|
||
<div class="demo-row">
|
||
<div>
|
||
<div class="mock-label">Ghost focused</div>
|
||
<button class="demo-btn-lm-ghost">Abbrechen</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sc">Ring: 2px navy #012851 · Offset: 2px (white gap) · focus-visible only</div>
|
||
</div>
|
||
<div class="sb">
|
||
<div class="sl">3B — Buttons (Primary & Ghost) <span class="mode-label dm">Dark</span></div>
|
||
<div class="mock-frame mock-dm">
|
||
<div class="mock-body">
|
||
<div class="demo-row">
|
||
<div>
|
||
<div class="mock-label">Primary focused</div>
|
||
<button class="demo-btn-dm-primary">Speichern</button>
|
||
</div>
|
||
<div>
|
||
<div class="mock-label">Ghost focused</div>
|
||
<button class="demo-btn-dm-ghost">Abbrechen</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sc">Ring: 2px mint #a1dcd8 · Offset: 2px (dark canvas gap) · focus-visible only</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="impl-ref">
|
||
<div class="impl-ref-hdr">Implementation Reference — Buttons
|
||
<span>Real values · mockup above is ~55% scale</span>
|
||
</div>
|
||
<table>
|
||
<thead><tr><th>Element</th><th>Tailwind classes</th><th>Real size</th><th>Notes</th></tr></thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>Any button (primary, ghost, destructive)</td>
|
||
<td><code>focus:outline-none focus-visible:ring-2 focus-visible:ring-focus-ring focus-visible:ring-offset-2</code></td>
|
||
<td><span class="ir-px">ring 2px, offset 2px</span></td>
|
||
<td><code>focus-visible:</code> not <code>focus:</code> — buttons only need the ring for keyboard navigation, not mouse clicks. Min height 44px for all touch targets.</td>
|
||
</tr>
|
||
<tr>
|
||
<td>ring-offset color (light)</td>
|
||
<td>Tailwind default <code>ring-offset-white</code> or omit (default = white)</td>
|
||
<td>—</td>
|
||
<td>On light backgrounds the default white offset is correct. On sand background (<code>bg-canvas</code>), add <code>focus-visible:ring-offset-canvas</code> so the gap matches the page background.</td>
|
||
</tr>
|
||
<tr>
|
||
<td>ring-offset color (dark)</td>
|
||
<td><code>focus-visible:ring-offset-canvas</code></td>
|
||
<td>—</td>
|
||
<td>Critical: without this, the white offset flashes on dark backgrounds. Add <code>focus-visible:ring-offset-canvas</code> to all buttons that appear on dark/canvas backgrounds.</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<!-- 3C: Icon Buttons (Header) -->
|
||
<div class="sg sg-2" style="margin-bottom:32px;margin-top:36px">
|
||
<div class="sb">
|
||
<div class="sl">3C — Icon Buttons on Header <span class="mode-label lm">Light (header = navy bg)</span></div>
|
||
<div class="mock-frame" style="background:#012851;border-color:#0a3d6b">
|
||
<div class="mock-body">
|
||
<div class="demo-row">
|
||
<div>
|
||
<div class="mock-label" style="color:rgba(255,255,255,.4)">Idle</div>
|
||
<span class="demo-icon-lm-idle" style="background:rgba(255,255,255,.1);color:#fff">🔔</span>
|
||
</div>
|
||
<div>
|
||
<div class="mock-label" style="color:rgba(255,255,255,.4)">Focused</div>
|
||
<span class="demo-nav-lm">🔔</span>
|
||
</div>
|
||
<div>
|
||
<div class="mock-label" style="color:rgba(255,255,255,.4)">Nav link focused</div>
|
||
<span class="demo-nav-lm">Dokumente</span>
|
||
</div>
|
||
<div>
|
||
<div class="mock-label" style="color:rgba(255,255,255,.4)">Idle link</div>
|
||
<span class="demo-nav-lm-idle">Personen</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sc">Ring: 2px mint #a1dcd8 · No offset (ring reads clearly on navy) · focus-visible only</div>
|
||
</div>
|
||
<div class="sb">
|
||
<div class="sl">3C — Icon Buttons on Header <span class="mode-label dm">Dark (header = mid-navy)</span></div>
|
||
<div class="mock-frame" style="background:#01335e;border-color:#0a3d6b">
|
||
<div class="mock-body">
|
||
<div class="demo-row">
|
||
<div>
|
||
<div class="mock-label" style="color:rgba(255,255,255,.4)">Focused</div>
|
||
<span class="demo-nav-dm">🔔</span>
|
||
</div>
|
||
<div>
|
||
<div class="mock-label" style="color:rgba(255,255,255,.4)">Nav link focused</div>
|
||
<span class="demo-nav-dm">Dokumente</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sc">Ring: 2px mint #a1dcd8 · 1px navy offset (#01335e) so ring floats · focus-visible only</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="impl-ref">
|
||
<div class="impl-ref-hdr">Implementation Reference — Header Icon Buttons & Nav Links
|
||
<span>Real values · mockup above is ~55% scale</span>
|
||
</div>
|
||
<table>
|
||
<thead><tr><th>Element</th><th>Tailwind classes</th><th>Real size</th><th>Notes</th></tr></thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>Header icon button (light + dark)</td>
|
||
<td><code>focus:outline-none focus-visible:ring-2 focus-visible:ring-focus-ring</code></td>
|
||
<td><span class="ir-px">ring 2px, no offset</span></td>
|
||
<td>No ring-offset: ring appears directly on navy bg. In light mode, <code>ring-focus-ring</code> = navy → same as header bg → use mint instead? No: the navy header IS the background, so the ring colour is always mint (token resolves correctly in both modes). Contrast of mint (#a1dcd8) on navy header (#012851) = 4.1:1 — WCAG AA ✓.</td>
|
||
</tr>
|
||
<tr>
|
||
<td>AppNav links (desktop)</td>
|
||
<td>Remove <code>focus-visible:ring-accent</code>, add <code>focus-visible:ring-focus-ring</code></td>
|
||
<td><span class="ir-px">ring 2px</span></td>
|
||
<td>Files: <code>AppNav.svelte</code> lines 44, 54, 64, 74, 86, 145, 155, 165, 176</td>
|
||
</tr>
|
||
<tr>
|
||
<td>UserMenu avatar + close</td>
|
||
<td>Remove <code>focus-visible:ring-accent</code>, add <code>focus-visible:ring-focus-ring</code></td>
|
||
<td><span class="ir-px">ring 2px</span></td>
|
||
<td>File: <code>UserMenu.svelte</code> lines 36, 47</td>
|
||
</tr>
|
||
<tr>
|
||
<td>ThemeToggle, LanguageSwitcher, NotificationBell</td>
|
||
<td>Remove <code>focus-visible:ring-accent</code>, add <code>focus-visible:ring-focus-ring</code></td>
|
||
<td><span class="ir-px">ring 2px</span></td>
|
||
<td>Files: <code>ThemeToggle.svelte:34</code>, <code>LanguageSwitcher.svelte:15</code>, <code>NotificationBell.svelte:157</code></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<!-- 3D: Tag chips and Person chips -->
|
||
<div class="sg sg-2" style="margin-bottom:32px;margin-top:36px">
|
||
<div class="sb">
|
||
<div class="sl">3D — Tag / Person Chips with Close Button <span class="mode-label lm">Light</span></div>
|
||
<div class="mock-frame mock-lm">
|
||
<div class="mock-body">
|
||
<div>
|
||
<div class="mock-label">Chip — close button focused</div>
|
||
<div class="demo-chip-lm">
|
||
Briefe
|
||
<button class="demo-chip-lm-close">×</button>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<div class="mock-label">Chip — close button idle (no focus)</div>
|
||
<div class="demo-chip-lm">
|
||
Briefe
|
||
<button class="demo-chip-lm-close-idle">×</button>
|
||
</div>
|
||
</div>
|
||
<div style="margin-top:4px">
|
||
<div class="mock-label">Input inside chip container (focused)</div>
|
||
<div style="border:1.5px solid #a1dcd8;background:#fff;border-radius:3px;padding:4px 8px;display:flex;gap:6px;align-items:center;box-shadow:0 0 0 2px #f0efe9, 0 0 0 4px #012851">
|
||
<div class="demo-chip-lm" style="box-shadow:none">Briefe <button class="demo-chip-lm-close-idle">×</button></div>
|
||
<span style="font-size:8px;color:#012851;opacity:.5;font-style:italic">Weiteres Tag…</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sc">Close button: 2px navy ring, no offset. Inner text input: 2px navy ring on the wrapping container.</div>
|
||
</div>
|
||
<div class="sb">
|
||
<div class="sl">3D — Tag / Person Chips with Close Button <span class="mode-label dm">Dark</span></div>
|
||
<div class="mock-frame mock-dm">
|
||
<div class="mock-body">
|
||
<div>
|
||
<div class="mock-label">Chip — close button focused</div>
|
||
<div class="demo-chip-dm">
|
||
Briefe
|
||
<button class="demo-chip-dm-close">×</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sc">Close button: 2px mint ring, no offset. Touch target: min 44×44px (chip row padding pads to this).</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="impl-ref">
|
||
<div class="impl-ref-hdr">Implementation Reference — Chip Close Buttons & TagInput / PersonMultiSelect
|
||
<span>Real values · mockup above is ~55% scale</span>
|
||
</div>
|
||
<table>
|
||
<thead><tr><th>Element</th><th>Tailwind classes</th><th>Real size</th><th>Notes</th></tr></thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>Chip close button (×)</td>
|
||
<td><code>focus:outline-none focus-visible:ring-2 focus-visible:ring-focus-ring rounded</code></td>
|
||
<td><span class="ir-px">ring 2px, no offset</span></td>
|
||
<td>CURRENTLY: <code>focus:outline-none</code> with no ring — zero visible focus indicator. This is a hard WCAG 2.4.11 fail. Files: <code>PersonMultiSelect.svelte:94</code>, <code>TagInput.svelte:98</code>.</td>
|
||
</tr>
|
||
<tr>
|
||
<td>TagInput inner text input</td>
|
||
<td><code>focus:outline-none focus:ring-2 focus:ring-focus-ring</code></td>
|
||
<td><span class="ir-px">ring 2px</span></td>
|
||
<td>CURRENTLY: <code>outline-none focus:ring-0</code> — completely suppressed. Remove both classes, add the focus ring. File: <code>TagInput.svelte:125</code>. The ring appears on the input itself (narrow), which is visually fine inside the chip container.</td>
|
||
</tr>
|
||
<tr>
|
||
<td>PersonMultiSelect inner input</td>
|
||
<td><code>focus:outline-none focus:ring-2 focus:ring-focus-ring</code></td>
|
||
<td><span class="ir-px">ring 2px</span></td>
|
||
<td>File: <code>PersonMultiSelect.svelte:117</code> — same <code>outline-none focus:ring-0</code> pattern. Same fix.</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Touch target for close button</td>
|
||
<td><code>min-h-[44px] min-w-[44px]</code> or ensure chip row is ≥44px tall</td>
|
||
<td><span class="ir-px">44×44px min</span></td>
|
||
<td>Most commonly undersized element — the × button inside chips is often 20–24px. Pad the chip row to 44px height or use <code>p-2</code> on the button itself.</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<!-- 3E: Checkboxes -->
|
||
<div class="sg sg-2" style="margin-bottom:32px;margin-top:36px">
|
||
<div class="sb">
|
||
<div class="sl">3E — Checkboxes <span class="mode-label lm">Light</span></div>
|
||
<div class="mock-frame mock-lm">
|
||
<div class="mock-body">
|
||
<div class="demo-row">
|
||
<label style="display:flex;align-items:center;gap:6px;font-size:9px;color:#012851;cursor:default">
|
||
<span class="demo-checkbox-lm"></span> Aktiv
|
||
</label>
|
||
<label style="display:flex;align-items:center;gap:6px;font-size:9px;color:#012851;cursor:default">
|
||
<span style="width:13px;height:13px;border-radius:2px;border:1.5px solid #e4e2d7;background:#fff;flex-shrink:0;display:inline-block"></span> Inaktiv (idle)
|
||
</label>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sc">Ring: 2px navy #012851 · Offset: 2px white gap · Standard Tailwind checkbox pattern</div>
|
||
</div>
|
||
<div class="sb">
|
||
<div class="sl">3E — Checkboxes <span class="mode-label dm">Dark</span></div>
|
||
<div class="mock-frame mock-dm">
|
||
<div class="mock-body">
|
||
<div class="demo-row">
|
||
<label style="display:flex;align-items:center;gap:6px;font-size:9px;color:#f0efe9;cursor:default">
|
||
<span class="demo-checkbox-dm"></span> Aktiv (focused)
|
||
</label>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sc">Ring: 2px mint #a1dcd8 · Offset: 2px dark canvas gap</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="impl-ref">
|
||
<div class="impl-ref-hdr">Implementation Reference — Checkboxes
|
||
<span>Real values · mockup above is ~55% scale</span>
|
||
</div>
|
||
<table>
|
||
<thead><tr><th>Element</th><th>Tailwind classes</th><th>Real size</th><th>Notes</th></tr></thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>input[type=checkbox]</td>
|
||
<td><code>rounded focus:ring-2 focus:ring-focus-ring focus:ring-offset-2</code></td>
|
||
<td><span class="ir-px">ring 2px, offset 2px</span></td>
|
||
<td>CURRENTLY: <code>focus:ring-accent</code> — fails light mode. File: <code>UserGroupsSection.svelte:21</code>. Tailwind does not need explicit <code>focus:outline-none</code> for checkboxes — it resets the outline via the preflight layer.</td>
|
||
</tr>
|
||
<tr>
|
||
<td>ring-offset-color (dark)</td>
|
||
<td><code>focus:ring-offset-canvas</code></td>
|
||
<td>—</td>
|
||
<td>Without this, the 2px ring offset shows as white on dark backgrounds. Add alongside the ring classes.</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<!-- 3F: Notifications pills -->
|
||
<div style="margin-top:36px">
|
||
<div class="sl">3F — Filter Pills (Notifications Page)</div>
|
||
<div class="note">
|
||
<strong>Current pattern is structurally correct</strong> — <code>focus-visible:ring-2 focus-visible:ring-offset-2</code> with <code>ring-accent</code>. Only change needed: replace <code>ring-accent</code> → <code>ring-focus-ring</code>.<br><br>
|
||
File: <code>notifications/+page.svelte</code> lines 114, 129, 152, 167 — four pill variants. Each currently has <code>focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2</code>.
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<!-- ═══════════════════════════════════════════════════════════
|
||
4. CSS IMPLEMENTATION
|
||
════════════════════════════════════════════════════════════ -->
|
||
<div class="sec">
|
||
<div class="sec-h"><span class="sec-num">4</span> CSS Implementation — Exact Diff</div>
|
||
<p style="font-size:11px;color:#666;margin-bottom:20px">Apply these changes first — all component-level fixes depend on the token existing.</p>
|
||
|
||
<div class="diff-block">
|
||
<div class="diff-block-hdr">frontend/src/routes/layout.css — token additions</div>
|
||
<pre><span class="diff-context">/* ─── 3. Semantic tokens ───────────────────────────────────────────────────── */
|
||
@theme inline {</span>
|
||
<span class="diff-context"> /* ... existing tokens ... */</span>
|
||
<span class="diff-add">+
|
||
+ /* Focus ring */
|
||
+ --color-focus-ring: var(--c-focus-ring);</span>
|
||
<span class="diff-context">}</span>
|
||
|
||
<span class="diff-context">/* ─── 4. Light mode (default) ─────────────────────────────────────────────── */
|
||
:root {</span>
|
||
<span class="diff-context"> /* ... existing tokens ... */</span>
|
||
<span class="diff-add">+
|
||
+ /* Focus ring — brand-navy on white/sand = 14:1 WCAG AAA */
|
||
+ --c-focus-ring: #012851;</span>
|
||
<span class="diff-context">}</span>
|
||
|
||
<span class="diff-context">/* ─── 5. Dark mode ─────────────────────────────────────────────────────────── */
|
||
@media (prefers-color-scheme: dark) {
|
||
:root:not([data-theme='light']) {</span>
|
||
<span class="diff-context"> /* ... existing tokens ... */</span>
|
||
<span class="diff-add">+
|
||
+ /* Focus ring — brand-mint on dark canvas = 14.3:1 WCAG AAA */
|
||
+ --c-focus-ring: #a1dcd8;</span>
|
||
<span class="diff-context"> }
|
||
}</span>
|
||
|
||
<span class="diff-context">/* Manual dark override */
|
||
:root[data-theme='dark'] {</span>
|
||
<span class="diff-context"> /* ... existing tokens ... */</span>
|
||
<span class="diff-add">+
|
||
+ --c-focus-ring: #a1dcd8;</span>
|
||
<span class="diff-context">}</span></pre>
|
||
</div>
|
||
|
||
<div class="diff-block" style="margin-top:16px">
|
||
<div class="diff-block-hdr">Component changes — ring-accent → ring-focus-ring (header elements)</div>
|
||
<pre><span class="diff-file">AppNav.svelte (lines 44, 54, 64, 74, 86, 145, 155, 165, 176):</span>
|
||
<span class="diff-remove">- focus-visible:ring-accent</span>
|
||
<span class="diff-add">+ focus-visible:ring-focus-ring</span>
|
||
|
||
<span class="diff-file">UserMenu.svelte (lines 36, 47):</span>
|
||
<span class="diff-remove">- focus-visible:ring-accent</span>
|
||
<span class="diff-add">+ focus-visible:ring-focus-ring</span>
|
||
|
||
<span class="diff-file">ThemeToggle.svelte (line 34):</span>
|
||
<span class="diff-remove">- focus-visible:ring-accent</span>
|
||
<span class="diff-add">+ focus-visible:ring-focus-ring</span>
|
||
|
||
<span class="diff-file">LanguageSwitcher.svelte (line 15):</span>
|
||
<span class="diff-remove">- focus-visible:ring-accent</span>
|
||
<span class="diff-add">+ focus-visible:ring-focus-ring</span>
|
||
|
||
<span class="diff-file">NotificationBell.svelte (line 157):</span>
|
||
<span class="diff-remove">- focus-visible:ring-accent</span>
|
||
<span class="diff-add">+ focus-visible:ring-focus-ring</span>
|
||
|
||
<span class="diff-file">notifications/+page.svelte (lines 114, 129, 152, 167):</span>
|
||
<span class="diff-remove">- focus-visible:ring-accent</span>
|
||
<span class="diff-add">+ focus-visible:ring-focus-ring</span></pre>
|
||
</div>
|
||
|
||
<div class="diff-block" style="margin-top:16px">
|
||
<div class="diff-block-hdr">Component changes — form inputs (ring-ink → ring-focus-ring + add ring-2)</div>
|
||
<pre><span class="diff-file">WhoWhenSection.svelte, DescriptionSection.svelte, TranscriptionSection.svelte,
|
||
SearchFilterBar.svelte, ConversationFilterBar.svelte (×2),
|
||
forgot-password/+page.svelte, UserPasswordSection.svelte, UserProfileSection.svelte,
|
||
PersonalInfoForm.svelte, PasswordChangeForm.svelte:</span>
|
||
<span class="diff-remove">- focus:border-ink focus:ring-ink
|
||
- (or) focus:border-ink focus:outline-none
|
||
- (or) focus:border-ink focus:ring-1 focus:ring-ink</span>
|
||
<span class="diff-add">+ focus:outline-none focus:ring-2 focus:ring-focus-ring focus:border-focus-ring</span>
|
||
|
||
<span class="diff-file">PanelHistory.svelte (lines 305, 320):</span>
|
||
<span class="diff-remove">- focus:ring-1 focus:ring-accent focus:outline-none</span>
|
||
<span class="diff-add">+ focus:outline-none focus:ring-2 focus:ring-focus-ring</span>
|
||
|
||
<span class="diff-file">MentionEditor.svelte (line 190):</span>
|
||
<span class="diff-remove">- focus:ring-1 focus:ring-accent focus:outline-none</span>
|
||
<span class="diff-add">+ focus:outline-none focus:ring-2 focus:ring-focus-ring</span></pre>
|
||
</div>
|
||
|
||
<div class="diff-block" style="margin-top:16px">
|
||
<div class="diff-block-hdr">Component changes — critical fixes (outline suppressed with no replacement)</div>
|
||
<pre><span class="diff-file">PersonMultiSelect.svelte (line 94) — chip close button:</span>
|
||
<span class="diff-remove">- class="ml-0.5 text-ink/50 hover:text-red-500 focus:outline-none"</span>
|
||
<span class="diff-add">+ class="ml-0.5 text-ink/50 hover:text-red-500 focus:outline-none focus-visible:ring-2 focus-visible:ring-focus-ring rounded"</span>
|
||
|
||
<span class="diff-file">PersonMultiSelect.svelte (line 117) — inner text input:</span>
|
||
<span class="diff-remove">- class="min-w-[120px] flex-1 border-none bg-transparent p-1 text-sm outline-none focus:ring-0"</span>
|
||
<span class="diff-add">+ class="min-w-[120px] flex-1 border-none bg-transparent p-1 text-sm outline-none focus:ring-2 focus:ring-focus-ring"</span>
|
||
|
||
<span class="diff-file">TagInput.svelte (line 98) — chip close button:</span>
|
||
<span class="diff-remove">- class="text-ink/50 hover:text-red-500 focus:outline-none"</span>
|
||
<span class="diff-add">+ class="text-ink/50 hover:text-red-500 focus:outline-none focus-visible:ring-2 focus-visible:ring-focus-ring rounded"</span>
|
||
|
||
<span class="diff-file">TagInput.svelte (line 125) — inner text input:</span>
|
||
<span class="diff-remove">- class="h-full w-full border-none bg-transparent p-1 text-sm outline-none focus:ring-0"</span>
|
||
<span class="diff-add">+ class="h-full w-full border-none bg-transparent p-1 text-sm outline-none focus:ring-2 focus:ring-focus-ring"</span>
|
||
|
||
<span class="diff-file">UserGroupsSection.svelte (line 21) — checkbox:</span>
|
||
<span class="diff-remove">- class="rounded border-line text-ink focus:ring-accent"</span>
|
||
<span class="diff-add">+ class="rounded border-line text-ink focus:ring-2 focus:ring-focus-ring focus:ring-offset-2"</span>
|
||
|
||
<span class="diff-file">PersonTypeahead.svelte (lines 157–158) — both input variants:</span>
|
||
<span class="diff-remove">- focus:border-primary focus:outline-none
|
||
- focus:border-accent focus:ring-accent</span>
|
||
<span class="diff-add">+ focus:outline-none focus:ring-2 focus:ring-focus-ring focus:border-focus-ring</span>
|
||
|
||
<span class="diff-file">PersonTypeahead.svelte (line 163) — dropdown list:</span>
|
||
<span class="diff-remove">- ring-1 ring-black</span>
|
||
<span class="diff-add">+ ring-1 ring-line (decorative shadow border — not a focus ring, leave as semantic border color)</span></pre>
|
||
</div>
|
||
|
||
<div class="warn-note" style="margin-top:16px">
|
||
<strong>Tailwind 4 ring default is 1px</strong> (changed from 3px in v3). Always specify <code>ring-2</code> explicitly — never rely on the default. Any component that has only <code>focus:ring</code> without a width is getting a 1px ring, which is too thin for WCAG 2.4.11 (the indicator perimeter area requirement).
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<!-- ═══════════════════════════════════════════════════════════
|
||
5. AUDIT TABLE
|
||
════════════════════════════════════════════════════════════ -->
|
||
<div class="sec">
|
||
<div class="sec-h"><span class="sec-num">5</span> Full Audit — Current vs Target</div>
|
||
|
||
<table class="audit-table">
|
||
<thead>
|
||
<tr>
|
||
<th>File</th>
|
||
<th>Current ring color</th>
|
||
<th>Current ring width</th>
|
||
<th>Light contrast</th>
|
||
<th>Status</th>
|
||
<th>Change</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>AppNav.svelte (×9)</td>
|
||
<td><code>ring-accent</code></td>
|
||
<td><code>ring-2</code> ✓</td>
|
||
<td>1.52:1 on navy bg</td>
|
||
<td><span class="tag-bad">Fail 2.4.11</span></td>
|
||
<td>→ <code>ring-focus-ring</code> (mint on navy = 4.1:1 ✓)</td>
|
||
</tr>
|
||
<tr>
|
||
<td>UserMenu.svelte (×2)</td>
|
||
<td><code>ring-accent</code></td>
|
||
<td><code>ring-2</code> ✓</td>
|
||
<td>1.52:1 on navy bg</td>
|
||
<td><span class="tag-bad">Fail 2.4.11</span></td>
|
||
<td>→ <code>ring-focus-ring</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td>ThemeToggle.svelte</td>
|
||
<td><code>ring-accent</code></td>
|
||
<td><code>ring-2</code> ✓</td>
|
||
<td>1.52:1 on navy bg</td>
|
||
<td><span class="tag-bad">Fail 2.4.11</span></td>
|
||
<td>→ <code>ring-focus-ring</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td>LanguageSwitcher.svelte</td>
|
||
<td><code>ring-accent</code></td>
|
||
<td><code>ring-2</code> ✓</td>
|
||
<td>1.52:1 on navy bg</td>
|
||
<td><span class="tag-bad">Fail 2.4.11</span></td>
|
||
<td>→ <code>ring-focus-ring</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td>NotificationBell.svelte</td>
|
||
<td><code>ring-accent</code></td>
|
||
<td><code>ring-2</code> ✓</td>
|
||
<td>1.52:1 on navy bg</td>
|
||
<td><span class="tag-bad">Fail 2.4.11</span></td>
|
||
<td>→ <code>ring-focus-ring</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td>notifications/+page.svelte (×4)</td>
|
||
<td><code>ring-accent</code></td>
|
||
<td><code>ring-2</code> ✓</td>
|
||
<td>1.52:1 (white offset)</td>
|
||
<td><span class="tag-bad">Fail 2.4.11</span></td>
|
||
<td>→ <code>ring-focus-ring</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td>PersonMultiSelect.svelte chip close</td>
|
||
<td>none (outline-none, no ring)</td>
|
||
<td>—</td>
|
||
<td>0 — invisible</td>
|
||
<td><span class="tag-bad">Fail 2.4.11</span></td>
|
||
<td>Add <code>focus-visible:ring-2 focus-visible:ring-focus-ring</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td>PersonMultiSelect.svelte inner input</td>
|
||
<td>none (outline-none focus:ring-0)</td>
|
||
<td>—</td>
|
||
<td>0 — invisible</td>
|
||
<td><span class="tag-bad">Fail 2.4.11</span></td>
|
||
<td>Add <code>focus:ring-2 focus:ring-focus-ring</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td>TagInput.svelte chip close</td>
|
||
<td>none (outline-none, no ring)</td>
|
||
<td>—</td>
|
||
<td>0 — invisible</td>
|
||
<td><span class="tag-bad">Fail 2.4.11</span></td>
|
||
<td>Add <code>focus-visible:ring-2 focus-visible:ring-focus-ring</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td>TagInput.svelte inner input</td>
|
||
<td>none (outline-none focus:ring-0)</td>
|
||
<td>—</td>
|
||
<td>0 — invisible</td>
|
||
<td><span class="tag-bad">Fail 2.4.11</span></td>
|
||
<td>Add <code>focus:ring-2 focus:ring-focus-ring</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td>UserGroupsSection.svelte checkbox</td>
|
||
<td><code>ring-accent</code></td>
|
||
<td>default (1px)</td>
|
||
<td>1.52:1 on white</td>
|
||
<td><span class="tag-bad">Fail 2.4.11</span></td>
|
||
<td>→ <code>ring-2 ring-focus-ring ring-offset-2</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td>PanelHistory.svelte inputs (×2)</td>
|
||
<td><code>ring-accent</code></td>
|
||
<td><code>ring-1</code> (thin)</td>
|
||
<td>1.52:1 on white</td>
|
||
<td><span class="tag-bad">Fail 2.4.11</span></td>
|
||
<td>→ <code>ring-2 ring-focus-ring</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td>MentionEditor.svelte textarea</td>
|
||
<td><code>ring-accent</code></td>
|
||
<td><code>ring-1</code> (thin)</td>
|
||
<td>1.52:1 on white</td>
|
||
<td><span class="tag-bad">Fail 2.4.11</span></td>
|
||
<td>→ <code>ring-2 ring-focus-ring</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td>PersonTypeahead.svelte input (compact)</td>
|
||
<td><code>border-primary</code> only</td>
|
||
<td>no ring</td>
|
||
<td>border only, no ring</td>
|
||
<td><span class="tag-bad">No ring</span></td>
|
||
<td>Add <code>ring-2 ring-focus-ring</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td>PersonTypeahead.svelte input (standard)</td>
|
||
<td><code>ring-accent</code></td>
|
||
<td>default (1px)</td>
|
||
<td>1.52:1 on white</td>
|
||
<td><span class="tag-bad">Fail 2.4.11</span></td>
|
||
<td>→ <code>ring-2 ring-focus-ring</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td>WhoWhenSection, DescriptionSection, TranscriptionSection inputs</td>
|
||
<td><code>ring-ink</code></td>
|
||
<td>default (1px)</td>
|
||
<td>14:1 on white ✓</td>
|
||
<td><span class="tag-ok">Color OK</span> <span class="tag-bad">Width thin</span></td>
|
||
<td>→ <code>ring-2 ring-focus-ring</code> (and add <code>border-focus-ring</code>)</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Profile forms, password forms</td>
|
||
<td><code>border-ink</code> only (outline-none, no ring)</td>
|
||
<td>no ring</td>
|
||
<td>border only</td>
|
||
<td><span class="tag-bad">No ring</span></td>
|
||
<td>Add <code>ring-2 ring-focus-ring</code> alongside <code>border-focus-ring</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td>SearchFilterBar, ConversationFilterBar inputs</td>
|
||
<td><code>ring-ink</code></td>
|
||
<td>default (1px)</td>
|
||
<td>14:1 on white ✓</td>
|
||
<td><span class="tag-ok">Color OK</span> <span class="tag-bad">Width thin</span></td>
|
||
<td>→ <code>ring-2 ring-focus-ring</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td>forgot-password/+page.svelte input</td>
|
||
<td><code>ring-ink</code></td>
|
||
<td><code>ring-1</code> (thin)</td>
|
||
<td>14:1 on white ✓</td>
|
||
<td><span class="tag-ok">Color OK</span> <span class="tag-bad">Width thin</span></td>
|
||
<td>→ <code>ring-2 ring-focus-ring</code></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
|
||
<!-- ═══════════════════════════════════════════════════════════
|
||
6. ACCEPTANCE CRITERIA
|
||
════════════════════════════════════════════════════════════ -->
|
||
<div class="sec">
|
||
<div class="sec-h"><span class="sec-num">6</span> Acceptance Criteria</div>
|
||
|
||
<div style="background:#fff;border:1px solid #E8E4DF;border-radius:8px;padding:20px 24px;font-size:12px;line-height:1.9">
|
||
<div style="font-size:8px;font-weight:800;text-transform:uppercase;letter-spacing:1px;color:#888;margin-bottom:14px">Issue #167 — All items must pass before closing</div>
|
||
<div style="display:flex;flex-direction:column;gap:6px">
|
||
<label style="display:flex;gap:10px;align-items:flex-start;cursor:default"><input type="checkbox" style="margin-top:3px;flex-shrink:0"> <span><code style="background:#f0efe9;padding:1px 4px;border-radius:3px;font-size:11px">--c-focus-ring</code> token defined in <code style="background:#f0efe9;padding:1px 4px;border-radius:3px;font-size:11px">layout.css</code> in all three blocks: <code>:root</code>, <code>@media (prefers-color-scheme: dark) :root:not([data-theme='light'])</code>, and <code>:root[data-theme='dark']</code></span></label>
|
||
<label style="display:flex;gap:10px;align-items:flex-start;cursor:default"><input type="checkbox" style="margin-top:3px;flex-shrink:0"> <span><code style="background:#f0efe9;padding:1px 4px;border-radius:3px;font-size:11px">--color-focus-ring: var(--c-focus-ring)</code> added to <code>@theme inline</code> block</span></label>
|
||
<label style="display:flex;gap:10px;align-items:flex-start;cursor:default"><input type="checkbox" style="margin-top:3px;flex-shrink:0"> <span>All 5 header components (AppNav, UserMenu, ThemeToggle, LanguageSwitcher, NotificationBell) updated from <code>ring-accent</code> → <code>ring-focus-ring</code></span></label>
|
||
<label style="display:flex;gap:10px;align-items:flex-start;cursor:default"><input type="checkbox" style="margin-top:3px;flex-shrink:0"> <span>All 4 notifications page filter pills updated from <code>ring-accent</code> → <code>ring-focus-ring</code></span></label>
|
||
<label style="display:flex;gap:10px;align-items:flex-start;cursor:default"><input type="checkbox" style="margin-top:3px;flex-shrink:0"> <span>TagInput chip close button: <code>focus:outline-none</code> replaced with <code>focus:outline-none focus-visible:ring-2 focus-visible:ring-focus-ring</code></span></label>
|
||
<label style="display:flex;gap:10px;align-items:flex-start;cursor:default"><input type="checkbox" style="margin-top:3px;flex-shrink:0"> <span>TagInput inner text input: <code>focus:ring-0</code> removed, <code>focus:ring-2 focus:ring-focus-ring</code> added</span></label>
|
||
<label style="display:flex;gap:10px;align-items:flex-start;cursor:default"><input type="checkbox" style="margin-top:3px;flex-shrink:0"> <span>PersonMultiSelect chip close button: same fix as TagInput close button</span></label>
|
||
<label style="display:flex;gap:10px;align-items:flex-start;cursor:default"><input type="checkbox" style="margin-top:3px;flex-shrink:0"> <span>PersonMultiSelect inner input: <code>focus:ring-0</code> removed, <code>focus:ring-2 focus:ring-focus-ring</code> added</span></label>
|
||
<label style="display:flex;gap:10px;align-items:flex-start;cursor:default"><input type="checkbox" style="margin-top:3px;flex-shrink:0"> <span>All form inputs (text, textarea, select) updated to <code>ring-2 ring-focus-ring</code> — no remaining <code>ring-ink</code>, <code>ring-accent</code>, <code>ring-primary</code>, <code>ring-black</code>, or bare <code>ring-1</code></span></label>
|
||
<label style="display:flex;gap:10px;align-items:flex-start;cursor:default"><input type="checkbox" style="margin-top:3px;flex-shrink:0"> <span>UserGroupsSection checkbox updated to <code>ring-2 ring-focus-ring ring-offset-2</code></span></label>
|
||
<label style="display:flex;gap:10px;align-items:flex-start;cursor:default"><input type="checkbox" style="margin-top:3px;flex-shrink:0"> <span>Keyboard-navigate all major pages in light mode — every focusable element shows a visible navy ring at ≥ 3:1 contrast against its background</span></label>
|
||
<label style="display:flex;gap:10px;align-items:flex-start;cursor:default"><input type="checkbox" style="margin-top:3px;flex-shrink:0"> <span>Keyboard-navigate all major pages in dark mode — every focusable element shows a visible mint ring at ≥ 3:1 contrast against its background</span></label>
|
||
<label style="display:flex;gap:10px;align-items:flex-start;cursor:default"><input type="checkbox" style="margin-top:3px;flex-shrink:0"> <span>Tab through TagInput and PersonMultiSelect — chip close buttons show a visible focus ring when reached by keyboard</span></label>
|
||
<label style="display:flex;gap:10px;align-items:flex-start;cursor:default"><input type="checkbox" style="margin-top:3px;flex-shrink:0"> <span>No element in the app has <code>focus:outline-none</code> or <code>outline: none</code> without a corresponding <code>focus-visible:ring-*</code> or <code>focus:ring-*</code> replacement</span></label>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
</div><!-- /doc -->
|
||
</body>
|
||
</html>
|