DEV Community

Cover image for Create 3D Model Card Hover Effect using HTML CSS and Javascript
Codebox
Codebox

Posted on

Create 3D Model Card Hover Effect using HTML CSS and Javascript

Introduction

In modern web design, interactivity plays a crucial role in enhancing user engagement and experience. To make your website stand out, try incorporating amazing hover effects.

A 3D model card is a sleek way to give depth and interactive features to your website.

In this tutorial, we’ll explain the complete steps on how to create a 3D model card hover effect using only HTML, CSS, and Javascript.

Whether you're working on a portfolio, e-commerce site, or personal project, this effect will boost your web design.

You May Also Like: Autoplay Image Slider with Manual Navigation Button with HTML CSS and JS

Watch Live Preview 👉👉

Click Here to Download Source Code

Prerequisites

Before diving into the code, it's important to have a basic understanding of the following concepts:

  • HTML: The structure of your web page.
  • CSS: The styling and layout of your web page.
  • CSS Transitions and Transforms: We'll use for smooth animations and transformations.
  • CSS Pseudo-classes: These allow you to apply styles based on the state of an element (e.g., :hover).

With that in mind, let’s begin.

Alert: To run the file, you'll need to start the XAMPP server

Source Code

HTML Code Structure

<!DOCTYPE html>
<html lang="en" >
<!-- Code by Keyframe Tech Solution -->
<head>
    <meta charset="UTF-8">
    <title>Marvel Character Select</title>
    <script type="module" src="https://ajax.googleapis.com/ajax/libs/model-viewer/4.0.0/model-viewer.min.js"></script>
    <link rel="stylesheet" href="./style.css">
</head>
<body>

<div class="card-container">
    <div class="card">
        <div class="overflow">
            <div class="model">
                <model-viewer camera-orbit="0deg 90deg 164m" camera-target="0m 140m 10m" src="models/spiderman.glb" shadow-intensity="0.4"></model-viewer>
            </div>
        </div>
        <div class="glass">
            <div class="gradient-blur">
                <div></div>
                <div></div>
                <div></div>
                <div></div>
                <div></div>
                <div></div>
            </div>
        </div>
        <div class="content">
            <h2>Spider-Man</h2>
            <p>Spider-Man is a superhero in American comic books published by Marvel Comics.</p>
        </div>
    </div>
</div>

<script  src="./script.js"></script>

</body>
</html>
Enter fullscreen mode Exit fullscreen mode

CSS Code Structure

@import url("https://fonts.googleapis.com/css2?family=Dancing+Script:wght@400..700&display=swap");
@import url("https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100..900&display=swap");
* {
    box-sizing: border-box;
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
    scroll-behavior: smooth;
}
html,
body {
    margin: 0;
    padding: 0;
    height: 100%;
    overflow: hidden;
}
body {
    background: #0096885c;
    --scale: 230px;
    position: relative;
}
body::before {
    content: "";
    width: 100%;
    height: 100%;
    position: absolute;
    background: radial-gradient(circle, #2d2d2d 20%, transparent 11%);
    background-size: 0.5em 0.5em;
    opacity: 0.5;
    inset: 0;
    z-index: 0;
}
.card-container {
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
    flex-wrap: nowrap;
    justify-content: center;
    align-items: center;
    margin: 0;
    padding-top: 28vh;
    padding-bottom: 14vh;
    overflow: hidden;
}
@media (max-width: 800px) {
    html,
    body {
        overflow: inherit;
    }
    .card-container {
        flex-direction: column;
        gap: 80px;
        height: fit-content;
    }
}

.card {
    --clr: #c63e3c;
    --fclr: #f0ffe2;

    width: var(--scale);
    height: 324px;
    min-width: var(--scale);

    &::before {
        content: "";
        display: block;
        position: absolute;
        left: 0;
        right: 0;
        top: 0;
        width: 100%;
        height: 107%;
        background: linear-gradient(var(--clr), #000000 100%);
        transform: skewY(10deg);
        box-shadow: 0 0 0 0px #f9da27, 1px 1px 3px 0px #0002,
            12px 42px 24px -8px #0001, 10px 24px 42px 0 #0001, 1px 4px 12px 0 #0002;

        transition: all 0.2s ease-in-out;
    }

    &::after {
        content: "";
        display: block;
        position: absolute;
        left: -6%;
        right: 0;
        top: 64%;
        width: 112%;
        height: 16%;
        background: var(--clr);
        transform: rotate(-8deg) skewX(-54deg);
        clip-path: polygon(
            -100% -100%,
            200% -100%,
            200% 200%,
            130% 200%,
            59% -100%,
            -30.4% -100%,
            41% 200%,
            -100% 200%
        );

        box-shadow: 0 0 0 0px #f9da27;
        scale: 0.6;
        opacity: 0;
        transition: all 0.2s ease-in-out;
    }

    position: relative;
    display: flex;
    flex-direction: column;
    justify-content: end;
    align-items: center;
    cursor: pointer;

    margin: auto 16px;

    &:hover {
        z-index: 2;

        &::before {
            box-shadow: 0 0 0 8px #234563, 1px 1px 3px 0px #0002,
                12px 42px 24px -8px #0001, 10px 24px 42px 0 #0001, 1px 4px 12px 0 #0002;
        }
    }

    .content {
        position: relative;
        z-index: 1;
        bottom: 0;
        top: unset;
        height: fit-content;
        padding: 12px 24px 0 24px;
        text-align: center;
        h2,
        p {
            margin: 0;
            padding: 0;
            line-height: 124%;
            margin-bottom: 8px;
            font-weight: 700;
            color: var(--fclr);
        }
        p {
            font-size: 14px;
            font-weight: 400;
            margin-bottom: 16px;
            font-family: "Roboto", serif;
        }
        h2{
            font-size: 30px;
            font-family: "Dancing Script", serif;
        }
    }

    .glass {
        pointer-events: none;
        position: absolute;
        bottom: 0;
        left: 0;
        width: 100%;
        height: 80%;
        background: linear-gradient(transparent, #000 100%);
        overflow: hidden;
    }
    .overflow {
        pointer-events: none;
        position: absolute;
        width: 200%;
        height: 200%;
        overflow: hidden;
        display: flex;
        justify-content: end;
        align-items: end;
        clip-path: polygon(25% 0, 75% 0, 75% 100%, 25% 100%);
    }
    .model {
        position: absolute;
        width: 100%;
        height: 100%;
        bottom: -10%;
        display: flex;
        justify-content: center;
        align-items: center;
        model-viewer {
            position: absolute;
            width: 500px;
            height: 500px;
            width: 500px;
            height: calc((5 / 3) * var(--scale));
            --progress-bar-color: none;
            --progress-bar-height: 0px;
            opacity: 0;
            transition: filter 0.4s ease-in-out;
            &.loaded {
                animation: onLoad 1s ease-in forwards;
            }
        }
    }
}
.card:hover .overflow {
    clip-path: unset !important;
}
@keyframes onLoad {
    to {
        opacity: 1;
    }
}

.gradient-blur {
    position: absolute;
    z-index: 1;
    height: 100%;
    inset: auto 0 0 0;
    pointer-events: none;
}
.gradient-blur > div,
.gradient-blur::before,
.gradient-blur::after {
    position: absolute;
    inset: 0;
    --p1: 0%;
    --p2: 12.5%;
    --p3: 25%;
    --p4: 37.5%;
    --p5: 50%;
    --p6: 62.5%;
    --p7: 75%;
    --p8: 87.5%;
    --p9: 100%;
    --bclr0: #0000;
    --bclr1: #000;
}
.gradient-blur::before {
    content: "";
    z-index: 1;
    backdrop-filter: blur(0.5px);
    mask: linear-gradient(
        to bottom,
        var(--bclr0) var(--p1),
        var(--bclr1) var(--p2),
        var(--bclr1) var(--p3),
        var(--bclr0) var(--p4)
    );
}
.gradient-blur > div:nth-of-type(1) {
    z-index: 2;
    backdrop-filter: blur(1px);
    mask: linear-gradient(
        to bottom,
        var(--bclr0) var(--p2),
        var(--bclr1) var(--p3),
        var(--bclr1) var(--p4),
        var(--bclr0) var(--p5)
    );
}
.gradient-blur > div:nth-of-type(2) {
    z-index: 3;
    backdrop-filter: blur(2px);
    mask: linear-gradient(
        to bottom,
        var(--bclr0) var(--p3),
        var(--bclr1) var(--p4),
        var(--bclr1) var(--p5),
        var(--bclr0) var(--p6)
    );
}
.gradient-blur > div:nth-of-type(3) {
    z-index: 4;
    backdrop-filter: blur(4px);
    mask: linear-gradient(
        to bottom,
        var(--bclr0) var(--p4),
        var(--bclr1) var(--p5),
        var(--bclr1) var(--p6),
        var(--bclr0) var(--p7)
    );
}
.gradient-blur > div:nth-of-type(4) {
    z-index: 5;
    backdrop-filter: blur(8px);
    mask: linear-gradient(
        to bottom,
        var(--bclr0) var(--p5),
        var(--bclr1) var(--p6),
        var(--bclr1) var(--p7),
        var(--bclr0) var(--p8)
    );
}
.gradient-blur > div:nth-of-type(5) {
    z-index: 6;
    backdrop-filter: blur(16px);
    mask: linear-gradient(
        to bottom,
        var(--bclr0) var(--p6),
        var(--bclr1) var(--p7),
        var(--bclr1) var(--p8),
        var(--bclr0) var(--p9)
    );
}
.gradient-blur > div:nth-of-type(6) {
    z-index: 7;
    backdrop-filter: blur(32px);
    mask: linear-gradient(
        to bottom,
        var(--bclr0) var(--p7),
        var(--bclr1) var(--p8),
        var(--bclr1) var(--p9)
    );
}
.gradient-blur::after {
    content: "";
    z-index: 8;
    backdrop-filter: blur(64px);
    mask: linear-gradient(
        to bottom,
        var(--bclr0) var(--p8),
        var(--bclr1) var(--p9)
    );
}
Enter fullscreen mode Exit fullscreen mode

JavaScript Code Structure

(() => {
    const modelViewers = document.querySelectorAll("model-viewer");
    const cards = document.querySelectorAll(".card");
    const defaultOrbit = "0deg 90deg 164m";
    const applyOrbit = (modelViewer, orbit, vert) => {
        modelViewer.setAttribute("camera-orbit", orbit);
        modelViewer.setAttribute("interpolation-decay", "200");
        modelViewer.setAttribute("camera-target", `0m ${vert}m 10m`);
    };
    cards.forEach((card, index) => {
        const modelViewer = modelViewers[index];
        if (modelViewer) {
            const calcOrbit = (xPos, cardRect) => {
                const normalizedX = (xPos - cardRect.left) / cardRect.width;
                const angle = normalizedX * 90 - 45;
                return `${-angle}deg 90deg 164m`;
            };
            card.addEventListener("mousemove", (event) => {
                const cardRect = card.getBoundingClientRect();
                const orbit = calcOrbit(event.clientX, cardRect);
                applyOrbit(modelViewer, orbit, 120);
            });
            card.addEventListener("mouseleave", () => {
                applyOrbit(modelViewer, defaultOrbit, 140);
            });
            modelViewer.addEventListener("load", () => {
                modelViewer.classList.add("loaded");
            });
        } else {
            console.log(`No model found for card at i:${index}`);
        }
    });
})();
Enter fullscreen mode Exit fullscreen mode

You May Also Like: How to Build Vertical Timeline Design using HTML CSS & jQuery

Testing and Debugging

Before deploying, it’s crucial to test your card hover effect in various browsers and on different devices to ensure smooth performance.

Make sure the animations are responsive and that the hover effect works as expected.

Conclusion

You’ve now created a 3D model card hover effect.

By using simple but powerful properties like perspective, transform, and transition, this technique creates an interactive and engaging user experience

You can easily adapt and customize this effect to suit your specific needs, such as adding more content, tweaking the design, or experimenting with different animations.

By mastering hover effects, you can add depth and interactivity to your websites, making them visually appealing and more engaging to users.

Happy coding!

Quadratic AI

Quadratic AI – The Spreadsheet with AI, Code, and Connections

  • AI-Powered Insights: Ask questions in plain English and get instant visualizations
  • Multi-Language Support: Seamlessly switch between Python, SQL, and JavaScript in one workspace
  • Zero Setup Required: Connect to databases or drag-and-drop files straight from your browser
  • Live Collaboration: Work together in real-time, no matter where your team is located
  • Beyond Formulas: Tackle complex analysis that traditional spreadsheets can't handle

Get started for free.

Watch The Demo 📊✨

Top comments (0)

Jetbrains Survey

Calling all developers!

Participate in the Developer Ecosystem Survey 2025 and get the chance to win a MacBook Pro, an iPhone 16, or other exciting prizes. Contribute to our research on the development landscape.

Take the survey

AWS Security LIVE!

Hosted by security experts, AWS Security LIVE! showcases AWS Partners tackling real-world security challenges. Join live and get your security questions answered.

Tune in to the full event

DEV is partnering to bring live events to the community. Join us or dismiss this billboard if you're not interested. ❤️