A Problem I Investigated for a Long Time
A seemingly simple styling issue of 'no blur' led me all the way to will-change. This article documents how I unraveled it last night.
A seemingly simple styling issue of 'no blur' led me all the way to will-change. This article documents how I unraveled it last night.
A late-night investigation that started with "styles not taking effect," which I eventually traced back to the host chain and will-change.
Last night, I sat in front of my computer, staring at the widgets for nearly half an hour, and the more I looked, the more something felt off.
The cards lacked the frosted glass effect they were supposed to have. It wasn't the kind of "completely missing styles" absence, but rather it looked like it was written, yet seemed not to take effect—you could sense it wanted to be glass, but it just fell short.
The most frustrating part was that it wasn't consistently "completely without blur." At some angles, on some cards, you could see a hint of it, as if it had been washed away by something. Within the same set of widgets, there was a visual mismatch where "this one looks like glass, that one looks like a regular translucent panel."
My first reaction, of course, was to check if backdrop-filter was written, if the opacity was too low, or if the background was being covered by something. I even started to wonder if I had stumbled into another browser compatibility pitfall.
But the more I looked, the more it didn't add up.
This is exactly what makes such issues so torturous: you think you're investigating a CSS property, but in reality, you're investigating an entire rendering pipeline.
In this widget system, what truly contributes to the visual outcome isn't just the card itself. It involves at least the following layers:
This is why I later forced myself not to adjust parameters based on gut feelings. Because if the responsible layer isn't pinpointed, any seemingly effective opacity tweak is just masking the problem, not solving it.
Especially with a frosted glass effect, it can't be faked with just a light-colored background pretending to be sophisticated. It must simultaneously satisfy three things: the background is actually sampled, the foreground has a glass-like shell, and the hierarchical relationship between them doesn't wash away the blur again. If any link is misaligned, what you end up seeing with your eyes is just a "wannabe glass that failed" translucent panel.
The best thing I did last night was to stop randomly testing in the live page. I forced myself to sit down and break the problem apart.
I divided the investigation into four stages:
This breakdown approach may seem clumsy, but its benefits are huge—I no longer have to speculate about a complex page with "everything inside," but can clearly know at which layer the problem first appears.
In the step-by-step addition from C0 to C4, I wasn't focusing on "which card looks more like glass," but which host condition, once restored, causes the blur to collapse.
Looking at it this way, the problem became less mysterious. The first stable point where it collapsed wasn't within the widget's own internal content layer, nor in the rail's decorative layer, but near the host item that carries motion.
Then I continued to break down within segment C, removing suspect conditions one by one:
overflow-y: auto → still didn't appearwill-change → wait, blur came backoverflow-x: clip → still thereThe result was very clear: The first node that restored the blur was removing will-change.
I stared at the screen for several seconds, thinking: That's it?
will-change: transform, opacityYes, it's this very common line of styling, often considered a "performance optimization standard":
will-change: transform, opacity;This thing usually looks completely harmless, even with a bit of "I'm professional, I've done performance optimization" vibe. But once it's permanently attached to the real host chain responsible for blur sampling, the situation changes entirely.
This time, the issue I encountered wasn't a theoretical "might have an impact under certain circumstances," but a very specific phenomenon: as long as this layer will-change is reattached, the frosted glass effect that should be established in Glass mode is noticeably broken; once it's removed from the real blur host chain, the result immediately starts to recover.
Why is this? I later checked MDN and roughly understood: backdrop-filter inherently heavily relies on the browser's handling of compositing layers, sampling areas, and painting order. After will-change pushes the element into a more aggressive compositing preparation state, the background sampling that should have been established on that chain is cut off, resulting in a different outcome. It's not necessarily that "the filter completely disappears," but ultimately you see a visual result that very much resembles "the glass effect not being established."
(In plain terms: you tell the browser "this element is about to move, prepare in advance," and it prepares too eagerly, "optimizing" away the glass effect.)
Precisely because of this, I later established a stricter rule for myself:
Any item serving as the real blur host is not allowed to permanently have will-change: transform, opacity.
This doesn't mean will-change can never be used, but rather that it can no longer be treated as a default safe sugar coating, especially not indiscriminately applied to glass hosts. If performance optimization is absolutely necessary, then:
By the end of last night's round of wrapping up, the things I confirmed were roughly as follows:
First, the main failure of the frosted glass effect for widgets in Glass mode is no longer a "speculative level" issue; the real recovery point has been found and can be stably reproduced and reversed.
Second, the visual perception of "a whole column of light-colored rectangular backing" on the rail is not due to some mysterious large container secretly painting a background layer, but rather a false impression created by the combination of scroll container clipping, card shadows being consumed, and the shell layer being too solid. This part was incidentally discovered and hasn't been fully cleaned up yet.
Third, music is not perfect from the start either. It just appears more "established" than other cards because it has more internal layers; the real work still needs to return to the unified shell layer and real host chain for calibration.
Of course, last night doesn't mean everything is over. There are still a few things I clearly know should not be conflated:
In other words, will-change's positioning solved "who is the main culprit of the fault," but it did not automatically answer all visual and interaction questions for me.
Looking back now, the most noteworthy point from last night wasn't which line of style I ultimately deleted, but that I was once again reminded:
In complex frontend issues, the most dangerous action often isn't making a mistake, but making changes too quickly.
If I had immediately started randomly adjusting opacity, shadows, borders, and blur radius on both Ticket and Glass sides last night, I likely would have ended up with a version that "looks temporarily acceptable," but I wouldn't have known what I actually fixed.
And this time, I was able to trace the problem to will-change not by inspiration, but by discipline:
I'm increasingly convinced that this investigation approach itself is also an engineering asset.
Because what's truly frightening is never a specific blur bug, but our habitual tendency to attribute everything to "let's try adjusting a few more parameters" when we lack evidence.
At least one thing I can say with certainty from last night: this "no frosted glass" fault is not a perception issue, nor a browser temperament issue. It indeed has a culprit, and I've already pulled that culprit out of the host chain.
(Next, I should investigate Ticket mode, but I need to get some sleep first.)
End of article. This article has undergone ChatGPT's stylistic optimization. However, anyone who has used GPT can tell it's not but is flying everywhere.