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 @@
+
+
+
+
-
-
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 @@
+
+
+
+
-
-