Search code examples
htmlcsscss-positioncss-grid

Why is my fixed-position element being pushed down?


I'm trying to put some text in the right side of the grid, but I have no idea why it's pushing down the background.

Maybe it is a problem with ::after.

I don't mind any suggestions to change that as long as the background is split 50% 50%.

I really appreciate all your help!

:root {
  --backColor: #F2F2EF;
  --mainColor: #37384E;
  --textColor: #9BBEC7;
}

.grid-container {
  display: grid;
  grid-template-areas: 'left right';
  grid-template-columns: 50% 50%;
  grid-template-rows: 100%;
}

body {
  background: var(--backColor);
  margin: 0%;
}

body:after {
  content: '';
  position: fixed;
  height: 100%;
  width: 50%;
  right: 50%;
  background: var(--mainColor);
}

.logo {
  margin: 0%;
  font-size: 60px;
  grid-area: right;
}
<div class="grid-container">
  <p class="logo">jZ</p>
</div>


Solution

  • The Problem

    The basic problem is your rendering order.

    The browser lays out your elements in this order:

    1. The body element.

    2. The .grid-container element.

    3. The body::after pseudo-element.

    The first thing to note is that your pseudo-element is rendered last. Why? Because it comes ::after the body element.

    By this time, the .grid-container element has already taken up its space, so the pseudo-element must appear on the next line.

    The quick and simple solution to this problem is to switch from ::after to ::before, so the pseudo-element moves to first in the rendering order. Done!

    ::after (original code)

    .grid-container {
      display: grid;
      grid-template-areas: 'left right';
      grid-template-columns: 50% 50%;
    }
    
    body {
      background: var(--backColor);
      margin: 0;
    }
    
    body::after {
      content: '';
      position: fixed;
      height: 100vh;
      width: 50%;
      right: 50%;
      background: var(--mainColor);
    }
    
    .logo {
      margin: 0;
      font-size: 60px;
      grid-area: right;
    }
    
    :root {
      --backColor: #F2F2EF;
      --mainColor: #37384E;
      --textColor: #9BBEC7;
    }
    <div class="grid-container">
      <p class="logo">jZ</p>
    </div>

    ::before (solution #1)

    .grid-container {
      display: grid;
      grid-template-areas: 'left right';
      grid-template-columns: 50% 50%;
    }
    
    body {
      background: var(--backColor);
      margin: 0;
    }
    
    body::before {  /* adjustment */
      content: '';
      position: fixed;
      height: 100vh;
      width: 50%;
      right: 50%;
      background: var(--mainColor);
    }
    
    .logo {
      margin: 0;
      font-size: 60px;
      grid-area: right;
    }
    
     :root {
      --backColor: #F2F2EF;
      --mainColor: #37384E;
      --textColor: #9BBEC7;
    }
    <div class="grid-container">
      <p class="logo">jZ</p>
    </div>

    But the pseudo-element has a fixed position. Why does it recognize the space of the .grid-container?

    • With position: fixed an element is removed from the document flow. Its containing block is the initial containing block which, for all intents and purposes, is equivalent to the viewport.

    • The CSS offset properties (top, bottom, left and right), which control the placement of elements that are absolutely-positioned (including fixed positioning), have an initial value of auto. This means that the element remains in the same place where it would normally be if it were in the document flow.

    • Said another way, when you set an element to position: absolute or position: fixed, you're specifying the type of positioning you want... but you're not positioning it anywhere.

    • It isn't until you define the offsets that the element is actually positioned.

    • In this case, that means that the ::after element, appearing below .grid-container for reasons described above, can be shifted into place with top: 0.

    ::after with top: 0 (solution #2)

    .grid-container {
      display: grid;
      grid-template-areas: 'left right';
      grid-template-columns: 50% 50%;
    }
    
    body {
      background: var(--backColor);
      margin: 0;
    }
    
    body::after {
      content: '';
      position: fixed;
      height: 100vh;
      width: 50%;
      right: 50%;
      background: var(--mainColor);
      top: 0; /* new */
    }
    
    .logo {
      margin: 0;
      font-size: 60px;
      grid-area: right;
    }
    
    :root {
      --backColor: #F2F2EF;
      --mainColor: #37384E;
      --textColor: #9BBEC7;
    }
    <div class="grid-container">
      <p class="logo">jZ</p>
    </div>

    .grid-container::after (solution #3)

    Yet another solution involves applying the pseudo-element to .grid-container instead of body. Now the fixed position element is rendered as a child of the grid container and before the body element closes. Both ::after and ::before solve the problem.

    .grid-container {
      display: grid;
      grid-template-areas: 'left right';
      grid-template-columns: 50% 50%;
    }
    
    body {
      background: var(--backColor);
      margin: 0;
    }
    
    .grid-container::after {
      content: '';
      position: fixed;
      height: 100vh;
      width: 50%;
      right: 50%;
      background: var(--mainColor);
    }
    
    .logo {
      margin: 0;
      font-size: 60px;
      grid-area: right;
    }
    
    :root {
      --backColor: #F2F2EF;
      --mainColor: #37384E;
      --textColor: #9BBEC7;
    }
    <div class="grid-container">
      <p class="logo">jZ</p>
    </div>