Skip to content

Overlay element that overcomes the stacking context trap

Notifications You must be signed in to change notification settings

PolymerLabs/iron-overlay

Folders and files

NameName
Last commit message
Last commit date

Latest commit

3350b5e · Jul 14, 2017

History

75 Commits
Jul 13, 2017
Jul 13, 2017
May 23, 2016
Mar 30, 2017
Mar 30, 2017
Mar 30, 2017
May 15, 2017
Sep 9, 2016
Jun 19, 2017
Jul 13, 2017
Jul 13, 2017
Jul 13, 2017

Repository files navigation

Build Status

The problem

Overlays should always render on the top layer. This is achieved using the css properties position: fixed and z-index. When an overlay is contained in a node that creates a new stacking context, it causes the overlay to be trapped within it (z-index and position will be relative to the new stacking context), resulting in problems like this one:

<style>
  .backdrop {
    position: fixed;
    top: 0; bottom: 0;
    left: 0; right: 0;
    z-index: 100;
    background-color: rgba(0, 0, 0, 0.5);
  }
  .overlay {
    position: fixed;
    z-index: 999;
  }
</style>
<div class="backdrop"></div>
<!-- this creates a new stacking context -->
<div style="transform: translateZ(0);">
  <div class="overlay">
    <button>click me!</button>
  </div>
</div>

iron-overlay works around this issue by "teleporting" its content to a stacking-context-safe node.

<!-- this creates a new stacking context -->
<div style="transform: translateZ(0);">
  <button onclick="this.nextElementSibling.opened = true">Open overlay</button>
  <iron-overlay>
    <template>
      <!-- overlay content -->
      This text will be stamped and appended to the document body.
    </template>
  </iron-overlay>
</div>

iron-overlay

It delegates rendering of the overlay content to a renderer (iron-overlay-renderer). It won't host the renderer, but request another element to host it through the events iron-overlay-attach and iron-overlay-detach, or append it to <body>. It requires overlay contents to be contained in a <template> (since they need to be hosted in the renderer).

iron-overlay-renderer

Takes care of the rendering (e.g. center overlay), and handles the switch from opened state to closed state & viceversa.

iron-overlay-container

Hosts the overlay renderers, keeps track of the opened overlays. This element should be placed in a stacking-context safe node (e.g. document.body).

<div style="transform: translateZ(0);">
  <button onclick="this.nextElementSibling.opened = true">Open overlay</button>
  <iron-overlay>
    <template>
      <!-- overlay content -->
      This text will be contained in iron-overlay-content
    </template>
  </iron-overlay>
</div>

<!-- it will contain all the overlay renderers -->
<iron-overlay-container></iron-overlay-container>

Styling

Styling must be done in the context of where iron-overlay will be hosted.

Styling the renderer

iron-overlay sets the renderer's data-overlay attribute to be its id, so that styling of the overlay can be done like this:

<custom-style><style is="custom-style">
  [data-overlay] {  
    --iron-overlay-background-color: yellow;
  }
  [data-overlay="overlay1"] {
    --iron-overlay-background-color: orange;
  }
</style></custom-style>

<div style="transform: translateZ(0);">
  <iron-overlay>
    <template>Overlay Content</template>
  </iron-overlay>
  <iron-overlay id="overlay1">
    <template>Overlay 1 Content</template>
  </iron-overlay>
</div>

Styling the content

Content can be styled by passing a <style> element into the template, but beware of possible conflicts with classes, as selectors will apply to all matching elements in the styling context where they're hosted:

<iron-overlay>
  <template>
    <style>
      .my-content {
        background-color: yellow;
      }
    </style>
    <div class="my-content">Content</div>
  </template>
</iron-overlay>

<iron-overlay>
  <template>
    <!-- Will have yellow background as well -->
    <div class="my-content">Other Content</div>
  </template>
</iron-overlay>

The best approach to ensure style encapsulation is to create a custom element for your content.

<iron-overlay>
  <template>
    <my-content>Content</my-content>
  </template>
</iron-overlay>

<iron-overlay>
  <template>
    <my-other-content>Other Content</my-other-content>
  </template>
</iron-overlay>