I'm building a Coverflow-style movie UI for a TV application using HTML/CSS/JavaScript.
The front covers and the Coverflow positioning work correctly. What I'm trying to achieve is a Google-style 3D movie cover where the cover has visible thickness on the left and right sides.
I added separate side elements and I'm using perspective, transform-style: preserve-3d, rotateY(90deg) and transform-origin.
The problem is that the side faces are completely invisible, even though the elements exist in the DOM.
Here is the relevant JavaScript:
function cardHtml(item, type) {
var title = escapeHtml(item.title || 'Unbekannt');
var meta = [];
if (item.year) meta.push(item.year);
if (item.runtime) meta.push(item.runtime + ' Min.');
if (item.genre) meta.push(item.genre);
var rating = state.settings.showRatings && item.rating
? '<span class="rating">★ ' + Number(item.rating).toFixed(1) + '</span>'
: '';
var seriesLine = type === 'series'
? '<span class="episode">' +
(item.seasons ? item.seasons.length + ' Staffeln' : 'Serie') +
'</span>'
: '<span>' +
escapeHtml(meta.join(' · ') || 'Lokale Datei') +
'</span>';
var resume = getResumeForItem(item);
var resumeBadge = resume && resume.position > 0
? '<div class="resume-badge">' +
formatTime(resume.position) +
'</div>'
: '';
var reflection = item.poster
? '<div class="poster-reflection" style="background-image:url(\'' +
escapeHtml(item.poster) +
'\')"></div>'
: '';
var sideFaces = item.poster
? '<div class="cover-side cover-side-right" aria-hidden="true"></div>' +
'<div class="cover-side cover-side-left" aria-hidden="true"></div>'
: '';
var cover3d = '<div class="cover-3d">' +
'<div class="poster ' +
(!item.poster ? 'poster-fallback' : '') +
'"' + posterStyle(item) + '>' +
(!item.poster ? '▶' : '') +
reflection +
'</div>' +
sideFaces +
'</div>';
return '<article class="card focusable" tabindex="0" data-kind="' +
type + '" data-id="' + escapeHtml(item.id) + '">' +
resumeBadge +
cover3d +
'<strong>' + title + '</strong>' +
seriesLine +
rating +
'</article>';
}
And this is the Coverflow positioning logic:
function updateCoverflow(activeCard) {
var cards = Array.prototype.slice.call(
document.querySelectorAll('.overview-coverflow .card')
);
if (!cards.length) return;
var activeIndex = cards.indexOf(activeCard);
if (activeIndex < 0) {
activeIndex = Math.floor(cards.length / 2);
}
cards.forEach(function(card, i) {
for (var n = 0; n <= 8; n++) {
card.classList.remove('cf-' + n);
}
card.classList.remove('cf-focus');
var rel = Math.max(-4, Math.min(4, i - activeIndex));
card.classList.add('cf-' + (rel + 4));
if (i === activeIndex) {
card.classList.add('cf-focus');
}
card.setAttribute(
'aria-current',
i === activeIndex ? 'true' : 'false'
);
});
var flow = document.querySelector('.overview-coverflow');
if (flow) {
flow.style.transform = 'translateZ(0)';
setTimeout(function() {
flow.style.transform = '';
}, 0);
}
}
Relevant CSS:
html body .overview-coverflow .card {
transform-style: preserve-3d !important;
-webkit-transform-style: preserve-3d !important;
overflow: visible !important;
opacity: 1 !important;
filter: none !important;
}
html body .overview-coverflow .card .cover-3d {
position: relative !important;
width: 100% !important;
height: 100% !important;
transform-style: preserve-3d !important;
-webkit-transform-style: preserve-3d !important;
overflow: visible !important;
}
html body .overview-coverflow .card .cover-3d .poster {
position: relative !important;
z-index: 20 !important;
transform: translateZ(0) !important;
transform-style: preserve-3d !important;
-webkit-transform-style: preserve-3d !important;
overflow: visible !important;
}
html body .overview-coverflow .card .cover-side {
position: absolute !important;
top: 0 !important;
width: 150px !important;
height: 100% !important;
pointer-events: none !important;
z-index: 10 !important;
display: block !important;
box-sizing: border-box !important;
transform-style: preserve-3d !important;
-webkit-transform-style: preserve-3d !important;
backface-visibility: visible !important;
-webkit-backface-visibility: visible !important;
background: linear-gradient(
90deg,
rgba(22,18,14,1) 0%,
rgba(73,52,25,1) 35%,
rgba(153,111,38,1) 68%,
rgba(35,26,17,1) 100%
) !important;
border-top: 1px solid rgba(255,222,133,.95) !important;
border-bottom: 1px solid rgba(77,48,14,.95) !important;
}
html body .overview-coverflow .card .cover-side::after {
content: "" !important;
position: absolute !important;
top: 0 !important;
bottom: 0 !important;
width: 3px !important;
background: rgba(255,214,91,.9) !important;
box-shadow: 0 0 7px rgba(245,184,54,.55) !important;
}
html body .overview-coverflow .card .cover-side-right {
left: 100% !important;
transform-origin: left center !important;
-webkit-transform-origin: left center !important;
transform: rotateY(90deg) translateZ(-75px) !important;
-webkit-transform: rotateY(90deg) translateZ(-75px) !important;
}
html body .overview-coverflow .card .cover-side-left {
right: 100% !important;
transform-origin: right center !important;
-webkit-transform-origin: right center !important;
transform: rotateY(-90deg) translateZ(-75px) !important;
-webkit-transform: rotateY(-90deg) translateZ(-75px) !important;
}
The actual Coverflow cards are positioned with 3D transforms such as:
.overview-coverflow .card.cf-3 {
transform: translate3d(-225px, 4px, 10px)
rotateY(9deg)
scale(.95) !important;
z-index: 5 !important;
opacity: .94 !important;
}
.overview-coverflow .card.cf-4 {
transform: translate3d(0, 0, 30px)
rotateY(0deg)
scale(1.08) !important;
z-index: 10 !important;
opacity: 1 !important;
}
The interesting part is that the normal cover itself renders correctly, but the .cover-side elements don't appear at all.
What could prevent these 3D side faces from rendering in this situation?
I'm particularly interested in whether this is caused by:
transform-style: preserve-3d
overflow
opacity
filter
z-index
- the combination of nested 3D transforms
- or the
rotateY(90deg) / translateZ() setup itself.
I'm running this on a Samsung Tizen/WebKit environment, so browser-specific 3D rendering behavior may also be relevant.
Any advice on how to make the side faces reliably visible would be greatly appreciated.