audit pass 10-11: type=button, XSS escaping, focus/ARIA fixes

HTML:
- Add type="button" to all buttons outside forms (22 instances)
- Add aria-label="Add comment" to unlabelled textarea#td-comment

JS:
- Escape alt text and link text in markdown renderer with escHtml()
  to prevent XSS in image alt/link content
- Fix nested modal focus: only restore trigger focus when no other
  modal is still open; add document.contains guard

CSS:
- Add .lt-nav-link:focus-visible focus ring (was missing entirely)
- Fix .lt-typeahead-option (dead selector) → .lt-typeahead-item with
  :hover, .is-focused, and :focus-visible for light theme

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-26 20:46:31 -04:00
parent 8b54efef61
commit ca2d6d225e
3 changed files with 58 additions and 45 deletions
+30 -30
View File
@@ -159,7 +159,7 @@
</div>
</div>
<div class="lt-notif-panel-footer">
<button class="lt-btn lt-btn-sm lt-btn-ghost" style="width:100%;font-size:0.72rem">View all notifications</button>
<button type="button" class="lt-btn lt-btn-sm lt-btn-ghost" style="width:100%;font-size:0.72rem">View all notifications</button>
</div>
</div>
</div>
@@ -225,7 +225,7 @@
<!-- Add a comment -->
<div class="lt-divider-label" style="margin:1rem 0 0.75rem">Add Comment</div>
<div class="lt-form-group" style="margin-bottom:0.5rem">
<textarea id="td-comment" class="lt-input lt-textarea" rows="2" placeholder="Leave a comment…" style="resize:vertical"></textarea>
<textarea id="td-comment" class="lt-input lt-textarea" rows="2" placeholder="Leave a comment…" style="resize:vertical" aria-label="Add comment"></textarea>
</div>
<button type="button" class="lt-btn lt-btn-sm" onclick="
const c=document.getElementById('td-comment');
@@ -267,7 +267,7 @@
<h1 class="lt-page-title">Dashboard</h1>
<div class="lt-btn-group">
<a href="/create" class="lt-btn lt-btn-primary">New Ticket</a>
<button class="lt-btn lt-btn-sm" data-modal-open="export-modal">Export</button>
<button type="button" class="lt-btn lt-btn-sm" data-modal-open="export-modal">Export</button>
</div>
</div>
@@ -370,8 +370,8 @@
</div>
<div class="lt-btn-group">
<button class="lt-btn lt-btn-sm">Apply</button>
<button class="lt-btn lt-btn-sm lt-btn-ghost">Reset</button>
<button type="button" class="lt-btn lt-btn-sm">Apply</button>
<button type="button" class="lt-btn lt-btn-sm lt-btn-ghost">Reset</button>
</div>
</div>
</aside>
@@ -388,7 +388,7 @@
</div>
<!-- Advanced filter dropdown -->
<div class="lt-dropdown-wrap" id="adv-filter-wrap">
<button class="lt-btn lt-btn-sm lt-dropdown-trigger" id="adv-filter-btn" aria-expanded="false" aria-haspopup="true">Advanced ▾</button>
<button type="button" class="lt-btn lt-btn-sm lt-dropdown-trigger" id="adv-filter-btn" aria-expanded="false" aria-haspopup="true">Advanced ▾</button>
<div class="lt-dropdown-panel" id="adv-filter-panel" aria-hidden="true">
<div style="padding:0.75rem;display:grid;gap:0.5rem;width:clamp(200px,60vw,260px)">
<div class="lt-form-group" style="margin:0">
@@ -433,7 +433,7 @@
<span class="lt-text-muted lt-text-xs lt-hide-xs" id="ticket-result-count">42 results</span>
<!-- Bulk actions dropdown -->
<div class="lt-dropdown-wrap" id="bulk-action-wrap">
<button class="lt-btn lt-btn-sm lt-btn-ghost lt-dropdown-trigger" id="bulk-action-btn" aria-expanded="false" aria-haspopup="true">Bulk Actions ▾</button>
<button type="button" class="lt-btn lt-btn-sm lt-btn-ghost lt-dropdown-trigger" id="bulk-action-btn" aria-expanded="false" aria-haspopup="true">Bulk Actions ▾</button>
<div class="lt-dropdown-panel lt-dropdown-panel--right" id="bulk-action-panel" aria-hidden="true">
<button type="button" class="lt-dropdown-item" onclick="lt.toast.success('Closed selected tickets');this.closest('.lt-dropdown-panel').setAttribute('aria-hidden','true')">✓ Close Selected</button>
<button type="button" class="lt-dropdown-item" onclick="lt.toast.info('Reassign dialog…');this.closest('.lt-dropdown-panel').setAttribute('aria-hidden','true')">↩ Reassign…</button>
@@ -486,7 +486,7 @@
<td data-label="Actions">
<div class="lt-btn-group">
<a href="/ticket/123456789" class="lt-btn lt-btn-sm">View</a>
<button class="lt-btn lt-btn-sm lt-btn-danger">Close</button>
<button type="button" class="lt-btn lt-btn-sm lt-btn-danger">Close</button>
</div>
</td>
</tr>
@@ -692,12 +692,12 @@
<div class="lt-subsection-header">Buttons</div>
<div class="lt-section-body">
<div class="lt-btn-group">
<button class="lt-btn">Default</button>
<button class="lt-btn lt-btn-primary">Primary</button>
<button class="lt-btn lt-btn-danger">Danger</button>
<button class="lt-btn lt-btn-sm">Small</button>
<button class="lt-btn lt-btn-ghost">Ghost</button>
<button class="lt-btn" disabled>Disabled</button>
<button type="button" class="lt-btn">Default</button>
<button type="button" class="lt-btn lt-btn-primary">Primary</button>
<button type="button" class="lt-btn lt-btn-danger">Danger</button>
<button type="button" class="lt-btn lt-btn-sm">Small</button>
<button type="button" class="lt-btn lt-btn-ghost">Ghost</button>
<button type="button" class="lt-btn" disabled>Disabled</button>
</div>
</div>
@@ -780,8 +780,8 @@
</label>
</div>
<div class="lt-btn-group">
<button class="lt-btn lt-btn-primary">Submit</button>
<button class="lt-btn lt-btn-ghost">Cancel</button>
<button type="button" class="lt-btn lt-btn-primary">Submit</button>
<button type="button" class="lt-btn lt-btn-ghost">Cancel</button>
</div>
</div>
<div>
@@ -876,21 +876,21 @@
<div class="lt-section-body">
<div>
<div class="lt-accordion">
<button class="lt-accordion-header" aria-expanded="false" aria-controls="acc-body-1" data-accordion>
<button type="button" class="lt-accordion-header" aria-expanded="false" aria-controls="acc-body-1" data-accordion>
SYSTEM OVERVIEW
<svg class="lt-accordion-icon" viewBox="0 0 10 6" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true"><path d="M1 1l4 4 4-4"/></svg>
</button>
<div class="lt-accordion-body" id="acc-body-1"><div class="lt-accordion-content">Node running at 72% CPU. 12 active processes. Last restart: 3d ago.</div></div>
</div>
<div class="lt-accordion">
<button class="lt-accordion-header" aria-expanded="false" aria-controls="acc-body-2" data-accordion>
<button type="button" class="lt-accordion-header" aria-expanded="false" aria-controls="acc-body-2" data-accordion>
NETWORK CONFIG
<svg class="lt-accordion-icon" viewBox="0 0 10 6" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true"><path d="M1 1l4 4 4-4"/></svg>
</button>
<div class="lt-accordion-body" id="acc-body-2"><div class="lt-accordion-content">eth0: 10.0.0.7 — MTU 1500 — RX 4.2 GB — TX 1.1 GB</div></div>
</div>
<div class="lt-accordion">
<button class="lt-accordion-header" aria-expanded="false" aria-controls="acc-body-3" data-accordion>
<button type="button" class="lt-accordion-header" aria-expanded="false" aria-controls="acc-body-3" data-accordion>
FIREWALL RULES
<svg class="lt-accordion-icon" viewBox="0 0 10 6" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true"><path d="M1 1l4 4 4-4"/></svg>
</button>
@@ -1039,15 +1039,15 @@
<div style="display:flex;flex-direction:column;gap:var(--space-md)">
<div style="display:flex;gap:var(--space-md);align-items:center;flex-wrap:wrap">
<div class="lt-badge-wrap">
<button class="lt-btn lt-btn-sm">Alerts</button>
<button type="button" class="lt-btn lt-btn-sm">Alerts</button>
<span class="lt-badge">3</span>
</div>
<div class="lt-badge-wrap">
<button class="lt-btn lt-btn-sm">Messages</button>
<button type="button" class="lt-btn lt-btn-sm">Messages</button>
<span class="lt-badge lt-badge--orange">12</span>
</div>
<div class="lt-badge-wrap">
<button class="lt-btn lt-btn-sm">Updates</button>
<button type="button" class="lt-btn lt-btn-sm">Updates</button>
<span class="lt-badge lt-badge--cyan">5</span>
</div>
</div>
@@ -1175,7 +1175,7 @@
</div>
<div class="lt-section-body">
<div class="lt-grid lt-grid-3" style="gap:1rem">
<div class="lt-card"><div class="lt-empty-state"><div class="lt-empty-state-icon">📭</div><div class="lt-empty-state-title">No Tickets Found</div><div class="lt-empty-state-body">No tickets match your current filters.</div><button class="lt-btn lt-btn-sm lt-btn-primary">Clear Filters</button></div></div>
<div class="lt-card"><div class="lt-empty-state"><div class="lt-empty-state-icon">📭</div><div class="lt-empty-state-title">No Tickets Found</div><div class="lt-empty-state-body">No tickets match your current filters.</div><button type="button" class="lt-btn lt-btn-sm lt-btn-primary">Clear Filters</button></div></div>
<div class="lt-card"><div class="lt-empty-state"><div class="lt-empty-state-icon">🔌</div><div class="lt-empty-state-title">No Workers Online</div><div class="lt-empty-state-body">All workers are offline or unreachable.</div><button type="button" class="lt-btn lt-btn-sm" onclick="lt.toast.warning('Checking workers…')">Retry</button></div></div>
<div class="lt-card"><div class="lt-empty-state lt-empty-state--sm"><div class="lt-empty-state-icon">🗂</div><div class="lt-empty-state-title">No Results</div><div class="lt-empty-state-body">Try a different search term.</div></div></div>
</div>
@@ -1204,7 +1204,7 @@
</div>
</div>
<div class="lt-flex lt-gap-md lt-align-center lt-wrap">
<span class="lt-notif-wrap" id="demo-notif-btn"><button class="lt-btn lt-btn-sm">🔔 Alerts</button></span>
<span class="lt-notif-wrap" id="demo-notif-btn"><button type="button" class="lt-btn lt-btn-sm">🔔 Alerts</button></span>
<button type="button" class="lt-btn lt-btn-sm" onclick="lt.notif.inc('#demo-notif-btn')">+1 Badge</button>
<button type="button" class="lt-btn lt-btn-sm" onclick="lt.notif.clear('#demo-notif-btn')">Clear</button>
<button type="button" class="lt-btn lt-btn-sm" onclick="lt.notif.inc('#lt-notif-bell')">+1 Header Bell</button>
@@ -1271,7 +1271,7 @@
<div data-context-menu="demo-ctx" class="lt-card" style="padding:0.75rem 1.25rem;cursor:context-menu;border-style:dashed;display:inline-block">
Right-click this card
</div>
<button class="lt-btn lt-btn-sm" id="demo-ctx-btn">Show Context Menu</button>
<button type="button" class="lt-btn lt-btn-sm" id="demo-ctx-btn">Show Context Menu</button>
</div>
</div>
</div>
@@ -1433,9 +1433,9 @@
</div>
<!-- Nav -->
<div class="lt-wizard-nav">
<button class="lt-btn lt-btn-sm" data-wizard-prev disabled>← Back</button>
<button class="lt-btn lt-btn-primary lt-btn-sm" data-wizard-next>Next →</button>
<button class="lt-btn lt-btn-primary lt-btn-sm" data-wizard-done style="display:none">Submit ✓</button>
<button type="button" class="lt-btn lt-btn-sm" data-wizard-prev disabled>← Back</button>
<button type="button" class="lt-btn lt-btn-primary lt-btn-sm" data-wizard-next>Next →</button>
<button type="button" class="lt-btn lt-btn-primary lt-btn-sm" data-wizard-done style="display:none">Submit ✓</button>
</div>
</div>
</div>
@@ -1484,8 +1484,8 @@
<div class="lt-stat-label">Stopwatch</div>
<div class="lt-countdown lt-num" id="demo-stopwatch">00:00:00</div>
<div class="lt-flex lt-gap-xs" style="margin-top:0.5rem">
<button class="lt-btn lt-btn-sm" id="sw-pause">Pause</button>
<button class="lt-btn lt-btn-sm" id="sw-reset">Reset</button>
<button type="button" class="lt-btn lt-btn-sm" id="sw-pause">Pause</button>
<button type="button" class="lt-btn lt-btn-sm" id="sw-reset">Reset</button>
</div>
</div>
<div class="lt-stat-card">