Search code examples
htmlcssbackground-imagepseudo-elementabsolute

Extra pixel in before and after pseudo elements


I'm trying to create a background effect using before and after pseudo elements by making it one pixel taller and wider than the actual element, but it always seems to have one extra pixel to the right or left. This only happens when the browsers is maximized (Firefox, Chrome and Edge), but does not happen when the browser has a smaller width.

*, *::before, *::after{ box-sizing: border-box; }

body {
    display: flex;
    flex-flow: column wrap;
    justify-content: center;
    align-items: center;
    background-color: #111;
}

img {
    max-width: 300px;
    display: block;
    padding: 4px;
}

.main-box {
    position: relative;
}

.img-box {
    padding: 0;
    margin: 0;
    background-color: #000;
}

.img-box::before{
    content: '';
    position: absolute;
    top: -1px;
    left: -1px;
    right: -1px;
    bottom: -1px;
    filter: blur(10px);
    z-index: -2;
}

.img-box::after{
    content: '';              
    position: absolute;
    top: -1px;
    left: -1px;
    right: -1px;
    bottom: -1px;
    z-index: -1;
}

.img-box::before, .img-box::after{
    background-image: linear-gradient(45deg, #ff0000, #111, #0000ff);
    opacity: 0.7;
    transition: opacity ease-out 150ms;
}

.main-box:hover .img-box::after {
    opacity: 1;
}
<div class="main-box">
   <div class="img-box"><img src="https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png" alt="keyboard"></div>
</div>

It's not quite clear in the picture, but in the browser all sides of the background-image is 1px more except for the left side where it's 2px more.

OUTPUT: Thicker line on the left


Solution

  • This looks like antialiasing. I guess you have either your browser's either your OS's zoom level set to something else than 100%.

    Some browsers will try to round the positionning, but at some zoom level, this can't be done properly and you'll end up having one side floored and the other ceiled.

    To circumvent this, you can use the translate property which should allow proper antialiasing to kick in (it will be blurry, but of the same size).

    *, *::before, *::after{ box-sizing: border-box; }
    
    body {
        display: flex;
        flex-flow: column wrap;
        justify-content: center;
        align-items: center;
        background-color: #111;
    }
    
    img {
        max-width: 300px;
        display: block;
        padding: 4px;
    }
    
    .main-box {
        position: relative;
    }
    
    .img-box {
        padding: 0;
        margin: 0;
        background-color: #000;
    }
    
    .img-box::before{
        content: '';
        position: absolute;
        top: 0px;
        left: 0px;
        width: calc( 100% + 2px );
        height: calc( 100% + 2px );
        transform: translate(-1px,-1px);
        filter: blur(10px);
        z-index: -2;
    }
    
    .img-box::after{
        content: '';              
        position: absolute;
        top: 0px;
        left: 0px;
        width: calc( 100% + 2px );
        height: calc( 100% + 2px );
        transform: translate(-1px,-1px);
        z-index: -1;
    }
    
    .img-box::before, .img-box::after{
        background-image: linear-gradient(45deg, #ff0000, #111, #0000ff);
        opacity: 0.7;
        transition: opacity ease-out 150ms;
    }
    
    .main-box:hover .img-box::after {
        opacity: 1;
    }
    <div class="main-box">
       <div class="img-box"><img src="https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png" alt="keyboard"></div>
    </div>