Skip to content

Commit 85c88b1

Browse files
committed
Replace native range input with custom slider for spotlight carousel progress and update styles for improved accessibility and consistency
1 parent f43dd11 commit 85c88b1

6 files changed

Lines changed: 151 additions & 182 deletions

File tree

MovieVerse-Frontend/css/style.css

Lines changed: 30 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1111,112 +1111,72 @@ header h1 {
11111111
height: 16px;
11121112
}
11131113

1114-
.home-page main.spotlight-carousel .scroll-progress-slider::-webkit-slider-runnable-track,
1115-
.spotlight-carousel .scroll-progress-slider::-webkit-slider-runnable-track {
1114+
.home-page main.spotlight-carousel .scroll-progress-slider::before,
1115+
.spotlight-carousel .scroll-progress-slider::before {
11161116
height: 4px;
11171117
transition: none;
11181118
}
11191119

1120-
.home-page main.spotlight-carousel .scroll-progress-slider::-webkit-slider-thumb,
1121-
.spotlight-carousel .scroll-progress-slider::-webkit-slider-thumb {
1120+
.home-page main.spotlight-carousel .scroll-progress-thumb,
1121+
.spotlight-carousel .scroll-progress-thumb {
11221122
width: 14px;
11231123
height: 14px;
1124-
margin-top: -5px;
11251124
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.2);
1126-
cursor: grab;
1127-
}
1128-
1129-
.home-page main.spotlight-carousel .scroll-progress-slider::-moz-range-track,
1130-
.spotlight-carousel .scroll-progress-slider::-moz-range-track {
1131-
height: 4px;
1132-
}
1133-
1134-
.home-page main.spotlight-carousel .scroll-progress-slider::-moz-range-progress,
1135-
.spotlight-carousel .scroll-progress-slider::-moz-range-progress {
1136-
height: 4px;
1137-
}
1138-
1139-
.home-page main.spotlight-carousel .scroll-progress-slider::-moz-range-thumb,
1140-
.spotlight-carousel .scroll-progress-slider::-moz-range-thumb {
1141-
width: 14px;
1142-
height: 14px;
1143-
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.2);
1144-
cursor: grab;
1145-
}
1146-
1147-
.home-page main.spotlight-carousel .scroll-progress-slider,
1148-
.spotlight-carousel .scroll-progress-slider {
1149-
cursor: pointer;
11501125
}
11511126
}
11521127

11531128
.home-page main.spotlight-carousel .scroll-progress-slider,
11541129
.spotlight-carousel .scroll-progress-slider {
11551130
--progress: 0%;
1131+
position: relative;
1132+
display: block;
11561133
width: 100%;
11571134
height: 18px;
11581135
background: transparent;
11591136
padding: 0;
11601137
cursor: pointer;
1161-
appearance: none;
11621138
touch-action: pan-y;
1139+
user-select: none;
1140+
-webkit-user-select: none;
1141+
outline: none;
11631142
}
11641143

1165-
.home-page main.spotlight-carousel .scroll-progress-slider:focus-visible,
1166-
.spotlight-carousel .scroll-progress-slider:focus-visible {
1167-
outline: 2px solid rgba(255, 134, 35, 0.9);
1168-
outline-offset: 4px;
1169-
}
1170-
1171-
.home-page main.spotlight-carousel .scroll-progress-slider::-webkit-slider-runnable-track,
1172-
.spotlight-carousel .scroll-progress-slider::-webkit-slider-runnable-track {
1144+
.home-page main.spotlight-carousel .scroll-progress-slider::before,
1145+
.spotlight-carousel .scroll-progress-slider::before {
1146+
content: '';
1147+
position: absolute;
1148+
left: 0;
1149+
right: 0;
1150+
top: 50%;
1151+
transform: translateY(-50%);
11731152
height: 6px;
11741153
border-radius: 999px;
11751154
background: linear-gradient(90deg, #ff8623 var(--progress), rgba(255, 255, 255, 0.25) var(--progress));
11761155
transition: background 0.2s ease;
1156+
pointer-events: none;
11771157
}
11781158

1179-
.home-page main.spotlight-carousel .scroll-progress-slider::-webkit-slider-thumb,
1180-
.spotlight-carousel .scroll-progress-slider::-webkit-slider-thumb {
1181-
appearance: none;
1182-
width: 18px;
1183-
height: 18px;
1184-
border-radius: 50%;
1185-
background: #ff8623;
1186-
border: 3px solid #ffffff;
1187-
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.25);
1188-
margin-top: -6px;
1189-
cursor: grab;
1190-
}
1191-
1192-
.home-page main.spotlight-carousel .scroll-progress-slider::-moz-range-track,
1193-
.spotlight-carousel .scroll-progress-slider::-moz-range-track {
1194-
height: 6px;
1195-
border-radius: 999px;
1196-
background: rgba(255, 255, 255, 0.25);
1197-
}
1198-
1199-
.home-page main.spotlight-carousel .scroll-progress-slider::-moz-range-progress,
1200-
.spotlight-carousel .scroll-progress-slider::-moz-range-progress {
1201-
height: 6px;
1202-
border-radius: 999px;
1203-
background: #ff8623;
1159+
.home-page main.spotlight-carousel .scroll-progress-slider:focus-visible,
1160+
.spotlight-carousel .scroll-progress-slider:focus-visible {
1161+
outline: 2px solid rgba(255, 134, 35, 0.9);
1162+
outline-offset: 4px;
12041163
}
12051164

1206-
.home-page main.spotlight-carousel .scroll-progress-slider::-moz-range-thumb,
1207-
.spotlight-carousel .scroll-progress-slider::-moz-range-thumb {
1165+
.home-page main.spotlight-carousel .scroll-progress-thumb,
1166+
.spotlight-carousel .scroll-progress-thumb {
1167+
position: absolute;
1168+
top: 50%;
1169+
left: 0%;
12081170
width: 18px;
12091171
height: 18px;
12101172
border-radius: 50%;
12111173
background: #ff8623;
12121174
border: 3px solid #ffffff;
12131175
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.25);
1176+
transform: translate(-50%, -50%);
12141177
cursor: grab;
1215-
}
1216-
1217-
.home-page main.spotlight-carousel .scroll-progress-slider::-moz-focus-outer,
1218-
.spotlight-carousel .scroll-progress-slider::-moz-focus-outer {
1219-
border: 0;
1178+
pointer-events: none;
1179+
will-change: left;
12201180
}
12211181

12221182
.home-page main.spotlight-carousel .spotlight-track,

MovieVerse-Frontend/js/favorites.js

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1294,17 +1294,26 @@ function ensureScrollProgress(mainElement) {
12941294
progress = document.createElement('div');
12951295
progress.className = 'scroll-progress';
12961296
progress.innerHTML =
1297-
'<input class="scroll-progress-slider" type="range" min="0" max="100" step="0.1" value="0" aria-label="Carousel position" />';
1297+
'<div class="scroll-progress-slider" role="slider" tabindex="0" aria-label="Carousel position" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0"><div class="scroll-progress-thumb"></div></div>';
12981298
mainElement.appendChild(progress);
12991299
const slider = progress.querySelector('.scroll-progress-slider');
13001300
if (slider) {
1301-
slider.value = '0';
13021301
slider.style.setProperty('--progress', '0%');
1302+
const thumb = slider.querySelector('.scroll-progress-thumb');
1303+
if (thumb) thumb.style.left = '0%';
13031304
}
13041305
}
13051306
return progress;
13061307
}
13071308

1309+
function applyScrollProgress(slider, percent) {
1310+
const clamped = Math.min(100, Math.max(0, percent));
1311+
slider.style.setProperty('--progress', `${clamped}%`);
1312+
slider.setAttribute('aria-valuenow', String(Math.round(clamped)));
1313+
const thumb = slider.querySelector('.scroll-progress-thumb');
1314+
if (thumb) thumb.style.left = `${clamped}%`;
1315+
}
1316+
13081317
function updateScrollProgress(mainElement) {
13091318
const track = getSpotlightTrack(mainElement);
13101319
const progress = mainElement.querySelector('.scroll-progress');
@@ -1314,13 +1323,17 @@ function updateScrollProgress(mainElement) {
13141323
const maxScroll = track.scrollWidth - track.clientWidth;
13151324
if (maxScroll <= 0) {
13161325
progress.style.display = 'none';
1317-
slider.value = '0';
1318-
slider.style.setProperty('--progress', '0%');
1326+
applyScrollProgress(slider, 0);
13191327
return;
13201328
}
13211329

13221330
progress.style.display = 'block';
13231331

1332+
if (mainElement.dataset.spotlightPristine === 'true') {
1333+
applyScrollProgress(slider, 0);
1334+
return;
1335+
}
1336+
13241337
const cards = getSpotlightCards(mainElement);
13251338
let percent = 0;
13261339

@@ -1329,21 +1342,15 @@ function updateScrollProgress(mainElement) {
13291342
const isScrubbing = mainElement.dataset.spotlightScrubbing === 'true';
13301343

13311344
if (layout === 'left' || isScrubbing) {
1332-
// Multi-card layout, or while the user is actively dragging the slider:
1333-
// follow raw scrollLeft so the thumb tracks the finger 1:1.
13341345
percent = Math.min(100, Math.max(0, (track.scrollLeft / maxScroll) * 100));
13351346
} else {
1336-
// Centered (one-card) layout: drive the thumb from the active-card
1337-
// index so scroll-snap jitter or ad-driven reflows can't leave the
1338-
// thumb out of sync with the card the user is actually viewing.
13391347
const activeIndex = Number(mainElement.dataset.spotlightIndex || 0);
13401348
const clampedIndex = Math.max(0, Math.min(cards.length - 1, activeIndex));
13411349
percent = (clampedIndex / (cards.length - 1)) * 100;
13421350
}
13431351
}
13441352

1345-
slider.value = `${percent}`;
1346-
slider.style.setProperty('--progress', `${percent}%`);
1353+
applyScrollProgress(slider, percent);
13471354
}
13481355

13491356
function bindSpotlightProgressSlider(mainElement) {
@@ -1376,24 +1383,21 @@ function bindSpotlightProgressSlider(mainElement) {
13761383
});
13771384
};
13781385

1379-
const handleInput = () => {
1386+
const handleInput = percent => {
13801387
const track = getSpotlightTrack(mainElement);
13811388
const maxScroll = track.scrollWidth - track.clientWidth;
13821389
if (maxScroll <= 0) return;
1383-
const percent = Number(slider.value);
1384-
const targetLeft = Math.min(maxScroll, Math.max(0, (percent / 100) * maxScroll));
1385-
slider.style.setProperty('--progress', `${percent}%`);
1386-
track.scrollLeft = targetLeft;
1390+
const clamped = Math.min(100, Math.max(0, percent));
1391+
applyScrollProgress(slider, clamped);
1392+
track.scrollLeft = Math.min(maxScroll, Math.max(0, (clamped / 100) * maxScroll));
13871393
};
13881394

13891395
let sliderPointerDown = false;
13901396
const setFromClientX = clientX => {
13911397
const rect = slider.getBoundingClientRect();
1392-
const clamped = Math.min(rect.width, Math.max(0, clientX - rect.left));
1393-
const percent = rect.width ? (clamped / rect.width) * 100 : 0;
1394-
slider.value = String(percent);
1395-
slider.style.setProperty('--progress', `${percent}%`);
1396-
handleInput();
1398+
const within = Math.min(rect.width, Math.max(0, clientX - rect.left));
1399+
const percent = rect.width ? (within / rect.width) * 100 : 0;
1400+
handleInput(percent);
13971401
};
13981402

13991403
const onPointerDown = event => {
@@ -1443,8 +1447,6 @@ function bindSpotlightProgressSlider(mainElement) {
14431447
endScrub();
14441448
};
14451449

1446-
slider.addEventListener('input', handleInput);
1447-
slider.addEventListener('change', handleInput);
14481450
interactionTarget.addEventListener('click', onClick);
14491451
if (supportsPointer) {
14501452
interactionTarget.addEventListener('pointerdown', onPointerDown);

MovieVerse-Frontend/js/movie-timeline.js

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -529,17 +529,26 @@ function ensureScrollProgress(mainElement) {
529529
progress = document.createElement('div');
530530
progress.className = 'scroll-progress';
531531
progress.innerHTML =
532-
'<input class="scroll-progress-slider" type="range" min="0" max="100" step="0.1" value="0" aria-label="Carousel position" />';
532+
'<div class="scroll-progress-slider" role="slider" tabindex="0" aria-label="Carousel position" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0"><div class="scroll-progress-thumb"></div></div>';
533533
mainElement.appendChild(progress);
534534
const slider = progress.querySelector('.scroll-progress-slider');
535535
if (slider) {
536-
slider.value = '0';
537536
slider.style.setProperty('--progress', '0%');
537+
const thumb = slider.querySelector('.scroll-progress-thumb');
538+
if (thumb) thumb.style.left = '0%';
538539
}
539540
}
540541
return progress;
541542
}
542543

544+
function applyScrollProgress(slider, percent) {
545+
const clamped = Math.min(100, Math.max(0, percent));
546+
slider.style.setProperty('--progress', `${clamped}%`);
547+
slider.setAttribute('aria-valuenow', String(Math.round(clamped)));
548+
const thumb = slider.querySelector('.scroll-progress-thumb');
549+
if (thumb) thumb.style.left = `${clamped}%`;
550+
}
551+
543552
function updateScrollProgress(mainElement) {
544553
const track = getSpotlightTrack(mainElement);
545554
const progress = mainElement.querySelector('.scroll-progress');
@@ -549,13 +558,17 @@ function updateScrollProgress(mainElement) {
549558
const maxScroll = track.scrollWidth - track.clientWidth;
550559
if (maxScroll <= 0) {
551560
progress.style.display = 'none';
552-
slider.value = '0';
553-
slider.style.setProperty('--progress', '0%');
561+
applyScrollProgress(slider, 0);
554562
return;
555563
}
556564

557565
progress.style.display = 'block';
558566

567+
if (mainElement.dataset.spotlightPristine === 'true') {
568+
applyScrollProgress(slider, 0);
569+
return;
570+
}
571+
559572
const cards = getSpotlightCards(mainElement);
560573
let percent = 0;
561574

@@ -564,21 +577,15 @@ function updateScrollProgress(mainElement) {
564577
const isScrubbing = mainElement.dataset.spotlightScrubbing === 'true';
565578

566579
if (layout === 'left' || isScrubbing) {
567-
// Multi-card layout, or while the user is actively dragging the slider:
568-
// follow raw scrollLeft so the thumb tracks the finger 1:1.
569580
percent = Math.min(100, Math.max(0, (track.scrollLeft / maxScroll) * 100));
570581
} else {
571-
// Centered (one-card) layout: drive the thumb from the active-card
572-
// index so scroll-snap jitter or ad-driven reflows can't leave the
573-
// thumb out of sync with the card the user is actually viewing.
574582
const activeIndex = Number(mainElement.dataset.spotlightIndex || 0);
575583
const clampedIndex = Math.max(0, Math.min(cards.length - 1, activeIndex));
576584
percent = (clampedIndex / (cards.length - 1)) * 100;
577585
}
578586
}
579587

580-
slider.value = `${percent}`;
581-
slider.style.setProperty('--progress', `${percent}%`);
588+
applyScrollProgress(slider, percent);
582589
}
583590

584591
function bindSpotlightProgressSlider(mainElement) {
@@ -611,24 +618,21 @@ function bindSpotlightProgressSlider(mainElement) {
611618
});
612619
};
613620

614-
const handleInput = () => {
621+
const handleInput = percent => {
615622
const track = getSpotlightTrack(mainElement);
616623
const maxScroll = track.scrollWidth - track.clientWidth;
617624
if (maxScroll <= 0) return;
618-
const percent = Number(slider.value);
619-
const targetLeft = Math.min(maxScroll, Math.max(0, (percent / 100) * maxScroll));
620-
slider.style.setProperty('--progress', `${percent}%`);
621-
track.scrollLeft = targetLeft;
625+
const clamped = Math.min(100, Math.max(0, percent));
626+
applyScrollProgress(slider, clamped);
627+
track.scrollLeft = Math.min(maxScroll, Math.max(0, (clamped / 100) * maxScroll));
622628
};
623629

624630
let sliderPointerDown = false;
625631
const setFromClientX = clientX => {
626632
const rect = slider.getBoundingClientRect();
627-
const clamped = Math.min(rect.width, Math.max(0, clientX - rect.left));
628-
const percent = rect.width ? (clamped / rect.width) * 100 : 0;
629-
slider.value = String(percent);
630-
slider.style.setProperty('--progress', `${percent}%`);
631-
handleInput();
633+
const within = Math.min(rect.width, Math.max(0, clientX - rect.left));
634+
const percent = rect.width ? (within / rect.width) * 100 : 0;
635+
handleInput(percent);
632636
};
633637

634638
const onPointerDown = event => {
@@ -678,8 +682,6 @@ function bindSpotlightProgressSlider(mainElement) {
678682
endScrub();
679683
};
680684

681-
slider.addEventListener('input', handleInput);
682-
slider.addEventListener('change', handleInput);
683685
interactionTarget.addEventListener('click', onClick);
684686
if (supportsPointer) {
685687
interactionTarget.addEventListener('pointerdown', onPointerDown);

0 commit comments

Comments
 (0)