DEV Community

Cover image for Follow Mouse on Hover in SCSS with Responsive. No JS!
Prahalad S
Prahalad S

Posted on • Edited on

Follow Mouse on Hover in SCSS with Responsive. No JS!

Follow Mouse on Hover in SCSS with Responsive. No JS!

So Let's create hover follow mouse using scss without using JavaScript. Below is the gif image for final output. Its also responsive.

Image description

Let's create list elements in html like below code. Also add <span class="item"></span> at the bottom of last <li>. This item span will follow the mouseover.

<ul id="list">
        <li class="box"><img src="img/1.jpg" alt=""><span>caption</span></li>
        <li class="box"><img src="img/2.jpg" alt=""><span>caption</span></li>
        <li class="box"><img src="img/3.jpg" alt=""><span>caption</span></li>
        <li class="box"><img src="img/4.jpg" alt=""><span>caption</span></li>
        <li class="box"><img src="img/5.jpg" alt=""><span>caption</span></li>
        <li class="box"><img src="img/6.jpg" alt=""><span>caption</span></li>
        <li class="box"><img src="img/7.jpg" alt=""><span>caption</span></li>
        <li class="box"><img src="img/8.jpg" alt=""><span>caption</span></li>
        <li class="box"><img src="img/9.jpg" alt=""><span>caption</span></li>
        <li class="box"><img src="img/10.jpg" alt=""><span>caption</span></li>
        <li class="box"><img src="img/11.jpg" alt=""><span>caption</span></li>
        <li class="box"><img src="img/12.jpg" alt=""><span>caption</span></li>
        <li class="box"><img src="img/13.jpg" alt=""><span>caption</span></li>
        <li class="box"><img src="img/14.jpg" alt=""><span>caption</span></li>
        <li class="box"><img src="img/15.jpg" alt=""><span>caption</span></li>
     <span class="item"></span>
</ul>
Enter fullscreen mode Exit fullscreen mode

Let's create scss variables $w, $h for width and height of <li>. Assign width, height to 100% to img and span(which has text: caption) tags which are inside <li> like below code. span will appear on hover of <li> and also <span class="item"></span> will follow on hover with our for loop logic.

$w: 150px;
$h: 100px;

body {
    margin: 0;
    padding: 0;
    background: #5b5b5b;
    font-family: monospace;
}

#list {
    display: flex;
    flex-wrap: wrap;
    margin: 50px auto;
    // width:750px;
    padding: 0;
    list-style-type: none;
    position: relative;

    li {
        &.box {
            box-sizing: border-box;
            -moz-box-sizing: border-box;
            -webkit-box-sizing: border-box;
            width: $w;
            height: $h;
            position: relative;
            overflow: hidden;
            border: 1px solid rgb(255, 255, 255);
            z-index: 1;
            margin: 0;
            padding: 0;
            cursor: pointer;

            img {
                width: 100%;
                height: 100%;
                opacity: 0.5;
            }

            span {
                font-size: 15px;
                opacity: 0;
                z-index: 2;
                position: absolute;
                color: white;
                width: 100%;
                height: 100%;
                left: 0;
                top: 0;
                z-index: 2;
                text-shadow: none;
                display: flex;
                align-items: center;
                justify-content: center;
            }

            &:hover {
                img {
                    opacity: 0.15;
                }
                span {
                    opacity: 1;
                    animation: fade 0.5s;
                }   
            }
        }
    }

    .item {
        position: absolute;
        width: $w;
        height: $h;
        left: unset;
        top: unset;
        background: rgb(0, 0, 0);
        opacity: 0;
        z-index: 0;
        transition: 0s ease;
        pointer-events: none;
    }
}

@keyframes fade {
    from {
        opacity: 0;
    }

    to {
        opacity: 1;
    }
}

Enter fullscreen mode Exit fullscreen mode

Output:

Image description

Now, I have two methods to do this. First lets go with first method. Please comment which method is better. Feedback is much appreciated.

Method 1:

@mixin responsive-styles($width, $col, $multiplier, $max-nth-child) {
    #list {
        width: $width;

        // Calculate the maximum range for the loop
        $limit: min($col * $multiplier, $max-nth-child);

        @for $i from 1 through $limit {
            li:nth-child(#{$i}):hover~.item {
                    $row: ceil($i / $col); // determine the row number
                    $rowOffset: ($row - 1) * $col * $w; // adjust based on row
                    left: #{$i * $w - $rowOffset - ($w)};
                    top: #{($row - 1) * $h};
                    transition: 0.25s ease;
                    opacity: 0.7;
              }
        }
    } 
}

// media queries
@media only screen and (max-width: 1920px) { 
   @include responsive-styles(750px, 5, 3, 15);
}

@media only screen and (max-width: 768px) { 
    @include responsive-styles(600px, 4, 4, 15);
 }

 @media only screen and (max-width: 600px) { 
    @include responsive-styles(450px, 3, 5, 15);
 }

 @media only screen and (max-width: 450px) { 
    @include responsive-styles(300px, 2, 8, 15);
 } 
Enter fullscreen mode Exit fullscreen mode

Output:

Image description
Method 2:

$max-nth-child: 15;
$breakpoints: (1920px:(col: 5, numOfRows: 3, width: 750px),
    768px: (col: 4, numOfRows: 4, width: 600px),
    600px: (col: 3, numOfRows: 5, width: 450px),
    450px: (col: 2, numOfRows: 8, width: 300px));

#list {
    @each $breakpoint,
    $settings in $breakpoints {
        @media only screen and (max-width: $breakpoint) {
            width: map-get($settings, width);
            $col: map-get($settings, col);
            $numOfRows: map-get($settings, numOfRows);
            $limit: min($col * $numOfRows, $max-nth-child);

            @for $i from 1 through $limit {
                li:nth-child(#{$i}):hover~.item {
                    $row: ceil($i / $col);
                    $rowOffset: ($row - 1) * $col * $w;
                    left: #{$i * $w - $rowOffset - ($w)};
                    top: #{($row - 1) * $h};
                    transition: 0.25s ease;
                    opacity: 0.7;
                }
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Output:
Image description

Thank you for watching...Please subscribe to my youtube channel

Click here for demo

Top comments (2)

Collapse
 
brandon_moore_c61b7ea602f profile image
Brandon Moore

I liked your idea but found a much simpler way to do it with only pure css and a lot less code and works without having to calculate against a defined grid.

codepen.io/bmooreitul/pen/LEGZbrP

Great work though... thx for the inspiration

Collapse
 
prahalad profile image
Prahalad S

Nice work with position-anchor. I really liked it. You can shorten more as you used class .tkb twice. I just went through your code and shortened like this.

.tkb {
    position: relative;
    display: flex;
    flex-wrap: wrap;
    width: calc(var(--tbox) * 10);
    margin: 20vh auto;
    box-shadow: 0 0 15px rgba(0,0,0,0.2), 0 0 35px rgba(0,0,0,0.5);
    background: linear-gradient(328deg, #7b87c33b, #bbced1c7);
    border-radius: 8px;

    & .tkb-target {
        width: var(--tbox);
        height: var(--tbox);
        opacity: 0;
        background: transparent;
        box-shadow: inset 0 0 5px rgba(0,0,0,0.8);
        border-radius: 8px;
        transition: all 1.25s ease;

        &:hover {
            anchor-name: --current-anchor;
            background: rgba(0,0,0,0.2);
            opacity: 0.5;
            transition: opacity 0.15s ease;

            & ~ .tkb-track {
                position-anchor: --current-anchor;
                position: absolute;
                pointer-events: none;
                top: anchor(top);
                left: anchor(left);
                right: anchor(right);
                bottom: anchor(bottom);
                transition: top var(--tduration) ease,
                            left var(--tduration) ease,
                            right var(--tduration) ease,
                            bottom var(--tduration) ease;
                opacity: 1;
                background: #008eff78;
                border-radius: 8px;
                box-shadow: 2px 5px 10px rgba(0,0,0,0.2);
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode