How to fix z-index conflicts with a feedback widget

A feedback button is hidden when another element paints above it in the same stacking context. Find the covering element in DevTools, then fix it in your own CSS by moving the bar or the modal. The widget has no z-index option, so the fix lives on your page.

By 路 Published 路 Last updated

Why does a feedback button end up under other elements?

A feedback button hides behind another element when that element is higher in the stacking order at the same spot on the page. The usual culprits are a fixed header, a sticky footer, a modal overlay or a cookie banner with a high z-index. The fix is almost always in your own CSS, not in the widget, because the widget's documented options are data-position, data-color, data-locale and data-trigger. None of them sets a z-index.

Start by identifying the element that covers the button. Guessing at z-index values across the stylesheet usually makes the problem worse, because raising one element can push another above the button in a different state.

How do stacking contexts decide what is on top?

Inside one stacking context, an element with a higher z-index paints above one with a lower value. Across contexts, the whole context moves as a unit. A child with a z-index of 9999 cannot escape a parent that sits lower in the order. Several common properties create a new stacking context on an element even without a z-index: transform, filter, perspective, opacity below 1, and will-change set to one of those properties.

Here is a short example of a modal trapped by a wrapper:

.sticky-header {
  position: fixed;
  top: 0;
  z-index: 1000;
}

/* A transform on the wrapper creates a stacking context. */
.app-wrapper {
  transform: translateZ(0);
}

/* 5000 only competes with siblings inside .app-wrapper. */
.app-modal {
  position: fixed;
  z-index: 5000;
}

The modal's value of 5000 does not beat the header at 1000, because the header sits outside the wrapper and is compared with the wrapper as a whole. The lesson is that a high z-index only means something inside its own context, so check the ancestors before raising a value.

How do you find the element that covers the widget?

Use the browser's inspector and a short script. Open the page, hover the floating button and look at the highlighted box in the Elements panel. The inspector shows the button's host element. Then open a second element list at the button's position with a script:

// Returns the topmost element at a point. Adjust the offsets to where your button sits.
function topElementAt(offsetFromRight, offsetFromBottom) {
  const x = window.innerWidth - offsetFromRight;
  const y = window.innerHeight - offsetFromBottom;
  return document.elementFromPoint(x, y);
}

const top = topElementAt(48, 48);
console.log(top && top.tagName, top && top.className);

When the widget is on top, elementFromPoint reports the widget's host element, because the browser retargets elements inside a Shadow DOM to their host. When your header or footer is on top, the script reports that element instead, and you have your culprit.

Pick the smallest change that clears the corner. Three fixes work well:

  1. Stop the sticky bar from reaching the corner. Give it a right edge offset, so the feedback button has clear space.
  2. Move the feedback button to the other corner with data-position.
  3. Replace the floating button with an inline trigger in the bar's own content, using data-trigger="none".

The first fix needs no z-index at all:

.sticky-footer {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 96px;
}

Pick the right offset from the size of the button on your page. The value 96px is an example and should match the space the button takes at the corner.

Avoid the temptation to win every overlap with a very large z-index on your own elements. Values such as 99999 spread through a stylesheet, and each new modal then needs a larger number than the last. Agree on a short scale of named layers for your page, such as header, dropdown, modal and toast, and keep each element on that scale. Because the widget's stacking value cannot be set from outside, place it in your scale by testing the layers together rather than by guessing a number.

When you change a layer, re-run the whole matrix, not only the template you were fixing. A header that clears the button on the home page can still cover a form further down, because the form lives in a different stacking context.

What about modals that open over the panel?

A modal from your app can open beneath the feedback panel, or the panel can open beneath your modal. Both are bugs. Test it by opening your modal, then clicking the feedback button. If the panel appears behind the modal, the visitor cannot see or type into it.

Avoid triggering the widget from inside your own modal. Close the modal first, then open the widget:

// Close the app modal, then open the feedback panel with a preselected type.
function openFeedbackAfterModal(closeModal, kind) {
  closeModal();
  if (window.EscutaProduto && typeof window.EscutaProduto.open === "function") {
    window.EscutaProduto.open({ kind: kind });
  }
}

If your modal is the page itself, such as an onboarding flow, use an inline trigger inside the modal's content instead. Then the feedback path stays inside the modal and never competes with it.

How do you test for overlaps in a repeatable way?

An overlap that appears once is easy to miss. Build a small matrix of states and run the same check on each one. The table below gives a practical starting point.

State What to check
Idle page The button is visible and not covered at both corners
Scrolled to the bottom Last lines of content are not hidden behind the button
Modal open The panel can open above the modal, or the modal closes first
Menu open The menu does not hide the button or the panel
Cookie banner visible Banner and button do not sit on the same spot
Phone width The same checks at a narrow viewport

Run the elementFromPoint check in each row, and record the element name for every failure. Repeat the run after each CSS change, because a fix for one template can break another.

Confirming the widget layout in Escuta Produto

Add the script with the default position, run the matrix above on your most crowded template, then change only your own CSS. The attributes are listed in the widget docs. For the reasoning behind corner choices, read where to place a feedback button. For keyboard focus when the panel opens over a page, how to make a feedback widget accessible covers what the visitor should see.

Frequently asked questions

Why is my feedback button behind my header?

Your fixed header has a higher stacking order at the same spot on the page. Use the browser inspector or elementFromPoint to find the covering element, then move or shrink that element in your own CSS.

Does the feedback widget have a z-index setting?

No. The documented options are position, color, locale and trigger. Stacking is controlled by your page CSS, so change the elements that cover the button rather than looking for a widget setting.

Why does a transform on a parent change my modal's layering?

A transform creates a new stacking context. The z-index of a child then competes only inside that parent, so the modal can no longer rise above elements outside it. Remove the transform or move the modal out of that wrapper.