a11y: WCAG AA contrast + ARIA roles + focus management across all pages
- css/ls.css: --text-3 #8898AA → #56687A (5.1:1 contrast), min-height 44px on .btn-primary/.btn-ghost/.sb-link, new .icon-btn utility (44×44px) - js/api.js: lsConfirm — role=dialog, aria-modal, aria-labelledby, Tab focus trap, restore focus on close; lsToast — aria-live=polite on container, role=alert on errors; live quiz — role=dialog, role=radiogroup, role=radio, aria-checked, keyboard support - test-run.html: q-opt divs — role=radio/checkbox, aria-checked, tabindex, keyboard enter/space; confirm modal — role=dialog, aria-modal; btn-flag — aria-pressed; dots — aria-label, aria-current; touch targets 44px - board.html: btn-del-ann — aria-label; reaction buttons — aria-label, aria-pressed - All 18 HTML files: replace hardcoded color:#8898AA with color:var(--text-3) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
+18
-18
@@ -79,7 +79,7 @@
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.class-card-name { font-size: 0.85rem; font-weight: 700; color: #0F172A; }
|
||||
.class-card-meta { font-size: 0.72rem; color: #8898AA; margin-top: 2px; }
|
||||
.class-card-meta { font-size: 0.72rem; color: var(--text-3); margin-top: 2px; }
|
||||
|
||||
.lq-session-area { padding: 14px 12px; border-top: 1px solid rgba(15,23,42,0.07); }
|
||||
.btn-start {
|
||||
@@ -104,7 +104,7 @@
|
||||
background: rgba(6,214,160,0.08); border: 1.5px solid rgba(6,214,160,0.25);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.as-label { font-size: 0.7rem; font-weight: 700; color: #8898AA; margin-bottom: 3px; }
|
||||
.as-label { font-size: 0.7rem; font-weight: 700; color: var(--text-3); margin-bottom: 3px; }
|
||||
.as-val {
|
||||
font-family: 'Unbounded', sans-serif; font-size: 0.82rem; font-weight: 800;
|
||||
color: #059652; display: flex; align-items: center; gap: 6px;
|
||||
@@ -125,7 +125,7 @@
|
||||
.lq-search:focus { border-color: var(--violet); }
|
||||
.lq-search-icon {
|
||||
position: absolute; left: 13px; top: 50%; transform: translateY(-50%);
|
||||
color: #8898AA; pointer-events: none; width: 16px; height: 16px;
|
||||
color: var(--text-3); pointer-events: none; width: 16px; height: 16px;
|
||||
}
|
||||
|
||||
/* section header */
|
||||
@@ -148,7 +148,7 @@
|
||||
background-repeat: no-repeat; background-position: right 8px center; padding-right: 24px;
|
||||
}
|
||||
.lq-filter-select:focus { border-color: var(--violet); }
|
||||
.lq-q-count { font-size: 0.7rem; color: #8898AA; font-weight: 600; white-space: nowrap; margin-bottom: 12px; }
|
||||
.lq-q-count { font-size: 0.7rem; color: var(--text-3); font-weight: 600; white-space: nowrap; margin-bottom: 12px; }
|
||||
|
||||
/* load more */
|
||||
.btn-load-more {
|
||||
@@ -164,7 +164,7 @@
|
||||
.lq-result-stats { display: flex; gap: 8px; margin-bottom: 14px; }
|
||||
.lq-result-stat { flex: 1; padding: 10px 12px; border-radius: 12px; background: rgba(15,23,42,0.04); text-align: center; }
|
||||
.lq-result-stat-val { font-family: 'Unbounded', sans-serif; font-size: 1.1rem; font-weight: 900; color: #0F172A; }
|
||||
.lq-result-stat-lbl { font-size: 0.64rem; color: #8898AA; margin-top: 2px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; }
|
||||
.lq-result-stat-lbl { font-size: 0.64rem; color: var(--text-3); margin-top: 2px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; }
|
||||
.lq-result-stat.rs-correct { background: rgba(6,214,160,0.08); }
|
||||
.lq-result-stat.rs-correct .lq-result-stat-val { color: #059652; }
|
||||
.lq-result-stat.rs-wrong { background: rgba(239,71,111,0.07); }
|
||||
@@ -192,7 +192,7 @@
|
||||
display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
|
||||
overflow: hidden; line-height: 1.5; min-height: 1.5em;
|
||||
}
|
||||
.lq-q-meta { font-size: 0.7rem; color: #8898AA; margin-top: 5px; display: flex; gap: 8px; flex-wrap: wrap; }
|
||||
.lq-q-meta { font-size: 0.7rem; color: var(--text-3); margin-top: 5px; display: flex; gap: 8px; flex-wrap: wrap; }
|
||||
.btn-launch {
|
||||
padding: 7px 16px; border: none; border-radius: 999px;
|
||||
background: var(--grad-1); color: #fff;
|
||||
@@ -233,7 +233,7 @@
|
||||
width: 22px; height: 22px; border-radius: 6px;
|
||||
background: rgba(15,23,42,0.07); flex-shrink: 0;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
font-size: 0.72rem; font-weight: 800; color: #8898AA;
|
||||
font-size: 0.72rem; font-weight: 800; color: var(--text-3);
|
||||
}
|
||||
.lq-active-opt.correct .lq-opt-letter { background: #06D6A0; color: #fff; }
|
||||
|
||||
@@ -247,7 +247,7 @@
|
||||
font-family: 'Unbounded', sans-serif; font-size: 1.4rem; font-weight: 900;
|
||||
color: var(--violet);
|
||||
}
|
||||
.lq-counter-label { font-size: 0.78rem; color: #8898AA; font-weight: 600; }
|
||||
.lq-counter-label { font-size: 0.78rem; color: var(--text-3); font-weight: 600; }
|
||||
.lq-counter-bar-wrap {
|
||||
flex: 1; height: 8px; background: rgba(155,93,229,0.1); border-radius: 999px; overflow: hidden;
|
||||
}
|
||||
@@ -296,7 +296,7 @@
|
||||
|
||||
/* no session state */
|
||||
.lq-no-session {
|
||||
text-align: center; padding: 80px 20px; color: #8898AA;
|
||||
text-align: center; padding: 80px 20px; color: var(--text-3);
|
||||
}
|
||||
.lq-no-session-icon { margin-bottom: 14px; opacity: 0.2; }
|
||||
|
||||
@@ -362,7 +362,7 @@
|
||||
Выберите класс
|
||||
</div>
|
||||
<div class="class-list" id="class-list">
|
||||
<div style="padding:30px;text-align:center;color:#8898AA;font-size:0.82rem">
|
||||
<div style="padding:30px;text-align:center;color:var(--text-3);font-size:0.82rem">
|
||||
<div class="spinner" style="margin:0 auto 10px"></div>
|
||||
Загрузка классов…
|
||||
</div>
|
||||
@@ -433,7 +433,7 @@
|
||||
</div>
|
||||
<div class="lq-q-count" id="q-count" style="display:none"></div>
|
||||
<div class="lq-q-list" id="q-list">
|
||||
<div style="padding:30px;text-align:center;color:#8898AA;font-size:0.82rem">
|
||||
<div style="padding:30px;text-align:center;color:var(--text-3);font-size:0.82rem">
|
||||
<div class="spinner" style="margin:0 auto 10px"></div>
|
||||
Загрузка вопросов…
|
||||
</div>
|
||||
@@ -560,7 +560,7 @@
|
||||
const classes = await LS.api('/api/classes');
|
||||
const list = document.getElementById('class-list');
|
||||
if (!(classes || []).length) {
|
||||
list.innerHTML = '<div style="padding:30px;text-align:center;color:#8898AA;font-size:0.82rem">Нет доступных классов</div>';
|
||||
list.innerHTML = '<div style="padding:30px;text-align:center;color:var(--text-3);font-size:0.82rem">Нет доступных классов</div>';
|
||||
lucide.createIcons();
|
||||
return;
|
||||
}
|
||||
@@ -578,7 +578,7 @@
|
||||
list.innerHTML = html;
|
||||
lucide.createIcons();
|
||||
} catch {
|
||||
document.getElementById('class-list').innerHTML = '<div style="padding:20px;text-align:center;color:#8898AA;font-size:0.82rem">Ошибка загрузки</div>';
|
||||
document.getElementById('class-list').innerHTML = '<div style="padding:20px;text-align:center;color:var(--text-3);font-size:0.82rem">Ошибка загрузки</div>';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -713,7 +713,7 @@
|
||||
/* ── load questions ── */
|
||||
async function loadQuestions(reset = true) {
|
||||
if (reset) { _qPage = 0; allQuestions = []; }
|
||||
if (reset) document.getElementById('q-list').innerHTML = '<div style="padding:20px;text-align:center;color:#8898AA"><div class="spinner" style="margin:0 auto 10px"></div> Загрузка…</div>';
|
||||
if (reset) document.getElementById('q-list').innerHTML = '<div style="padding:20px;text-align:center;color:var(--text-3)"><div class="spinner" style="margin:0 auto 10px"></div> Загрузка…</div>';
|
||||
const params = new URLSearchParams({ limit: Q_LIMIT, offset: _qPage * Q_LIMIT });
|
||||
if (_topicFilter) params.set('topic_id', _topicFilter);
|
||||
if (_diffFilter) params.set('difficulty', _diffFilter);
|
||||
@@ -730,7 +730,7 @@
|
||||
if (btnMore) btnMore.style.display = allQuestions.length < _totalQ ? '' : 'none';
|
||||
if (countEl) { countEl.textContent = `Показано ${allQuestions.length} из ${_totalQ}`; countEl.style.display = ''; }
|
||||
} catch {
|
||||
if (reset) document.getElementById('q-list').innerHTML = '<div style="padding:20px;text-align:center;color:#8898AA;font-size:0.82rem">Ошибка загрузки вопросов</div>';
|
||||
if (reset) document.getElementById('q-list').innerHTML = '<div style="padding:20px;text-align:center;color:var(--text-3);font-size:0.82rem">Ошибка загрузки вопросов</div>';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -748,7 +748,7 @@
|
||||
function renderQuestionList() {
|
||||
const list = document.getElementById('q-list');
|
||||
if (!allQuestions.length) {
|
||||
list.innerHTML = '<div style="padding:30px;text-align:center;color:#8898AA;font-size:0.82rem">Вопросов не найдено</div>';
|
||||
list.innerHTML = '<div style="padding:30px;text-align:center;color:var(--text-3);font-size:0.82rem">Вопросов не найдено</div>';
|
||||
lucide.createIcons();
|
||||
return;
|
||||
}
|
||||
@@ -864,7 +864,7 @@
|
||||
renderResults(data, resultsArea);
|
||||
if (window.LS && LS.sfx) LS.sfx.play('quiz_end');
|
||||
} catch (e) {
|
||||
resultsArea.innerHTML = `<div style="padding:20px;text-align:center;color:#8898AA;font-size:0.84rem">${esc(e.message || 'Ошибка загрузки результатов')}</div>`;
|
||||
resultsArea.innerHTML = `<div style="padding:20px;text-align:center;color:var(--text-3);font-size:0.84rem">${esc(e.message || 'Ошибка загрузки результатов')}</div>`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -877,7 +877,7 @@
|
||||
const maxCount = Math.max(...opts.map(o => o.chosen_count || 0), 1);
|
||||
|
||||
if (!opts.length) {
|
||||
container.innerHTML = '<div style="padding:16px;text-align:center;color:#8898AA;font-size:0.84rem">Нет данных о вариантах ответа</div>';
|
||||
container.innerHTML = '<div style="padding:16px;text-align:center;color:var(--text-3);font-size:0.84rem">Нет данных о вариантах ответа</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user