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:
Maxim Dolgolyov
2026-04-16 11:42:38 +03:00
parent 3a4623a60a
commit 26ba289019
22 changed files with 362 additions and 299 deletions
+24 -24
View File
@@ -52,7 +52,7 @@
}
.lesson-topbar-back {
display: flex; align-items: center; gap: 6px;
text-decoration: none; color: #8898AA; font-size: 0.8rem; font-weight: 700;
text-decoration: none; color: var(--text-3); font-size: 0.8rem; font-weight: 700;
transition: color 0.15s; flex-shrink: 0;
}
.lesson-topbar-back:hover { color: var(--violet); }
@@ -71,7 +71,7 @@
.lesson-edit-btn:hover { background: rgba(155,93,229,0.12); border-color: var(--violet); }
.lesson-bm-btn {
padding: 6px 10px; border: 1.5px solid rgba(15,23,42,0.12); border-radius: 999px;
background: transparent; color: #8898AA; cursor: pointer; display: flex; align-items: center; gap: 4px;
background: transparent; color: var(--text-3); cursor: pointer; display: flex; align-items: center; gap: 4px;
font-family: 'Manrope', sans-serif; font-size: 0.78rem; font-weight: 700; transition: all 0.15s;
}
.lesson-bm-btn:hover { border-color: #FFD166; color: #FFD166; }
@@ -92,7 +92,7 @@
color: #0F172A; letter-spacing: -0.03em; line-height: 1.25; margin-bottom: 10px;
}
.lesson-course-crumb {
font-size: 0.78rem; color: #8898AA; display: flex; align-items: center; gap: 6px;
font-size: 0.78rem; color: var(--text-3); display: flex; align-items: center; gap: 6px;
}
.lesson-course-crumb a { color: var(--violet); text-decoration: none; }
.lesson-course-crumb a:hover { text-decoration: underline; }
@@ -133,7 +133,7 @@
border: 1px solid rgba(15,23,42,0.08);
}
.block-image-caption {
text-align: center; font-size: 0.76rem; color: #8898AA;
text-align: center; font-size: 0.76rem; color: var(--text-3);
margin-top: 8px; font-style: italic;
}
@@ -187,7 +187,7 @@
padding: 16px; background: #fff; border-radius: 14px;
border: 1.5px solid rgba(168,85,247,0.12); overflow-x: auto;
}
.diagram-caption { font-size: 0.82rem; color: #8898AA; margin-top: 8px; }
.diagram-caption { font-size: 0.82rem; color: var(--text-3); margin-top: 8px; }
/* ── geogebra block ── */
.geogebra-embed {
@@ -195,13 +195,13 @@
border: 1.5px solid rgba(34,197,94,0.15);
}
.geogebra-embed iframe { width: 100%; height: 100%; border: none; }
.geogebra-caption { font-size: 0.82rem; color: #8898AA; margin-top: 8px; text-align: center; }
.geogebra-caption { font-size: 0.82rem; color: var(--text-3); margin-top: 8px; text-align: center; }
/* ── audio block ── */
.block-audio audio {
width: 100%; border-radius: 10px;
}
.audio-caption { font-size: 0.82rem; color: #8898AA; margin-top: 6px; }
.audio-caption { font-size: 0.82rem; color: var(--text-3); margin-top: 6px; }
/* ── columns block ── */
.block-columns {
@@ -275,7 +275,7 @@
.lesson-nav-btn:hover { border-color: var(--violet); color: var(--violet); box-shadow: 0 4px 14px rgba(15,23,42,0.09); }
.lesson-nav-btn-prev { justify-content: flex-start; }
.lesson-nav-btn-next { justify-content: flex-end; margin-left: auto; }
.lesson-nav-btn-label { font-size: 0.7rem; font-weight: 600; color: #8898AA; display: block; }
.lesson-nav-btn-label { font-size: 0.7rem; font-weight: 600; color: var(--text-3); display: block; }
.lesson-nav-btn-title { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 160px; }
/* ── complete button ── */
@@ -313,7 +313,7 @@
/* ── video block ── */
.block-video { border-radius: 16px; overflow: hidden; background: #0F172A; }
.block-video iframe { width: 100%; aspect-ratio: 16/9; border: none; display: block; }
.block-video-caption { text-align: center; font-size: 0.76rem; color: #8898AA; margin-top: 8px; font-style: italic; }
.block-video-caption { text-align: center; font-size: 0.76rem; color: var(--text-3); margin-top: 8px; font-style: italic; }
/* ── table block ── */
.block-table { overflow-x: auto; border-radius: 14px; border: 1.5px solid rgba(15,23,42,0.09); }
@@ -352,7 +352,7 @@
transform: rotateY(180deg);
}
.flashcard-hint {
text-align: center; font-size: 0.7rem; color: #8898AA; margin-top: 8px;
text-align: center; font-size: 0.7rem; color: var(--text-3); margin-top: 8px;
display: flex; align-items: center; justify-content: center; gap: 4px;
}
@@ -371,7 +371,7 @@
padding: 10px 16px; background: rgba(15,23,42,0.03);
border-top: 1px solid rgba(155,93,229,0.1);
}
.sim-caption { font-size: 0.78rem; color: #8898AA; font-weight: 600; }
.sim-caption { font-size: 0.78rem; color: var(--text-3); font-weight: 600; }
.sim-fullscreen-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 6px 14px; border-radius: 999px; border: 1.5px solid rgba(155,93,229,0.25);
@@ -387,7 +387,7 @@
}
.notes-toggle {
display: flex; align-items: center; gap: 8px;
font-size: 0.8rem; font-weight: 700; color: #8898AA;
font-size: 0.8rem; font-weight: 700; color: var(--text-3);
cursor: pointer; background: none; border: none;
padding: 8px 0; transition: color 0.15s; font-family: 'Manrope', sans-serif;
}
@@ -415,13 +415,13 @@
/* ── read time in topbar ── */
.topbar-read-time {
font-size: 0.74rem; color: #8898AA; display: flex; align-items: center; gap: 4px;
font-size: 0.74rem; color: var(--text-3); display: flex; align-items: center; gap: 4px;
}
/* ── toc ── */
.toc-title {
font-family: 'Unbounded', sans-serif; font-size: 0.67rem; font-weight: 800;
color: #8898AA; text-transform: uppercase; letter-spacing: 0.08em;
color: var(--text-3); text-transform: uppercase; letter-spacing: 0.08em;
padding: 0 16px; margin-bottom: 14px;
display: flex; align-items: center; gap: 8px;
}
@@ -435,7 +435,7 @@
text-decoration: none; font-size: 0.79rem; color: #6B7A8E; font-weight: 600;
line-height: 1.45; transition: all 0.12s; margin-bottom: 1px;
}
.toc-item.h3 { padding-left: 28px; font-size: 0.75rem; color: #8898AA; font-weight: 500; }
.toc-item.h3 { padding-left: 28px; font-size: 0.75rem; color: var(--text-3); font-weight: 500; }
.toc-item:hover { color: var(--violet); border-left-color: rgba(155,93,229,0.3); background: rgba(155,93,229,0.04); }
.toc-item.active { color: var(--violet); border-left-color: var(--violet); background: rgba(155,93,229,0.06); font-weight: 700; }
@@ -563,7 +563,7 @@
}
.ordering-item.dragging { opacity: 0.4; }
.ordering-grip {
color: #8898AA; flex-shrink: 0; display: flex; align-items: center;
color: var(--text-3); flex-shrink: 0; display: flex; align-items: center;
}
.ordering-item.correct {
border-color: #06D6A0 !important; background: rgba(6,214,160,0.07) !important; color: #047857 !important;
@@ -594,7 +594,7 @@
}
.comments-count {
font-family: 'Manrope', sans-serif; font-size: 0.72rem; font-weight: 700;
color: #8898AA; background: rgba(15,23,42,0.06); padding: 2px 8px; border-radius: 99px;
color: var(--text-3); background: rgba(15,23,42,0.06); padding: 2px 8px; border-radius: 99px;
}
/* compose */
.comment-compose {
@@ -648,12 +648,12 @@
}
.ci-role-teacher { background: rgba(6,214,160,0.12); color: #059652; }
.ci-role-admin { background: rgba(239,71,111,0.1); color: #EF476F; }
.ci-time { font-size: 0.7rem; color: #8898AA; }
.ci-time { font-size: 0.7rem; color: var(--text-3); }
.ci-text { font-size: 0.86rem; line-height: 1.6; color: #3D4F6B; white-space: pre-wrap; word-wrap: break-word; }
.ci-actions { display: flex; gap: 12px; margin-top: 6px; }
.ci-action-btn {
background: none; border: none; font-size: 0.74rem; font-weight: 600;
color: #8898AA; cursor: pointer; padding: 0;
color: var(--text-3); cursor: pointer; padding: 0;
font-family: 'Manrope', sans-serif; transition: color 0.12s;
}
.ci-action-btn:hover { color: var(--violet); }
@@ -683,7 +683,7 @@
}
.ci-reply-send:disabled { opacity: 0.4; }
.comments-empty {
text-align: center; padding: 28px; color: #8898AA; font-size: 0.84rem;
text-align: center; padding: 28px; color: var(--text-3); font-size: 0.84rem;
}
/* ── Mobile responsive ── */
@@ -1198,7 +1198,7 @@
}
// already embed or direct
if (!embedUrl && (url.includes('/embed/') || url.includes('player.'))) embedUrl = url;
if (!embedUrl) return `<div class="lesson-block block-sim"><div style="color:#8898AA;font-size:0.84rem">Видео: ${esc(url)}</div></div>`;
if (!embedUrl) return `<div class="lesson-block block-sim"><div style="color:var(--text-3);font-size:0.84rem">Видео: ${esc(url)}</div></div>`;
return `<div class="lesson-block block-video">
<iframe src="${esc(embedUrl)}" allowfullscreen loading="lazy"></iframe>
${d.caption ? `<div class="block-video-caption">${esc(d.caption)}</div>` : ''}
@@ -1240,7 +1240,7 @@
return `<div class="lesson-block block-sim">
${embedUrl
? `<iframe class="sim-embed-frame" src="${embedUrl}" loading="lazy" allow="fullscreen"></iframe>`
: `<div style="height:200px;display:flex;align-items:center;justify-content:center;color:#8898AA">Симуляция не указана</div>`}
: `<div style="height:200px;display:flex;align-items:center;justify-content:center;color:var(--text-3)">Симуляция не указана</div>`}
<div class="sim-embed-bar">
<span class="sim-caption">${caption ? esc(caption) : (simId ? '<svg class="ic" viewBox="0 0 24 24"><path d="M6 18h8"/><path d="M3 22h18"/><path d="M14 22a7 7 0 1 0 0-14h-1"/><path d="M9 14l2-7"/><path d="M12 14l2-7"/></svg> Интерактивная симуляция' : '')}</span>
<a class="sim-fullscreen-btn" href="${fullUrl}" target="_blank">
@@ -1354,7 +1354,7 @@
case 'geogebra': {
const mid = d.materialId || '';
return `<div class="lesson-block block-geogebra">
${mid ? `<div class="geogebra-embed"><iframe src="https://www.geogebra.org/material/iframe/id/${escAll(mid)}/width/800/height/500/border/888888/sfsb/true/smb/false/stb/false/stbh/false/ai/false/asb/false/sri/false/rc/false/ld/false/sdz/false/ctl/false" allowfullscreen></iframe></div>` : '<div style="text-align:center;padding:20px;color:#8898AA">Не указан ID материала GeoGebra</div>'}
${mid ? `<div class="geogebra-embed"><iframe src="https://www.geogebra.org/material/iframe/id/${escAll(mid)}/width/800/height/500/border/888888/sfsb/true/smb/false/stb/false/stbh/false/ai/false/asb/false/sri/false/rc/false/ld/false/sdz/false/ctl/false" allowfullscreen></iframe></div>` : '<div style="text-align:center;padding:20px;color:var(--text-3)">Не указан ID материала GeoGebra</div>'}
${d.caption ? `<div class="geogebra-caption">${esc(d.caption)}</div>` : ''}
</div>`;
}
@@ -1466,7 +1466,7 @@
try {
lesson = await LS.api('/api/lessons/' + lessonId);
} catch (e) {
document.getElementById('lesson-body').innerHTML = '<div style="text-align:center;padding:60px;color:#8898AA">Урок не найден</div>';
document.getElementById('lesson-body').innerHTML = '<div style="text-align:center;padding:60px;color:var(--text-3)">Урок не найден</div>';
return;
}