Glossary: replace slider with auto-fit grid filling container width (like Action Plan)

This commit is contained in:
ask 2026-05-17 21:15:43 +05:00
parent 97e5df11b2
commit 27e5307265

View File

@ -858,79 +858,21 @@ main {
.tag.blue { background: rgba(0,102,204,0.1); color: var(--blue); } .tag.blue { background: rgba(0,102,204,0.1); color: var(--blue); }
/* ── GLOSSARY (horizontal slider) ── */ /* ── GLOSSARY (auto-fit grid, fills container) ── */
.glossary-slider {
margin-top: 40px;
width: 100%;
min-width: 0;
}
.glossary { .glossary {
display: flex; margin-top: 40px;
display: grid;
gap: 12px; gap: 12px;
overflow-x: auto; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
overflow-y: hidden;
scroll-snap-type: x mandatory;
-webkit-overflow-scrolling: touch;
padding-bottom: 8px;
/* CRITICAL: constrain to parent so content overflows instead of expanding container */
width: 100%;
min-width: 0;
max-width: 100%;
} }
.glossary::-webkit-scrollbar { height: 6px; }
.glossary::-webkit-scrollbar-track { background: transparent; }
.glossary::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }
.gloss-item { .gloss-item {
border: 1px solid var(--border); border: 1px solid var(--border);
border-radius: 12px; border-radius: 12px;
background: #fff; background: #fff;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
scroll-snap-align: start; min-height: 160px;
min-height: 200px;
flex: 0 0 280px; /* fixed card width, no shrinking */
min-width: 280px;
}
@media (min-width: 640px) {
.gloss-item { min-height: 220px; flex-basis: 320px; min-width: 320px; }
}
/* nav controls: arrows + counter, centered below cards */
.gloss-nav {
margin-top: 20px;
display: flex;
align-items: center;
justify-content: center;
gap: 16px;
}
.gloss-nav-btn {
width: 40px; height: 40px;
border: 1px solid var(--border);
background: #fff;
border-radius: 50%;
display: flex; align-items: center; justify-content: center;
cursor: pointer;
transition: background 150ms ease, opacity 150ms ease, border-color 150ms ease;
color: var(--text-65);
}
.gloss-nav-btn:hover:not(:disabled) {
background: var(--blue);
color: #fff;
border-color: var(--blue);
}
.gloss-nav-btn:disabled { opacity: 0.3; cursor: not-allowed; }
.gloss-nav-btn svg { width: 18px; height: 18px; }
.gloss-counter {
font-size: 0.8125rem;
color: var(--text-55);
font-variant-numeric: tabular-nums;
min-width: 64px;
text-align: center;
} }
.gloss-trigger { .gloss-trigger {
@ -1610,18 +1552,7 @@ footer {
без технического жаргона. без технического жаргона.
</p> </p>
</div> </div>
<div class="glossary-slider">
<div class="glossary" id="glossary-list"></div> <div class="glossary" id="glossary-list"></div>
<div class="gloss-nav">
<button type="button" id="gloss-prev" class="gloss-nav-btn" aria-label="Назад">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="10,3 5,8 10,13"/></svg>
</button>
<span class="gloss-counter" id="gloss-counter">1 / 14</span>
<button type="button" id="gloss-next" class="gloss-nav-btn" aria-label="Вперёд">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6,3 11,8 6,13"/></svg>
</button>
</div>
</div>
</div> </div>
</section> </section>
@ -1916,56 +1847,6 @@ const SCHEDULE = [
glossaryEl.appendChild(div); glossaryEl.appendChild(div);
}); });
// Slider controls — index-based, uses scrollIntoView (more reliable than scrollTo with snap)
const glossPrev = document.getElementById('gloss-prev');
const glossNext = document.getElementById('gloss-next');
const glossCounter = document.getElementById('gloss-counter');
const cards = Array.from(glossaryEl.querySelectorAll('.gloss-item'));
const totalCards = cards.length;
let currentIdx = 0;
function goTo(i) {
currentIdx = Math.max(0, Math.min(totalCards - 1, i));
cards[currentIdx].scrollIntoView({ behavior: 'smooth', inline: 'start', block: 'nearest' });
updateNavState();
}
function updateNavState() {
glossCounter.textContent = `${currentIdx + 1} / ${totalCards}`;
glossPrev.disabled = currentIdx === 0;
glossNext.disabled = currentIdx === totalCards - 1;
}
// Sync currentIdx when user scrolls manually (touch/trackpad)
function syncFromScroll() {
const containerLeft = glossaryEl.getBoundingClientRect().left;
let bestIdx = 0, bestDist = Infinity;
for (let i = 0; i < cards.length; i++) {
const d = Math.abs(cards[i].getBoundingClientRect().left - containerLeft);
if (d < bestDist) { bestDist = d; bestIdx = i; }
}
if (bestIdx !== currentIdx) {
currentIdx = bestIdx;
updateNavState();
}
}
glossPrev.addEventListener('click', () => {
console.log('[gloss] prev clicked, was idx=' + currentIdx);
goTo(currentIdx - 1);
});
glossNext.addEventListener('click', () => {
console.log('[gloss] next clicked, was idx=' + currentIdx);
goTo(currentIdx + 1);
});
glossaryEl.addEventListener('scroll', syncFromScroll, { passive: true });
// Defer to ensure layout is settled before reporting/computing metrics
requestAnimationFrame(() => {
console.log('[gloss] slider init: cards=' + totalCards +
' scrollWidth=' + glossaryEl.scrollWidth +
' clientWidth=' + glossaryEl.clientWidth +
' canScroll=' + (glossaryEl.scrollWidth > glossaryEl.clientWidth));
updateNavState();
});
// Render projects // Render projects
const projectsEl = document.getElementById('projects-grid'); const projectsEl = document.getElementById('projects-grid');