CSS Grayscale Filter Calculation and Child Elements

The CSS filter: grayscale(100%) function transforms an element's visual appearance by converting all of its colors into shades of gray. This conversion relies on a standardized luminance algorithm based on human visual perception rather than a simple average of RGB values. Furthermore, applying this filter affects not just the target element itself, but alters the rendering pipeline for all nested child elements, creating specific structural side effects such as new stacking contexts and irreversible inherited visual styles.

The Grayscale Calculation

Under the W3C Filter Effects Module Level 1 specification, filter: grayscale() uses a linear color matrix transformation to determine luminance. Human eyes do not perceive all wavelengths of light equally; green appears substantially brighter than red, which in turn appears brighter than blue.

To account for human perception, the browser applies the ITU-R BT.709 color space coefficients to the color channels:

  • Red: 0.2126 (21.26%)
  • Green: 0.7152 (71.52%)
  • Blue: 0.0722 (7.22%)

When grayscale(100%) (or a factor of 1) is applied, the browser calculates the gray intensity value (\(Y\)) for every pixel:

\[Y = (0.2126 \times R) + (0.7152 \times G) + (0.0722 \times B)\]

The resulting intensity value \(Y\) is then assigned equally to the red, green, and blue channels of the output pixel:

  • \(R_{out} = Y\)
  • \(G_{out} = Y\)
  • \(B_{out} = Y\)

Alpha (opacity) values are left unaffected during this transformation.

For partial values (such as grayscale(50%)), the browser uses an interpolation matrix where the identity matrix blends proportionally into the luminance matrix:

\[\begin{bmatrix} 0.2126 + 0.7874(1 - d) & 0.7152 - 0.7152(1 - d) & 0.0722 - 0.0722(1 - d) \\ 0.2126 - 0.2126(1 - d) & 0.7152 + 0.2848(1 - d) & 0.0722 - 0.0722(1 - d) \\ 0.2126 - 0.2126(1 - d) & 0.7152 - 0.7152(1 - d) & 0.0722 + 0.9278(1 - d) \end{bmatrix}\]

When \(d = 1\) (100%), all rows collapse identically to \([0.2126, 0.7152, 0.0722]\).


How It Affects Child Elements

Applying filter: grayscale(100%) affects descendant elements in two critical ways: visual flattening and structural layout behavior.

1. Visual Composition and Non-Reversibility

The filter property is technically not inherited through CSS cascade rules—child elements do not automatically receive filter: grayscale(100%) on their own computed styles. Instead, the filter is applied post-composition.

The browser first renders the parent element along with all of its child elements off-screen as a single graphical buffer (bitmap). Once this combined layer is rasterized, the grayscale algorithm runs on the entire output.

Because the filter applies to the flattened result of the parent container:

  • Children cannot opt out: Setting filter: none or filter: grayscale(0%) on a child element will not restore its color. The child's colors are already converted during the final rendering pass of the parent.
  • Child filters chain additively: Applying another filter (like filter: sepia(100%)) to a child element will apply before the parent's grayscale filter, meaning the child will be converted to sepia first and then stripped of all color by the parent's grayscale pass.

2. Stacking Context and Containing Block Behavior

Any non-default value on the filter property alters the DOM element's behavior in the layout engine:

  • Creates a New Stacking Context: The filtered element behaves like an element with an explicit z-index, isolating its children's layering relative to outside elements.
  • Becomes a Containing Block: The filtered element becomes the containing block for all absolute (position: absolute) and fixed (position: fixed) descendant elements. As a result, fixed-position children will anchor to the filtered ancestor rather than the viewport.