Positioning

Notes about basic Positioning.

Initially, every element has static positioning:

position: static

Anytime you change the position value, the element becomes positioned. Positioned elements are removed from the document flow so that you can place them anywhere on the screen.

Fixed positioning

Positions elements arbitrarily within the viewport (the containing block) using the following properties:

top:
bottom:
left:
right:

Fixed elements are removed from the document flow – static elements display as if the fixed elements do not exist.

For static side navs, add a margin to the content to make sure that it doesn’t flow behind it.

Fixed example

An example of a position: fixed element is a modal. Here is the HTML:

...
<div>
    <div class="modal" id="modal"></div>
    <div class="modal-backdrop"></div>
    <div class="modal-body">
        <button class="modal-close" id="close" />
        ... modal contents
    </div>
</div>

You position in in the viewport using the following:

/* The modal-backdrop (grayed-out area behind the actual modal) */
/* The backdrop covers the entire viewport. */
.modal-backdrop {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    background-color: rgba(0, 0, 0, 0.5);
}

/* Like the backdrop, the modal-body is positioned within the viewport */
.modal-body {
    position: fixed;
    top: 3em;
    right: 20%;
    bottom: 3em;
    left: 20%;
    background-color: #fff;
    /* allow scroll if necessary */
    overflow: auto; 
}

Use JS to grab the modal id, then use display: none or display: block to toggle it on or off.

Absolute positioning

The location of absolute positioned elements is based on the closest positioned ancestor element. This ancestor element is its containing block.

Absolute positioning is used frequently with JS to build menus, tooltips, info boxes, etc.

Absolute example

In this example, we will position a ‘close’ button for the modal described in the Fixed section. The ‘close’ icon looks like this:

/* positions the clickable area in the top-right
of the modal. Indents it 10em to hide the <button>Close</button>
while allowing a screenreader to read it*/
.modal-close {
  position: absolute;

  top: 0.3rem;
  right: 0.3rem;

  padding: 1.75rem;
  border: none;
  height: 1rem;
  width: 1rem;

  text-indent: 10em;
  overflow: hidden;
  background-color: transparent;
}

/* Adds the X icon w black circle bg*/
.modal-close::after {
  position: absolute;
  line-height: 0.5;
  top: 0.5rem;
  right: 0.5rem;
  text-indent: 0;
  font-size: 2.5rem;
  cursor: pointer;

  background-color: lightgray;
  border-radius: 50%;
  padding: 0.5rem;

  content: "\00d7";
}

When the browser reads this position: absolute, it searches up the DOM heirarchy until it finds a positioned element and uses that as the positioning reference, or containing block.

Relative positioning

Applying relative positioning does not impact the elements around the positioned elements. It moves the element relative to its original position in the document flow.

The main use for relative positioning is to create a containing block for an absolutely positioned element.

Relative example

In the following example:

  • dropdown is the relative-positioned containing block
  • dropdown-menu is the absolute-positioned element that is hidden initially
<div class="container">
    <nav>
        <div class="dropdown">
            <div class="dropdown-label">Main Menu</div>
            <div class="dropdown-menu">
                <ul class="submenu">
                    <li><a href="#">Home</a></li>
                    <li><a href="#">Coffees</a></li>
                    <li><a href="#">Brewers</a></li>
                    <li><a href="#">Specials</a></li>
                    <li><a href="#">About us</a></li>
                </ul>
            </div>
        </div>
    </nav>
</div>

The following CSS creates the positioned context:

...
.dropdown {
    display: inline-block;
    position: relative;
}
...

.dropdown-menu {
    display: none;
    position: absolute;
    left: 0;
    top: 2.1em;
    min-width: 100%;
    background-color: #eee;
}

/* when you hover on dropdown, the menu displays. Toggle with JS */
.dropdown:hover > .dropdown-menu {
    display: block;
}

Sticky positioning

This is a hybrid between relative and fixed positioning. The element scrolls until it reaches a specified spot in the viewport and then it ‘sticks’ in place.

You do not need a containing block for sticky positioning.

Stacking contexts with z-index

When you remove an element from the document flow, you become responsible for all the things that the document flow normally does for you. This includes making sure that the positioned element does not:

  • Overflow outside the browser viewport and then hidden from the user
  • Cover important content

When the browser parses HTML, it creates a render tree that represents the appearance and position of each element. The browser paints elements in the order in which they are listed in the HTML. The position determines the order that the browser paints the elements:

  • First, it paints non-positioned elements.
  • Next, it paints positioned elements using z-index.

Elements with a z-index establish a stacking context. A stacking context is a n element or a group of elements that are painted together with a browser. Elements with a higher z-index are positioned in front of elements with a lower z-index. Elements with a negative z-index are positioned behind static elements. No element outside the stacking context can be positioned between elements in the stacking context.

Best practice

Put your z-index values in variables:

--z-loading-indicator: 100;
--z-nav-menu:          200;
--z-dropdown-menu:     300;
--z-modal-backdrop:    400;
--z-modal-body:        410;