Skip to content

Conversation

@yangchristina
Copy link
Collaborator

@yangchristina yangchristina commented Dec 21, 2025

Resolves #3283

IOS:

Screen.Recording.2026-01-02.at.12.50.05.PM.mov

Chrome:

Screen.Recording.2026-01-02.at.12.52.25.PM.mov

@yangchristina
Copy link
Collaborator Author

yangchristina commented Dec 27, 2025

@fbmcipher what is the reason we have a z-index on navbar? Is it possible to remove it? I can't add z-index to the drawer, since adding z-index creates a new stacking context, which breaks blending (ex. either progressive blur breaks, or sheet content blending with backdrop breaks).

With all the blending in the new mocks, I think we are going to have to start using z-index sparingly, since it creates a new stacking context.

Screen.Recording.2025-12-26.at.11.47.56.PM.mov

@yangchristina
Copy link
Collaborator Author

@fbmcipher Also aside from the navbar issue, the new code allows the fade in only (no slide in) effect for the backdrop, and allows us to control opacity based on height of the drawer

Screen.Recording.2025-12-27.at.12.07.57.AM.mov

@raineorshine
Copy link
Contributor

@fbmcipher what is the reason we have a z-index on navbar? Is it possible to remove it? I can't add z-index to the drawer, since adding z-index creates a new stacking context, which breaks blending (ex. either progressive blur breaks, or sheet content blending with backdrop breaks).

With all the blending in the new mocks, I think we are going to have to start using z-index sparingly, since it creates a new stacking context.

I don't think there is a way to avoid z-index, as we need to control the z order of absolutely positioned elements. We have a nice, centralized, global z-index schedule.

The only other way I know to control z order is by source order (literally the order of elements in the DOM), which offers less control and can sometimes create impossible situations if there are other source ordering dependencies.

@yangchristina
Copy link
Collaborator Author

@fbmcipher what is the reason we have a z-index on navbar? Is it possible to remove it? I can't add z-index to the drawer, since adding z-index creates a new stacking context, which breaks blending (ex. either progressive blur breaks, or sheet content blending with backdrop breaks).
With all the blending in the new mocks, I think we are going to have to start using z-index sparingly, since it creates a new stacking context.

I don't think there is a way to avoid z-index, as we need to control the z order of absolutely positioned elements. We have a nice, centralized, global z-index schedule.

The only other way I know to control z order is by source order (literally the order of elements in the DOM), which offers less control and can sometimes create impossible situations if there are other source ordering dependencies.

@raineorshine I see, I did think of a way to get this to work with z-index, by moving the progressive blur outside the sheet. My issue now is that I can't get the Done button to blend properly on ios (works on chrome).

@fbmcipher any ideas for this Done button?

Chrome:
Screenshot 2025-12-27 at 10 27 14 AM

Safari:
Screenshot 2025-12-27 at 10 27 25 AM

The blending on this done button seems to break really easily, since I've had to attempt to fix this button many times now. Is there another way we can do this? Ex. have an image for this button instead?

@fbmcipher
Copy link
Collaborator

Hi @yangchristina - thanks for sticking with this through all the challenges. Admittedly, it has proven more difficult than I expected to work with blending and compositing on the web, particularly on Safari.

This issue stood out as strange to me, because the PanelCommand component in the Command Center also uses blending modes – and those seem to work reliably.

Using the Layers pane in the Safari developer tools, I noticed that the "Done" button isn't automatically being promoted to its own compositing layer. Each of the PanelCommand buttons, where blend modes do work, are on their own layer.

Since the Done button blends as expected in Chrome, I used the Layers pane in Chrome DevTools to see if a compositing layer for the "Done" button is created there. Indeed, it is.

So Safari is not promoting the Done button to its own compositing layer, whereas Chrome does so automatically.


Not to be a shoddy workman blaming his tools, but this does look like yet another Safari rendering quirk that we need to anticipate and build for, and looks very similar to the issue we discussed in the Command Center buttons task.


Proposed solution

Some brief testing showed that using will-change on the Done button to manually promote the Done button to its own compositing layer solves the issue. This matches the behaviour seen in Chrome.

<div
  className={css({
    gridArea: 'button',
    background: 'fgOverlay20',
    borderRadius: 46,
    mixBlendMode: 'soft-light',
  })}
  style={{
    // Safari fix: will-change forces GPU layer creation which fixes blend mode rendering bug.
    willChange: 'transform',
  }}
/>
<button
  {...fastClick(onClose)}
  className={css({
    all: 'unset',
    gridArea: 'button',
    mixBlendMode: 'lighten',
    opacity: 0.5,
    fontWeight: 500,
    cursor: 'pointer',
    padding: '8px 16px',
  })}
  style={{
    // Safari fix: will-change forces GPU layer creation which fixes blend mode rendering bug.
    willChange: 'transform',
  }}
>
  Done
</button>

In the thread linked above, we decided will-change should not be used frivolously, but in specific circumstances where we need to work around such rendering quirks, it is often the most performant solution to a problem.

Here, we just use it to forcibly promote the "Done" button and its label to separate compositing layers and to match the behavior Chrome takes automatically. The impact on memory is measurable at about 150KB.

As far as I can tell, as of this PR the Command Center's content is now unmounted when closed. So as soon as the Command Center is closed, those new layers will get removed and the memory will be freed up.

So I think we can justify its use here – what do you think? cc @raineorshine

@raineorshine
Copy link
Contributor

Sure, that seems acceptable.

@yangchristina
Copy link
Collaborator Author

@fbmcipher Thank you so much! For the will-change on the button element, I can't see any difference it makes:

Screenshot 2026-01-02 at 11 56 44 AM Screenshot 2026-01-02 at 11 57 04 AM

Does the mixBlendMode: 'soft-light', even do anything? I can't see any difference with or without this mix-blend-mode.

Screenshot 2026-01-02 at 12 00 54 PM Screenshot 2026-01-02 at 12 00 45 PM

@yangchristina
Copy link
Collaborator Author

@fbmcipher Right now, if you see the video inside the PR description, the active button glow fades in after the modal slides open. This is because it doesn't blend properly with the backdrop if it starts blending with the backdrop before the backdrop has even faded in. Is this acceptable?

@yangchristina yangchristina marked this pull request as ready for review January 2, 2026 18:00
@fbmcipher
Copy link
Collaborator

fbmcipher commented Jan 2, 2026

@yangchristina

For the will-change on the button element, I can't see any difference it makes:

On Chrome, there's no difference – it works with or without will-change.

But on Safari, without will-change, the mix-blend-mode isn't applied properly due to the compositing layer rendering bug we discussed.

Screenshot 2026-01-02 at 20 24 00

Does the mixBlendMode: 'soft-light', even do anything? I can't see any difference with or without this mix-blend-mode.

Toggling it does change the appearance for me. Here's what I see:

Screen.Recording.2026-01-02.at.20.19.15.mov

Would be curious to know if you're toggling in dev tools (and if so which browser) or in code w/ live server? Maybe the changes you're making aren't taking effect for some reason.


In any case, your comment shows the correct appearance of the button in all of the screenshots!

@fbmcipher
Copy link
Collaborator

fbmcipher commented Jan 2, 2026

@fbmcipher Right now, if you see the video inside the PR description, the active button glow fades in after the modal slides open. This is because it doesn't blend properly with the backdrop if it starts blending with the backdrop before the backdrop has even faded in. Is this acceptable?

@yangchristina I see why you've implemented it this way, but I don't think this is quite what we want.

Showing a button in a disabled-looking state and then transitioning it to active after a delay could be confusing to a user. We want state indication to feel consistent. It also breaks the sense of continuity we're trying to create throughout the app with this redesign.


This is because it doesn't blend properly with the backdrop if it starts blending with the backdrop before the backdrop has even faded in.

Let's talk more about this, as I noticed some new code isGlowReady etc. designed to mitigate this.

I bypassed isGlowReady by force-setting it to true to see what the blend looked like without it. Indeed, the blend does look different than expected:

Screenshot 2026-01-02 at 21 17 29

Is something like image 1 what you're referring to having seen in before implementing isGlowReady?

If so, this issue doesn't seem to be caused by mix-blend-mode itself, but rather by the opacity on ActiveButtonGlowImage isn’t being applied correctly the first time it's rendered.

This can be verified using getComputedStyle to see what the opacity of the ActiveButtonGlowImage element is:

Screenshot 2026-01-02 at 21 25 50

Toggling the favorite button off and on again sets the opacity to 0.75 as expected, so this seems to be an issue of the opacity not being set correctly on first render.

Since opacity acts as a modulator for blend intensity, the opacity being set to 1 causes a stronger blend than intended.


Given this, I think the problem will be solved if we can figure out why opacity isn’t set correctly on first render, rather than delaying activation. What do you think?

Copy link
Collaborator

@fbmcipher fbmcipher left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This generally looks great, and I'm really happy with the new drawer! I especially like the way the background glow is tied to the gesture, it feels really smooth.

Just a couple small bits from me.

enterActive: { transition: `opacity {durations.medium} ease 0ms` },
exitActive: { transition: `opacity {durations.medium} ease 0ms` },
},
commandCenterDrawer: {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find myself missing the easing in the new drawer implementation. Without the easing, the animation feels a bit abrupt.

The easing added a subtle smoothness to the way the light and panel animate.

Is there a way for us to use the old easing curve with the new drawer?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@fbmcipher is it okay to have the same easing for ease in and ease out? Here's with MUI's ease in, for both easing in and out.

Screen.Recording.2026-01-03.at.1.02.36.AM.mov

@yangchristina
Copy link
Collaborator Author

@yangchristina

For the will-change on the button element, I can't see any difference it makes:

On Chrome, there's no difference – it works with or without will-change.

But on Safari, without will-change, the mix-blend-mode isn't applied properly due to the compositing layer rendering bug we discussed.

Screenshot 2026-01-02 at 20 24 00 > Does the mixBlendMode: 'soft-light', even do anything? I can't see any difference with or without this mix-blend-mode.

Toggling it does change the appearance for me. Here's what I see:

Screen.Recording.2026-01-02.at.20.19.15.mov
Would be curious to know if you're toggling in dev tools (and if so which browser) or in code w/ live server? Maybe the changes you're making aren't taking effect for some reason.

In any case, your comment shows the correct appearance of the button in all of the screenshots!

@fbmcipher I'm talking about the mix-blend-mode on the button element, not the div

<div>
  <div mix-blend-mode:soft-light />
  <button mix-blend-mode:lighten />
</div>

The mix-blend-mode (not will-change) on the <button mix-blend-mode:lighten /> doesn't look like it makes any difference, but maybe that's just my device.

@fbmcipher
Copy link
Collaborator

@yangchristina Got you. Yeah, I don't see any difference with the mix-blend-mode:lighten on button either – feel free to remove it.

…tate and props, and enhance fade transition with new appearance slots
@yangchristina
Copy link
Collaborator Author

yangchristina commented Jan 3, 2026

@fbmcipher Right now, if you see the video inside the PR description, the active button glow fades in after the modal slides open. This is because it doesn't blend properly with the backdrop if it starts blending with the backdrop before the backdrop has even faded in. Is this acceptable?

@yangchristina I see why you've implemented it this way, but I don't think this is quite what we want.

Showing a button in a disabled-looking state and then transitioning it to active after a delay could be confusing to a user. We want state indication to feel consistent. It also breaks the sense of continuity we're trying to create throughout the app with this redesign.

This is because it doesn't blend properly with the backdrop if it starts blending with the backdrop before the backdrop has even faded in.

Let's talk more about this, as I noticed some new code isGlowReady etc. designed to mitigate this.

I bypassed isGlowReady by force-setting it to true to see what the blend looked like without it. Indeed, the blend does look different than expected:

Screenshot 2026-01-02 at 21 17 29 Is something like **image 1** what you're referring to having seen in before implementing `isGlowReady`?

If so, this issue doesn't seem to be caused by mix-blend-mode itself, but rather by the opacity on ActiveButtonGlowImage isn’t being applied correctly the first time it's rendered.

This can be verified using getComputedStyle to see what the opacity of the ActiveButtonGlowImage element is:

Screenshot 2026-01-02 at 21 25 50 Toggling the favorite button off and on again sets the opacity to 0.75 as expected, so this seems to be an issue of the opacity not being set correctly on first render.

Since opacity acts as a modulator for blend intensity, the opacity being set to 1 causes a stronger blend than intended.

Given this, I think the problem will be solved if we can figure out why opacity isn’t set correctly on first render, rather than delaying activation. What do you think?

@fbmcipher you are totally right! Thanks for noticing this! My bad for not seeing that. 797ea6d

Screen.Recording.2026-01-03.at.12.27.40.AM.mov

@yangchristina
Copy link
Collaborator Author

not sure why tests are failing, they pass locally

@raineorshine
Copy link
Contributor

raineorshine commented Jan 4, 2026

The failing unit test appears to be occurring in main. Will investigate asap!
Fixed the failing unit tests in 26a582f.

Could you look into the failing puppeteer test?

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

Successfully merging this pull request may close these issues.

[Liminal UI] Replace SwipeableDrawer with a more flexible drawer implementation

3 participants