Interactive Advertising Bureau Guidelines for z-index
From the IAB’s perspective, things are pretty straightforward. They have a whole table specifying z-index
ranges for almost all types of content, not just ads.
Z-Index Range | Content Type | Details |
---|---|---|
< 0 | Background Elements | |
0 – 4,999 | Main Content, Standard Ads | Standard ad tags in place with regular content. Includes OBA Self Regulation Message (CLEAR Ad Notice) |
5,000 – 1,999,999 | Expanding Advertising | The entire expandable ad unit should be set within this range |
2,000,000 – 2,999,999 | Floating Advertising | Over The Page ads (OTP’s) |
3,000,000 – 3,999,999 | Pop-up Elements | Chat windows, message notifications |
4,000,000 – 4,999,999 | Non-anchored Floating Elements | Survey recruitment panels |
5,000,000 – 5,999,999 | Expanding Site Navigation Elements | Drop down navigation, site warnings, etc. Only the expanding portion of navigation elements should be included on this level. |
6,000,000+ | Full-page Overlays | Full-window Over-the-Page (OTP) ads and Between-the-Page ads IF they cover page content |
How big can z-index
be anyway?
The CSS specs do not mention an upper limit for z-index
but there is a maximum value because of the type of variable used to store that value (a 32-bit signed integer); thus the limit in modern browsers is 2,147,483,647
.
Despite this, there are ad guidelines that recommend using 2,147,483,648
.
What happens when greater values are used, like this one I found in a page once?
<div id="fresco-thirdparty-tag" style="position: absolute;
z-index: 10000000000; display: none; top: 0; left: 0;
width: 970px; height: 250px;">
Modern browsers treat such values as the same as the upper limit. In other words, using 100,000,000,000
is the same as using 2,147,483,647
.
Does any of this matter?
The short answer: no, it does not matter one bit. And that’s because stacking contexts are atomic:Stacking contexts can contain further stacking contexts. A stacking context is atomic from the point of view of its parent stacking context; boxes in other stacking contexts may not come between any of its boxes. 9.9 Layered presentationIn other words, no
z-index
value can change the position of a box in relation to boxes outside of the stack it belongs to. This is why it is impossible to manage stacks in a predictable manner without knowing about the document tree and the stacking contexts of a page.
So what are z-index
guidelines for?
What are those guidelines based on? Source order, as well as z-order of ancestors, play too large a part when it comes to painting the rendering tree – so simply setting z-index
ranges cannot be a solution.
For example, if you look at the dynamic demonstration of IAB Z-Index guidelines you’ll see that things work nicely because they were designed against a specific set of requirements
; but in reality, this is a rather brittle construct as even simple changes could require rethinking most stacking contexts within that page.
For one, imagine styling the “Header” of that demo page with position: fixed
(so it stays at the top of the viewport as users scroll down); what z-index
value should be used there? Because the header needs to show over the “300×250 Base Ad”, its z-index
should be at least 2,000,000
. But note that anything above that value makes the header compete with “Floating Ads” for which the z-index
range is 2,000,000 - 2,999,999
.
Now imagine we have a search box above the left/right nav, with a drop-down à la “Search Assist“; what z-index
value should be used there? Because the drop-down would need to show over the nav, its z-index
value should be at least 6,000,000
. But note that anything above that value makes the dropdown compete with the “Full Page Overlay” for which the z-index
range is 6,000,000+
(even if there is little chance that both show at once).
Of course, there are also issues related to “state”. Any given box could require that it be positioned differently in the stack depending on its behavior or the behavior of surrounding boxes (dropdowns, flyouts, tooltips, expanding ads, etc.). In such cases, z-index
values need to be updated on the fly – see demo further below.
A recipe for disaster
As the IAB demo shows, boxes that do not belong to a stack may be positioned anywhere in relation to other stacks. This is the reason why managing “granular” stacks necessitates centralizingz-index
values and enforcing strict rules related to contexts – something that is bound to fail in a multi-team environment where developers may not have access to either the data or the tool (i.e. variables in a pre-processor file).
Proposal
This is about implementing a defensive mechanism to manage stacking contexts through a page. We need a solution that allows us to position a box in relation to other boxes within the same stack or even boxes outside the stack it belongs to.Explicit contexts
The idea is to style the main boxes of a page to create an explicit “top-level” stack order. This leverages the atomic nature of stacking contexts and ensures containment at the highest level. We can then predict the behavior of nested boxes regardless of their ownz-index
values
.
Dynamic stacks
Once we have top-level stacking contexts, moving a main box up and down the stack suffices to move its nested boxes through that stack as well. Allowing a change of stacking contexts is crucial because there are times when boxes need to be positioned differently within a stack. When looking at the Demo (From time to time, SitePoint removes years-old demos hosted on separate HTML pages. We do this to reduce the risk of outdated code with exposed vulnerabilities posing a risk to our users. Thank you for your understanding.), pay attention to the number that appears in the black circle at the top of the right rail. That number is thez-index
value of the column. The value changes as users interact with elements inside the column. That change allows boxes to switch between stacking contexts (the boxes can move through different stacks).
Declarative switch
We rely ondata-*
attributes to expose z-index
values needed to move any given box through the stack (see code example below).
This way we can manage the ordering of boxes in the stack without knowing anything about the page itself (either its DOM tree or its stacking contexts).
The same mechanism can be used to reset the z-index
of a main box to allow its nested boxes to participate in the main stacking context.
More explicit contexts
Removing a box from the main stacking context allows its nested stacking contexts to become part of the higher stack. This means thez-index
of these inner stacks dictate their position in regard to the ordering of the main boxes.
For example, in the Demo (From time to time, SitePoint removes years-old demos hosted on separate HTML pages. We do this to reduce the risk of outdated code with exposed vulnerabilities posing a risk to our users. Thank you for your understanding.), when the modal is triggered, the removal of the right rail from the top-level stacking context allows the ad to compete with other main boxes on the page. In other words, if the ad had a z-index
of 10+
it would appear on top of the Header.
Because of the risk associated with resetting the z-index
of a main box, it is preferable to create explicit stacks for modules we suspect to be styled with a high z-index
value. Those can be ads or other modules that page owners do not have control of. Sandboxing these elements prevents boxes from appearing anywhere on the “z” axis. This way, we can still control these boxes even though their ancestor is styled with z-index: auto
.
Both issues are due to identical z-index
values (maximum value) that paint elements in the rendering tree according to their positioning in the markup. The Header shows behind the ad because it comes first. The modal button shows in front of the ad because it comes last.
Implementation
This solution requires sharing a simple setup and a common vocabulary across grids (when there are different grids involved as it would be the case for Yahoo sites for example – Finance, Sports, Home Page, Answers, etc.). The “4-step program”:- All main boxes on the page must be positioned and styled with a
z-index
other thanauto
. - Each of these boxes must be identified via a common class:
stacking-context
. - Those same boxes must have
data-zindex-max
anddata-zindex-top
attributes containing thez-index
values necessary to move a box up in the stack. - The class
inner-stack
is applied to the wrapper of any module susceptible to being styled with a highz-index
value.
<div id="right-rail" class="stacking-context"
data-zindex-max="5" data-zindex-top="10">
<div class="inner-stack">
<div id="fresco-thirdparty-tag"
style="position: absolute; z-index: 10000000000;">
data-zindex-max
This attribute is used to declare the highest z-order the box is allowed to be styled with. For example, this could be a column meant to always show behind the Header.
data-zindex-top
This attribute is used to declare the highest z-order set within the page. For example, this would be the position of the Header inside the stack. Any element styled with that z-index
value would appear in front of the Header (as long as that element comes later in the source code).
.inner-stack
This class is used to keep modules in check by preventing their own z-index
to come into play. That z-index
value is set via CSS by the page/grid owner.
Advantages of this solution
It reduces the risk of breakage By leveraging the atomic nature of stacking contexts, we can sandbox modules, making theirz-index
values irrelevant in relation to other boxes on the page.
It is predictable
No more guessing game; authors do not need to know anything about the document tree or its stacking contexts to be able to move boxes through stacks.
It centralizes responsibilities
Everything lies in the hands of the team responsible for the page itself – the team that knows everything about the document – its tree, its stacking context, etc.
Further thinking
In projects that use a single grid as well as in projects that share a common one, things could be made more declarative by using a preset of rules to swapz-index
based on meaningful classes. For example:
.coverTheHeader {...}
.coverAllColumns {...}
.coverTheRightRail {...}
.coverTheLeftRail {...}
.coverTheMainContent {...}
- etc.
Code Example
Below is the JavaScript used in the main demo page (From time to time, SitePoint removes years-old demos hosted on separate HTML pages. We do this to reduce the risk of outdated code with exposed vulnerabilities posing a risk to our users. Thank you for your understanding.). This script is responsible for switching thez-index
value of the column that holds the ad and the “modal” button. It also “frees” these elements by resetting the z-index
value set on their wrapper. It is the combination of both of these styles that allows these elements to behave as expected even though a very high z-index
is applied to them.
(function(document) {
function findAncestor(el, cls) {
while ((el = el.parentElement) && !el.classList.contains(cls));
return el;
}
// constants
var AUTO = 'auto',
CSS_MODAL_ON = 'modal-on',
CSS_INNER_STACK = 'inner-stack',
DOC = document,
WIN = DOC.defaultView,
HTML = DOC.documentElement,
// elements
theAd = DOC.getElementById('ad'),
theAncestorStack = findAncestor(theAd, 'stacking-context'),
theInnerStack = DOC.getElementsByClassName(CSS_INNER_STACK)[0],
theModal = DOC.getElementById('modal'),
theStackUpdate = DOC.getElementById('stackUpdate'),
// globals
theInnerStackAncestor,
zIndexAncestor = WIN.getComputedStyle(theAncestorStack, null).zIndex,
zIndexCurrent, // will be assigned
zIndexInnerStack = WIN.getComputedStyle(theInnerStack, null).zIndex,
zIndexMax = theAncestorStack.getAttribute('data-zindex-max'),
zIndexTop = theAncestorStack.getAttribute('data-zindex-top');
function switchStackingContext (e) {
// we move the ancestor through the stack according to zIndexMax value (highest value allowed for that ancestor box)
if (WIN.getComputedStyle(theAncestorStack, null).zIndex === zIndexMax) {
theAncestorStack.style.zIndex = zIndexAncestor;
} else {
theAncestorStack.style.zIndex = zIndexMax;
}
// we get the ancestor that wraps boxes with potential high/crazy z-index
theInnerStackAncestor = findAncestor(e.target, CSS_INNER_STACK);
// we check for the actual value before we change it
zIndexCurrent = WIN.getComputedStyle(theInnerStackAncestor, null).zIndex;
// we reset the z-index from the parent ancestor allowing the box to move up (wherever it wants to)
if (zIndexCurrent !== AUTO) {
theInnerStackAncestor.style.zIndex = AUTO;
} else {
theInnerStackAncestor.style.zIndex = zIndexInnerStack;
}
// we update the value on the page
theStackUpdate.innerText = theAncestorStack.style.zIndex;
}
function resetStackingContext (e) {
// we reset the z-index of the ancestor to free the inner box
if (WIN.getComputedStyle(theAncestorStack, null).zIndex === AUTO) {
theAncestorStack.style.zIndex = zIndexAncestor;
} else {
theAncestorStack.style.zIndex = AUTO;
}
// we get the ancestor that wraps boxes with potential high/crazy z-index
theInnerStackAncestor = findAncestor(e.target, CSS_INNER_STACK);
// we check for the actual value before we change it
zIndexCurrent = WIN.getComputedStyle(theInnerStackAncestor, null).zIndex;
// we reset the z-index from the parent ancestor allowing the box to move up (wherever it wants to)
if (zIndexCurrent !== AUTO) {
theInnerStackAncestor.style.zIndex = AUTO;
} else {
theInnerStackAncestor.style.zIndex = zIndexInnerStack;
}
// we toggle this class to contextually style the modal (nothing to do with the solution per se)
HTML.classList.toggle(CSS_MODAL_ON);
// we update the value on the page
theStackUpdate.innerHTML = theAncestorStack.style.zIndex;
}
theAd.addEventListener('mouseenter', switchStackingContext, false);
theAd.addEventListener('mouseleave', switchStackingContext, false);
theModal.addEventListener('click', resetStackingContext, false);
}(document));
For reference, below are the two demo links referenced in the article. The first one (referenced multiple times in the article) is the one with the JavaScript, which “manages” the stack as users interact with the ad or the “modal”. The second one is the same thing without the script. It is included to show the issues we’d be facing if we were relying on third-parties to manage stacks.
- Managing Stacking Contexts: Demo 1(From time to time, SitePoint removes years-old demos hosted on separate HTML pages. We do this to reduce the risk of outdated code with exposed vulnerabilities posing a risk to our users. Thank you for your understanding.)
- Managing Stacking Contexts: Demo 2 (without the script)
Frequently Asked Questions about CSS Stacking Contexts
What is a stacking context in CSS?
A stacking context in CSS is a three-dimensional conceptualization of HTML elements along an imaginary z-axis relative to the user. It determines the stacking order of elements (which element would be in front and which would be at the back) when there is a potential overlap. The stacking context is created by specific CSS properties such as opacity, transform, z-index, and others. Understanding stacking contexts is crucial for managing the layout and visibility of different elements on a webpage.
How does the z-index property influence the stacking context?
The z-index property in CSS plays a significant role in determining the stacking context. It allows you to control the vertical stacking order of elements that overlap. The element with the highest z-index will be displayed on top. However, z-index only works on positioned elements, and it creates a new stacking context when applied with a value other than ‘auto’.
What are the properties that create a new stacking context?
Several CSS properties can create a new stacking context. These include: position (with a value of absolute, relative, or fixed), flex (with a value other than ‘none’), grid (with a value other than ‘none’), opacity (with a value less than 1), mix-blend-mode (with a value other than ‘normal’), and transform, filter, clip-path, mask/mask-image/mask-border (with a value other than ‘none’).
How does opacity affect the stacking context?
The opacity property in CSS can create a new stacking context. When an element has an opacity value less than 1, it forms a new stacking context. This means that the element and its children will be grouped together in terms of stacking order, separate from the parent context.
What is the role of the ‘isolation’ property in CSS?
The ‘isolation’ property in CSS is used to create a new stacking context. When set to ‘isolate’, it forms a new stacking context, isolating the element from its siblings. This can be useful in controlling the blending of overlapping elements.
How does the ‘transform’ property affect the stacking context?
The ‘transform’ property, when set to any value other than ‘none’, creates a new stacking context. This means that the transformed element and its children form a separate group for stacking purposes. This can be useful when you want to manipulate an element without affecting the stacking order of other elements.
How can I manage multiple stacking contexts on a single page?
Managing multiple stacking contexts on a single page requires careful planning and understanding of how different CSS properties affect the stacking order. You can use the z-index property to control the order of elements within a single stacking context. For multiple stacking contexts, you need to consider the properties that create new contexts and how they interact with each other.
What is the default stacking order of elements without a z-index value?
By default, elements without a z-index value stack in the order they appear in the HTML. Elements later in the HTML will appear on top of elements that appear earlier. However, this order can be altered by creating new stacking contexts or applying a z-index value.
How does the ‘position’ property affect the stacking context?
The ‘position’ property, when set to ‘absolute’, ‘relative’, or ‘fixed’, can create a new stacking context when used in conjunction with a z-index value other than ‘auto’. This allows you to control the stacking order of positioned elements independently from other elements.
Can I nest stacking contexts within each other?
Yes, stacking contexts can be nested within each other. Each new stacking context created by a CSS property forms a separate layer, which can contain other stacking contexts. This allows for complex layering and overlapping effects. However, it’s important to manage these carefully to avoid confusion and maintain control over your layout.
Thierry is a French front-end developer who works for Yahoo (Sunnyvale, Ca.).