Skip to main content

Layout Diagnostics

Functions for finding layout issues across the page.

findOverflows

Find all elements with horizontal or vertical overflow.

window.__devtool.findOverflows()

Parameters: None

Returns:

[
{
selector: ".sidebar",
element: {tag: "aside", classes: ["sidebar"]},
overflow: "horizontal",
scrollWidth: 400,
clientWidth: 250,
excess: 150,
overflowStyle: "hidden"
},
{
selector: ".content-area",
element: {tag: "main", classes: ["content-area"]},
overflow: "vertical",
scrollHeight: 2000,
clientHeight: 800,
excess: 1200,
overflowStyle: "auto"
},
{
selector: ".table-wrapper",
element: {tag: "div", classes: ["table-wrapper"]},
overflow: "both",
scrollWidth: 1200,
clientWidth: 600,
scrollHeight: 500,
clientHeight: 300,
excess: {horizontal: 600, vertical: 200},
overflowStyle: "scroll"
}
]

Example:

const overflows = window.__devtool.findOverflows()
console.log(`Found ${overflows.length} elements with overflow`)
overflows.forEach(o => {
console.log(`${o.selector}: ${o.overflow} overflow, ${o.excess}px excess`)
})

findStackingContexts

Find all stacking contexts on the page.

window.__devtool.findStackingContexts()

Parameters: None

Returns:

[
{
selector: "#modal",
element: {tag: "div", id: "modal", classes: ["modal"]},
zIndex: 1000,
reason: "position: fixed with z-index",
parentContext: "html"
},
{
selector: ".dropdown",
element: {tag: "div", classes: ["dropdown", "open"]},
zIndex: 100,
reason: "position: absolute with z-index",
parentContext: "#header"
},
{
selector: ".animated-card",
element: {tag: "div", classes: ["animated-card"]},
zIndex: "auto",
reason: "transform",
parentContext: "#main"
},
{
selector: ".overlay",
element: {tag: "div", classes: ["overlay"]},
zIndex: 50,
reason: "opacity less than 1 (0.8)",
parentContext: "html"
}
]

Reasons for creating stacking context:

  • "root element"
  • "position: fixed/absolute/relative/sticky with z-index"
  • "position: fixed/sticky"
  • "opacity less than 1 (value)"
  • "transform"
  • "filter"
  • "perspective"
  • "clip-path"
  • "mask"
  • "isolation: isolate"
  • "mix-blend-mode"
  • "will-change: opacity/transform"
  • "contain: layout/paint"

Example:

const contexts = window.__devtool.findStackingContexts()
contexts.sort((a, b) => (b.zIndex || 0) - (a.zIndex || 0))
console.log('Stacking order:', contexts.map(c => `${c.selector} (z-index: ${c.zIndex})`))

findOffscreen

Find elements positioned outside the viewport.

window.__devtool.findOffscreen()

Parameters: None

Returns:

[
{
selector: ".hidden-menu",
element: {tag: "nav", classes: ["hidden-menu"]},
position: "left",
distance: -300,
bounds: {top: 0, right: -100, bottom: 500, left: -400}
},
{
selector: ".slide-out-panel",
element: {tag: "aside", classes: ["slide-out-panel"]},
position: "right",
distance: 50,
bounds: {top: 0, right: 1370, bottom: 800, left: 1320}
},
{
selector: ".footer-extra",
element: {tag: "div", classes: ["footer-extra"]},
position: "below",
distance: 500,
bounds: {top: 1500, right: 800, bottom: 1700, left: 0}
}
]

Positions:

  • "above" - Above the viewport
  • "below" - Below the viewport
  • "left" - To the left of viewport
  • "right" - To the right of viewport

Example:

const offscreen = window.__devtool.findOffscreen()
console.log('Hidden elements:')
offscreen.forEach(e => {
console.log(`${e.selector} is ${Math.abs(e.distance)}px ${e.position}`)
})

Common Patterns

Debug Horizontal Scroll Issue

// Find what's causing horizontal scroll
const overflows = window.__devtool.findOverflows()
const horizontalOverflows = overflows.filter(o =>
o.overflow === 'horizontal' || o.overflow === 'both'
)

console.log('Horizontal overflow sources:')
horizontalOverflows.forEach(o => {
console.log(`${o.selector}: content is ${o.scrollWidth}px in ${o.clientWidth}px container`)
window.__devtool.highlight(o.selector, {color: 'rgba(255,0,0,0.3)'})
})

Z-Index Debugging

// Understand z-index stacking
const contexts = window.__devtool.findStackingContexts()

// Group by parent context
const tree = {}
contexts.forEach(c => {
const parent = c.parentContext || 'root'
tree[parent] = tree[parent] || []
tree[parent].push(c)
})

// Print stacking tree
Object.entries(tree).forEach(([parent, children]) => {
console.log(`\n${parent}:`)
children.sort((a, b) => (b.zIndex || 0) - (a.zIndex || 0))
children.forEach(c => console.log(` ${c.selector} (z-index: ${c.zIndex}) - ${c.reason}`))
})

Find Hidden Navigation

// Find slide-out menus and hidden panels
const offscreen = window.__devtool.findOffscreen()
const leftHidden = offscreen.filter(e => e.position === 'left')
const rightHidden = offscreen.filter(e => e.position === 'right')

console.log('Left slide-out panels:', leftHidden.map(e => e.selector))
console.log('Right slide-out panels:', rightHidden.map(e => e.selector))

Comprehensive Layout Audit

function auditLayout() {
const overflows = window.__devtool.findOverflows()
const stackingContexts = window.__devtool.findStackingContexts()
const offscreen = window.__devtool.findOffscreen()

return {
issues: {
overflows: overflows.length,
unexpectedStackingContexts: stackingContexts.filter(c =>
c.reason !== 'root element' && c.zIndex === 'auto'
).length,
offscreenElements: offscreen.length
},
details: {
horizontalOverflows: overflows.filter(o => o.overflow === 'horizontal' || o.overflow === 'both'),
highZIndex: stackingContexts.filter(c => c.zIndex > 100),
hiddenLeft: offscreen.filter(e => e.position === 'left')
}
}
}

const audit = auditLayout()
console.log('Layout audit:', audit.issues)

Fix Overflow Loop

// Iteratively find and inspect overflow issues
function debugOverflowChain(selector) {
const parents = window.__devtool.walkParents(selector)

console.log('Checking overflow chain for', selector)

parents.forEach(parent => {
const overflow = window.__devtool.getOverflow(parent.selector || parent.tag)
if (overflow.hasHorizontalOverflow || overflow.hasVerticalOverflow) {
console.log(` ${parent.tag}${parent.id ? '#' + parent.id : ''}: has overflow`)
console.log(` Content: ${overflow.scrollWidth}x${overflow.scrollHeight}`)
console.log(` Container: ${overflow.clientWidth}x${overflow.clientHeight}`)
}
})
}

Performance Notes

  • These functions scan significant portions of the DOM
  • Results are computed fresh each call (not cached)
  • Use sparingly on very large pages
  • Consider limiting scope with CSS selectors when debugging specific areas

See Also