feat: галерея-слайдер, листалка в просмотрщике и TUI braille-спиннер

- case-media-slider: переиспользуемый слайдер (prev/next + точки, swipe, клавиатура, lazy)
- image-viewer: листалка prev/next в тулбаре как пилюли, пагинация-точки в тулбаре слева,
  группа масштаба (slider + текст) с gap 0.25rem, текст масштаба --text-muted/400
- tui-spinner: переиспользуемый braille-спиннер (чистый CSS), лоадер просмотрщика
- design-system.html: живой пример TUI braille-спиннера
- fix: удалён мёртвый textContent='Загрузка...', убивавший .tui-spinner при повторном открытии
This commit is contained in:
root committed 2026-09-09 17:49:56 +02:00
1 parent 9b91ed165f
commit 95b4eaca93
14 files changed
+753 -34

No files matched your search

+318 -13
View File
@@ -2162,11 +2162,18 @@ html[data-theme="dark"] {
}
.case-image-viewer__zoom {
min-width: 3.5rem;
min-width: 2.5rem;
text-align: center;
font-size: 0.82rem;
color: var(--text);
font-weight: 600;
color: var(--text-muted);
font-weight: 400;
}
/* Группа слайдер + текст масштаба с уменьшенным внутренним зазором */
.case-image-viewer__scale-group {
display: inline-flex;
align-items: center;
gap: 0.25rem;
}
.case-image-viewer__slider {
@@ -2310,29 +2317,82 @@ html[data-theme="dark"] {
opacity 0.16s ease;
}
.case-image-viewer__loading {
/* TUI braille-спиннер (переиспользуемый, чистый CSS) */
.tui-spinner {
position: relative;
display: inline-flex;
align-items: center;
justify-content: center;
width: 1.1em;
height: 1.1em;
font-family: ui-monospace, "Cascadia Code", "Consolas", "Courier New", monospace;
font-size: 1.5rem;
line-height: 1;
letter-spacing: 0.1em;
color: var(--accent);
}
.tui-spinner > span {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
animation: tui-spinner-step 0.9s steps(1, end) infinite;
}
.tui-spinner > span:nth-child(1) { animation-delay: 0s; }
.tui-spinner > span:nth-child(2) { animation-delay: 0.09s; }
.tui-spinner > span:nth-child(3) { animation-delay: 0.18s; }
.tui-spinner > span:nth-child(4) { animation-delay: 0.27s; }
.tui-spinner > span:nth-child(5) { animation-delay: 0.36s; }
.tui-spinner > span:nth-child(6) { animation-delay: 0.45s; }
.tui-spinner > span:nth-child(7) { animation-delay: 0.54s; }
.tui-spinner > span:nth-child(8) { animation-delay: 0.63s; }
.tui-spinner > span:nth-child(9) { animation-delay: 0.72s; }
.tui-spinner > span:nth-child(10) { animation-delay: 0.81s; }
@keyframes tui-spinner-step {
0%, 9.999% { opacity: 1; }
10%, 100% { opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
.tui-spinner > span { animation: none; opacity: 0; }
.tui-spinner > span:nth-child(1) { opacity: 1; }
}
/* Лоадер изображения в просмотрщике (обёртка вокруг .tui-spinner) */
.case-image-viewer__loader {
position: absolute;
left: 50%;
top: 50%;
z-index: 2;
transform: translate(-50%, -50%);
min-width: 8.5rem;
padding: 0.5rem 0.8rem;
border-radius: 999px;
text-align: center;
font-size: 0.8rem;
color: var(--text);
background: color-mix(in srgb, var(--surface) 86%, transparent);
border: 1px solid color-mix(in srgb, var(--line) 88%, transparent);
display: inline-flex;
align-items: center;
text-shadow: 0 1px 6px hsla(0, 0%, 0%, 0.35);
pointer-events: none;
opacity: 0;
transition: opacity 0.14s ease;
}
.case-image-viewer.is-loading .case-image-viewer__loading {
.case-image-viewer.is-loading .case-image-viewer__loader {
opacity: 1;
}
.case-image-viewer__loader.is-error .tui-spinner {
display: none;
}
.case-image-viewer__loader-msg {
font-family: var(--font, inherit);
font-size: 0.82rem;
color: var(--text-muted);
margin-left: 0.5rem;
}
.case-image-viewer.is-loading .case-image-viewer__img {
opacity: 0;
}
@@ -2347,6 +2407,80 @@ html[data-theme="dark"] {
cursor: grabbing;
}
/* Кнопки пред/след в тулбаре: группа с отрицательным зазором и скруглением только снаружи */
.case-image-viewer__nav-group {
display: inline-flex;
border-radius: 999px;
overflow: hidden;
}
.case-image-viewer__btn--nav {
--imgv-nav-size: 2rem;
width: var(--imgv-nav-size);
height: var(--imgv-nav-size);
padding: 0;
border-radius: 0;
display: inline-flex;
align-items: center;
justify-content: center;
}
/* Соседняя кнопка сдвигается на 1px, перекрывая разделительный бордер */
.case-image-viewer__btn--nav + .case-image-viewer__btn--nav {
margin-left: -1px;
}
.case-image-viewer__btn--nav[hidden] {
display: none;
}
/* Скругления только на крайних кнопках группы (все 4 угла заданы явно) */
.case-image-viewer__btn--nav:first-child {
border-radius: 999px 0 0 999px;
}
.case-image-viewer__btn--nav:last-child {
border-radius: 0 999px 999px 0;
}
/* У одиночной кнопки в группе — скругление со всех сторон */
.case-image-viewer__btn--nav:only-child {
border-radius: 999px;
}
/* Точки-пагинация в тулбаре просмотрщика */
.case-image-viewer__dots {
display: flex;
align-items: center;
gap: 0.4rem;
padding: 0 0.15rem;
}
.case-image-viewer__dots[hidden] {
display: none;
}
.case-image-viewer__dot {
width: 0.5rem;
height: 0.5rem;
padding: 0;
border: none;
border-radius: 50%;
background: color-mix(in srgb, var(--text-muted) 40%, transparent);
cursor: pointer;
transition: background 0.18s ease, transform 0.18s ease;
}
.case-image-viewer__dot:hover,
.case-image-viewer__dot:focus-visible {
background: color-mix(in srgb, var(--text-muted) 65%, transparent);
}
.case-image-viewer__dot.is-active {
background: var(--accent);
transform: scale(1.25);
}
@media (max-width: 640px) {
.case-link-card {
grid-template-columns: 1fr;
@@ -5194,3 +5328,174 @@ body.content-edit-shift-active.content-edit-mode-active .content-edit-fab-stack
.sep--air-lg {
height: 2.5rem;
}
/* Слайдер изображений в лонгриде кейса: одна картинка + стрелки + точки */
.case-media-slider {
--slider-gap: 0;
--slider-radius: var(--radius-card);
--slider-ctrl-size: 2.5rem;
--slider-ctrl-inset: 0.75rem;
--slider-dot-size: 0.5rem;
--slider-dot-gap: 0.5rem;
position: relative;
margin: 0;
overflow: hidden;
border-radius: var(--slider-radius);
}
.case-media-slider__viewport {
overflow: hidden;
width: 100%;
touch-action: pan-y;
cursor: grab;
}
.case-media-slider__viewport:active {
cursor: grabbing;
}
.case-media-slider__track {
display: flex;
width: 100%;
transition: transform 0.45s cubic-bezier(0.22, 0.61, 0.36, 1);
will-change: transform;
}
.case-media-slider__slide {
flex: 0 0 100%;
min-width: 0;
position: relative;
display: flex;
align-items: center;
justify-content: center;
}
.case-media-slider__slide img {
display: block;
width: 100%;
height: auto;
max-height: 80vh;
object-fit: contain;
border-radius: var(--slider-radius);
}
/* Подпись под активным слайдом (как у case-media) */
.case-media-slider__slide figcaption,
.case-media-slider figcaption {
position: absolute;
bottom: 0;
left: 0;
right: 0;
padding: 0.6rem 0.9rem;
font-size: 0.82rem;
color: var(--text-muted);
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 {
position: absolute;
top: 50%;
transform: translateY(-50%);
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%;
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;
}
.case-media-slider__nav--prev {
left: var(--slider-ctrl-inset);
}
.case-media-slider__nav--next {
right: var(--slider-ctrl-inset);
}
.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);
}
.case-media-slider__nav:disabled {
opacity: 0.35;
cursor: default;
}
.case-media-slider__nav-ico {
width: 1rem;
height: 1rem;
display: block;
background: currentColor;
mask-size: contain;
-webkit-mask-size: contain;
mask-repeat: no-repeat;
-webkit-mask-repeat: no-repeat;
mask-position: center;
-webkit-mask-position: center;
}
.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");
}
.case-media-slider__nav-ico--right {
mask-image: url("../assets/shared/icons/shevron-right.svg");
-webkit-mask-image: url("../assets/shared/icons/shevron-right.svg");
}
/* Точки-пагинация */
.case-media-slider__dots {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: var(--slider-dot-gap);
margin-top: 0.9rem;
}
.case-media-slider__dot {
width: var(--slider-dot-size);
height: var(--slider-dot-size);
padding: 0;
border: none;
border-radius: 50%;
background: color-mix(in srgb, var(--text-muted) 40%, transparent);
cursor: pointer;
transition: background 0.18s ease, transform 0.18s ease;
}
.case-media-slider__dot:hover,
.case-media-slider__dot:focus-visible {
background: color-mix(in srgb, var(--text-muted) 65%, transparent);
}
.case-media-slider__dot.is-active {
background: var(--accent);
transform: scale(1.25);
}
/* Неактивные слайды скрыты от скринридеров и не ловят клики по viewer */
.case-media-slider__slide:not(.is-active) {
visibility: hidden;
}
@media (prefers-reduced-motion: reduce) {
.case-media-slider__track {
transition: none;
}
}