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:
+31
-31
@@ -33,7 +33,7 @@
|
||||
}
|
||||
.etb-back {
|
||||
display: flex; align-items: center; gap: 6px; text-decoration: none;
|
||||
font-size: 0.8rem; font-weight: 700; color: #8898AA; transition: color 0.15s;
|
||||
font-size: 0.8rem; font-weight: 700; color: var(--text-3); transition: color 0.15s;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.etb-back:hover { color: var(--violet); }
|
||||
@@ -54,19 +54,19 @@
|
||||
.etb-select:focus { border-color: var(--violet); }
|
||||
.etb-spacer { flex: 1; }
|
||||
.etb-word-count {
|
||||
font-size: 0.74rem; color: #8898AA; font-weight: 600; white-space: nowrap;
|
||||
font-size: 0.74rem; color: var(--text-3); font-weight: 600; white-space: nowrap;
|
||||
background: rgba(15,23,42,0.04); border-radius: 8px; padding: 4px 9px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.etb-actions { display: flex; gap: 6px; align-items: center; flex-shrink: 0; }
|
||||
.etb-status {
|
||||
font-size: 0.76rem; color: #8898AA; font-weight: 600;
|
||||
font-size: 0.76rem; color: var(--text-3); font-weight: 600;
|
||||
transition: color 0.3s; white-space: nowrap;
|
||||
}
|
||||
.etb-status.saved { color: #059652; }
|
||||
.etb-icon-btn {
|
||||
width: 30px; height: 30px; border: 1.5px solid rgba(15,23,42,0.1); border-radius: 8px;
|
||||
background: #f8f9fc; color: #8898AA; cursor: pointer;
|
||||
background: #f8f9fc; color: var(--text-3); cursor: pointer;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
transition: all 0.12s; flex-shrink: 0;
|
||||
}
|
||||
@@ -130,7 +130,7 @@
|
||||
.palette-search:focus { border-color: var(--violet); background: #fff; }
|
||||
.palette-label {
|
||||
font-size: 0.66rem; font-weight: 800; text-transform: uppercase; letter-spacing: 0.08em;
|
||||
color: #8898AA; margin-bottom: 8px; padding: 0 4px;
|
||||
color: var(--text-3); margin-bottom: 8px; padding: 0 4px;
|
||||
}
|
||||
.palette-btn {
|
||||
width: 100%; padding: 8px 10px; border-radius: 10px;
|
||||
@@ -182,7 +182,7 @@
|
||||
|
||||
.block-header {
|
||||
display: flex; align-items: center; gap: 8px;
|
||||
padding: 10px 14px 0; color: #8898AA;
|
||||
padding: 10px 14px 0; color: var(--text-3);
|
||||
}
|
||||
.block-drag-handle {
|
||||
cursor: grab; color: #CBD5E1; padding: 2px; flex-shrink: 0;
|
||||
@@ -191,7 +191,7 @@
|
||||
.block-drag-handle:active { cursor: grabbing; }
|
||||
.block-type-label {
|
||||
font-size: 0.66rem; font-weight: 700; text-transform: uppercase;
|
||||
letter-spacing: 0.07em; color: #8898AA; flex: 1;
|
||||
letter-spacing: 0.07em; color: var(--text-3); flex: 1;
|
||||
}
|
||||
.block-actions { display: flex; gap: 4px; }
|
||||
.block-action-btn {
|
||||
@@ -251,7 +251,7 @@
|
||||
.sym-cat-row { display: flex; gap: 4px; margin-bottom: 5px; flex-wrap: wrap; }
|
||||
.sym-cat-btn { padding: 2px 8px; border-radius: 5px; border: 1px solid rgba(15,23,42,0.1);
|
||||
background: none; cursor: pointer; font-size: .72rem; font-weight: 600;
|
||||
color: #8898AA; transition: .12s; }
|
||||
color: var(--text-3); transition: .12s; }
|
||||
.sym-cat-btn.active { background: rgba(155,93,229,0.1); border-color: rgba(155,93,229,0.3); color: #9B5DE5; }
|
||||
|
||||
/* ── heading level selector ── */
|
||||
@@ -283,7 +283,7 @@
|
||||
.canvas-hint {
|
||||
text-align: center; padding: 60px 28px;
|
||||
border: 2px dashed rgba(155,93,229,0.15); border-radius: 20px;
|
||||
color: #8898AA; font-size: 0.88rem; line-height: 1.8;
|
||||
color: var(--text-3); font-size: 0.88rem; line-height: 1.8;
|
||||
}
|
||||
.canvas-hint-title { font-family: 'Unbounded', sans-serif; font-size: 0.95rem; font-weight: 800; color: #0F172A; margin-bottom: 8px; }
|
||||
|
||||
@@ -465,7 +465,7 @@
|
||||
}
|
||||
.img-upload-btn:hover { background: rgba(6,214,160,0.14); border-color: #06D6A0; }
|
||||
.img-upload-progress {
|
||||
font-size: 0.78rem; color: #8898AA; font-weight: 600;
|
||||
font-size: 0.78rem; color: var(--text-3); font-weight: 600;
|
||||
}
|
||||
|
||||
/* ── code preview ── */
|
||||
@@ -519,7 +519,7 @@
|
||||
.preview-block .pv-img-wrap.align-right .pv-image { max-width: 55%; }
|
||||
.preview-block .pv-img-wrap.align-full .pv-image { width: 100%; }
|
||||
.preview-block .pv-image-caption {
|
||||
text-align: center; font-size: 0.82rem; color: #8898AA; margin-top: 6px;
|
||||
text-align: center; font-size: 0.82rem; color: var(--text-3); margin-top: 6px;
|
||||
}
|
||||
.align-btns { display: flex; gap: 4px; margin-bottom: 10px; }
|
||||
.align-btn {
|
||||
@@ -581,7 +581,7 @@
|
||||
transform: rotateY(180deg);
|
||||
}
|
||||
.pv-fc-label {
|
||||
font-size: 0.65rem; font-weight: 800; color: #8898AA; text-transform: uppercase;
|
||||
font-size: 0.65rem; font-weight: 800; color: var(--text-3); text-transform: uppercase;
|
||||
letter-spacing: 0.08em; margin-bottom: 10px;
|
||||
}
|
||||
.pv-fc-text { font-size: 1rem; font-weight: 700; color: #0F172A; line-height: 1.55; }
|
||||
@@ -721,7 +721,7 @@
|
||||
background: rgba(155,93,229,0.04); border-radius: 0 12px 12px 0;
|
||||
}
|
||||
.preview-block .pv-quote-text { font-size: 1.05rem; font-style: italic; color: #1E293B; line-height: 1.7; }
|
||||
.preview-block .pv-quote-author { font-size: 0.82rem; font-weight: 700; color: #8898AA; margin-top: 8px; }
|
||||
.preview-block .pv-quote-author { font-size: 0.82rem; font-weight: 700; color: var(--text-3); margin-top: 8px; }
|
||||
|
||||
/* ── C2: checklist ── */
|
||||
.checklist-item-row { display: flex; align-items: center; gap: 8px; margin-bottom: 7px; }
|
||||
@@ -732,7 +732,7 @@
|
||||
font-size: 0.9rem; border-bottom: 1px solid rgba(15,23,42,0.05); cursor: pointer;
|
||||
}
|
||||
.preview-block .pv-checklist-item:last-child { border-bottom: none; }
|
||||
.preview-block .pv-checklist-item.checked { color: #8898AA; text-decoration: line-through; }
|
||||
.preview-block .pv-checklist-item.checked { color: var(--text-3); text-decoration: line-through; }
|
||||
.preview-block .pv-checklist-cb { width: 17px; height: 17px; accent-color: var(--violet); cursor: pointer; flex-shrink: 0; }
|
||||
|
||||
/* ── C3: button/CTA ── */
|
||||
@@ -759,7 +759,7 @@
|
||||
.outline-panel.open { display: block; }
|
||||
.outline-panel-title {
|
||||
font-size: 0.66rem; font-weight: 800; text-transform: uppercase; letter-spacing: 0.08em;
|
||||
color: #8898AA; margin-bottom: 10px; padding: 0 4px;
|
||||
color: var(--text-3); margin-bottom: 10px; padding: 0 4px;
|
||||
}
|
||||
.outline-item {
|
||||
display: block; width: 100%; text-align: left; border: none; background: none;
|
||||
@@ -813,7 +813,7 @@
|
||||
/* ── E5: block type select ── */
|
||||
.block-type-select {
|
||||
font-size: 0.66rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.07em;
|
||||
color: #8898AA; background: none; border: none; cursor: pointer;
|
||||
color: var(--text-3); background: none; border: none; cursor: pointer;
|
||||
font-family: 'Manrope', sans-serif; padding: 2px 4px; border-radius: 4px;
|
||||
flex: 1; max-width: 110px;
|
||||
}
|
||||
@@ -830,7 +830,7 @@
|
||||
<div class="etb-title" id="etb-title">Загрузка…</div>
|
||||
|
||||
<div class="etb-section-wrap" id="etb-section-wrap" style="display:none">
|
||||
<label style="font-size:0.74rem;color:#8898AA;font-weight:600">Раздел:</label>
|
||||
<label style="font-size:0.74rem;color:var(--text-3);font-weight:600">Раздел:</label>
|
||||
<select id="section-select" class="etb-select" onchange="assignSection(this.value)">
|
||||
<option value="">— без раздела —</option>
|
||||
</select>
|
||||
@@ -1662,7 +1662,7 @@
|
||||
case 'button':
|
||||
return renderButtonEditor(b);
|
||||
|
||||
default: return '<span style="color:#8898AA;font-size:0.82rem">Неизвестный тип блока</span>';
|
||||
default: return '<span style="color:var(--text-3);font-size:0.82rem">Неизвестный тип блока</span>';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1855,7 +1855,7 @@
|
||||
<button class="btn-add-item" onclick="document.getElementById('vfile-${bid}').click()">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" style="display:inline-block;vertical-align:middle;margin-right:4px"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>Загрузить файл
|
||||
</button>
|
||||
<span id="vfile-name-${bid}" style="font-size:0.78rem;color:#8898AA"></span>
|
||||
<span id="vfile-name-${bid}" style="font-size:0.78rem;color:var(--text-3)"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="block-row">
|
||||
@@ -2085,7 +2085,7 @@
|
||||
${pairs.map((p, i) => `<div class="matching-pair-row">
|
||||
<input class="block-input" type="text" placeholder="Левая ${i+1}" value="${escAttr(p.left||'')}"
|
||||
oninput="updateMatchingPair('${bid}',${i},'left',this.value)" />
|
||||
<span style="color:#8898AA;font-weight:700;flex-shrink:0"><svg class="ic" viewBox="0 0 24 24"><line x1="3" y1="12" x2="21" y2="12"/><polyline points="8 17 3 12 8 7"/><polyline points="16 7 21 12 16 17"/></svg></span>
|
||||
<span style="color:var(--text-3);font-weight:700;flex-shrink:0"><svg class="ic" viewBox="0 0 24 24"><line x1="3" y1="12" x2="21" y2="12"/><polyline points="8 17 3 12 8 7"/><polyline points="16 7 21 12 16 17"/></svg></span>
|
||||
<input class="block-input" type="text" placeholder="Правая ${i+1}" value="${escAttr(p.right||'')}"
|
||||
oninput="updateMatchingPair('${bid}',${i},'right',this.value)" />
|
||||
<button class="block-action-btn danger" onclick="removeMatchingPair('${bid}',${i})" title="Удалить пару">
|
||||
@@ -2129,7 +2129,7 @@
|
||||
return `<div>
|
||||
<div class="block-field">
|
||||
<div class="block-row-label">Текст с пропусками</div>
|
||||
<div style="font-size:0.74rem;color:#8898AA;margin-bottom:6px">Оберните ответы в фигурные скобки: <code style="background:rgba(155,93,229,0.1);padding:1px 5px;border-radius:4px;font-size:0.74rem">{ответ}</code></div>
|
||||
<div style="font-size:0.74rem;color:var(--text-3);margin-bottom:6px">Оберните ответы в фигурные скобки: <code style="background:rgba(155,93,229,0.1);padding:1px 5px;border-radius:4px;font-size:0.74rem">{ответ}</code></div>
|
||||
<button class="btn-add-opt" style="margin-bottom:6px"
|
||||
onmousedown="event.preventDefault()"
|
||||
onclick="wrapFillBlank('${bid}')">
|
||||
@@ -2180,10 +2180,10 @@
|
||||
oninput="updateBlockData('${bid}','question',this.value);markDirty()" />
|
||||
</div>
|
||||
<div class="block-row-label" style="margin-top:6px">Элементы (текущий порядок = правильный)</div>
|
||||
<div class="ordering-hint" style="font-size:0.74rem;color:#8898AA;margin-bottom:6px">Элементы будут перемешаны для ученика.</div>
|
||||
<div class="ordering-hint" style="font-size:0.74rem;color:var(--text-3);margin-bottom:6px">Элементы будут перемешаны для ученика.</div>
|
||||
<div id="ordering-items-${bid}">
|
||||
${items.map((item, i) => `<div class="ordering-item-row">
|
||||
<span style="color:#8898AA;font-weight:700;font-size:0.8rem;width:22px;text-align:center;flex-shrink:0">${i+1}</span>
|
||||
<span style="color:var(--text-3);font-weight:700;font-size:0.8rem;width:22px;text-align:center;flex-shrink:0">${i+1}</span>
|
||||
<input class="block-input" type="text" placeholder="Элемент ${i+1}" value="${escAttr(item||'')}"
|
||||
oninput="updateOrderingItem('${bid}',${i},this.value)" />
|
||||
<button class="block-action-btn danger" onclick="removeOrderingItem('${bid}',${i})" title="Удалить">
|
||||
@@ -2295,7 +2295,7 @@
|
||||
return `<div>
|
||||
<div class="block-field">
|
||||
<div class="block-row-label">Mermaid-код диаграммы</div>
|
||||
<div style="font-size:0.74rem;color:#8898AA;margin-bottom:6px">
|
||||
<div style="font-size:0.74rem;color:var(--text-3);margin-bottom:6px">
|
||||
Синтаксис: <a href="https://mermaid.js.org/syntax/flowchart.html" target="_blank" style="color:var(--violet)">mermaid.js.org</a>
|
||||
</div>
|
||||
<textarea class="block-textarea" rows="6" style="font-family:monospace;font-size:0.84rem"
|
||||
@@ -2331,7 +2331,7 @@
|
||||
return `<div>
|
||||
<div class="block-field">
|
||||
<div class="block-row-label">ID материала GeoGebra</div>
|
||||
<div style="font-size:0.74rem;color:#8898AA;margin-bottom:6px">
|
||||
<div style="font-size:0.74rem;color:var(--text-3);margin-bottom:6px">
|
||||
Введите ID из URL: geogebra.org/m/<b>abc123</b>
|
||||
</div>
|
||||
<input class="block-input" type="text" placeholder="abc123" value="${escAttr(d.materialId||'')}"
|
||||
@@ -3080,7 +3080,7 @@
|
||||
return `<div class="preview-block"><div class="pv-callout pv-${d.style||'info'}">${d.title?`<strong>${esc(d.title)}</strong><br>`:''}${d.text||''}</div></div>`;
|
||||
case 'video': {
|
||||
const embed = getEmbedUrl(d.url||'');
|
||||
return `<div class="preview-block">${embed ? `<div style="aspect-ratio:16/9;border-radius:12px;overflow:hidden"><iframe src="${escAttr(embed)}" style="width:100%;height:100%;border:none" allowfullscreen></iframe></div>` : ''}${d.caption ? `<div style="font-size:0.82rem;color:#8898AA;margin-top:6px">${esc(d.caption)}</div>` : ''}</div>`;
|
||||
return `<div class="preview-block">${embed ? `<div style="aspect-ratio:16/9;border-radius:12px;overflow:hidden"><iframe src="${escAttr(embed)}" style="width:100%;height:100%;border:none" allowfullscreen></iframe></div>` : ''}${d.caption ? `<div style="font-size:0.82rem;color:var(--text-3);margin-top:6px">${esc(d.caption)}</div>` : ''}</div>`;
|
||||
}
|
||||
case 'table': {
|
||||
const rows = Array.isArray(d.rows) ? d.rows : [];
|
||||
@@ -3129,11 +3129,11 @@
|
||||
return `<div class="preview-block">${items.map(it => `<div style="display:flex;gap:12px;margin-bottom:14px"><div style="display:flex;flex-direction:column;align-items:center"><div style="width:12px;height:12px;border-radius:50%;background:#0EA5E9;flex-shrink:0"></div><div style="width:2px;flex:1;background:rgba(14,165,233,0.2)"></div></div><div><div style="font-size:0.78rem;font-weight:700;color:#0EA5E9">${esc(it.date||'')}</div><div style="font-weight:700;color:#0F172A">${esc(it.title||'')}</div>${it.text?`<div style="font-size:0.85rem;color:#6B7A8E;margin-top:2px">${esc(it.text)}</div>`:''}</div></div>`).join('')}</div>`;
|
||||
}
|
||||
case 'diagram':
|
||||
return `<div class="preview-block"><div class="diagram-preview"><div class="mermaid">${esc(d.code||'')}</div></div>${d.caption?`<div style="text-align:center;font-size:0.82rem;color:#8898AA;margin-top:6px">${esc(d.caption)}</div>`:''}</div>`;
|
||||
return `<div class="preview-block"><div class="diagram-preview"><div class="mermaid">${esc(d.code||'')}</div></div>${d.caption?`<div style="text-align:center;font-size:0.82rem;color:var(--text-3);margin-top:6px">${esc(d.caption)}</div>`:''}</div>`;
|
||||
case 'geogebra':
|
||||
return `<div class="preview-block">${d.materialId?`<div class="geogebra-preview"><iframe src="https://www.geogebra.org/material/iframe/id/${escAttr(d.materialId)}/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="color:#8898AA;text-align:center;padding:20px">Не указан ID материала GeoGebra</div>'}${d.caption?`<div style="text-align:center;font-size:0.82rem;color:#8898AA;margin-top:6px">${esc(d.caption)}</div>`:''}</div>`;
|
||||
return `<div class="preview-block">${d.materialId?`<div class="geogebra-preview"><iframe src="https://www.geogebra.org/material/iframe/id/${escAttr(d.materialId)}/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="color:var(--text-3);text-align:center;padding:20px">Не указан ID материала GeoGebra</div>'}${d.caption?`<div style="text-align:center;font-size:0.82rem;color:var(--text-3);margin-top:6px">${esc(d.caption)}</div>`:''}</div>`;
|
||||
case 'audio':
|
||||
return `<div class="preview-block">${d.url?`<audio controls src="${escAttr(d.url)}" style="width:100%"></audio>`:''}${d.caption?`<div style="font-size:0.82rem;color:#8898AA;margin-top:6px">${esc(d.caption)}</div>`:''}</div>`;
|
||||
return `<div class="preview-block">${d.url?`<audio controls src="${escAttr(d.url)}" style="width:100%"></audio>`:''}${d.caption?`<div style="font-size:0.82rem;color:var(--text-3);margin-top:6px">${esc(d.caption)}</div>`:''}</div>`;
|
||||
case 'columns': {
|
||||
const cols = Array.isArray(d.cols) ? d.cols : [];
|
||||
return `<div class="preview-block"><div style="display:grid;grid-template-columns:repeat(${cols.length},1fr);gap:16px">${cols.map(c => `<div style="font-size:0.88rem;line-height:1.7;color:#3D4F6B">${c.content||'<span style="color:#ccc">Пусто</span>'}</div>`).join('')}</div></div>`;
|
||||
@@ -3212,14 +3212,14 @@
|
||||
'.tpl-card{background:#f8f9fc;border:1.5px solid rgba(15,23,42,0.08);border-radius:14px;padding:14px 16px;cursor:pointer;transition:all .15s;}' +
|
||||
'.tpl-card:hover{border-color:rgba(155,93,229,0.3);background:rgba(155,93,229,0.04);transform:translateY(-2px);box-shadow:0 4px 16px rgba(15,23,42,0.08);}' +
|
||||
'.tpl-card-title{font-size:0.88rem;font-weight:700;color:#0F172A;margin-bottom:4px;display:flex;align-items:center;gap:6px;}' +
|
||||
'.tpl-card-meta{font-size:0.72rem;color:#8898AA;display:flex;align-items:center;gap:8px;}' +
|
||||
'.tpl-card-meta{font-size:0.72rem;color:var(--text-3);display:flex;align-items:center;gap:8px;}' +
|
||||
'.tpl-card-cat{font-size:0.66rem;font-weight:700;padding:2px 8px;border-radius:99px;background:rgba(155,93,229,0.08);color:var(--violet);}' +
|
||||
'.tpl-card-actions{display:flex;gap:4px;margin-top:8px;}' +
|
||||
'.tpl-card-btn{padding:5px 12px;border:1.5px solid rgba(155,93,229,0.2);border-radius:99px;background:rgba(155,93,229,0.06);color:var(--violet);font-family:"Manrope",sans-serif;font-size:0.74rem;font-weight:700;cursor:pointer;transition:all .12s;}' +
|
||||
'.tpl-card-btn:hover{background:var(--violet);color:#fff;}' +
|
||||
'.tpl-card-btn.danger{border-color:rgba(241,91,181,0.2);background:rgba(241,91,181,0.06);color:#E0335E;}' +
|
||||
'.tpl-card-btn.danger:hover{background:#E0335E;color:#fff;}' +
|
||||
'.tpl-empty{text-align:center;padding:32px;color:#8898AA;font-size:0.86rem;}' +
|
||||
'.tpl-empty{text-align:center;padding:32px;color:var(--text-3);font-size:0.86rem;}' +
|
||||
'</style>');
|
||||
|
||||
const CAT_LABELS = { general:'Общее', lecture:'Лекция', practice:'Практика', lab:'Лабораторная', test:'Контроль' };
|
||||
|
||||
Reference in New Issue
Block a user