Firefox: How to Grayscale an Entire Page without breaking fixed-positioned elements?

泪湿孤枕 提交于 2020-07-09 17:13:22

问题


Why do CSS filters, (ones that seem to have nothing to do with size/positioning) break your ability to give descendant-elements a fixed position?

In addition to answering this question, please suggest a solution that addresses the problem I showcase below.

The CSS and HTML below simply produces a little red box in the center of the viewport:

#box
{
  background: red; color: white;
  display: inline-block;
  border: solid #333333 10px;
  padding: 5px;
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}
<!DOCTYPE html>
<html lang="en">
<head>
    <title>Before Filter Corrupts Fixed Positioning</title>
</head>
<body>
<div id ="box">Dead Center<div>
</body>
</html>

Now, let's assume we have a requirement that this whole page must be displayed in grayscale. The only effective change below, is the addition of a CSS grayscale filter. However, upon adding this filter, the box will no longer honor the center page positioning we prescribed:

body { filter: grayscale(100%); }
#box
{
  background: red; color: white;
  display: inline-block;
  border: solid #333333 10px;
  padding: 5px;
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}
<!DOCTYPE html>
<html lang="en">
<head>
    <title>Filter Corrupts Fixed Positioning</title>
</head>
<body>
<div id ="box">Dead Center<div>
</body>
</html>

Notice that the box is no longer centered vertically. Is this a bug, or is it just stupid by design?

Update 1:

Temani Afif recommended, in the comments, applying the filter on the html element (instead of the body element). While this does fix the issue in Chrome, it doesn't in Firefox 78:

html { filter: grayscale(100%); }
#box
{
  background: red; color: white;
  display: inline-block;
  border: solid #333333 10px;
  padding: 5px;
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}
<!DOCTYPE html>
<html lang="en">
<head>
    <title>Filter Corrupts Fixed Positioning</title>
</head>
<body>
<div id ="box">Dead Center<div>
</body>
</html>

Update 2:

Based on feedback, here I try applying the filter to :root, instead of html. While this does fix the issue in Chrome, it doesn't in Firefox 78:

:root { filter: grayscale(100%); }
#box
{
  background: red; color: white;
  display: inline-block;
  border: solid #333333 10px;
  padding: 5px;
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}
<!DOCTYPE html>
<html lang="en">
<head>
    <title>Filter Corrupts Fixed Positioning</title>
</head>
<body>
<div id ="box">Dead Center<div>
</body>
</html>

I submitted this issue to Firefox.

Summary: Even though the spec allows you to apply the filter to the document root, to avoid the encapsulation of fixed/absolute-descendants, I'm of the opinion that the spec could be improved by avoiding this behavior altogether on filters that have nothing to do with modifying size and position. Filters like grayscale should have zero impact on the size or position of descendants and therefore it shouldn't matter where you apply that filter (root or not). On filters like grayscale, there should never be any wrapping of descendants. If you agree, please help me explain it to the W3C here.

Update 3: @NateG recommended applying the filter to body > *. So far, that seems to work in both Chromium and Firefox! See below:

body > * { filter: grayscale(100%); }
#box
{
  background: red; color: white;
  display: inline-block;
  border: solid #333333 10px;
  padding: 5px;
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}
<!DOCTYPE html>
<html lang="en">
<head>
    <title>Filter Corrupts Fixed Positioning</title>
</head>
<body>
<div id ="box">Dead Center<div>
</body>
</html>

@NateG, post your comment as an answer so I can give it a big green check-mark!


回答1:


What if you apply the filter to body > *? Less performant, but may solve this issue. I admit to not fully considering new issues it may raise, but I can't think of a scenario in which it would alter the containing block of second depth elements.




回答2:


You should not mess with the body/html when applying some styles because you will face a lot of unexpected result due to the nature of those elements.

Related question to see that you will face more issues than expected playing with filter on html: CSS filter:invert not working with background-color

You need to also consider propagation for some properties like overflow and background

Here is one idea to simulate your fixed element using position:sticky and considering an extra wrapper to avoid any kind of issue:

.html {
  min-height: 100vh;
  filter: grayscale(100%);
}

#box {
  background: red;
  color: white;
  border: solid #333333 10px;
  padding: 5px;
  position: sticky;
  top: 50vh;
  left: 50%;
  transform: translate(-50%, -50%);
  /* the combination of float and transparent shape outside will make the element
     * shrink to fit
     * will not affect the other elements
  */
  float: left;
  shape-outside: linear-gradient(transparent, transparent);
}


/* to simulate content */

.content {
  font-size: 40px;
}

body {
  margin: 0;
}
<div class="html">
  <div id="box">Dead Center
  </div>
  <div class="content">
    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus volutpat eu ante sed lacinia. In sit amet sapien euismod nibh malesuada convallis pulvinar non mauris. Ut id felis posuere, pharetra justo eget, viverra lacus. Vestibulum tellus libero,
    euismod ac tellus vitae, dapibus mollis tellus. Donec ornare consectetur dui. Vestibulum iaculis neque leo, nec bibendum nisl consectetur in. Curabitur at viverra augue, ac semper neque.
  </div>
</div>



回答3:


In the spec it says

A value other than none for the filter property results in the creation of a containing block for absolute and fixed positioned descendants unless the element it applies to is a document root element in the current browsing context. The list of functions are applied in the order provided.

So your fixed element with id box is being fixed to a new containing block and no longer fixed to the viewport. I also found this much more thorough answer by Temani Afif



来源:https://stackoverflow.com/questions/62717289/firefox-how-to-grayscale-an-entire-page-without-breaking-fixed-positioned-eleme

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!