audit pass 12: type=button, focus restore, :focus-visible on links

HTML:
- Add type="button" to all remaining buttons (nav drawer close, menu
  btn, theme toggle, notif bell, right drawer close, tab buttons,
  sidebar toggle, alert close x4, code copy, tab bar buttons x4,
  detail panel open, modal close x2, keyboard shortcuts close)
- Add aria-label="Search commands" to command palette input
- Notification panel close(true): restore focus to bell on Escape
- Generic dropdowns: add Escape key handler with trigger focus restore

CSS:
- Add a:focus-visible global focus ring
- Add .lt-nav-dropdown-menu li a:focus-visible
- Add .lt-markdown a:focus-visible
- Fix dead .lt-typeahead-option selector → .lt-typeahead-item with
  :hover, .is-focused, :focus-visible for light theme

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-27 13:49:23 -04:00
parent ca2d6d225e
commit 45b968b77d
2 changed files with 45 additions and 26 deletions
+10
View File
@@ -227,6 +227,10 @@ a:hover {
color: var(--accent-orange);
text-shadow: var(--glow-orange);
}
a:focus-visible {
outline: 2px solid var(--accent-cyan);
outline-offset: 2px;
}
ul, ol { list-style: none; }
img, svg { display: block; max-width: 100%; }
@@ -543,6 +547,11 @@ hr {
text-shadow: var(--glow-orange);
padding-left: 1.1rem;
}
.lt-nav-dropdown-menu li a:focus-visible {
outline: 2px solid var(--accent-cyan);
outline-offset: -2px;
color: var(--accent-cyan);
}
/* Header user + admin badge */
.lt-header-user {
@@ -5055,6 +5064,7 @@ body.lt-is-offline .lt-main { margin-top: 2rem; transition: margin-top 0.25s eas
.lt-markdown hr { border: none; border-top: 1px solid var(--border-dim); margin: 1rem 0; }
.lt-markdown a { color: var(--accent-cyan); text-decoration: none; }
.lt-markdown a:hover { text-decoration: underline; }
.lt-markdown a:focus-visible { outline: 2px solid var(--accent-cyan); outline-offset: 2px; }
.lt-markdown strong { color: var(--text-primary); }
.lt-markdown img { max-width: 100%; border: 1px solid var(--border-dim); }
.lt-markdown table { width: 100%; border-collapse: collapse; font-size: 0.78rem; margin: 0.75rem 0; }
+35 -26
View File
@@ -65,7 +65,7 @@
<div id="lt-nav-drawer" class="lt-nav-drawer" aria-hidden="true" role="dialog" aria-modal="true" aria-label="Navigation menu">
<div class="lt-nav-drawer-header">
<span class="lt-brand-title">MY APP</span>
<button class="lt-nav-drawer-close" id="lt-nav-drawer-close" aria-label="Close menu"></button>
<button type="button" class="lt-nav-drawer-close" id="lt-nav-drawer-close" aria-label="Close menu"></button>
</div>
<nav class="lt-nav-drawer-links" aria-label="Mobile navigation">
<a href="/" class="lt-nav-drawer-link active" aria-current="page">Dashboard</a>
@@ -84,7 +84,7 @@
<div class="lt-header-left">
<!-- Hamburger — visible only on tablet/mobile (CSS hides on desktop) -->
<button class="lt-menu-btn" id="lt-menu-btn" aria-label="Open navigation menu" aria-expanded="false" aria-controls="lt-nav-drawer">
<button type="button" class="lt-menu-btn" id="lt-menu-btn" aria-label="Open navigation menu" aria-expanded="false" aria-controls="lt-nav-drawer">
<span></span><span></span><span></span>
</button>
@@ -119,10 +119,10 @@
<span class="lt-dot"></span><span>Offline</span>
</div>
<!-- Theme toggle -->
<button class="lt-theme-btn" id="lt-theme-btn" aria-label="Switch to light mode" title="Switch to light mode"></button>
<button type="button" class="lt-theme-btn" id="lt-theme-btn" aria-label="Switch to light mode" title="Switch to light mode"></button>
<!-- Notifications with badge + dropdown -->
<div class="lt-notif-dropdown-wrap" id="lt-notif-bell">
<button class="lt-btn lt-btn-sm lt-notif-bell-btn" id="lt-notif-bell-btn" aria-label="Open notifications" aria-expanded="false" aria-haspopup="true" style="padding:0 0.6rem;">🔔</button>
<button type="button" class="lt-btn lt-btn-sm lt-notif-bell-btn" id="lt-notif-bell-btn" aria-label="Open notifications" aria-expanded="false" aria-haspopup="true" style="padding:0 0.6rem;">🔔</button>
<div class="lt-notif-panel" id="lt-notif-panel" role="region" aria-label="Notifications" aria-hidden="true">
<div class="lt-notif-panel-header">
<span>Notifications</span>
@@ -174,7 +174,7 @@
<div id="lt-detail-drawer" class="lt-drawer-right" aria-hidden="true" role="dialog" aria-modal="true" aria-label="Detail panel" data-overlay="lt-detail-overlay">
<div class="lt-drawer-right-header">
<span class="lt-drawer-right-title">// TICKET DETAIL</span>
<button class="lt-drawer-right-close" data-drawer-close aria-label="Close detail panel"></button>
<button type="button" class="lt-drawer-right-close" data-drawer-close aria-label="Close detail panel"></button>
</div>
<div class="lt-drawer-right-body">
<!-- Read-only meta row -->
@@ -310,9 +310,9 @@
TAB NAVIGATION
========================================================== -->
<div class="lt-tabs" role="tablist" aria-label="Main views">
<button class="lt-tab active" role="tab" data-tab="tab-table" aria-selected="true" aria-controls="tab-table" id="tab-btn-table">Table View</button>
<button class="lt-tab" role="tab" data-tab="tab-kanban" aria-selected="false" aria-controls="tab-kanban" id="tab-btn-kanban">Kanban</button>
<button class="lt-tab" role="tab" data-tab="tab-workers" aria-selected="false" aria-controls="tab-workers" id="tab-btn-workers">Workers</button>
<button type="button" class="lt-tab active" role="tab" data-tab="tab-table" aria-selected="true" aria-controls="tab-table" id="tab-btn-table">Table View</button>
<button type="button" class="lt-tab" role="tab" data-tab="tab-kanban" aria-selected="false" aria-controls="tab-kanban" id="tab-btn-kanban">Kanban</button>
<button type="button" class="lt-tab" role="tab" data-tab="tab-workers" aria-selected="false" aria-controls="tab-workers" id="tab-btn-workers">Workers</button>
</div>
<!-- ==========================================================
@@ -324,7 +324,7 @@
<aside class="lt-sidebar" id="lt-sidebar">
<div class="lt-sidebar-header">
Filters
<button class="lt-sidebar-toggle" data-sidebar-toggle="lt-sidebar"
<button type="button" class="lt-sidebar-toggle" data-sidebar-toggle="lt-sidebar"
aria-label="Collapse filters"></button>
</div>
<div class="lt-sidebar-body">
@@ -905,22 +905,22 @@
<div class="lt-alert">
<span class="lt-alert-icon"></span>
<div class="lt-alert-body"><div class="lt-alert-title">Information</div><div class="lt-alert-msg">Scheduled maintenance window: Sunday 02:0004:00 UTC.</div></div>
<button class="lt-alert-close" data-alert-close aria-label="Dismiss"></button>
<button type="button" class="lt-alert-close" data-alert-close aria-label="Dismiss"></button>
</div>
<div class="lt-alert lt-alert--warning">
<span class="lt-alert-icon"></span>
<div class="lt-alert-body"><div class="lt-alert-title">Warning</div><div class="lt-alert-msg">Disk usage on NODE-03 at 87%. Consider cleanup.</div></div>
<button class="lt-alert-close" data-alert-close aria-label="Dismiss"></button>
<button type="button" class="lt-alert-close" data-alert-close aria-label="Dismiss"></button>
</div>
<div class="lt-alert lt-alert--error">
<span class="lt-alert-icon"></span>
<div class="lt-alert-body"><div class="lt-alert-title">Critical</div><div class="lt-alert-msg">NODE-11 unreachable. Last seen 14m ago.</div></div>
<button class="lt-alert-close" data-alert-close aria-label="Dismiss"></button>
<button type="button" class="lt-alert-close" data-alert-close aria-label="Dismiss"></button>
</div>
<div class="lt-alert lt-alert--success">
<span class="lt-alert-icon"></span>
<div class="lt-alert-body"><div class="lt-alert-title">Success</div><div class="lt-alert-msg">Deployment v2.4.1 completed successfully.</div></div>
<button class="lt-alert-close" data-alert-close aria-label="Dismiss"></button>
<button type="button" class="lt-alert-close" data-alert-close aria-label="Dismiss"></button>
</div>
</div>
@@ -967,7 +967,7 @@
<div class="lt-code-block">
<div class="lt-code-header">
<span class="lt-code-lang">bash</span>
<button class="lt-code-copy" data-copy="systemctl restart nginx && systemctl status nginx">COPY</button>
<button type="button" class="lt-code-copy" data-copy="systemctl restart nginx && systemctl status nginx">COPY</button>
</div>
<pre><code><span class="tok-cmt"># Restart and verify nginx</span>
<span class="tok-kw">systemctl</span> restart nginx <span class="tok-kw">&amp;&amp;</span> <span class="tok-kw">systemctl</span> status nginx
@@ -1064,10 +1064,10 @@
<div class="lt-section-header">Tab Bar</div>
<div class="lt-section-body">
<div class="lt-tab-bar" role="tablist">
<button class="lt-tab active" role="tab" id="tab2-btn-overview" data-tab-target="tab-overview" aria-selected="true" aria-controls="tab-overview">Overview</button>
<button class="lt-tab" role="tab" id="tab2-btn-logs" data-tab-target="tab-logs" aria-selected="false" aria-controls="tab-logs">Logs</button>
<button class="lt-tab" role="tab" id="tab2-btn-metrics" data-tab-target="tab-metrics" aria-selected="false" aria-controls="tab-metrics">Metrics</button>
<button class="lt-tab" role="tab" id="tab2-btn-config" data-tab-target="tab-config" aria-selected="false" aria-controls="tab-config">Config</button>
<button type="button" class="lt-tab active" role="tab" id="tab2-btn-overview" data-tab-target="tab-overview" aria-selected="true" aria-controls="tab-overview">Overview</button>
<button type="button" class="lt-tab" role="tab" id="tab2-btn-logs" data-tab-target="tab-logs" aria-selected="false" aria-controls="tab-logs">Logs</button>
<button type="button" class="lt-tab" role="tab" id="tab2-btn-metrics" data-tab-target="tab-metrics" aria-selected="false" aria-controls="tab-metrics">Metrics</button>
<button type="button" class="lt-tab" role="tab" id="tab2-btn-config" data-tab-target="tab-config" aria-selected="false" aria-controls="tab-config">Config</button>
</div>
<div class="lt-tab-panels" style="padding:var(--space-md) 0">
<div id="tab-overview" class="lt-tab-panel active" role="tabpanel" aria-labelledby="tab2-btn-overview">System overview: all nodes nominal. 14 active sessions.</div>
@@ -1254,7 +1254,7 @@
<div class="lt-section-body">
<p style="font-size:0.78rem;color:var(--text-secondary);margin-bottom:1rem">Detail/inspect panel from the right. Focus trap, ESC close, overlay backdrop, return-focus.</p>
<div class="lt-flex lt-gap-sm lt-wrap">
<button class="lt-btn lt-btn-primary" data-drawer-open="lt-detail-drawer">Open Detail Panel</button>
<button type="button" class="lt-btn lt-btn-primary" data-drawer-open="lt-detail-drawer">Open Detail Panel</button>
<code style="font-size:0.72rem;color:var(--accent-cyan)">lt.rightDrawer.open('id') | .close() | .toggle()</code>
</div>
</div>
@@ -1609,7 +1609,7 @@ Storage array link-down on `compute-storage-01`.
<div class="lt-modal" role="dialog" aria-modal="true" aria-labelledby="export-modal-title">
<div class="lt-modal-header">
<span class="lt-modal-title" id="export-modal-title">Export Tickets</span>
<button class="lt-modal-close" data-modal-close aria-label="Close"></button>
<button type="button" class="lt-modal-close" data-modal-close aria-label="Close"></button>
</div>
<div class="lt-modal-body">
<div class="lt-form-group">
@@ -1640,7 +1640,7 @@ Storage array link-down on `compute-storage-01`.
<div class="lt-modal" role="dialog" aria-modal="true" aria-labelledby="keys-help-title">
<div class="lt-modal-header">
<span class="lt-modal-title" id="keys-help-title">Keyboard Shortcuts</span>
<button class="lt-modal-close" data-modal-close aria-label="Close"></button>
<button type="button" class="lt-modal-close" data-modal-close aria-label="Close"></button>
</div>
<div class="lt-modal-body">
<table class="lt-data-table" style="width:100%">
@@ -1661,7 +1661,7 @@ Storage array link-down on `compute-storage-01`.
</table>
</div>
<div class="lt-modal-footer">
<button class="lt-btn" data-modal-close>Close</button>
<button type="button" class="lt-btn" data-modal-close>Close</button>
</div>
</div>
</div>
@@ -1673,7 +1673,7 @@ Storage array link-down on `compute-storage-01`.
<div class="lt-cmd-palette" id="lt-cmd-palette">
<div class="lt-cmd-input-wrap">
<span class="lt-cmd-prompt">&gt;</span>
<input id="lt-cmd-input" class="lt-cmd-input" type="text" placeholder="Search commands…" autocomplete="off" spellcheck="false">
<input id="lt-cmd-input" class="lt-cmd-input" type="text" placeholder="Search commands…" autocomplete="off" spellcheck="false" aria-label="Search commands">
</div>
<div class="lt-cmd-results" id="lt-cmd-results">
<div class="lt-cmd-empty">Start typing to search…</div>
@@ -1880,9 +1880,10 @@ Storage array link-down on `compute-storage-01`.
btn.setAttribute('aria-expanded', 'true');
// Mark all as read visually
}
function close() {
function close(restoreFocus) {
panel.setAttribute('aria-hidden', 'true');
btn.setAttribute('aria-expanded', 'false');
if (restoreFocus) btn.focus();
}
btn.addEventListener('click', e => {
@@ -1916,8 +1917,8 @@ Storage array link-down on `compute-storage-01`.
if (!document.getElementById('lt-notif-bell').contains(e.target)) close();
});
// Esc close
document.addEventListener('keydown', e => { if (e.key === 'Escape') close(); });
// Esc close — restore focus to bell button
document.addEventListener('keydown', e => { if (e.key === 'Escape' && !panel.hasAttribute('aria-hidden')) close(true); });
}());
// ----- Generic dropdown toggle (Advanced filter + Bulk Actions) -----
@@ -1950,6 +1951,14 @@ Storage array link-down on `compute-storage-01`.
if (t) t.setAttribute('aria-expanded', 'false');
});
});
document.addEventListener('keydown', e => {
if (e.key !== 'Escape') return;
document.querySelectorAll('.lt-dropdown-panel:not([aria-hidden])').forEach(p => {
p.setAttribute('aria-hidden', 'true');
const t = p.closest('.lt-dropdown-wrap').querySelector('.lt-dropdown-trigger');
if (t) { t.setAttribute('aria-expanded', 'false'); t.focus(); }
});
});
// Footer year
const footerYear = document.getElementById('footer-year');