diff --git a/public/cases/template-elements-demo/index.html b/public/cases/template-elements-demo/index.html index d01804a..373b3b2 100644 --- a/public/cases/template-elements-demo/index.html +++ b/public/cases/template-elements-demo/index.html @@ -453,13 +453,15 @@ Слайдер, кадр 4 +
+ + +
- -
diff --git a/public/css/main.css b/public/css/main.css index e561d1a..1a28c38 100644 --- a/public/css/main.css +++ b/public/css/main.css @@ -2363,7 +2363,17 @@ html[data-theme="dark"] { .tui-spinner > span:nth-child(1) { opacity: 1; } } -/* Лоадер изображения в просмотрщике (обёртка вокруг .tui-spinner) */ +/* Универсальный эффект «продавливания» иконки при нажатии кнопки */ +.icon-press { + transition: transform 0.12s ease; +} + +[class*="btn"]:active:not(:disabled) .icon-press, +[class*="nav"]:active:not(:disabled) .icon-press, +.btn:active:not(:disabled) .icon-press, +.case-media-slider__nav.is-pressed:not(:disabled) .icon-press { + transform: scale(0.82); +} .case-image-viewer__loader { position: absolute; left: 50%; @@ -5338,16 +5348,28 @@ body.content-edit-shift-active.content-edit-mode-active .content-edit-fab-stack --slider-dot-size: 0.5rem; --slider-dot-gap: 0.5rem; position: relative; - margin: 0; - overflow: hidden; + margin: 0.85rem 0 1.2rem; border-radius: var(--slider-radius); } .case-media-slider__viewport { + position: relative; overflow: hidden; width: 100%; + border: 1px solid var(--line); + border-radius: var(--slider-radius); + box-sizing: border-box; + background: color-mix(in srgb, var(--surface) 55%, transparent); touch-action: pan-y; cursor: grab; + transition: + border-color 0.2s ease, + box-shadow 0.2s ease; +} + +.case-media-slider:hover .case-media-slider__viewport { + border-color: var(--accent); + box-shadow: 0 8px 24px color-mix(in srgb, var(--accent) 18%, transparent); } .case-media-slider__viewport:active { @@ -5376,7 +5398,6 @@ body.content-edit-shift-active.content-edit-mode-active .content-edit-fab-stack height: auto; max-height: 80vh; object-fit: contain; - border-radius: var(--slider-radius); } /* Подпись под активным слайдом (как у case-media) */ @@ -5392,48 +5413,67 @@ body.content-edit-shift-active.content-edit-mode-active .content-edit-fab-stack text-align: center; background: color-mix(in srgb, var(--surface) 78%, transparent); backdrop-filter: blur(6px); - border-bottom-left-radius: var(--slider-radius); - border-bottom-right-radius: var(--slider-radius); } /* Стрелки */ -.case-media-slider__nav { +/* Группа кнопок пред/след — слитая «пилюля» в нижнем правом углу */ +.case-media-slider__nav-group { position: absolute; - top: 50%; - transform: translateY(-50%); + right: var(--slider-ctrl-inset); + bottom: var(--slider-ctrl-inset); + z-index: 3; + display: inline-flex; + border-radius: 999px; + overflow: hidden; + opacity: 0; + transition: opacity 0.18s ease; +} + +.case-media-slider:hover .case-media-slider__nav-group, +.case-media-slider__nav-group:focus-within { + opacity: 1; +} + +.case-media-slider__nav { width: var(--slider-ctrl-size); height: var(--slider-ctrl-size); display: inline-flex; align-items: center; justify-content: center; border: 1px solid var(--line); - border-radius: 50%; + border-radius: 0; background: color-mix(in srgb, var(--surface) 86%, transparent); color: var(--text-muted); cursor: pointer; - z-index: 3; - transition: color 0.18s ease, border-color 0.18s ease, background 0.18s ease, - opacity 0.18s ease; + transition: color 0.18s ease, border-color 0.18s ease, background 0.18s ease; } -.case-media-slider__nav--prev { - left: var(--slider-ctrl-inset); +/* Соседняя кнопка перекрывает разделительный бордер */ +.case-media-slider__nav + .case-media-slider__nav { + margin-left: -1px; } -.case-media-slider__nav--next { - right: var(--slider-ctrl-inset); +/* Скругления только на крайних кнопках группы */ +.case-media-slider__nav:first-child { + border-radius: 999px 0 0 999px; +} + +.case-media-slider__nav:last-child { + border-radius: 0 999px 999px 0; } .case-media-slider__nav:hover:not(:disabled), .case-media-slider__nav:focus-visible:not(:disabled) { color: var(--accent-text); border-color: color-mix(in srgb, var(--accent) 45%, var(--line)); - background: var(--accent-soft); + position: relative; + z-index: 1; } .case-media-slider__nav:disabled { - opacity: 0.35; + opacity: 0.4; cursor: default; + pointer-events: none; } .case-media-slider__nav-ico { @@ -5447,8 +5487,11 @@ body.content-edit-shift-active.content-edit-mode-active .content-edit-fab-stack -webkit-mask-repeat: no-repeat; mask-position: center; -webkit-mask-position: center; + transition: transform 0.12s ease; } +/* Нажатие: иконка слегка «продавливается» внутрь (общий .icon-press) */ + .case-media-slider__nav-ico--left { mask-image: url("../assets/shared/icons/shevron-left.svg"); -webkit-mask-image: url("../assets/shared/icons/shevron-left.svg"); @@ -5463,9 +5506,11 @@ body.content-edit-shift-active.content-edit-mode-active .content-edit-fab-stack .case-media-slider__dots { display: flex; flex-wrap: wrap; + align-items: center; justify-content: center; gap: var(--slider-dot-gap); margin-top: 0.9rem; + padding: 0.2rem 0; } .case-media-slider__dot { @@ -5486,12 +5531,41 @@ body.content-edit-shift-active.content-edit-mode-active .content-edit-fab-stack .case-media-slider__dot.is-active { background: var(--accent); - transform: scale(1.25); + transform: scale(1.35); } -/* Неактивные слайды скрыты от скринридеров и не ловят клики по viewer */ +/* Неактивные слайды: скрыты от скринридеров (aria-hidden в JS), но остаются видимыми, + чтобы при сдвиге трека не было «дырки» от мгновенно исчезнувшего кадра. */ .case-media-slider__slide:not(.is-active) { - visibility: hidden; + pointer-events: none; +} + +/* Настраиваемая ширина — как у статичных .case-media (--wide / --content / --natural). + Кнопки пред/след внутри viewport, поэтому не участвуют в расчёте ширины. */ +.case-media-slider.case-media--content, +.case-media-slider.case-media--wide { + display: block; +} + +.case-media-slider.case-media--content .case-media-slider__viewport, +.case-media-slider.case-media--wide .case-media-slider__viewport { + width: 100%; +} + +.case-media-slider.case-media--natural { + display: block; +} + +.case-media-slider.case-media--natural .case-media-slider__viewport { + width: auto; + max-width: 100%; + margin-inline: auto; +} + +.case-media-slider.case-media--natural .case-media-slider__slide img { + width: auto; + max-width: 100%; + margin-inline: auto; } @media (prefers-reduced-motion: reduce) { diff --git a/public/js/modules/cases/image-viewer.js b/public/js/modules/cases/image-viewer.js index e73fd6f..1ec8dff 100644 --- a/public/js/modules/cases/image-viewer.js +++ b/public/js/modules/cases/image-viewer.js @@ -17,10 +17,10 @@ export function initCaseImageViewer() {
diff --git a/public/js/modules/cases/media-slider.js b/public/js/modules/cases/media-slider.js index 5943f78..9ad4141 100644 --- a/public/js/modules/cases/media-slider.js +++ b/public/js/modules/cases/media-slider.js @@ -42,6 +42,20 @@ export function initCaseMediaSlider() { const track = root.querySelector("[data-slider-track]"); const prevBtn = root.querySelector("[data-slider-prev]"); const nextBtn = root.querySelector("[data-slider-next]"); + const navBtns = [prevBtn, nextBtn].filter(Boolean); + + // Имитация :active для иконки (swipe-обработчик viewport сбрасывает нативный :active) + navBtns.forEach((btn) => { + const press = () => btn.classList.add("is-pressed"); + const release = () => btn.classList.remove("is-pressed"); + btn.addEventListener("pointerdown", press); + btn.addEventListener("mousedown", press); + btn.addEventListener("pointerup", release); + btn.addEventListener("mouseup", release); + btn.addEventListener("pointerleave", release); + btn.addEventListener("mouseleave", release); + btn.addEventListener("pointercancel", release); + }); const dotsWrap = root.querySelector("[data-slider-dots]"); if (!viewport || !track) return; diff --git a/public/js/modules/ui/image-load-placeholder.js b/public/js/modules/ui/image-load-placeholder.js index da4ce31..c07b1cb 100644 --- a/public/js/modules/ui/image-load-placeholder.js +++ b/public/js/modules/ui/image-load-placeholder.js @@ -3,7 +3,7 @@ import { isTinyPlaceholderSrc } from "../shared/image-src.js"; const HOST_SELECTOR = ".case-media, .case-media-gallery__item, .case-link-card__media, .case-media-tall__clip, .mp-card__media, .portfolio-card__cover, figure"; -const EXCLUDE_ANCESTOR = ".contact-card__icon, .cookie-notice, .case-image-viewer, [data-no-img-placeholder]"; +const EXCLUDE_ANCESTOR = ".contact-card__icon, .cookie-notice, .case-image-viewer, .case-media-slider, [data-no-img-placeholder]"; function shouldWatch(img) { if (!(img instanceof HTMLImageElement)) return false; diff --git a/src/pages/cases/template-elements-demo.html b/src/pages/cases/template-elements-demo.html index 7222831..c3c8f34 100644 --- a/src/pages/cases/template-elements-demo.html +++ b/src/pages/cases/template-elements-demo.html @@ -264,13 +264,15 @@ Слайдер, кадр 4
+
+ + +
- -