Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Filters applied to non-root elements generate containing blocks that corrupt the intended position of fixed/absolute descendants #402

Open
Lonniebiz opened this issue Jul 4, 2020 · 5 comments

Comments

@Lonniebiz
Copy link

Lonniebiz commented Jul 4, 2020

CSS filters that only modify the color of a document (grayscale for example). Should not cause all fixed and absolute positioned elements to be places into containing blocks.

Please take a look at this example:
https://stackoverflow.com/questions/62717289/

The link above is convenient, because you can run the code snippets by simply clicking a button.

I'm in agreement with someone called "heycam", who said:

"heycam: you could restrict this filter behaviour for only some predefined filter keywords"

To see this quote in context, hit ctrl-F to find it on this page:
https://www.w3.org/2015/02/10-fx-minutes.html#action02

Basically, I feel very strongly about my opinion which is:

"If a filter has nothing to do with size or position, then that filter should not have any effect on the position of its fixed and absolute descendants".

The current spec should be modified so that the filter behavior that encapsulates fixed and absolutely positioned descendants into containing blocks, only happens on filters that actually effect the size or position of descendants and NOT on filters that only change the color of descendants.

We need more granularity than this all or nothing approach that prescribes that ALL filters must cause containing blocks.

@Lonniebiz Lonniebiz changed the title Not all Filter Should Lead to Containing Blocks on All Fixed/Absolute Descentantsz Not all Filter Should Lead to Containing Blocks on All Fixed/Absolute Descendants Jul 4, 2020
@Lonniebiz Lonniebiz changed the title Not all Filter Should Lead to Containing Blocks on All Fixed/Absolute Descendants Not all Filters Should Lead to Containing Blocks on All Fixed/Absolute Descendants Jul 4, 2020
@dbaron
Copy link
Member

dbaron commented Jul 5, 2020

The problem with what you propose is that the content to be filtered would then not be atomic in terms of z-order; there could be external elements that are in-between (in terms of z-ordering) the different parts of the element to be filtered. Filters, however, are defined in terms of painting the entire contents of the element to be filtered into a buffer, applying the filter to that buffer, and then drawing that buffer onto the background. If the filter is applied to multiple parts separately which are then separately drawn onto the background, it produces substantially different and incorrect results.

To take a simple example:

<div style="background: black; width: 100px; height: 10px">
  <div style="background: yellow; width: 100px; height: 10px; position: absolute"></div>
</div>

Here's what it looks like with the original, the correct filtered (with blur(5px)) result, and the incorrect separately filtered result (see source html):

Screenshot from 2020-07-05 09-13-52

To avoid this incorrect result, the spec takes the same approach that it does for a number of other properties and ensures that the elements create a containing block, which in turn allows their entire subtree to be visually atomic.

@Lonniebiz
Copy link
Author

Lonniebiz commented Jul 5, 2020

@dbaron : Blur is more than just a color-change. I'm speaking exclusive about filters that only change the color of elements. In your example CSS, change only the text that says filter: blur(5px) to filter: grayscale(100), instead. Zero corruption occurs. Also, then change filter: grayscale(100) to filter: invert(100%). Again, no corruption occurs. I'm requesting that exceptions be made when all combined filters are exclusively dedicated to color change.

Wouldn't it be possible to apply grayscale or invert to a collection of elements without causing unintended side effects? You've already made an exception for this on the root element, why not let the developer choose whether or not he cares about z-order?

I'll be honest with you, I don't fully understand why it is impossible to achieve proper rendering without the addition containing blocks. In Chromium, I can see that you're able to achieve this just fine as long as the filter is applied to the root element. Why is it so hard to do the same thing for elements that are not root?

The whole idea behind CSS, is to change the style of the structured HTML. If CSS has to change the structure of the HTML/DOM, in order to achieve a visual effect, it seems to me that something is being done the wrong way. To me, corrupting the position of fixed/absolute positioned descendant (with containing blocks) seems worst than z-order corruption.

What does a containing block give you that a document fragment does not? Couldn't this drawing be done into a document fragment container instead a block-element? If not, why not clone the element that needs to be filtered, add it to the DOM with visibility:hidden, draw over it and the switch it out with the original?

As a developer (not a spec writer), I've had to do tricks like this before in order to be more performant in switching out something that took while to render incrementally invisibly between recursive timeouts.

I don't understand this problem thoroughly enough yet. I hope you don't mind me throwing these ideas at you. I'm just hoping something I say triggers an idea where this problem can be solved in a better way.

Thankfully, the root element is an exception, but it doesn't help in Firefox yet. Seems like grayscale and invert could also be exceptions not requiring a containing block.

I'll study your example some more. I must be missing something.

@dbaron
Copy link
Member

dbaron commented Jul 5, 2020

The problem is still present for color changes when the upper layers are not fully opaque, although the examples are not as dramatically different. Consider this modified example where the front element is 50% opaque:

<div style="background: black; width: 100px; height: 10px">
  <div style="background: rgba(255, 255, 0, 0.5); width: 100px; height: 10px; position: absolute"></div>
</div>

Here's what it looks like with the original, the correct filtered (with hue-rotate(180deg)) result, and the incorrect separately filtered result (see source html):

Screenshot from 2020-07-05 12-59-40

@Lonniebiz
Copy link
Author

Lonniebiz commented Jul 6, 2020

This is the problem, in one sentence:

"Filters applied to non-root elements generate containing blocks that corrupt the intended position of fixed/absolute descendants."

How can this exact problem be addressed, in light of the W3C's priority of preventing z-order related side-effects? Please direct you wonderful intelligence to solving this problem, now that you've proven the z-ordering problem.

I've got one idea. How about if the spec stipulated something like this:

"When a containing block, is generated by a filter, fixed and absolute descendant must NOT honor that containing block, they must instead honor the parent of that containing block, as it pertains to positioning and all rendering must be done to those descendants at that position, while honoring their z-order context at that position."

It seems like the filter could take into consideration a descendant's z-index and draw over it based on what's behind it at that position. Perhaps it is just too complicated, intensive and burdensome to achieve.

As is, the spec favors one side-effect's prevention while causing another. Surely, there is someone out there smart enough to change this spec to where both problems are solved? I usually have a pretty good instinct when it comes to deciding whether something is possible or not. To me, this seems possible, but its obviously very difficult (or intensive), else someone would have already figured it out by now.

I'm surprised that the spec would allow CSS to change the structure of a document in order to accomplish STYLE. From years ago, I remember that website CSS Zen Garden, where it showcased thousands of style sheets applied to the exact same HTML and I was amazed at all the completely different designs that could be achieved without modifying the HTML at all.

This problem (with filters) breaks the universality of style sheets. You can no long expect to control the position of descendants when a filter is applied to a non-root element.

The reason universality matters to me, is because I try to apply my own style sheets to all web pages using a Firefox plugin called Dark Mode. Over the years, my eye's have gotten tired of reading web pages with white backgrounds and black text. That extension does a great job most of the time. Here's how GitHub looks to me:
GitHub

But, on its filter-based themes (that use grayscale and invert), positioning gets mangled too often. I'll click a dialog that is suppose to pop up dead center of the screen, and instead I'll have to scroll up or down to see it.

Also, I have an application where I want my users to be able to change their theme, but I have to be very careful with filter based styling, else I'll end up corrupting the position of in-page dialogs.

@Lonniebiz Lonniebiz changed the title Not all Filters Should Lead to Containing Blocks on All Fixed/Absolute Descendants Filters applied to non-root elements generate containing blocks that corrupt the intended position of fixed/absolute descendants Jul 6, 2020
@oldmansutton
Copy link

oldmansutton commented Jul 12, 2022

This is also present for backdrop-filter. Adding "backdrop-filter: blur(4px)" to an absolutely positioned element breaks the intended position of the element to become relative to its parent container, rather than the intended purpose of absolute position. This is very counter-intuitive, and I agree with @Lonniebiz on this one. I spent hours this morning trying to figure out why backdrop-filter would break my drag-and-drop module, considering that nowhere in my DOM were any of the elements styled with a "position: relative". That a container element was added seems like an anti-pattern for CSS, especially given how heavily things like "backdrop-filter" are used for frosted glass effects in most UI's.

EDIT: If a container is added, it should at least be exposed in the DOM so that it can be overrode if so desired. The spec is divorced from the reality of the sites we wish to design, and is more hurtful than helpful.

EDIT 2: mix-blend-mode is able to work on absolutely placed elements without adding a container, why can't "filter: blur()" or "backdrop-filter: blur()"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants