In this technical deep dive, we'll explore the implementation details, design decisions, and the clever techniques used to create this polished component.
Published on December 24, 2024 by Soulless Studio
jekyll html css javascript component-design
10 min READ
The interactive game card component is a cornerstone of our website’s user interface, offering an engaging way to showcase our games portfolio. In this technical deep dive, we’ll explore the implementation details, design decisions, and the clever techniques used to create this polished component.
This flippable card is primarily used on our games portfolio section to provide visitors with a quick, interactive look at each game. The circular design and DVD-inspired styling help unify the brand aesthetic, generate a sense of nostalgia or intrigue, and draw users in.
At its heart, the game card is a flippable circular component that presents game information in an engaging manner. When users interact with it, the card smoothly rotates to reveal additional details about the game. This interaction is achieved through a combination of modern CSS transforms and carefully orchestrated animations.
The component’s HTML structure is deliberately organised in a hierarchical manner:
<div class="game-circle-wrapper">
<div class="game-circle">
<div class="game-circle-front">
<h3 class="game-title"></h3>
</div>
<div class="game-circle-back">
<a href="" class="learn-more-btn">Learn More</a>
</div>
</div>
</div>
This structure serves several crucial purposes:
game-circle-wrapper
) establishes the perspective context for 3D transformsgame-circle
) handles the actual rotationgame-circle-front
and game-circle-back
) represent the two faces of our cardWhile the is-flipped
class is referenced throughout this guide, you may want to see how it’s actually applied in practice. The is-flipped
class is implemented through a simple yet effective event listener system. When the DOM content is fully loaded, we attach click event listeners to all game circle wrappers. Here’s the actual implementation:
document.addEventListener('DOMContentLoaded', function() {
const circles = document.querySelectorAll('.game-circle-wrapper');
circles.forEach(circle => {
circle.addEventListener('click', function() {
const gameCircle = this.querySelector('.game-circle');
gameCircle.classList.toggle('is-flipped');
});
});
});
When focusing on keyboard interactions, you can extend this mechanism to support Enter or Space key presses. This implementation:
DOMContentLoaded
.querySelectorAll()
is-flipped
class on the inner game circle element when clickedThe toggle effect is then handled by our CSS transitions, which create the smooth flipping animation.
The first crucial aspect is setting up the 3D space for our animations:
.game-circle-wrapper {
perspective: 1000px !important;
width: 320px !important;
height: 320px !important;
}
The perspective
property is vital here - it determines the depth of our 3D space. At 1000px, it creates a natural-looking rotation effect that’s neither too shallow nor too extreme. We’ve chosen this specific value after extensive experimentation to achieve the most visually pleasing result.
.game-circle {
transform-style: preserve-3d !important;
transition: transform 0.8s !important;
}
.game-circle.is-flipped {
transform: rotateY(180deg) !important;
}
The preserve-3d
value is crucial here - it ensures that child elements maintain their position in 3D space during transformations. The transition duration of 0.8 seconds was carefully chosen to provide a smooth animation that gives users enough time to register the movement without feeling sluggish.
Developers can fine-tune exactly where the flip should pivot from using transform-origin. Typically, you’ll want this centered (transform-origin: center center;) to maintain the illusion of a perfect circular rotation.
.game-circle-front,
.game-circle-back {
position: absolute !important;
backface-visibility: hidden !important;
border-radius: 50% !important;
}
The backface-visibility: hidden
property is particularly interesting here. It prevents the reverse side of each face from being visible during the rotation, which is essential for maintaining the illusion of a physical card flip.
One of the more interesting aspects is how we handle the game title during the flip animation:
.game-title {
opacity: 1 !important;
transition: opacity 0.2s !important;
transition-delay: 0.4s !important;
}
.game-circle.is-flipped .game-title {
opacity: 0 !important;
transition-delay: 0s !important;
}
This implementation creates a nuanced animation where the title fades out precisely halfway through the flip animation. We achieve this by:
The component handles game images through a clever use of pseudo-elements:
.game-circle-front::before {
content: "" !important;
position: absolute !important;
background-size: cover !important;
background-position: center !important;
z-index: 0 !important;
}
This approach offers several advantages:
If you have high-resolution assets, consider serving multiple image sizes or using srcset
(if using actual <img>
tags) to ensure a crisp look on Retina or other high-DPI displays. This helps maintain visual fidelity across devices.
One of the most distinctive features of our game card is its homage to the classic DVD design, complete with a centre hole and a mesmerising gradient background.
The centre hole is implemented using pseudo-elements on both the front and back faces:
.game-circle-front::after,
.game-circle-back::after {
content: '' !important;
position: absolute !important;
top: 50% !important;
left: 50% !important;
transform: translate(-50%, -50%) !important;
width: 100px !important;
height: 100px !important;
background-color: #2a2a2a !important;
border-radius: 50% !important;
z-index: 1 !important;
border: 2px solid #808080 !important;
}
This creates a perfect circular hole effect by:
The back face features a stunning conic gradient that pays homage to the iridescent surface of DVDs:
.game-circle-back {
background: conic-gradient(
#00BFFF,
#4169E1,
#32CD32,
#98FB98,
#FFD700,
#00CED1,
#1E90FF,
#90EE90,
#00BFFF
) !important;
opacity: 0.25 !important;
}
This gradient implementation:
The combination of the centre hole and gradient background creates a nostalgic yet modern design that perfectly complements our gaming theme while maintaining visual sophistication.
Several optimisations have been implemented to ensure smooth performance:
transform
for animations instead of properties that trigger layout recalculations (like width
or left
), we ensure the animations run on the GPUtransform
automatically promotes elements to their own compositor layerWe tested this component across various devices (including older smartphones) to confirm smooth, 60fps animations. Animations relying on GPU-accelerated transforms generally perform well even under heavier loads.
To improve accessibility for users with motion sensitivity, consider using the prefers-reduced-motion media query:
@media (prefers-reduced-motion: reduce) {
.game-circle {
transition: none !important;
}
}
This ensures that when the user’s system setting indicates reduced motion, the flip animation is minimized or disabled altogether.
The implementation uses well-supported CSS properties, ensuring broad browser compatibility. The use of !important
declarations, while generally discouraged, is necessary here to ensure our styles take precedence over any potential framework styles that might be applied.
In a production environment, developers may prefer stricter scoping or more specific selectors over !important
. In smaller prototypes or style-override situations, !important
can be a quick solution. Just be mindful it can make debugging and maintenance more challenging down the line.
While the current implementation is robust, there are several potential enhancements we could consider:
The game card component demonstrates how modern CSS features can be leveraged to create engaging, interactive elements. By carefully considering the interaction design, animation timing, and performance implications, we’ve created a component that’s both visually appealing and technically sound. The use of 3D transforms, coupled with carefully timed transitions, creates a polished user experience that enhances our portfolio presentation. While the implementation might seem complex at first glance, each decision serves a specific purpose in creating a cohesive and performant component.
This implementation showcases the power of modern web technologies while maintaining broad browser compatibility and performance - a testament to thoughtful web component design.