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

[Paper] Support dark mode brightening based on elevation #21748

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions docs/pages/api-docs/paper.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,31 @@ Any other props supplied will be provided to the root element (native element).
| <span class="prop-name">elevation22</span> | <span class="prop-name">.MuiPaper-elevation22</span> |
| <span class="prop-name">elevation23</span> | <span class="prop-name">.MuiPaper-elevation23</span> |
| <span class="prop-name">elevation24</span> | <span class="prop-name">.MuiPaper-elevation24</span> |
| <span class="prop-name">overlay0</span> | <span class="prop-name">.MuiPaper-overlay0</span> |
| <span class="prop-name">overlay1</span> | <span class="prop-name">.MuiPaper-overlay1</span> |
| <span class="prop-name">overlay2</span> | <span class="prop-name">.MuiPaper-overlay2</span> |
| <span class="prop-name">overlay3</span> | <span class="prop-name">.MuiPaper-overlay3</span> |
| <span class="prop-name">overlay4</span> | <span class="prop-name">.MuiPaper-overlay4</span> |
| <span class="prop-name">overlay5</span> | <span class="prop-name">.MuiPaper-overlay5</span> |
| <span class="prop-name">overlay6</span> | <span class="prop-name">.MuiPaper-overlay6</span> |
| <span class="prop-name">overlay7</span> | <span class="prop-name">.MuiPaper-overlay7</span> |
| <span class="prop-name">overlay8</span> | <span class="prop-name">.MuiPaper-overlay8</span> |
| <span class="prop-name">overlay9</span> | <span class="prop-name">.MuiPaper-overlay9</span> |
| <span class="prop-name">overlay10</span> | <span class="prop-name">.MuiPaper-overlay10</span> |
| <span class="prop-name">overlay11</span> | <span class="prop-name">.MuiPaper-overlay11</span> |
| <span class="prop-name">overlay12</span> | <span class="prop-name">.MuiPaper-overlay12</span> |
| <span class="prop-name">overlay13</span> | <span class="prop-name">.MuiPaper-overlay13</span> |
| <span class="prop-name">overlay14</span> | <span class="prop-name">.MuiPaper-overlay14</span> |
| <span class="prop-name">overlay15</span> | <span class="prop-name">.MuiPaper-overlay15</span> |
| <span class="prop-name">overlay16</span> | <span class="prop-name">.MuiPaper-overlay16</span> |
| <span class="prop-name">overlay17</span> | <span class="prop-name">.MuiPaper-overlay17</span> |
| <span class="prop-name">overlay18</span> | <span class="prop-name">.MuiPaper-overlay18</span> |
| <span class="prop-name">overlay19</span> | <span class="prop-name">.MuiPaper-overlay19</span> |
| <span class="prop-name">overlay20</span> | <span class="prop-name">.MuiPaper-overlay20</span> |
| <span class="prop-name">overlay21</span> | <span class="prop-name">.MuiPaper-overlay21</span> |
| <span class="prop-name">overlay22</span> | <span class="prop-name">.MuiPaper-overlay22</span> |
| <span class="prop-name">overlay23</span> | <span class="prop-name">.MuiPaper-overlay23</span> |
| <span class="prop-name">overlay24</span> | <span class="prop-name">.MuiPaper-overlay24</span> |

You can override the style of the component thanks to one of these customization points:

Expand Down
3 changes: 0 additions & 3 deletions docs/src/modules/components/ThemeContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -198,9 +198,6 @@ export function ThemeProvider(props) {
main: paletteType === 'light' ? darken(pink.A400, 0.1) : pink[200],
},
type: paletteType,
background: {
default: paletteType === 'light' ? '#fff' : '#121212',
},
...paletteColors,
},
spacing,
Expand Down
2 changes: 1 addition & 1 deletion docs/src/pages/components/paper/SimplePaper.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export default function SimplePaper() {

return (
<div className={classes.root}>
<Paper elevation={0} />
<Paper variant="outlined" elevation={0} />
<Paper />
<Paper elevation={3} />
</div>
Expand Down
2 changes: 1 addition & 1 deletion docs/src/pages/components/paper/SimplePaper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export default function SimplePaper() {

return (
<div className={classes.root}>
<Paper elevation={0} />
<Paper variant="outlined" elevation={0} />
<Paper />
<Paper elevation={3} />
</div>
Expand Down
27 changes: 26 additions & 1 deletion packages/material-ui/src/Paper/Paper.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,32 @@ export type PaperClassKey =
| 'elevation21'
| 'elevation22'
| 'elevation23'
| 'elevation24';
| 'elevation24'
| 'overlay0'
| 'overlay1'
| 'overlay2'
| 'overlay3'
| 'overlay4'
| 'overlay5'
| 'overlay6'
| 'overlay7'
| 'overlay8'
| 'overlay9'
| 'overlay10'
| 'overlay11'
| 'overlay12'
| 'overlay13'
| 'overlay14'
| 'overlay15'
| 'overlay16'
| 'overlay17'
| 'overlay18'
| 'overlay19'
| 'overlay20'
| 'overlay21'
| 'overlay22'
| 'overlay23'
| 'overlay24';

/**
*
Expand Down
33 changes: 33 additions & 0 deletions packages/material-ui/src/Paper/Paper.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,19 @@ import PropTypes from 'prop-types';
import clsx from 'clsx';
import { chainPropTypes } from '@material-ui/utils';
import withStyles from '../styles/withStyles';
import { fade, useTheme } from '../styles';

// Inspired by https://github.com/material-components/material-components-ios/blob/bca36107405594d5b7b16265a5b0ed698f85a5ee/components/Elevation/src/UIColor%2BMaterialElevation.m#L61
Copy link
Member Author

Choose a reason for hiding this comment

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

It was so hard to find an algorithm for the brightening values...

Copy link
Member

Choose a reason for hiding this comment

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

I wonder if 80% of the value wouldn't come from the dark theme palette of grey colors as opposed to the Paper supporting it (which help too). It could be very interesting to integrate it into the system.

function calculateAlpha(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);
}

export const styles = (theme) => {
const elevations = {};
Expand All @@ -12,12 +25,28 @@ export const styles = (theme) => {
};
});

const overlays = {};
theme.shadows.forEach((_, index) => {
overlays[`overlay${index}`] = {
'&:before': {
content: '""',
position: 'absolute',
left: 0,
right: 0,
top: 0,
bottom: 0,
background: fade('#fff', calculateAlpha(index)),
},
};
});

return {
/* Styles applied to the root element. */
root: {
backgroundColor: theme.palette.background.paper,
color: theme.palette.text.primary,
transition: theme.transitions.create('box-shadow'),
position: 'relative',
},
/* Styles applied to the root element if `square={false}`. */
rounded: {
Expand All @@ -28,6 +57,7 @@ export const styles = (theme) => {
border: `1px solid ${theme.palette.divider}`,
},
...elevations,
...overlays,
};
};

Expand All @@ -42,13 +72,16 @@ const Paper = React.forwardRef(function Paper(props, ref) {
...other
} = props;

const theme = useTheme();

return (
<Component
className={clsx(
classes.root,
{
[classes.rounded]: !square,
[classes[`elevation${elevation}`]]: variant === 'elevation',
[classes[`overlay${elevation}`]]: theme.palette.type === 'dark',
[classes.outlined]: variant === 'outlined',
},
className,
Expand Down
59 changes: 40 additions & 19 deletions packages/material-ui/src/Paper/Paper.test.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import * as React from 'react';
import { expect } from 'chai';
import { createShallow, getClasses, createMount, describeConformance } from 'test/utils';
import { createClientRender, getClasses, createMount, describeConformance } from 'test/utils';
import * as PropTypes from 'prop-types';
import Paper from './Paper';
import { createMuiTheme, ThemeProvider } from '../styles';

describe('<Paper />', () => {
const mount = createMount();
let shallow;
let classes;
const render = createClientRender();

before(() => {
shallow = createShallow({ dive: true });
classes = getClasses(<Paper />);
});

Expand All @@ -25,42 +24,64 @@ describe('<Paper />', () => {

describe('prop: square', () => {
it('can disable the rounded class', () => {
const wrapper = mount(<Paper square>Hello World</Paper>);
expect(wrapper.find(`.${classes.root}`).some(`.${classes.rounded}`)).to.equal(false);
const { container } = render(<Paper square>Hello World</Paper>);
expect(container.firstChild).not.to.have.class(classes.rounded);
});

it('adds a rounded class to the root when omitted', () => {
const wrapper = mount(<Paper>Hello World</Paper>);
expect(wrapper.find(`.${classes.root}`).every(`.${classes.rounded}`)).to.equal(true);
const { container } = render(<Paper>Hello World</Paper>);
expect(container.firstChild).to.have.class(classes.rounded);
});
});

describe('prop: variant', () => {
it('adds a outlined class', () => {
const wrapper = mount(<Paper variant="outlined">Hello World</Paper>);
expect(wrapper.find(`.${classes.root}`).some(`.${classes.outlined}`)).to.equal(true);
const { container } = render(<Paper variant="outlined">Hello World</Paper>);
expect(container.firstChild).to.have.class(classes.outlined);
});
});

it('should set the elevation elevation class', () => {
const wrapper = shallow(<Paper elevation={16}>Hello World</Paper>);
expect(wrapper.hasClass(classes.elevation16)).to.equal(true);
wrapper.setProps({ elevation: 24 });
expect(wrapper.hasClass(classes.elevation24)).to.equal(true);
wrapper.setProps({ elevation: 2 });
expect(wrapper.hasClass(classes.elevation2)).to.equal(true);
it('should set the overlay class based on the elevation prop in dark theme', () => {
const darkTheme = createMuiTheme({ palette: { type: 'dark' } });
const { container, rerender } = render(
<ThemeProvider theme={darkTheme}>
<Paper elevation={16}>Hello World</Paper>
</ThemeProvider>,
);
expect(container.firstChild).to.have.class(classes.overlay16);
rerender(
<ThemeProvider theme={darkTheme}>
<Paper elevation={24}>Hello World</Paper>
</ThemeProvider>,
);
expect(container.firstChild).to.have.class(classes.overlay24);
rerender(
<ThemeProvider theme={darkTheme}>
<Paper elevation={2}>Hello World</Paper>
</ThemeProvider>,
);
expect(container.firstChild).to.have.class(classes.overlay2);
});

it('should set the elevation class based on the elevation prop', () => {
const { container, setProps } = render(<Paper elevation={16}>Hello World</Paper>);
expect(container.firstChild).to.have.class(classes.elevation16);
setProps({ elevation: 24 });
expect(container.firstChild).to.have.class(classes.elevation24);
setProps({ elevation: 2 });
expect(container.firstChild).to.have.class(classes.elevation2);
});

it('allows custom elevations via theme.shadows', () => {
const theme = createMuiTheme();
theme.shadows.push('20px 20px');
const wrapper = mount(
const { container } = render(
<ThemeProvider theme={theme}>
<Paper data-testid="paper" classes={{ elevation25: 'custom-elevation' }} elevation={25} />
<Paper classes={{ elevation25: 'custom-elevation' }} elevation={25} />
</ThemeProvider>,
);

expect(wrapper.find('div[data-testid="paper"]').hasClass('custom-elevation')).to.equal(true);
expect(container.firstChild).to.have.class('custom-elevation');
});

describe('warnings', () => {
Expand Down
6 changes: 3 additions & 3 deletions packages/material-ui/src/styles/createPalette.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export const light = {
// Consistency between these values is important.
background: {
paper: common.white,
default: grey[50],
default: common.white,
},
// The colors used to style the action elements.
action: {
Expand Down Expand Up @@ -61,8 +61,8 @@ export const dark = {
},
divider: 'rgba(255, 255, 255, 0.12)',
background: {
paper: grey[800],
default: '#303030',
paper: '#121212',
default: '#121212',
Comment on lines +64 to +65
Copy link
Member

Choose a reason for hiding this comment

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

I think that we should explore how to integrate this:
https://github.com/mui-org/material-ui/blob/38b259eff17f56d92083058543c29c62ce22d6e3/docs/src/modules/components/ThemeContext.js#L235-L239

Into the library. The fact that we had to extend likely suggest that there is an opportunity.

Choose a reason for hiding this comment

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

Looking into the usage of background level1 and 2 I see it is for hard-coding elevation into the documentation's appBar, drawer etc.
If the color by elevation logic will be implemented the reason for having background levels is not needed because the paper in those components will provide the proper color.
I think providing the elevation colors built in, as a default option at least, will be very comfortable in most use cases.

Copy link
Member

Choose a reason for hiding this comment

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

Looking into the usage of background level1 and 2

We aim to remove the custom theme values of the documentation. It's a signal that the default palette is wrong.

We definitely want to fix #18309

},
action: {
active: common.white,
Expand Down