mui / material-ui

Material UI: Comprehensive React component library that implements Google's Material Design. Free forever.
https://mui.com/material-ui/
MIT License
93.45k stars 32.16k forks source link

ListSubheader can no longer be styled to match the background color of dark-mode Paper components #30468

Open sjdemartini opened 2 years ago

sjdemartini commented 2 years ago

Duplicates

Latest version

Current behavior šŸ˜Æ

When using dark mode, if a List is created with ListSubheaders (that are sticky, the default) and placed inside of a Paper (or Card) component, the sticky headers cannot be made to match the background color of the Paper. This leads to an awkward-looking list, with inconsistent background colors:

image

One could change the entire List to have a different background-color than the Paper (and use that for the ListSubheaers), but that is also awkward/non-ideal, as that visually separates the list from everything else.

Expected behavior šŸ¤”

It should be possible to consistently create a List with sticky subheaders where list items, list subheaders, and the rest of the paper all have the same background color.

Steps to reproduce šŸ•¹

Demo:

https://codesandbox.io/s/pinnedsubheaderlist-material-demo-forked-0q1n4?file=/demo.js:438-448

Steps:

  1. Set the theme to dark mode
  2. Create a Paper component (with default elevation)
  3. Put a List with ListSubheaders inside the Paper component
  4. Observe that the background-color of the subheaders does not match the background-color of the Paper component or the rest of the list

Context šŸ”¦

The main problem here is that in dark mode in MUI v5, elevated Paper does not use a fixed background-color, but rather uses a background-image to apply different color depending on the elevation (per https://github.com/mui-org/material-ui/pull/25522). ListSubheaders set background.paper as its background color: https://github.com/mui-org/material-ui/blob/a9903917f919092f80d84075f39fb51d51f241f2/packages/mui-material/src/ListSubheader/ListSubheader.js#L65

One would expect that you should be able to match the background color of the Paper so that all content within the List and the rest of the paper has the same background color. This is related to this other open issue, about styling custom components: https://github.com/mui-org/material-ui/issues/27980

Your environment šŸŒŽ

`npx @mui/envinfo` Using Chrome. This is on my system, but reproduced with CodeSandbox as well as linked above. ``` System: OS: macOS 11.6 Binaries: Node: 14.18.2 - ~/.nvm/versions/node/v14.18.2/bin/node Yarn: 1.22.10 - ~/repos/project/frontend/node_modules/.bin/yarn npm: 6.14.15 - ~/.nvm/versions/node/v14.18.2/bin/npm Browsers: Chrome: 96.0.4664.110 Edge: Not Found Firefox: 91.0.1 Safari: 15.0 npmPackages: @emotion/react: ^11.7.1 => 11.7.1 @emotion/styled: ^11.6.0 => 11.6.0 @mui/base: 5.0.0-alpha.62 @mui/icons-material: ^5.2.5 => 5.2.5 @mui/lab: ^5.0.0-alpha.62 => 5.0.0-alpha.62 @mui/material: ^5.2.6 => 5.2.6 @mui/private-theming: 5.2.3 @mui/styled-engine: 5.2.6 @mui/styles: ^5.2.3 => 5.2.3 @mui/system: ^5.2.6 => 5.2.6 @mui/types: 7.1.0 @mui/utils: 5.2.3 @mui/x-data-grid: ^5.2.1 => 5.2.1 @types/react: ^17.0.19 => 17.0.19 react: ^17.0.2 => 17.0.2 react-dom: ^17.0.2 => 17.0.2 typescript: ^4.4.3 => 4.4.3 ```
siriwatknp commented 2 years ago

I mark this issue as related to the Paper component rather than the List.

sjdemartini commented 2 years ago

@siriwatknp Yeah, agreed. This a problem in how Paper is implemented in dark-mode with MUI v5.

lucianthorr commented 2 years ago

I'm pretty new to MUI but I'm running into the same issue. Has anyone found a workaround or is this something that needs to be fixed? For now, I'm just avoiding wrapping tables with the Paper component.

sjdemartini commented 2 years ago

@lucianthorr My hacky workaround for now is that I've added the following constant to my project:

// Paper components in dark mode in MUI v5 use a background-image to set the background "color"
// depending on the elevation of the Paper. As such, choosing `palette.background.paper` in dark
// mode will *not* match the paper background color if using elevation (the default). This is the
// color that matches the background-image's color when the total elevation is 1 (the Paper is on
// top of only the default background, with elevation=1), which is useful if we need "sticky" header
// placement in a scrolling container.
export const DARK_MODE_PAPER_BACKGROUND_COLOR_ELEVATION_1 = "#1e1e1e";

and then I use this as the backgroundColor for whatever element I need (typically a position: sticky element), like

    // We match the Paper elevation background in dark mode by using the
    // explicit color, since background.paper will *not* match
    backgroundColor:
      theme.palette.mode === "dark"
        ? DARK_MODE_PAPER_BACKGROUND_COLOR_ELEVATION_1
        : theme.palette.background.paper,

Don't love it, but it let me unblock the v4->v5 MUI upgrade.

tomkontra commented 2 years ago

@sjdemartini @lucianthorr A slightly cleaner workaround would be to replicate what Paper does and add a semi-transparent background image to the ListSubHeader.

export const OVERLAY_ALPHA = 0.16
// Based on implementation of Paper component at: 
// https://github.com/mui/material-ui/blob/master/packages/mui-material/src/Paper/Paper.js
backgroundImage: `linear-gradient(${alpha('#fff', OVERLAY_ALPHA)}, ${alpha('#fff', OVERLAY_ALPHA)})`

You can either use a const for the alpha value or copy the getOverlayAlpha function from Paper.js:

export const getOverlayAlpha = (elevation) => {
  let alphaValue;
  if (elevation < 1) {
    alphaValue = 5.11916 * elevation ** 2;
  } else {
    alphaValue = 4.5 * Math.log(elevation + 1) + 2;
  }
  return (alphaValue / 100).toFixed(2);
};

Saves having to figure out the different colors that result from applying the overlay.

theotonge commented 7 months ago

I have added a work around using the following in our theme, this uses the same underlying logic as Paper and patches the nested Sticky headers css - I can still see room for certain edge cases and specificity causing issues - but it should work in most scenarios:

import { alpha, createTheme, getOverlayAlpha } from '@mui/material/styles'

function overridesForList() {
  const elevations = new Array(24).fill(null).map((_, i) => i + 1)
  return elevations.reduce(acc, curr) => {
    const calculatedColor = alpha('#fff', parseFloat(getOverlayAlpha(curr)))
    acc[`&.MuiPaper-elevation${curr} .MuiListSubheader-sticky`] = {
      backgroundImage: `linear-gradient(${calculatedColor}, ${calculatedColor})`,
    }
    return acc
  }, {})
}

const theme = createTheme({
  palette: {
    mode: 'dark',
  },
  components: {
    MuiPaper: {
      styleOverrides: {
        root: {
          ...overridesForList(),
        },
      },
    },
  },
})