Using theme palette colors in custom MUI component variants with light/dark mode - reactjs

For readability reasons, I want to split the components object passed in the createTheme function (components may have large variants) and I do have light/dark mode.
According to docs, this is how we get the design tokens:
const getDesignTokens = (mode: PaletteMode) => ({
palette: {
mode,
...(mode === 'light'
? {
// palette values for light mode
primary: amber,
divider: amber[200],
text: {
primary: grey[900],
secondary: grey[800],
},
}
: {
// palette values for dark mode
primary: deepOrange,
divider: deepOrange[700],
background: {
default: deepOrange[900],
paper: deepOrange[900],
},
text: {
primary: '#fff',
secondary: grey[500],
},
}),
},
});
After that, I create the theme depending on whether the mode is light or dark.
The problem is when I'm trying to add a component to the theme (as stated before, each component will be only referenced in the createTheme, the definition would be somewhere else) I cannot use colors from the theme without wrapping the component is a function which has the parameter the mode used.
I wonder if there is any solution like with the sx prop when you're referencing the color as a string, let's say sx={{ backgroundColor: 'button.background' }} and that would automatically be used from the theme.
Wrapping each component in a function with a parameter does the job, but I would like to know if there is any better solution.
How the code is now:
const dashedVariants = (palette) => ({
props: {variant: 'dashed'},
style: {
border: `1px dashed ${palette.dashColor}`
}
})
const Button = (palette) => ({
styleOverrides: {},
variants: [dashedVariants(palette)]
})
vs what I'm trying to acheive:
const dashedVariants = {
props: {variant: 'dashed'},
style: {
border: `1px dashed palette.dashColor` //something like that??
}
}
Note: I've looked over this existing question, but unfortunately this does seem to help.

Due to complexity of CSS properties, many UI toolkit and its themes only parse singular properties(or provide a detour utility); MUI's one of them. Use separate border properties to make parser working. Palette color property parser only works for the type of React.CSSProperties['color'] property. .border property is Property.Border type. The color parser won't work in this case.
Palette type only works with appropriate properties. it does not provide dashColor property. according to MUI doc, working properties are:
.palette.primary.light
.palette.primary.main
.palette.primary.dark
.palette.primary.contrastText
// ...
.palette.secondary
.palette.error
.palette.warning
.palette.info
.palette.success
const theme = {
palette: {
secondary: {
main: '#...' // user defiend color
}
}
}
const dashedVariants = {
props: {variant: 'dashed'},
style: {
borderColor: 'secondary.main',
borderWidth: '1px',
borderStyle: 'dashed',
}
}
There's an experimental CSS variable feature. With this, it is possible to define CSS variable inside complex property. This is probably the closest to the goal but it's currently experimental stage, might be unstable for production use. I am also looking forward to using this in the future.

Related

How to change background color in Material UI?

I have a switcher that changes the theme of the websitesite to light and dark. Now it uses default colors: black and white. How to change these colors to my custom?
Code provided in SandBox
Trying to change colors in palette attribute, it doesnt helps
You can see in the documentation the instructions to customize the palette according to the selected mode.
Here is a possible solution based on the light #b42b2b and dark #222 colors present in your example :
const theme = React.useMemo(
() => {
const customProps = (mode === "light") ? {
background: {
default: "#b42b2b",
paper: "#b42b2b"
}
} : {
background: {
default: "#222",
paper: "#222"
}
};
return createTheme({
palette: {
mode,
...customProps
}
});
},
[mode]
);
I only customized background but you can customize much more properties. Here the list of properties present in the default theme.
You can see their default values in light and dark mode, so you can decide which ones you want to customize.
If you want to add custom color, you must provide all these properties light, main, dark and contrastText.
const theme = createTheme({
palette: {
custom: {
light: '#ffa726',
main: '#f57c00',
dark: '#ef6c00',
contrastText: 'rgba(0, 0, 0, 0.87)',
}
}
});
and use the custom color like this :
<Button color="custom">
const Div = styled("div")(({ theme }) => ({
backgroundColor: theme.palette.custom.main
}));

Customizing how Chakra components use the color scheme

Chakra allows you to define and pass color schemes to components, but as far as I've been able to tell, it doesn't allow you to customize how those color schemes are used. For example, the button component will use the 500 shade of whichever color scheme you pass in as its default background and a darker shade for the hover background. Suppose I wanted to invert these though, I'm hoping to do something like...
Button: {
baseStyle: {
bg: colorScheme.700,
_hover: {
bg: colorScheme.500,
},
},
}
Is there any way to define how color schemes are used for a given component? It seems like to override the default shading choices, you have to override all colors explicitly, but hopefully I'm missing something.
You can explore Chakra's source to see how they do this for their components. They're not coded any differently than how you can define your own styles.
We know Buttons utilize the colorScheme prop, so I looked in the code to find out how they apply the prop.
Their core component styles are defined in a base theme and Buttons are no different. The source shows various examples of variants being used, but here's the ghost variant definition:
const variantGhost = defineStyle((props) => {
const { colorScheme: c, theme } = props
if (c === "gray") {
return {
color: mode(`inherit`, `whiteAlpha.900`)(props),
_hover: {
bg: mode(`gray.100`, `whiteAlpha.200`)(props),
},
_active: { bg: mode(`gray.200`, `whiteAlpha.300`)(props) },
}
}
const darkHoverBg = transparentize(`${c}.200`, 0.12)(theme)
const darkActiveBg = transparentize(`${c}.200`, 0.24)(theme)
return {
color: mode(`${c}.600`, `${c}.200`)(props),
bg: "transparent",
_hover: {
bg: mode(`${c}.50`, darkHoverBg)(props),
},
_active: {
bg: mode(`${c}.100`, darkActiveBg)(props),
},
}
})
As you can see, there's something special about this definition (in addition to some helper functions being utilized).
In all of Chakra's examples, they use static objects to override styles. But that's not a necessity. You can use this defineStyle function to return a dynamic object based not only on the current colorMode, but the colorScheme passed to the component! defineStyle can be used on your baseStyle property, as well as your variants and sizes properties too.
You can access defineStyle by importing it from #chakra-ui/react like this:
import { defineStyle } from '#chakra-ui/react'
const theme = extendTheme({
components: {
Button: defineStyleConfig({
baseStyle: defineStyle(props => {
return {
color: `${props.colorScheme}.500`
}
}),
}),
},
})
It's not necessary, but you can get fancier, like in the Chakra source, and use the mode helper function to select different colors for your light and dark mode.

Material-UI: Customise theme for hover state

I am trying to customise MUI V5 theme to define colours of a design system for all components according to variant, color and state(hover).
I tried to follow the approach mentioned in: https://mui.com/blog/callback-support-in-style-overrides/ and it works fine when I tried it for MuiButton as shown below. but to apply the design system I have to write the same conditions for all defined components(Fab, chip, selector,...) with all supported colors and variants :
MuiButton: {
defaultProps: {
variant: 'contained',
},
styleOverrides:{
root: ({ ownerState, theme }) => ({
...(ownerState.color === 'error' && {
...(ownerState.variant === 'contained' &&{
'&:hover':{
backgroundColor: '#530F00',
}
}),
...(ownerState.variant === 'outlined' &&{
borderColor: '#D48E80',
'&:hover':{
backgroundColor: '#F8EDEB',
}
}),
}),
},
},
My question: Is there a general way to define styles for all components with same variants? Or is there a better way to achieve this?

How can I disable some Material UI css for ListItem

I am using the Material-UI ListItem component and I would like to get rid of the default background color. I've already tried using classes but my value gets overwritten by this:
How can I disable this default rule?
You can globally override the style for any Material UI component.
Look for the point in your application where you call createMuiTheme, and add and overrides field like this.
const theme = createMuiTheme({
palette: {
/* you should already have stuff here, keep it */
},
typography: {
/* you should already have stuff here, keep it */
},
overrides: {
MuiListItem: {
root: {
backgroundColor: "red",
},
},
},
});

material-ui: AppBar: strategy for restricting an image height to AppBar height?

can anyone provide guidance around an idiomatic way to place an image in an AppBar and have it be restricted to the standard material height (e.g. 64px for a desktop)?
i'm currently using material-ui#next (1.0.0-beta.2 currently).
i have found that something like:
<AppBar>
<Toolbar>
<IconButton color="contrast" aria-label="Menu">
<MenuIcon />
</IconButton>
<img src={logo} style={{height: 64}}/>
<Typography type="title" color="inherit">
React Scratch
</Typography>
</Toolbar>
</AppBar>
works well.
the actual logo is a png file with a height greater than 64, so if i don't ratchet it down, it expands the height of the AppBar out of Material spec.
in the current master branch version of src/styles there is a getMuiTheme.js which seems to deliver this height readily, but in the #next version i am looking at, that file doesn't even exist and tbh, i can't easily determine how that height is being set anymore.
i found that the AppBar is currently being renovated for composability, so that churn might make it challenging to answer this question, but just in case anyone has a good handle on this, i figured i would toss the question out there.
thanks!
In all cases I've seen, an AppBar is implemented with a Toolbar as it's first child. The Toolbar's stylesheet dictates it's height based on the breakpoints defined in the theme.
Take a look here: https://github.com/callemall/material-ui/blob/v1-beta/src/Toolbar/Toolbar.js
You can use a similar approach to define a stylesheet with a class for your AppBar images that varies the height for the applicable breakpoints. Then when rendering the component, apply the class to your image.
Note: if you use the withStyles HOC, as is done in the Toolbar, AppBar etc, the classes defined in that stylesheet will be available through a prop named classes.
You are right about the AppBar's need for composability, but that issue has not been solved yet, and this is the beta branch anyway. When it is solved, there should be a better solution that would be worth migrating towards.
I hope this answer helps. I would have added code samples but I am answering from my phone while waiting in a grocery store parking lot. If I get a chance I will update this answer.
Here's one approach, duplicating the styles in a new reusable component:
import createStyleSheet from 'material-ui/styles/createStyleSheet';
import withStyles from 'material-ui/styles/withStyles';
// define these styles once, if changes are needed because of a change
// to the material-ui beta branch, the impact is minimal
const styleSheet = createStyleSheet('ToolbarImage', theme => ({
root: {
height: 56,
[`${theme.breakpoints.up('xs')} and (orientation: landscape)`]: {
height: 48,
},
[theme.breakpoints.up('sm')]: {
height: 64,
},
},
}));
// a reusable component for any image you'd need in a toolbar/appbar
const ToolbarImage = (props) => {
const { src, classes } = this.props;
return (
<img src={src} className={classes.root} />
);
};
// this higher order component uses styleSheet to add
// a classes prop that contains the name of the classes
export default withStyles(styleSheet)(ToolbarImage);
Another approach is to add the standard toolbar heights to the theme as business variables, override the root class for all Toolbars so that it makes use of them, and use the theme whenever you need to reference them again:
// define the standard heights in one place
const toolbarHeights = {
mobilePortrait: 56,
mobileLandscape: 48,
tabletDesktop: 64,
};
// create the theme as you normally would, but add the heights
let theme = createMuiTheme({
palette: createPalette({
primary: blue,
accent: pink,
}),
standards: {
toolbar: {
heights: toolbarHeights,
},
},
});
// recreate the theme, overriding the toolbar's root class
theme = createMuiTheme({
...theme,
overrides: {
MuiToolbar: {
// Name of the styleSheet
root: {
position: 'relative',
display: 'flex',
alignItems: 'center',
minHeight: theme.standards.toolbar.heights.mobilePortrait,
[`${theme.breakpoints.up('xs')} and (orientation: landscape)`]: {
minHeight: theme.standards.toolbar.heights.mobileLandscape,
},
[theme.breakpoints.up('sm')]: {
minHeight: theme.standards.toolbar.heights.tabletDesktop,
},
},
},
},
});
Then you can reference these heights in any stylesheet you create because they're part of the theme.
UPDATED FOLLOWING THE RELEASE OF 1.0.0-beta.11:
There is now a toolbar mixin available on the theme that provides the toolbar minHeight for each breakpoint. If you need to style an element relative to the standard height of the AppBar component, you can use this object to build your own styles:
const toolbarRelativeProperties = (property, modifier = value => value) => theme =>
Object.keys(theme.mixins.toolbar).reduce((style, key) => {
const value = theme.mixins.toolbar[key];
if (key === 'minHeight') {
return { ...style, [property]: modifier(value) };
}
if (value.minHeight !== undefined) {
return { ...style, [key]: { [property]: modifier(value.minHeight) } };
}
return style;
}, {});
In this example, toolbarRelativeProperties returns a function that will return an object that can be spread into your style object. It addresses the simple case of setting a specified property to a value that is based on the AppBar height.
A simple usage example would be the generation of a dynamic CSS expression for height calculation, which is depending on the standard height of the AppBar:
const componentStyle = theme => ({
root: {
height: toolbarRelativeProperties('height', value => `calc(100% - ${value}px)`)(theme)
}
});
The generated style definition might look like this:
{
height: 'calc(100% - 56px)',
'#media (min-width:0px) and (orientation: landscape)': {
height: 'calc(100% - 48px)'
},
'#media (min-width:600px)': {
height: 'calc(100% - 64px)'
}
}

Resources