Why CSS Filter Breaks Position Fixed

Applying a CSS filter to a parent element breaks child position: fixed viewport anchoring because the CSS specification mandates that any non-none filter establishes a new containing block for all descendants. Consequently, instead of anchoring relative to the browser viewport, the fixed child positions itself relative to the filtered parent, effectively behaving like an absolutely positioned element.

The CSS Specification Behind the Behavior

Under normal circumstances, an element styled with position: fixed calculates its coordinates (top, left, right, bottom) relative to the initial containing block, which corresponds to the browser viewport. This allows fixed elements—such as navigation bars, modals, and floating action buttons—to remain stationary when the user scrolls the page.

However, according to the W3C CSS Filter Effects Module Level 1 specification, any value other than none for the filter property causes the element to become the containing block for all descendants, including fixed-position elements.

To apply a visual filter (such as blur, grayscale, or drop-shadow), the browser's rendering engine must isolate the target element and its entire subtree into an offscreen bitmap buffer. The graphical operations are then computed on that flattened buffer before drawing it back onto the screen. Because the filtered element must be processed as a self-contained coordinate space, the viewport's reference frame cannot pierce through this boundary. As a result, the filtered parent intercepts the child’s fixed coordinates.

The filter property is not the only CSS declaration that alters the containing block for fixed elements. The same behavior is triggered by any property that isolates rendering or modifies the coordinate matrix, including:

  • transform (any value other than none)
  • perspective (any value other than none)
  • backdrop-filter (any value other than none)
  • contain (with values such as paint, layout, strict, or content)
  • will-change (if set to any property that generates a containing block, such as will-change: filter)

How to Fix the Problem

Because this behavior is intentional according to the CSS specification, it cannot be overridden using custom CSS on the child element. To maintain viewport anchoring, you must change how the DOM or styling is structured:

  1. Move the Fixed Element Outside the Filtered Container: Relocate the fixed element in the HTML hierarchy so that it is a sibling of the filtered element or placed directly under the <body> tag. In JavaScript frameworks, this can be achieved using portals (such as React's createPortal or Vue's <Teleport>).
  2. Apply the Filter to a Dedicated Layer: Instead of applying the filter to a general wrapper that holds both fixed elements and regular content, apply the filter exclusively to the specific elements that require it, or use an absolutely positioned pseudo-element (::before or ::after) behind your content.