I am seeking suggestions to enhance both the visual appeal and usability of a complex web-based animation that utilizes extensive CSS and JavaScript. The complete code is also available on my CodePen: https://codepen.io/krn1337/pen/WNBXQKB)
How can the CSS transitions be optimized for smoother animation effects? What improvements can be made to enhance accessibility and usability?
Any insights, detailed feedback, or suggestions would be greatly appreciated!
const elApp = document.querySelector("#app");
const elPlay = document.querySelector('.play');
const machine = {
initial: "small",
states: {
small: {
on: {
click: "medium"
}
},
medium: {
on: {
click: "small"
}
},
large: {
on: {
click: "small"
}
}
}
};
function activate(state) {
elApp.dataset.state = state;
}
function transition(state, event) {
return machine.states[state].on[event] || state;
}
document.querySelectorAll('.thumbnail').forEach(thumbnail => {
thumbnail.addEventListener('click', (e) => {
e.stopPropagation();
const state = elApp.dataset.state;
const newState = transition(state, 'click');
if (state === 'medium' || newState !== state) {
activate(newState);
}
});
});
document.querySelectorAll('.hero-video').forEach(video => {
video.addEventListener('click', (e) => {
if (elApp.dataset.state !== 'small') {
activate('small');
}
e.stopPropagation();
});
});
document.querySelectorAll('.play').forEach(play => {
play.addEventListener('click', (e) => {
e.stopPropagation();
if (elApp.dataset.state !== 'small') {
const c = play.parentNode.querySelector('.empty-iframe');
play.remove(),
c.innerHTML = `<iframe width="1268" height="713" src="https://www.youtube.com/embed/KJwYBJMSbPI" title="EARTH 4K - Relaxation Film - Peaceful Relaxing Music - Nature 4k Video UltraHD - OUR PLANET" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>`;
activate('medium');
}
});
});
document.querySelectorAll('.return-btn').forEach(returnButton => {
returnButton.addEventListener('click', (e) => {
e.stopPropagation();
if (elApp.dataset.state !== 'small') {
let c = returnButton.parentNode.querySelector('.empty-iframe');
let d = returnButton.parentNode.querySelector('.thumbnail');
activate('small');
}
});
});
@import url('https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap');
:root {
--color-primary: #469d38;
--color-dark-gray: #f3f3f3;
--gutter: var(--space-s-l);
--space-s-l: clamp(1.125rem, 0.9545rem + 0.8523vi, 3rem);
--duration: 0.6s;
--long-duration: calc(var(--duration) * 2);
--ease: cubic-bezier(0.2, 0, 0.1, 1);
--ease-out: cubic-bezier(0.2, 0, 1, 0.5);
--blue: #00b5d5;
}
.vid__wr {
aspect-ratio: 16 / 9;
}
body {
font-family: "Montserrat";
}
.w-100 {
width: 100%;
}
.grid {
display: grid;
}
.wrapper {
margin-inline: auto;
max-width: clamp(16rem, var(--wrapper-max-width, 100vw), calc(71.25rem + 2 * var(--gutter)));
padding-left: var(--gutter);
padding-right: var(--gutter);
position: relative;
}
.region {
padding-block: var(--region-space, var(--space-l-xl));
}
.bg-dark-gray {
background: var(--color-dark-gray);
}
#app {
overflow: hidden;
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr;
grid-gap: 2rem;
align-items: stretch;
position: relative;
}
#app[data-state="medium"] .hero-image,
#app[data-state="large"] .hero-image {
width: 100%;
height: 100%;
max-height: 641px;
-o-object-fit: cover;
object-fit: cover;
max-width: unset;
transition: all 300ms ease;
}
.details {
grid-column: 1;
grid-row: 1;
align-self: center;
transition: transform var(--duration) var(--ease), opacity var(--duration) linear;
}
.flow-space-s-m {
--flow-space: clamp(1rem, 0.7rem + 2vw, 1.875rem);
}
.region {
padding-block: var(--region-space, var(--space-l-xl));
}
@media (max-width: 1200px) {
.h__s .region {
--region-space: var(--space-m) 0;
}
}
.container {
max-width: 860px;
padding-right: 20px;
padding-left: 20px;
margin-right: auto;
margin-left: auto;
display: contents;
}
.hero {
margin: 0;
padding: 0;
overflow: hidden;
transition: transform var(--duration) var(--ease);
}
.hero-inner {
display: block;
width: 100%;
height: 100%;
transition: inherit;
overflow: hidden;
}
.hero-image {
display: block;
transition: inherit;
-o-object-fit: cover;
object-fit: cover;
width: 100%;
height: 100%;
transition: all 300ms ease;
}
.play {
--size: 15vmin;
height: var(--size);
width: var(--size);
margin: auto;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 99;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
border-radius: var(--size);
cursor: pointer;
}
.play:before {
font-size: calc(var(--size) / 2);
height: 80%;
width: 80%;
content: '▸';
color: #333;
position: absolute;
background-color: rgba(255, 255, 255, .5);
display: flex;
justify-content: center;
align-items: center;
border-radius: var(--size);
will-change: transform;
transition: transform var(--duration) var(--easing);
transition-duration: var(--duration);
}
.play:after {
content: '';
position: absolute;
height: 100%;
width: 100%;
top: 0;
left: 0;
border: 4vmin solid var(--blue);
border-radius: inherit;
transition-property: transform, opacity;
transition-duration: var(--duration), 0s;
}
.return-btn {
display: none;
}
@media (min-width: 1024px) {
[data-state="small"]::before {
transform: translateY(100%)
}
[data-state="small"] .hero {
transform: translateX(50%);
cursor: pointer
}
[data-state="small"] .hero-inner {}
[data-state="small"] .play:before {
transition-duration: 0s;
transform: translateY(150%)
}
[data-state="small"] .play:after {
opacity: 0;
transform: scale(0)
}
[data-state="medium"] {
--duration: 1s;
}
[data-state="medium"]::before {
transform: translateY(0)
}
[data-state="medium"] .play:before {
transition-duration: var(--duration);
transition-delay: calc(var(--duration) * 0.5);
transform: none
}
[data-state="medium"] .play:after {
opacity: 0;
transform: scale(0.1)
}
[data-state="medium"] .details {
--duration: 0.5s;
--ease: var(--ease-out);
transform: translateY(-200%);
opacity: 0
}
[data-state="medium"] .hero-details {
transition-delay: calc(var(--duration) * .7)
}
[data-state="medium"] .hero-details-inner {
transform: translateY(0)
}
[data-state="medium"] .graph::before,
[data-state="medium"] .graph::after {
transform: translateX(0)
}
[data-state="large"] {
--duration: 1s;
}
[data-state="large"] .play:before {
--ease: var(--ease-out);
transform: translateY(-120%);
transition-delay: calc(var(--duration) * 0.25)
}
[data-state="large"] .play:after {
--duration: 1.5s;
--ease: var(--ease-out);
transform: scale(5)
}
[data-state="large"] .hero {
cursor: pointer
}
[data-state="large"] .details {
transform: translateY(-200%);
opacity: 0;
}
}
.container {
max-width: 860px;
padding-right: 20px;
padding-left: 20px;
margin-right: auto;
margin-left: auto;
display: contents
}
.player__poster {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 2;
transition-duration: 250ms;
transition-property: opacity
}
.player__poster--hidden {
opacity: 0;
pointer-events: none
}
.player__poster-btn {
position: absolute;
top: 50%;
left: 50%;
font-size: 0;
color: transparent;
line-height: 1;
outline: none;
cursor: pointer;
border: none;
background: transparent;
border: none;
transition-duration: 250ms;
transition-property: transform;
transform: translate(-50%, -50%)
}
.player__poster-btn:hover {
transform: translate(-50%, -50%) scale(1.2)
}
.player__poster-btn::before {
font-size: 72px;
color: #fff;
content: "\25B7";
text-shadow: 0 0 5px rgba(0, 0, 0, .75)
}
.player__poster-thumb {
display: block;
max-width: 100%
}
.player__video {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
width: 100%;
height: 100%
}
.player__progress {
position: absolute;
right: 10px;
bottom: -10px;
left: 10px;
z-index: 1;
height: 3px;
background-color: red;
transition-duration: 250ms;
transition-property: bottom
}
.player:not(.player--state-loading):hover .player__progress {
bottom: 10px
}
.player__progress-indicator {
position: absolute;
top: -4px;
left: 0;
width: 11px;
height: 11px;
background-color: red;
border-radius: 50%;
transition-duration: 100ms;
transition-property: left;
transform: translateX(-50%)
}
.player--state-loading .player__poster,
.player--state-loading .player__video {
visibility: hidden
}
@media (min-width: 1024px) {
[data-state="small"] .hero-image {
transform: translateX(25%);
}
}
@media (min-width: 1024px) {
.hero {
grid-column: 1 / -1;
grid-row: 1 / 2;
}
[data-state="small"] :is(.hero-image,
.hero-video) {
max-height: 300px;
max-width: 460px;
}
}
.hero-inner .empty-iframe:has(>iframe)+.hero-image.thumbnail {
display: none;
}
iframe {
width: 100%;
}
[data-state="small"] iframe {
max-height: 300px;
}
[data-state="medium"] .return-btn {
top: 0;
cursor: pointer;
background: white;
border-radius: 0px 0px 2px 0px;
padding: 5px;
position: absolute;
display: block;
}
<div class="h bg-dark-gray region">
<div class="wrapper">
<div id="app" class="h__s grid" data-state="small">
<div class="region space-m flow flow-space-s-m details">
<h1>Lorem ipsum dolor sit amet</h1>
<ul>
<li>Duis lacinia eu nisi ut egestas</li>
<li>Etiam pellentesque urna a accumsan interdum</li>
<li>Suspendisse ut consequat purus</li>
<li>Nulla sollicitudin justo vitae volutpat pharetra</li>
</ul>
</div>
<div class="container vid__wr">
<figure class="hero">
<div class="hero-inner">
<div data-url="https://youtu.be/KJwYBJMSbPI" class="hero-video empty-iframe"></div>
<img class="hero-image thumbnail" src="https://images.unsplash.com/photo-1718043634215-7e011ead1bb3?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" alt="Video Poster">
<div class="play"></div>
<div class="return-btn backArrow"> ➜ </div>
</div>
</figure>
</div>
</div>
</div>
</div>