From eb369576d5f84bb6680dd4c8c79b557de0b60655 Mon Sep 17 00:00:00 2001 From: Matt Brookes Date: Sat, 13 Jan 2018 12:21:51 +0000 Subject: [PATCH] [core] Revise the theme.palette.primary & secondary approach (#9794) * [core] Revise the theme.palette.primary & secondary approach https://github.com/mui-org/material-ui/issues/8075#issuecomment-355831371 Closes 8075 * Revise the theme.palette.primary & secondary approach * Update & expand createPalette tests * Update createMuiTheme tests * colorManipulator refactoring * Bump package size --- .size-limit | 4 +- docs/src/modules/components/AppDrawer.js | 2 +- docs/src/modules/components/Link.js | 4 +- .../src/modules/components/MarkdownElement.js | 2 +- docs/src/modules/styles/getPageContext.js | 4 +- docs/src/pages/customization/CssInJs.js | 2 +- docs/src/pages/customization/Palette.js | 10 +- docs/src/pages/customization/WithTheme.js | 2 +- docs/src/pages/customization/themes.md | 78 +++++- .../DetailedExpansionPanel.js | 2 +- .../demos/grid-list/SingleLineGridList.js | 2 +- docs/src/pages/demos/grid-list/grid-list.md | 2 +- .../pages/demos/menus/ListItemComposition.js | 2 +- docs/src/pages/demos/tables/EnhancedTable.js | 9 +- docs/src/pages/guides/ReactJss.js | 2 +- docs/src/pages/guides/interoperability.md | 2 +- docs/src/pages/layout/MediaQuery.js | 4 +- pages/_document.js | 2 +- pages/index.js | 4 +- src/AppBar/AppBar.js | 8 +- src/Badge/Badge.js | 8 +- .../BottomNavigationAction.js | 2 +- src/Button/Button.js | 30 +-- src/Checkbox/Checkbox.js | 2 +- src/Form/FormHelperText.js | 2 +- src/Form/FormLabel.js | 4 +- src/Icon/Icon.js | 8 +- src/IconButton/IconButton.js | 6 +- src/Input/Input.js | 4 +- src/List/ListSubheader.js | 2 +- src/MobileStepper/MobileStepper.js | 2 +- src/Progress/CircularProgress.js | 4 +- src/Progress/LinearProgress.js | 27 +- src/Radio/Radio.js | 2 +- src/Snackbar/SnackbarContent.js | 2 +- src/Stepper/StepIcon.js | 2 +- src/Stepper/StepPositionIcon.js | 4 +- src/SvgIcon/SvgIcon.js | 8 +- src/Switch/Switch.js | 4 +- src/Tabs/Tab.js | 4 +- src/Tabs/TabIndicator.js | 4 +- src/Typography/Typography.js | 6 +- src/styles/colorManipulator.d.ts | 2 +- src/styles/colorManipulator.js | 97 ++++---- src/styles/colorManipulator.spec.js | 14 +- src/styles/createMuiTheme.spec.js | 7 +- src/styles/createPalette.js | 92 +++---- src/styles/createPalette.spec.js | 230 +++++++++++++++--- test/regressions/tests/Grid/StressGrid.js | 2 +- test/regressions/tests/Tabs/AdvancedTabs.js | 4 +- test/regressions/tests/Tabs/SimpleTabs.js | 4 +- 51 files changed, 471 insertions(+), 265 deletions(-) diff --git a/.size-limit b/.size-limit index b8ee4b591182ba..765ddee1b06e04 100644 --- a/.size-limit +++ b/.size-limit @@ -2,12 +2,12 @@ { "description": "The initial cost people pay for using one component", "path": "test/size/overhead.js", - "limit": "25.6 KB" + "limit": "26 KB" }, { "description": "The size of the whole library.", "path": "build/index.js", - "limit": "95.6 KB" + "limit": "96 KB" }, { "description": "The main bundle of the documentation", diff --git a/docs/src/modules/components/AppDrawer.js b/docs/src/modules/components/AppDrawer.js index 2b96ec3d64ac28..537727045df8af 100644 --- a/docs/src/modules/components/AppDrawer.js +++ b/docs/src/modules/components/AppDrawer.js @@ -20,7 +20,7 @@ const styles = theme => ({ title: { color: theme.palette.text.secondary, '&:hover': { - color: theme.palette.primary[500], + color: theme.palette.primary.main, }, }, // https://github.com/philipwalton/flexbugs#3-min-height-on-a-flex-container-wont-apply-to-its-flex-items diff --git a/docs/src/modules/components/Link.js b/docs/src/modules/components/Link.js index dd85b9c8b9759a..70c2f866cc2f44 100644 --- a/docs/src/modules/components/Link.js +++ b/docs/src/modules/components/Link.js @@ -16,10 +16,10 @@ const styles = theme => ({ color: 'inherit', }, variantPrimary: { - color: theme.palette.primary[500], + color: theme.palette.primary.main, }, variantAccent: { - color: theme.palette.secondary.A400, + color: theme.palette.secondary.main, }, variantButton: { '&:hover': { diff --git a/docs/src/modules/components/MarkdownElement.js b/docs/src/modules/components/MarkdownElement.js index a7e159023c1bdb..3bf597e71f5452 100644 --- a/docs/src/modules/components/MarkdownElement.js +++ b/docs/src/modules/components/MarkdownElement.js @@ -198,7 +198,7 @@ const styles = theme => ({ }, '& a, & a code': { // Style taken from the Link component - color: theme.palette.secondary.A400, + color: theme.palette.secondary.main, textDecoration: 'none', '&:hover': { textDecoration: 'underline', diff --git a/docs/src/modules/styles/getPageContext.js b/docs/src/modules/styles/getPageContext.js index 8f73a45ce461ee..36a4b4b494a412 100644 --- a/docs/src/modules/styles/getPageContext.js +++ b/docs/src/modules/styles/getPageContext.js @@ -4,14 +4,12 @@ import { create, SheetsRegistry } from 'jss'; import rtl from 'jss-rtl'; import { createMuiTheme, createGenerateClassName, jssPreset } from 'material-ui/styles'; import blue from 'material-ui/colors/blue'; -import pink from 'material-ui/colors/pink'; export function getTheme(theme) { return createMuiTheme({ direction: theme.direction, palette: { - primary: blue, - secondary: pink, + primary: { light: blue[300], main: blue[500], dark: blue[700] }, type: theme.paletteType, }, }); diff --git a/docs/src/pages/customization/CssInJs.js b/docs/src/pages/customization/CssInJs.js index 1128d3fc038c80..b1f09c09d5afee 100644 --- a/docs/src/pages/customization/CssInJs.js +++ b/docs/src/pages/customization/CssInJs.js @@ -14,7 +14,7 @@ const styles = theme => ({ }, }, primary: { - color: theme.palette.primary[500], + color: theme.palette.primary.main, }, }); diff --git a/docs/src/pages/customization/Palette.js b/docs/src/pages/customization/Palette.js index 77ce273f600a31..2f6c8f3ad54b2b 100644 --- a/docs/src/pages/customization/Palette.js +++ b/docs/src/pages/customization/Palette.js @@ -1,18 +1,12 @@ import React from 'react'; import { MuiThemeProvider, createMuiTheme } from 'material-ui/styles'; import purple from 'material-ui/colors/purple'; -import green from 'material-ui/colors/green'; -import red from 'material-ui/colors/red'; import Button from 'material-ui/Button'; const theme = createMuiTheme({ palette: { - primary: purple, // Purple and green play nicely together. - secondary: { - ...green, - A400: '#00e677', - }, - error: red, + primary: { main: purple[500] }, // Purple and green play nicely together. + secondary: { main: '#11cb5f' }, // This is just green.A700 as hex. }, }); diff --git a/docs/src/pages/customization/WithTheme.js b/docs/src/pages/customization/WithTheme.js index 416e5e4565bc07..06c2ee2cd37bd2 100644 --- a/docs/src/pages/customization/WithTheme.js +++ b/docs/src/pages/customization/WithTheme.js @@ -6,7 +6,7 @@ import { withTheme } from 'material-ui/styles'; function WithTheme(props) { const { theme } = props; const primaryText = theme.palette.text.primary; - const primaryColor = theme.palette.primary[500]; + const primaryColor = theme.palette.primary.main; const styles = { primaryText: { diff --git a/docs/src/pages/customization/themes.md b/docs/src/pages/customization/themes.md index d82731e3233780..5fcb244bf226b5 100644 --- a/docs/src/pages/customization/themes.md +++ b/docs/src/pages/customization/themes.md @@ -1,24 +1,23 @@ # Themes -Themes let you apply a consistent tone to your app. -It allows you to **customize all design aspects** of your project in order to meet the specific needs of your business or brand. +The theme specifies the color of the components, darkness of the surfaces, level of shadow, appropriate opacity of ink elements, etc. -The theme specifies the darkness of the surfaces, level of shadow, appropriate opacity of ink elements, etc. -To promote greater consistency between apps, light and dark themes are available to choose from. -We use [jss](https://github.com/cssinjs/jss) under the hood. +Themes let you apply a consistent tone to your app. It allows you to **customize all design aspects** of your project in order to meet the specific needs of your business or brand. + +To promote greater consistency between apps, light and dark theme types are available to choose from. By default, components use the light theme type. ## Theme provider -If you wish to customise the theme, you need to use the `MuiThemeProvider` component in order to inject a theme into your application. +If you wish to customize the theme, you need to use the `MuiThemeProvider` component in order to inject a theme into your application. However, this is optional; Material-UI components come with a default theme. -`MuiThemeProvider` relies on the context feature of React to pass the theme down, +`MuiThemeProvider` relies on the context feature of React to pass the theme down to the components, so you need to make sure that `MuiThemeProvider` is a parent of the components you are trying to customize. You can learn more about this in [the API section](#muithemeprovider). ## Theme configuration variables -Changing the theme configuration variables is the most effective way to match Material-UI to your needs. By default, your Material-UI application will use the light theme. +Changing the theme configuration variables is the most effective way to match Material-UI to your needs. ### Palette @@ -30,18 +29,71 @@ The theme expose the following color intentions: - primary - used to represent primary interface elements for a user. - secondary - used to represent secondary interface elements for a user. -- error - used to represent interface elements that the user should be careful of. +- error - used to represent interface elements that the user should be made aware of. + +The default palette uses the shades prefixed with `A` (`A200`, etc.) for the accent color +and the un-prefixed shades for the other intentions, +but you are free to select any shade when customizing the colors, +or simply pass in any HTML color value as a string. -The palette uses the shades prefixed with `A` (`A200`, etc.) for the accent color and the hues unprefixed for the other intentions. If you want to learn more about color, you can check out [the color section](/style/color). +#### Default palette + +You may override the default palette values by including a `palette` object as part of your theme. +The following example illustrates how you could recreate the the default palette values. + + +```jsx +import { createMuiTheme } from 'material-ui/styles'; +import indigo from 'material-ui/colors/indigo'; +import pink from 'material-ui/colors/pink'; +import red from 'material-ui/colors/red'; + +const defaultTheme = createMuiTheme() + +// All the following keys are optional. +// We try our best to provide a great default value. +const theme = createMuiTheme({ + palette: { + contrastThreshold: 3.1, + tonalOffset: 0.07, + primary: { + light: indigo[300], + main: indigo[500], + dark: indigo[700], + contrastText: defaultTheme.palette.getContrastText(indigo[500]), + }, + secondary: { + light: pink.A200, + main: pink.A400, + dark: pink.A700, + contrastText: defaultTheme.palette.getContrastText(pink.A400), + }, + error: red.A400, + }, +}); +``` + +If `palette.primary` or `palette.secondary` objects are provided, +they will replace the defaults. + +If the `dark` and / or `light` keys are omitted, their value(s) will be calculated from `main`, +according to the `tonalOffset` value. + +If `contrastText` is omitted, its value will be calculated to contrast with `main`, +according to the`contrastThreshold` value. + +Both the `tonalOffset` and `contrastThreshold` values may be customized as needed. +Note that `contrastThreshold` follows a non-linear curve. + #### Example {{"demo": "pages/customization/Palette.js"}} ### Dark/light theme -You can make a theme dark by setting `type` to `dark`. +You can make the theme dark by setting `type` to `dark`. {{"demo": "pages/customization/DarkTheme.js", "hideEditButton": true}} @@ -53,7 +105,7 @@ The theme provides a **limited set of type sizes** that work well together along These sizes are used across the components. Have a look at the following example regarding changing the default values, such as the font family. -If you want to learn more about typograpy, you can check out [the typography section](/style/typography). +If you want to learn more about typography, you can check out [the typography section](/style/typography). {{"demo": "pages/customization/TypographyTheme.js"}} @@ -73,7 +125,7 @@ html { } ``` -*You need to apply the above CSS on the html element of this page to see the below demo render correctly* +*You need to apply the above CSS on the html element of this page to see the below demo rendered correctly* {{"demo": "pages/customization/FontSizeTheme.js"}} diff --git a/docs/src/pages/demos/expansion-panels/DetailedExpansionPanel.js b/docs/src/pages/demos/expansion-panels/DetailedExpansionPanel.js index 2d912794de8295..3527a431eeedd6 100644 --- a/docs/src/pages/demos/expansion-panels/DetailedExpansionPanel.js +++ b/docs/src/pages/demos/expansion-panels/DetailedExpansionPanel.js @@ -40,7 +40,7 @@ const styles = theme => ({ padding: `${theme.spacing.unit}px ${theme.spacing.unit * 2}px`, }, link: { - color: theme.palette.primary[500], + color: theme.palette.primary.main, textDecoration: 'none', '&:hover': { textDecoration: 'underline', diff --git a/docs/src/pages/demos/grid-list/SingleLineGridList.js b/docs/src/pages/demos/grid-list/SingleLineGridList.js index 01bf5ee7fc34bb..6a711b6be7b233 100644 --- a/docs/src/pages/demos/grid-list/SingleLineGridList.js +++ b/docs/src/pages/demos/grid-list/SingleLineGridList.js @@ -20,7 +20,7 @@ const styles = theme => ({ transform: 'translateZ(0)', }, title: { - color: theme.palette.primary[200], + color: theme.palette.primary.light, }, titleBar: { background: diff --git a/docs/src/pages/demos/grid-list/grid-list.md b/docs/src/pages/demos/grid-list/grid-list.md index d279b40a56490c..6006f2a0f51ccd 100644 --- a/docs/src/pages/demos/grid-list/grid-list.md +++ b/docs/src/pages/demos/grid-list/grid-list.md @@ -27,7 +27,7 @@ The overlay can accommodate a `title`, `subtitle` and secondary action - in this ## Advanced Grid list This example demonstrates "featured" tiles, using the `rows` and `cols` props to adjust the size of the tile, and the `padding` prop to adjust the spacing. -The tiles have a customised titlebar, positioned at the top and with a custom gradient `titleBackground`. +The tiles have a customized titlebar, positioned at the top and with a custom gradient `titleBackground`. The secondary action `IconButton` is positioned on the left. {{"demo": "pages/demos/grid-list/AdvancedGridList.js", "hideEditButton": true}} diff --git a/docs/src/pages/demos/menus/ListItemComposition.js b/docs/src/pages/demos/menus/ListItemComposition.js index 3358991b318c0a..54a60003a4da7d 100644 --- a/docs/src/pages/demos/menus/ListItemComposition.js +++ b/docs/src/pages/demos/menus/ListItemComposition.js @@ -11,7 +11,7 @@ import SendIcon from 'material-ui-icons/Send'; const styles = theme => ({ menuItem: { '&:focus': { - backgroundColor: theme.palette.primary[500], + backgroundColor: theme.palette.primary.main, '& $text, & $icon': { color: theme.palette.common.white, }, diff --git a/docs/src/pages/demos/tables/EnhancedTable.js b/docs/src/pages/demos/tables/EnhancedTable.js index 540d216a4e9c18..e04213f1a92b96 100644 --- a/docs/src/pages/demos/tables/EnhancedTable.js +++ b/docs/src/pages/demos/tables/EnhancedTable.js @@ -19,6 +19,7 @@ import IconButton from 'material-ui/IconButton'; import Tooltip from 'material-ui/Tooltip'; import DeleteIcon from 'material-ui-icons/Delete'; import FilterListIcon from 'material-ui-icons/FilterList'; +import { lighten } from 'material-ui/styles/colorManipulator'; let counter = 0; function createData(name, calories, fat, carbs, protein) { @@ -98,12 +99,12 @@ const toolbarStyles = theme => ({ highlight: theme.palette.type === 'light' ? { - color: theme.palette.secondary.A700, - backgroundColor: theme.palette.secondary.A100, + color: theme.palette.secondary.dark, + backgroundColor: lighten(theme.palette.secondary.light, 0.4), } : { - color: theme.palette.secondary.A100, - backgroundColor: theme.palette.secondary.A700, + color: lighten(theme.palette.secondary.light, 0.4), + backgroundColor: theme.palette.secondary.dark, }, spacer: { flex: '1 1 100%', diff --git a/docs/src/pages/guides/ReactJss.js b/docs/src/pages/guides/ReactJss.js index dae0efb7b1535e..5f362bc059dc16 100644 --- a/docs/src/pages/guides/ReactJss.js +++ b/docs/src/pages/guides/ReactJss.js @@ -7,7 +7,7 @@ import Typography from 'material-ui/Typography'; // 1. We define the styles. const styles = theme => ({ root: { - color: props => (props.variant === 'primary' ? theme.palette.primary[500] : 'inherit'), + color: props => (props.variant === 'primary' ? theme.palette.primary.main : 'inherit'), textDecoration: 'inherit', '&:hover': { textDecoration: 'underline', diff --git a/docs/src/pages/guides/interoperability.md b/docs/src/pages/guides/interoperability.md index 58880d7e1cb262..f0d3a95ca485c9 100644 --- a/docs/src/pages/guides/interoperability.md +++ b/docs/src/pages/guides/interoperability.md @@ -105,7 +105,7 @@ In the following demo we demonstrate how to use `injectSheet()` and "the styles const styles = theme => ({ root: { color: props => (props.variant === 'primary' - ? theme.palette.primary[500] + ? theme.palette.primary.main : 'inherit'), textDecoration: 'inherit', }, diff --git a/docs/src/pages/layout/MediaQuery.js b/docs/src/pages/layout/MediaQuery.js index 673b58242b87dc..c5e7312e58ba4a 100644 --- a/docs/src/pages/layout/MediaQuery.js +++ b/docs/src/pages/layout/MediaQuery.js @@ -9,10 +9,10 @@ const styles = theme => ({ root: { padding: theme.spacing.unit, [theme.breakpoints.up('md')]: { - backgroundColor: theme.palette.primary[500], + backgroundColor: theme.palette.primary.main, }, [theme.breakpoints.down('sm')]: { - backgroundColor: theme.palette.secondary.A400, + backgroundColor: theme.palette.secondary.main, }, }, }); diff --git a/pages/_document.js b/pages/_document.js index fe5e4e2edd8783..167e056803c5e2 100644 --- a/pages/_document.js +++ b/pages/_document.js @@ -33,7 +33,7 @@ class MyDocument extends Document { */} {/* PWA primary color */} - + ({ justifyContent: 'center', alignItems: 'center', backgroundColor: - theme.palette.type === 'light' ? theme.palette.primary[500] : theme.palette.primary[800], - color: theme.palette.getContrastText(theme.palette.primary[500]), + theme.palette.type === 'light' ? theme.palette.primary.main : theme.palette.primary.dark, + color: theme.palette.getContrastText(theme.palette.primary.main), }, content: { paddingTop: theme.spacing.unit * 8, diff --git a/src/AppBar/AppBar.js b/src/AppBar/AppBar.js index 2bfb816c33804b..1fde4ef2b5b0a0 100644 --- a/src/AppBar/AppBar.js +++ b/src/AppBar/AppBar.js @@ -37,12 +37,12 @@ export const styles = theme => ({ color: theme.palette.getContrastText(theme.palette.background.appBar), }, colorPrimary: { - backgroundColor: theme.palette.primary[500], - color: theme.palette.getContrastText(theme.palette.primary[500]), + backgroundColor: theme.palette.primary.main, + color: theme.palette.primary.contrastText, }, colorAccent: { - backgroundColor: theme.palette.secondary.A200, - color: theme.palette.getContrastText(theme.palette.secondary.A200), + backgroundColor: theme.palette.secondary.light, + color: theme.palette.getContrastText(theme.palette.secondary.light), }, }); diff --git a/src/Badge/Badge.js b/src/Badge/Badge.js index cd8c0b5d44d5c3..67cad3c09cb386 100644 --- a/src/Badge/Badge.js +++ b/src/Badge/Badge.js @@ -34,12 +34,12 @@ export const styles = theme => ({ zIndex: 1, // Render the badge on top of potential ripples. }, colorPrimary: { - backgroundColor: theme.palette.primary[500], - color: theme.palette.getContrastText(theme.palette.primary[500]), + backgroundColor: theme.palette.primary.main, + color: theme.palette.primary.contrastText, }, colorAccent: { - backgroundColor: theme.palette.secondary.A200, - color: theme.palette.getContrastText(theme.palette.secondary.A200), + backgroundColor: theme.palette.secondary.light, + color: theme.palette.getContrastText(theme.palette.secondary.light), }, }); diff --git a/src/BottomNavigation/BottomNavigationAction.js b/src/BottomNavigation/BottomNavigationAction.js index d54e72c0e15de6..38602e2504d843 100644 --- a/src/BottomNavigation/BottomNavigationAction.js +++ b/src/BottomNavigation/BottomNavigationAction.js @@ -23,7 +23,7 @@ export const styles = theme => ({ }, selected: { paddingTop: 6, - color: theme.palette.primary[500], + color: theme.palette.primary.main, }, selectedIconOnly: { paddingTop: theme.spacing.unit * 2, diff --git a/src/Button/Button.js b/src/Button/Button.js index 682d9acd8b44b8..9c2b2f487462d7 100644 --- a/src/Button/Button.js +++ b/src/Button/Button.js @@ -45,9 +45,9 @@ export const styles = theme => ({ justifyContent: 'inherit', }, flatPrimary: { - color: theme.palette.primary[500], + color: theme.palette.primary.main, '&:hover': { - backgroundColor: fade(theme.palette.primary[500], 0.12), + backgroundColor: fade(theme.palette.primary.main, 0.12), // Reset on mouse devices '@media (hover: none)': { backgroundColor: 'transparent', @@ -55,9 +55,9 @@ export const styles = theme => ({ }, }, flatAccent: { - color: theme.palette.secondary.A200, + color: theme.palette.secondary.light, '&:hover': { - backgroundColor: fade(theme.palette.secondary.A200, 0.12), + backgroundColor: fade(theme.palette.secondary.light, 0.12), // Reset on mouse devices '@media (hover: none)': { backgroundColor: 'transparent', @@ -65,9 +65,9 @@ export const styles = theme => ({ }, }, flatContrast: { - color: theme.palette.getContrastText(theme.palette.primary[500]), + color: theme.palette.primary.contrastText, '&:hover': { - backgroundColor: fade(theme.palette.getContrastText(theme.palette.primary[500]), 0.12), + backgroundColor: fade(theme.palette.primary.contrastText, 0.12), // Reset on mouse devices '@media (hover: none)': { backgroundColor: 'transparent', @@ -108,29 +108,29 @@ export const styles = theme => ({ }, keyboardFocused: {}, raisedPrimary: { - color: theme.palette.getContrastText(theme.palette.primary[500]), - backgroundColor: theme.palette.primary[500], + color: theme.palette.primary.contrastText, + backgroundColor: theme.palette.primary.main, '&:hover': { - backgroundColor: theme.palette.primary[700], + backgroundColor: theme.palette.primary.dark, // Reset on mouse devices '@media (hover: none)': { - backgroundColor: theme.palette.primary[500], + backgroundColor: theme.palette.primary.main, }, }, }, raisedAccent: { - color: theme.palette.getContrastText(theme.palette.secondary.A200), - backgroundColor: theme.palette.secondary.A200, + color: theme.palette.getContrastText(theme.palette.secondary.light), + backgroundColor: theme.palette.secondary.light, '&:hover': { - backgroundColor: theme.palette.secondary.A400, + backgroundColor: theme.palette.secondary.main, // Reset on mouse devices '@media (hover: none)': { - backgroundColor: theme.palette.secondary.A200, + backgroundColor: theme.palette.secondary.light, }, }, }, raisedContrast: { - color: theme.palette.getContrastText(theme.palette.primary[500]), + color: theme.palette.primary.contrastText, }, disabled: { color: theme.palette.action.disabled, diff --git a/src/Checkbox/Checkbox.js b/src/Checkbox/Checkbox.js index 633554f1ab018e..966fb64620dc2a 100644 --- a/src/Checkbox/Checkbox.js +++ b/src/Checkbox/Checkbox.js @@ -9,7 +9,7 @@ export const styles = theme => ({ color: theme.palette.text.secondary, }, checked: { - color: theme.palette.primary[500], + color: theme.palette.primary.main, }, disabled: { color: theme.palette.action.disabled, diff --git a/src/Form/FormHelperText.js b/src/Form/FormHelperText.js index ddacb87065a853..c945873a1c9507 100644 --- a/src/Form/FormHelperText.js +++ b/src/Form/FormHelperText.js @@ -18,7 +18,7 @@ export const styles = theme => ({ marginTop: theme.spacing.unit / 2, }, error: { - color: theme.palette.error.A400, + color: theme.palette.error.main, }, disabled: { color: theme.palette.input.disabled, diff --git a/src/Form/FormLabel.js b/src/Form/FormLabel.js index 7302d114b1d800..af206cd8e458c5 100644 --- a/src/Form/FormLabel.js +++ b/src/Form/FormLabel.js @@ -4,7 +4,7 @@ import classNames from 'classnames'; import withStyles from '../styles/withStyles'; export const styles = theme => { - const focusColor = theme.palette.primary[theme.palette.type === 'light' ? 'A700' : 'A200']; + const focusColor = theme.palette.primary[theme.palette.type === 'light' ? 'dark' : 'light']; return { root: { fontFamily: theme.typography.fontFamily, @@ -17,7 +17,7 @@ export const styles = theme => { color: focusColor, }, error: { - color: theme.palette.error.A400, + color: theme.palette.error.main, }, disabled: { color: theme.palette.input.disabled, diff --git a/src/Icon/Icon.js b/src/Icon/Icon.js index d25d6d7f63d535..d67ec3bd8b3937 100644 --- a/src/Icon/Icon.js +++ b/src/Icon/Icon.js @@ -9,22 +9,22 @@ export const styles = theme => ({ userSelect: 'none', }, colorAccent: { - color: theme.palette.secondary.A200, + color: theme.palette.secondary.light, }, colorAction: { color: theme.palette.action.active, }, colorContrast: { - color: theme.palette.getContrastText(theme.palette.primary[500]), + color: theme.palette.primary.contrastText, }, colorDisabled: { color: theme.palette.action.disabled, }, colorError: { - color: theme.palette.error[500], + color: theme.palette.error.main, }, colorPrimary: { - color: theme.palette.primary[500], + color: theme.palette.primary.main, }, }); diff --git a/src/IconButton/IconButton.js b/src/IconButton/IconButton.js index 253ad1c92d53bf..384fa3362bc635 100644 --- a/src/IconButton/IconButton.js +++ b/src/IconButton/IconButton.js @@ -25,13 +25,13 @@ export const styles = theme => ({ }), }, colorAccent: { - color: theme.palette.secondary.A200, + color: theme.palette.secondary.light, }, colorContrast: { - color: theme.palette.getContrastText(theme.palette.primary[500]), + color: theme.palette.primary.contrastText, }, colorPrimary: { - color: theme.palette.primary[500], + color: theme.palette.primary.main, }, colorInherit: { color: 'inherit', diff --git a/src/Input/Input.js b/src/Input/Input.js index 71e71ae36184c8..1470abca90af69 100644 --- a/src/Input/Input.js +++ b/src/Input/Input.js @@ -74,7 +74,7 @@ export const styles = theme => { }, inkbar: { '&:after': { - backgroundColor: theme.palette.primary[theme.palette.type === 'light' ? 'A700' : 'A200'], + backgroundColor: theme.palette.primary[theme.palette.type === 'light' ? 'dark' : 'light'], left: 0, bottom: 0, // Doing the other way around crash on IE11 "''" https://github.com/cssinjs/jss/issues/242 @@ -95,7 +95,7 @@ export const styles = theme => { }, error: { '&:after': { - backgroundColor: theme.palette.error.A400, + backgroundColor: theme.palette.error.main, transform: 'scaleX(1)', // error is always underlined in red }, }, diff --git a/src/List/ListSubheader.js b/src/List/ListSubheader.js index c2b956bf42480f..2a57e8888053c5 100644 --- a/src/List/ListSubheader.js +++ b/src/List/ListSubheader.js @@ -17,7 +17,7 @@ export const styles = theme => ({ fontSize: theme.typography.pxToRem(theme.typography.fontSize), }, colorPrimary: { - color: theme.palette.primary[500], + color: theme.palette.primary.main, }, colorInherit: { color: 'inherit', diff --git a/src/MobileStepper/MobileStepper.js b/src/MobileStepper/MobileStepper.js index 8389e7fe6d7b92..9c7befe9e889ba 100644 --- a/src/MobileStepper/MobileStepper.js +++ b/src/MobileStepper/MobileStepper.js @@ -44,7 +44,7 @@ export const styles = theme => ({ margin: '0 2px', }, dotActive: { - backgroundColor: theme.palette.primary[500], + backgroundColor: theme.palette.primary.main, }, progress: { width: '50%', diff --git a/src/Progress/CircularProgress.js b/src/Progress/CircularProgress.js index d1e0b0a48acad4..09b540028dbeca 100644 --- a/src/Progress/CircularProgress.js +++ b/src/Progress/CircularProgress.js @@ -15,10 +15,10 @@ export const styles = theme => ({ display: 'inline-block', }, primaryColor: { - color: theme.palette.primary[500], + color: theme.palette.primary.main, }, accentColor: { - color: theme.palette.secondary.A200, + color: theme.palette.secondary.light, }, svgIndeterminate: { animation: 'mui-progress-circular-rotate 1.4s linear infinite', diff --git a/src/Progress/LinearProgress.js b/src/Progress/LinearProgress.js index ef45082f378fbb..10a195664f3353 100644 --- a/src/Progress/LinearProgress.js +++ b/src/Progress/LinearProgress.js @@ -3,6 +3,7 @@ import PropTypes from 'prop-types'; import classNames from 'classnames'; import warning from 'warning'; import withStyles from '../styles/withStyles'; +import { lighten } from '../styles/colorManipulator'; const TRANSITION_DURATION = 4; // 400ms @@ -13,28 +14,30 @@ export const styles = theme => ({ height: 5, }, primaryColor: { - backgroundColor: theme.palette.primary[100], + backgroundColor: lighten(theme.palette.primary.light, 0.6), }, primaryColorBar: { - backgroundColor: theme.palette.primary[500], + backgroundColor: theme.palette.primary.main, }, primaryDashed: { - background: `radial-gradient(${theme.palette.primary[100]} 0%, ${ - theme.palette.primary[100] - } 16%, transparent 42%)`, + background: `radial-gradient(${lighten(theme.palette.primary.light, 0.6)} 0%, ${lighten( + theme.palette.primary.light, + 0.6, + )} 16%, transparent 42%)`, backgroundSize: '10px 10px', backgroundPosition: '0px -23px', }, accentColor: { - backgroundColor: theme.palette.secondary.A100, + backgroundColor: lighten(theme.palette.secondary.light, 0.4), }, accentColorBar: { - backgroundColor: theme.palette.secondary.A400, + backgroundColor: theme.palette.secondary.main, }, accentDashed: { - background: `radial-gradient(${theme.palette.secondary.A100} 0%, ${ - theme.palette.secondary.A100 - } 16%, transparent 42%)`, + background: `radial-gradient(${lighten(theme.palette.secondary.light, 0.4)} 0%, ${lighten( + theme.palette.secondary.light, + 0.6, + )} 16%, transparent 42%)`, backgroundSize: '10px 10px', backgroundPosition: '0px -23px', }, @@ -84,11 +87,11 @@ export const styles = theme => ({ }, bufferBar2Primary: { transition: `transform .${TRANSITION_DURATION}s linear`, - backgroundColor: theme.palette.primary[100], + backgroundColor: lighten(theme.palette.primary.light, 0.6), }, bufferBar2Accent: { transition: `transform .${TRANSITION_DURATION}s linear`, - backgroundColor: theme.palette.secondary.A100, + backgroundColor: lighten(theme.palette.secondary.light, 0.4), }, // Legends: // || represents the viewport diff --git a/src/Radio/Radio.js b/src/Radio/Radio.js index e5bb7f7430b21b..58b6a6950bae4b 100644 --- a/src/Radio/Radio.js +++ b/src/Radio/Radio.js @@ -10,7 +10,7 @@ export const styles = theme => ({ color: theme.palette.text.secondary, }, checked: { - color: theme.palette.primary[500], + color: theme.palette.primary.main, }, disabled: { color: theme.palette.action.disabled, diff --git a/src/Snackbar/SnackbarContent.js b/src/Snackbar/SnackbarContent.js index 0a7c880557e7c2..a59c37d316c37f 100644 --- a/src/Snackbar/SnackbarContent.js +++ b/src/Snackbar/SnackbarContent.js @@ -9,7 +9,7 @@ import Typography from '../Typography'; export const styles = theme => { const reverseType = theme.palette.type === 'light' ? 'dark' : 'light'; - const backgroundColor = theme.palette.shades[reverseType].background.default; + const backgroundColor = theme.palette.types[reverseType].background.default; return { root: { diff --git a/src/Stepper/StepIcon.js b/src/Stepper/StepIcon.js index 025d6041b76922..852153531db089 100644 --- a/src/Stepper/StepIcon.js +++ b/src/Stepper/StepIcon.js @@ -10,7 +10,7 @@ export const styles = theme => ({ display: 'block', }, completed: { - fill: theme.palette.primary[500], + fill: theme.palette.primary.main, }, }); diff --git a/src/Stepper/StepPositionIcon.js b/src/Stepper/StepPositionIcon.js index dddc8d6e7288cb..e09d44e445f6c6 100644 --- a/src/Stepper/StepPositionIcon.js +++ b/src/Stepper/StepPositionIcon.js @@ -9,10 +9,10 @@ export const styles = theme => ({ fill: theme.palette.action.disabled, }, active: { - fill: theme.palette.primary[500], + fill: theme.palette.primary.main, }, text: { - fill: theme.palette.getContrastText(theme.palette.primary[500]), + fill: theme.palette.primary.contrastText, fontSize: theme.typography.caption.fontSize, fontFamily: theme.typography.fontFamily, }, diff --git a/src/SvgIcon/SvgIcon.js b/src/SvgIcon/SvgIcon.js index 302d1604a5e825..6b8d477f74e211 100644 --- a/src/SvgIcon/SvgIcon.js +++ b/src/SvgIcon/SvgIcon.js @@ -17,22 +17,22 @@ export const styles = theme => ({ }), }, colorAccent: { - color: theme.palette.secondary.A200, + color: theme.palette.secondary.light, }, colorAction: { color: theme.palette.action.active, }, colorContrast: { - color: theme.palette.getContrastText(theme.palette.primary[500]), + color: theme.palette.primary.contrastText, }, colorDisabled: { color: theme.palette.action.disabled, }, colorError: { - color: theme.palette.error[500], + color: theme.palette.error.main, }, colorPrimary: { - color: theme.palette.primary[500], + color: theme.palette.primary.main, }, }); diff --git a/src/Switch/Switch.js b/src/Switch/Switch.js index b703382007049b..cf6905b335457d 100644 --- a/src/Switch/Switch.js +++ b/src/Switch/Switch.js @@ -45,10 +45,10 @@ export const styles = theme => ({ }), }, checked: { - color: theme.palette.primary[500], + color: theme.palette.primary.main, transform: 'translateX(14px)', '& + $bar': { - backgroundColor: theme.palette.primary[500], + backgroundColor: theme.palette.primary.main, opacity: 0.5, }, }, diff --git a/src/Tabs/Tab.js b/src/Tabs/Tab.js index 3c7ac93427a03d..e2b68fa0acf5eb 100644 --- a/src/Tabs/Tab.js +++ b/src/Tabs/Tab.js @@ -29,7 +29,7 @@ export const styles = theme => ({ color: theme.palette.text.secondary, }, rootAccentSelected: { - color: theme.palette.secondary.A200, + color: theme.palette.secondary.light, }, rootAccentDisabled: { color: theme.palette.text.disabled, @@ -38,7 +38,7 @@ export const styles = theme => ({ color: theme.palette.text.secondary, }, rootPrimarySelected: { - color: theme.palette.primary[500], + color: theme.palette.primary.main, }, rootPrimaryDisabled: { color: theme.palette.text.disabled, diff --git a/src/Tabs/TabIndicator.js b/src/Tabs/TabIndicator.js index d7e9d3a6b6f75c..b64d37adcfc001 100644 --- a/src/Tabs/TabIndicator.js +++ b/src/Tabs/TabIndicator.js @@ -14,10 +14,10 @@ export const styles = theme => ({ willChange: 'left, width', }, colorAccent: { - backgroundColor: theme.palette.secondary.A200, + backgroundColor: theme.palette.secondary.light, }, colorPrimary: { - backgroundColor: theme.palette.primary[500], + backgroundColor: theme.palette.primary.main, }, }); diff --git a/src/Typography/Typography.js b/src/Typography/Typography.js index aa3302365d5503..273174ec664f55 100644 --- a/src/Typography/Typography.js +++ b/src/Typography/Typography.js @@ -47,16 +47,16 @@ export const styles = theme => ({ color: 'inherit', }, colorPrimary: { - color: theme.palette.primary[500], + color: theme.palette.primary.main, }, colorSecondary: { color: theme.palette.text.secondary, }, colorAccent: { - color: theme.palette.secondary.A400, + color: theme.palette.secondary.main, }, colorError: { - color: theme.palette.error.A400, + color: theme.palette.error.main, }, }); diff --git a/src/styles/colorManipulator.d.ts b/src/styles/colorManipulator.d.ts index 089dc40d3cb696..c41cebaf1914be 100644 --- a/src/styles/colorManipulator.d.ts +++ b/src/styles/colorManipulator.d.ts @@ -4,7 +4,7 @@ export interface ColorObject { values: [number, number, number] | [number, number, number, number]; } -export function convertColorToString(color: ColorObject): string; +export function recomposeColor(color: ColorObject): string; export function convertHexToRGB(hex: string): string; export function decomposeColor(color: string): ColorObject; export function getContrastRatio(foreground: string, background: string): number; diff --git a/src/styles/colorManipulator.js b/src/styles/colorManipulator.js index 9ec3ea88e09ed7..5b79f5f371a8f5 100644 --- a/src/styles/colorManipulator.js +++ b/src/styles/colorManipulator.js @@ -11,7 +11,7 @@ import warning from 'warning'; * @param {number} max The upper boundary of the output range * @returns {number} A number in the range [min, max] */ -function clamp(value, min, max) { +function clamp(value, min = 0, max = 1) { warning( value >= min && value <= max, `Material-UI: the value provided ${value} is out of range [${min}, ${max}].`, @@ -26,41 +26,6 @@ function clamp(value, min, max) { return value; } -/** - * Converts a color object with type and values to a string. - * - * @param {object} color - Decomposed color - * @param {string} color.type - One of, 'rgb', 'rgba', 'hsl', 'hsla' - * @param {array} color.values - [n,n,n] or [n,n,n,n] - * @returns {string} A CSS color string - */ -export function convertColorToString(color: Object) { - const { type, values } = color; - - if (type.indexOf('rgb') > -1) { - // Only convert the first 3 values to int (i.e. not alpha) - for (let i = 0; i < 3; i += 1) { - values[i] = parseInt(values[i], 10); - } - } - - let colorString; - - if (type.indexOf('hsl') > -1) { - colorString = `${color.type}(${values[0]}, ${values[1]}%, ${values[2]}%`; - } else { - colorString = `${color.type}(${values[0]}, ${values[1]}, ${values[2]}`; - } - - if (values.length === 4) { - colorString += `, ${color.values[3]})`; - } else { - colorString += ')'; - } - - return colorString; -} - /** * Converts a color from CSS hex format to CSS rgb format. * @@ -68,21 +33,16 @@ export function convertColorToString(color: Object) { * @returns {string} A CSS rgb color string */ export function convertHexToRGB(color: string) { - if (color.length === 4) { - let extendedColor = '#'; - for (let i = 1; i < color.length; i += 1) { - extendedColor += color.charAt(i) + color.charAt(i); - } - color = extendedColor; - } + color = color.substr(1); + + const re = new RegExp(`.{1,${color.length / 3}}`, 'g'); + let colors = color.match(re); - const values = { - r: parseInt(color.substr(1, 2), 16), - g: parseInt(color.substr(3, 2), 16), - b: parseInt(color.substr(5, 2), 16), - }; + if (colors && colors[0].length === 1) { + colors = colors.map(n => n + n); + } - return `rgb(${values.r}, ${values.g}, ${values.b})`; + return colors ? `rgb(${colors.map(n => parseInt(n, 16)).join(', ')})` : ''; } /** @@ -91,7 +51,7 @@ export function convertHexToRGB(color: string) { * Note: Does not support rgb % values. * * @param {string} color - CSS color, i.e. one of: #nnn, #nnnnnn, rgb(), rgba(), hsl(), hsla() - * @returns {{type: string, values: number[]}} A MUI color object + * @returns {object} - A MUI color object: {type: string, values: number[]} */ export function decomposeColor(color: string) { if (color.charAt(0) === '#') { @@ -106,6 +66,31 @@ export function decomposeColor(color: string) { return { type, values }; } +/** + * Converts a color object with type and values to a string. + * + * @param {object} color - Decomposed color + * @param {string} color.type - One of: 'rgb', 'rgba', 'hsl', 'hsla' + * @param {array} color.values - [n,n,n] or [n,n,n,n] + * @returns {string} A CSS color string + */ +export function recomposeColor(color: Object) { + const { type } = color; + let { values } = color; + + if (type.indexOf('rgb') > -1) { + // Only convert the first 3 values to int (i.e. not alpha) + values = values.map((n, i) => (i < 3 ? parseInt(n, 10) : n)); + } + + if (type.indexOf('hsl') > -1) { + values[1] = `${values[1]}%`; + values[2] = `${values[2]}%`; + } + + return `${color.type}(${values.join(', ')})`; +} + /** * Calculates the contrast ratio between two colors. * @@ -169,14 +154,14 @@ export function emphasize(color: string, coefficient: number = 0.15) { */ export function fade(color: string, value: number) { color = decomposeColor(color); - value = clamp(value, 0, 1); + value = clamp(value); if (color.type === 'rgb' || color.type === 'hsl') { color.type += 'a'; } color.values[3] = value; - return convertColorToString(color); + return recomposeColor(color); } /** @@ -188,7 +173,7 @@ export function fade(color: string, value: number) { */ export function darken(color: string, coefficient: number) { color = decomposeColor(color); - coefficient = clamp(coefficient, 0, 1); + coefficient = clamp(coefficient); if (color.type.indexOf('hsl') > -1) { color.values[2] *= 1 - coefficient; @@ -197,7 +182,7 @@ export function darken(color: string, coefficient: number) { color.values[i] *= 1 - coefficient; } } - return convertColorToString(color); + return recomposeColor(color); } /** @@ -209,7 +194,7 @@ export function darken(color: string, coefficient: number) { */ export function lighten(color: string, coefficient: number) { color = decomposeColor(color); - coefficient = clamp(coefficient, 0, 1); + coefficient = clamp(coefficient); if (color.type.indexOf('hsl') > -1) { color.values[2] += (100 - color.values[2]) * coefficient; @@ -219,5 +204,5 @@ export function lighten(color: string, coefficient: number) { } } - return convertColorToString(color); + return recomposeColor(color); } diff --git a/src/styles/colorManipulator.spec.js b/src/styles/colorManipulator.spec.js index 8dc4fd0d4fd702..94bda903e552d3 100644 --- a/src/styles/colorManipulator.spec.js +++ b/src/styles/colorManipulator.spec.js @@ -1,7 +1,7 @@ import { assert } from 'chai'; import consoleErrorMock from '../../test/utils/consoleErrorMock'; import { - convertColorToString, + recomposeColor, convertHexToRGB, darken, decomposeColor, @@ -21,10 +21,10 @@ describe('utils/colorManipulator', () => { consoleErrorMock.reset(); }); - describe('convertColorToString', () => { + describe('recomposeColor', () => { it('converts a decomposed rgb color object to a string` ', () => { assert.strictEqual( - convertColorToString({ + recomposeColor({ type: 'rgb', values: [255, 255, 255], }), @@ -34,7 +34,7 @@ describe('utils/colorManipulator', () => { it('converts a decomposed rgba color object to a string` ', () => { assert.strictEqual( - convertColorToString({ + recomposeColor({ type: 'rgba', values: [255, 255, 255, 0.5], }), @@ -44,7 +44,7 @@ describe('utils/colorManipulator', () => { it('converts a decomposed hsl color object to a string` ', () => { assert.strictEqual( - convertColorToString({ + recomposeColor({ type: 'hsl', values: [100, 50, 25], }), @@ -54,7 +54,7 @@ describe('utils/colorManipulator', () => { it('converts a decomposed hsla color object to a string` ', () => { assert.strictEqual( - convertColorToString({ + recomposeColor({ type: 'hsla', values: [100, 50, 25, 0.5], }), @@ -158,7 +158,7 @@ describe('utils/colorManipulator', () => { assert.strictEqual(emphasize('rgb(250, 240, 230)', 0.3), darken('rgb(250, 240, 230)', 0.3)); }); - it('lightens a dark rgb color with the c0efficient 0.15 by default', () => { + it('lightens a dark rgb color with the coefficient 0.15 by default', () => { assert.strictEqual(emphasize('rgb(1, 2, 3)'), lighten('rgb(1, 2, 3)', 0.15)); }); diff --git a/src/styles/createMuiTheme.spec.js b/src/styles/createMuiTheme.spec.js index f459d60855d0f3..20ededa85dfab5 100644 --- a/src/styles/createMuiTheme.spec.js +++ b/src/styles/createMuiTheme.spec.js @@ -12,8 +12,11 @@ describe('createMuiTheme', () => { }); it('should have the custom palette', () => { - const muiTheme = createMuiTheme({ palette: { primary: deepOrange, secondary: green } }); - assert.strictEqual(muiTheme.palette.primary, deepOrange, 'should have a palette'); + const muiTheme = createMuiTheme({ + palette: { primary: { main: deepOrange[500] }, secondary: { main: green.A400 } }, + }); + assert.strictEqual(muiTheme.palette.primary.main, deepOrange[500], 'should have a palette'); + assert.strictEqual(muiTheme.palette.secondary.main, green.A400, 'should have a palette'); }); it('should allow providing a partial structure', () => { diff --git a/src/styles/createPalette.js b/src/styles/createPalette.js index b0fd40e720e073..a66ba866e06ee7 100644 --- a/src/styles/createPalette.js +++ b/src/styles/createPalette.js @@ -7,7 +7,7 @@ import pink from '../colors/pink'; import grey from '../colors/grey'; import red from '../colors/red'; import common from '../colors/common'; -import { getContrastRatio } from './colorManipulator'; +import { getContrastRatio, darken, lighten } from './colorManipulator'; export const light = { text: { @@ -79,21 +79,47 @@ export const dark = { export default function createPalette(palette: Object) { const { - primary = indigo, - secondary = pink, - error = red, + primary = { + light: indigo[300], + main: indigo[500], + dark: indigo[700], + contrastText: '', + }, + secondary = { + light: pink.A200, + main: pink.A400, + dark: pink.A700, + contrastText: '', + }, + error = { + main: red[500], + }, type = 'light', // Same value used by material-components-web // https://github.com/material-components/material-components-web/blob/ac46b8863c4dab9fc22c4c662dc6bd1b65dd652f/packages/mdc-theme/_functions.scss#L49 - contrastThreshold = 3.1, + contrastThreshold = 3, + tonalOffset = 0.2, ...other } = palette; - const shades = { dark, light }; - warning(Boolean(shades[type]), `Material-UI: the palette type \`${type}\` is not supported.`); + if (!primary.light) { + primary.light = lighten(primary.main, tonalOffset); + } + + if (!primary.dark) { + primary.dark = darken(primary.main, tonalOffset * 1.5); + } + + if (!secondary.light) { + secondary.light = lighten(secondary.main, tonalOffset); + } + + if (!secondary.dark) { + secondary.dark = darken(secondary.main, tonalOffset * 1.5); + } function getContrastText(background) { - // Use the same logic than + // Use the same logic as // Bootstrap: https://github.com/twbs/bootstrap/blob/1d6e3710dd447de1a200f29e8fa521f8a0908f70/scss/_functions.scss#L59 // and material-components-web https://github.com/material-components/material-components-web/blob/ac46b8863c4dab9fc22c4c662dc6bd1b65dd652f/packages/mdc-theme/_functions.scss#L54 const contrastText = @@ -116,6 +142,17 @@ export default function createPalette(palette: Object) { return contrastText; } + if (!primary.contrastText || primary.contrastText === '') { + primary.contrastText = getContrastText(primary.main); + } + + if (!secondary.contrastText || secondary.contrastText === '') { + secondary.contrastText = getContrastText(secondary.main); + } + + const types = { dark, light }; + warning(Boolean(types[type]), `Material-UI: the palette type \`${type}\` is not supported.`); + const paletteOutput = deepmerge( { common, @@ -124,12 +161,12 @@ export default function createPalette(palette: Object) { secondary, error, grey, - shades, - text: shades[type].text, - input: shades[type].input, - action: shades[type].action, - background: shades[type].background, - line: shades[type].line, + types, + text: types[type].text, + input: types[type].input, + action: types[type].action, + background: types[type].background, + line: types[type].line, contrastThreshold, getContrastText, }, @@ -139,32 +176,5 @@ export default function createPalette(palette: Object) { }, ); - // Dev warnings - if (process.env.NODE_ENV !== 'production') { - const difference = (base, compare) => { - if (!compare) { - compare = {}; - } - - return Object.keys(base).filter(hue => !compare[hue]); - }; - - const paletteColorError = (name, base, compare) => { - const missing = difference(base, compare); - warning( - missing.length === 0, - [ - `Material-UI: ${name} color is missing the following hues: ${missing.join(',')}`, - 'See the default colors, indigo, or pink, as exported from material-ui/colors.', - ].join('\n'), - ); - }; - - paletteColorError('primary', indigo, paletteOutput.primary); - paletteColorError('secondary', pink, paletteOutput.secondary); - paletteColorError('error', red, paletteOutput.error); - paletteColorError('grey', red, paletteOutput.grey); - } - return paletteOutput; } diff --git a/src/styles/createPalette.spec.js b/src/styles/createPalette.spec.js index 860bb4a8aecfdc..a51da45834b2fe 100644 --- a/src/styles/createPalette.spec.js +++ b/src/styles/createPalette.spec.js @@ -2,8 +2,9 @@ import { assert } from 'chai'; import createPalette, { dark, light } from './createPalette'; -import { indigo, pink, deepOrange, green, common } from '../colors'; +import { indigo, pink, deepOrange, green } from '../colors'; import consoleErrorMock from '../../test/utils/consoleErrorMock'; +import { lighten, darken } from '../styles/colorManipulator'; describe('createPalette()', () => { before(() => { @@ -16,8 +17,46 @@ describe('createPalette()', () => { it('should create a material design palette according to spec', () => { const palette = createPalette({}); - assert.strictEqual(palette.primary, indigo, 'should use indigo as the default primary color'); - assert.strictEqual(palette.secondary, pink, 'should use pink as the default secondary color'); + assert.strictEqual( + palette.primary.main, + indigo[500], + 'should use indigo[500] as the default primary main color', + ); + assert.strictEqual( + palette.primary.light, + indigo[300], + 'should use indigo[300] as the default primary light color', + ); + assert.strictEqual( + palette.primary.dark, + indigo[700], + 'should use indigo[700] as the default primary dark color', + ); + assert.strictEqual( + palette.primary.contrastText, + dark.text.primary, + 'should use dark.text.primary as the default primary contrastText color', + ); + assert.strictEqual( + palette.secondary.main, + pink.A400, + 'should use pink.A400 as the default secondary main color', + ); + assert.strictEqual( + palette.secondary.light, + pink.A200, + 'should use pink.A200 as the default secondary light color', + ); + assert.strictEqual( + palette.secondary.dark, + pink.A700, + 'should use pink.A700 as the default secondary dark color', + ); + assert.strictEqual( + palette.secondary.contrastText, + dark.text.primary, + 'should use dark.text.primary as the default secondary contrastText color', + ); assert.strictEqual( palette.text, light.text, @@ -26,16 +65,164 @@ describe('createPalette()', () => { }); it('should create a palette with custom colors', () => { - const palette = createPalette({ primary: deepOrange, secondary: green }); - assert.strictEqual(palette.primary, deepOrange, 'should use deepOrange as the primary color'); - assert.strictEqual(palette.secondary, green, 'should use green as the secondary color'); + const palette = createPalette({ + primary: { + main: deepOrange[500], + light: deepOrange[300], + dark: deepOrange[700], + contrastText: '#ffffff', + }, + secondary: { + main: green.A400, + light: green.A200, + dark: green.A700, + contrastText: '#000000', + }, + }); + assert.strictEqual( + palette.primary.main, + deepOrange[500], + 'should use deepOrange[500] as the primary main color', + ); + assert.strictEqual( + palette.primary.light, + deepOrange[300], + 'should use deepOrange[300] as the primary light color', + ); + assert.strictEqual( + palette.primary.dark, + deepOrange[700], + 'should use deepOrange[700] as the primary dark color', + ); + assert.strictEqual( + palette.primary.contrastText, + '#ffffff', + 'should use #ffffff as the secondary contrastText color', + ); + assert.strictEqual( + palette.secondary.main, + green.A400, + 'should use green.A400 as the secondary main color', + ); + assert.strictEqual( + palette.secondary.light, + green.A200, + 'should use green.A200 as the secondary light color', + ); + assert.strictEqual( + palette.secondary.dark, + green.A700, + 'should use green.A700 as the secondary dark color', + ); + assert.strictEqual( + palette.secondary.contrastText, + '#000000', + 'should use #000000 as the secondary contrastText color', + ); assert.strictEqual(palette.text, light.text, 'should use light theme text'); }); + it('should calculate light and dark colors if not provided', () => { + const palette = createPalette({ + primary: { main: deepOrange[500] }, + secondary: { main: green.A400 }, + }); + assert.strictEqual( + palette.primary.main, + deepOrange[500], + 'should use deepOrange[500] as the primary main color', + ); + assert.strictEqual( + palette.primary.light, + lighten(deepOrange[500], 0.2), + 'should use lighten(deepOrange[500], 0.2) as the primary light color', + ); + assert.strictEqual( + palette.primary.dark, + darken(deepOrange[500], 0.3), + 'should use darken(deepOrange[500], 0.3) as the primary dark color', + ); + assert.strictEqual( + palette.secondary.main, + green.A400, + 'should use green.A400 as the secondary main color', + ); + assert.strictEqual( + palette.secondary.light, + lighten(green.A400, 0.2), + 'should use lighten(green.A400, 0.2) as the secondary light color', + ); + assert.strictEqual( + palette.secondary.dark, + darken(green.A400, 0.3), + 'should use darken(green.A400, 0.3) as the secondary dark color', + ); + }); + + it('should calculate light and dark colors using the provided tonalOffset', () => { + const palette = createPalette({ + primary: { main: deepOrange[500] }, + secondary: { main: green.A400 }, + tonalOffset: 0.1, + }); + assert.strictEqual( + palette.primary.main, + deepOrange[500], + 'should use deepOrange[500] as the primary main color', + ); + assert.strictEqual( + palette.primary.light, + lighten(deepOrange[500], 0.1), + 'should use lighten(deepOrange[500], 0.1) as the primary light color', + ); + assert.strictEqual( + palette.primary.dark, + darken(deepOrange[500], 0.15), + 'should use darken(deepOrange[500], 0.1) as the primary dark color', + ); + assert.strictEqual( + palette.secondary.main, + green.A400, + 'should use green.A400 as the secondary main color', + ); + assert.strictEqual( + palette.secondary.light, + lighten(green.A400, 0.1), + 'should use lighten(green.A400, 0.1) as the secondary light color', + ); + assert.strictEqual( + palette.secondary.dark, + darken(green.A400, 0.15), + 'should use darken(green.A400, 0.1) as the secondary dark color', + ); + }); + + it('should calculate contrastText using the provided contrastThreshold', () => { + const palette = createPalette({ contrastThreshold: 7 }); + assert.strictEqual( + palette.primary.contrastText, + light.text.primary, + 'should use dark.text.primary as the default primary contrastText color', + ); + assert.strictEqual( + palette.secondary.contrastText, + light.text.primary, + 'should use dark.text.primary as the default secondary contrastText color', + ); + }); + it('should create a dark palette', () => { const palette = createPalette({ type: 'dark' }); - assert.strictEqual(palette.primary, indigo, 'should use indigo as the default primary color'); - assert.strictEqual(palette.secondary, pink, 'should use pink as the default secondary color'); + assert.strictEqual( + palette.primary.main, + indigo[500], + 'should use indigo as the default primary color', + ); + assert.strictEqual( + palette.secondary.main, + pink.A400, + 'should use pink as the default secondary color', + ); assert.strictEqual(palette.text, dark.text, 'should use dark theme text'); assert.strictEqual(consoleErrorMock.callCount(), 0); }); @@ -50,31 +237,4 @@ describe('createPalette()', () => { /Material-UI: the palette type `foo` is not supported/, ); }); - - it('should throw an exception when a non-palette primary color is specified', () => { - createPalette({ primary: null }); - assert.strictEqual(consoleErrorMock.callCount(), 2); - assert.match( - consoleErrorMock.args()[1][0], - /Material-UI: primary color is missing the following hues: 50,100,200,300,400/, - ); - }); - - it('should throw an exception when a non-palette secondary color is specified', () => { - createPalette({ secondary: common.fullBlack }); - assert.strictEqual(consoleErrorMock.callCount(), 3); - assert.match( - consoleErrorMock.args()[2][0], - /Material-UI: secondary color is missing the following hues: 50,100,200,300,400/, - ); - }); - - it('should throw an exception when a non-palette error color is specified', () => { - createPalette({ error: common.fullBlack }); - assert.strictEqual(consoleErrorMock.callCount(), 4); - assert.match( - consoleErrorMock.args()[3][0], - /Material-UI: error color is missing the following hues: 50,100,200,300,400/, - ); - }); }); diff --git a/test/regressions/tests/Grid/StressGrid.js b/test/regressions/tests/Grid/StressGrid.js index 4c106359482389..11b2e14c0f86f4 100644 --- a/test/regressions/tests/Grid/StressGrid.js +++ b/test/regressions/tests/Grid/StressGrid.js @@ -7,7 +7,7 @@ import Grid from 'material-ui/Grid'; const styles = theme => ({ root: { width: 400, - backgroundColor: theme.palette.secondary.A400, + backgroundColor: theme.palette.secondary.main, }, paper: { padding: 16, diff --git a/test/regressions/tests/Tabs/AdvancedTabs.js b/test/regressions/tests/Tabs/AdvancedTabs.js index 5f2881a799eae6..9dd2a47c429abc 100644 --- a/test/regressions/tests/Tabs/AdvancedTabs.js +++ b/test/regressions/tests/Tabs/AdvancedTabs.js @@ -13,8 +13,8 @@ const styles = theme => ({ width: 600, }, appBar: { - backgroundColor: theme.palette.primary[500], - color: theme.palette.getContrastText(theme.palette.primary[500]), + backgroundColor: theme.palette.primary.main, + color: theme.palette.getContrastText(theme.palette.primary.main), }, }); diff --git a/test/regressions/tests/Tabs/SimpleTabs.js b/test/regressions/tests/Tabs/SimpleTabs.js index faa44d38618d52..b780d40c9258c3 100644 --- a/test/regressions/tests/Tabs/SimpleTabs.js +++ b/test/regressions/tests/Tabs/SimpleTabs.js @@ -13,8 +13,8 @@ const styles = theme => ({ width: 600, }, appBar: { - backgroundColor: theme.palette.primary[500], - color: theme.palette.getContrastText(theme.palette.primary[500]), + backgroundColor: theme.palette.primary.main, + color: theme.palette.getContrastText(theme.palette.primary.main), }, });