a11y: Web Interface Guidelines — 19 правок доступности и семантики
- base.html: lang динамический, meta theme-color, skip-link, убран дубль Dashboard - Фильтры: labels на search/select, type=search, placeholder с …, autocomplete=off - llm-retry: <span> → <button> во всех шаблонах (_llm_report, scan, package) - sortable th: tabindex=0, role=button через JS + keyboard handler - sort-icon: aria-hidden=true - style.css: color-scheme:dark, prefers-reduced-motion, tabular-nums, touch-action - app.js: clipboard fallback для не-HTTPS
This commit is contained in:
@@ -1,14 +1,45 @@
|
||||
// GuardDog Nexus — shared UI utilities
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
document.querySelectorAll('th.sortable').forEach(function (th) {
|
||||
th.setAttribute('tabindex', '0');
|
||||
th.setAttribute('role', 'button');
|
||||
});
|
||||
});
|
||||
|
||||
document.addEventListener('keydown', function (e) {
|
||||
if ((e.key === 'Enter' || e.key === ' ') && e.target.matches('th.sortable')) {
|
||||
e.preventDefault();
|
||||
e.target.click();
|
||||
}
|
||||
});
|
||||
|
||||
function copyCode(btn, codeId) {
|
||||
var el = document.getElementById(codeId);
|
||||
if (!el) return;
|
||||
navigator.clipboard.writeText(el.textContent).then(function () {
|
||||
btn.textContent = 'Copied!';
|
||||
btn.classList.add('copied');
|
||||
setTimeout(function () {
|
||||
btn.textContent = 'Copy';
|
||||
btn.classList.remove('copied');
|
||||
}, 2000);
|
||||
});
|
||||
if (navigator.clipboard) {
|
||||
navigator.clipboard.writeText(el.textContent).then(function () {
|
||||
showCopied(btn);
|
||||
});
|
||||
} else {
|
||||
// Fallback for non-HTTPS
|
||||
var ta = document.createElement('textarea');
|
||||
ta.value = el.textContent;
|
||||
ta.style.position = 'fixed';
|
||||
ta.style.left = '-9999px';
|
||||
document.body.appendChild(ta);
|
||||
ta.select();
|
||||
document.execCommand('copy');
|
||||
document.body.removeChild(ta);
|
||||
showCopied(btn);
|
||||
}
|
||||
}
|
||||
|
||||
function showCopied(btn) {
|
||||
btn.textContent = 'Copied!';
|
||||
btn.classList.add('copied');
|
||||
setTimeout(function () {
|
||||
btn.textContent = 'Copy';
|
||||
btn.classList.remove('copied');
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
/* GuardDog Nexus — Web UI styles */
|
||||
|
||||
html { color-scheme: dark; }
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Status / severity colours */
|
||||
/* ------------------------------------------------------------------ */
|
||||
@@ -35,10 +37,16 @@
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Tables */
|
||||
/* ------------------------------------------------------------------ */
|
||||
table { font-size: 0.9rem; }
|
||||
table { font-size: 0.9rem; touch-action: manipulation; }
|
||||
table.compact { font-size: 0.82rem; }
|
||||
table.compact th,
|
||||
table.compact td { padding: 0.35rem 0.5rem; }
|
||||
table td:first-child { font-variant-numeric: tabular-nums; }
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.spinner { animation: none; }
|
||||
.status-scanning { animation: none; }
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
table { display: block; overflow-x: auto; white-space: nowrap; }
|
||||
@@ -182,9 +190,14 @@ table.compact td { padding: 0.35rem 0.5rem; }
|
||||
font-size: 0.7rem;
|
||||
opacity: 0.5;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
border-bottom: 1px dashed;
|
||||
background: none;
|
||||
padding: 0;
|
||||
color: inherit;
|
||||
}
|
||||
.llm-retry:hover { opacity: 0.8; }
|
||||
.llm-retry:focus-visible { outline: 2px solid #2196f3; outline-offset: 2px; }
|
||||
|
||||
.llm-disclaimer {
|
||||
margin-top: 0.6rem;
|
||||
|
||||
Reference in New Issue
Block a user