Material-UI: Customise theme for hover state - reactjs

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?

Related

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.

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

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.

Target CSS child selector created by in material ui

I have styles like this:
const useStyles = makeStyles(theme => ({
root: {
margin: 5
},
container: {
backgroundColor: 'red'
},
active: {
// here I want to target the 'container' className I created above like
'& .container': {
backgroundColor: 'green'
}
}
});
I want to target the container className I created inside of the active className. The above won't work because in the DOM, MUI will generate a unique name so I won't be targeting the right class. Wasn't able to find any SO answer or blog or documentation addressing this.
$ rulename is used for this purpose. Here is the documentation of it on Material-UI.
CSS in JS documentation also explains this feature.
container: {
//other styles
},
active: {
"& $container": {
background: "green",
color: "#fff"
}
}
Here one thing which is important that for referencing 'containerrule, it should be defined in the rules object. trying to use"& $containerwithout defining thecontainerrule inside themakeStyles` will not work.
Here is the working demo:
You can refer using $
You will have to modify your DOM little bit such that the active className is not the parent of container. Instead add the active className to the conatiner element itself.
so your css style might look like below
const useStyles = makeStyles(theme => ({
root: {
margin: 5
},
container: {
backgroundColor: 'red',
'&$active': {
backgroundColor: 'green'
}
},
});
I think this is what you are looking for $rulename
How to Use $ruleName to reference a local rule within the same style sheet
In your case i think the solution would be
const useStyles = makeStyles(theme => ({
root: {
margin: 5
},
container: {
backgroundColor: 'red'
},
active: {
.container: {
backgroundColor: 'green'
}
}
});
Which should compile to
.root.container.active{}
and on the target tag taking a example of button here
<Button
classes={{
root: classes.root,
container: classes.container,
active: classes.active,
}}>
Havent worked with MUI yet but even in vue or react the way this is achived is by setting a dynamic name on the tag that is targeted via script.

Trying to style an item with a specific custom class with material UI

I am trying to manage a music playlist with a material ui table. And I would like to change the appearance of the item I am currently playing. At first I was juste setting it as "selected" but now, I have custom theme, and I don't know what I should overwrite to have the "selected" appearances coherent with the theme of my playlist. So, instead, I would prefer to add a class to my row "playing", and then, style my item differently regarding the class it has.
I did not find anything working to do so. When I tried to add my custom class into my theme, I got this error :
mergeClasses.js:25 Material-UI: The key `&.playing` provided to the classes prop is not implemented in ForwardRef(TableCell).
You can only override one of the following: root,head,body,footer,sizeSmall,paddingCheckbox,paddingNone,alignLeft,alignCenter,alignRight,alignJustify,stickyHeader.
So, I guess what I was trying to do must not be done like I tried to. I tried this :
const StyledTableCell = withStyles((theme) => ({
root: {
borderColor: theme.root.borderColor,
},
head: {
backgroundColor: theme.palette.background.default,
color: theme.palette.common.white,
},
body: {
fontSize: 14,
},
"&.playing": { backgroundColor: "rgba(0, 0, 0, 0.16)" },
}))(TableCell);
How can I add specific rendering rules to a custom classe in material ui ?
&.playing should be inside of root to be applied to the root element.
const StyledTableCell = withStyles((theme) => ({
root: {
'&.playing': {
backgroundColor: '#ccc',
}
},
}))(TableCell);

Material-ui - TextField - Can't change helper text error color

I have a form with a very awkward background color. Would like to change the color of the helper text of the Outlined TextField when it is in an error state but can't seem to override it. It persists the red.
See CodeSandBox .
For some reason, the error text color is generated under the following className: .MuiFormHelperText-root.Mui-error
So overriding error rule alone is not enough.
This will do the trick:
const helperTextStyles = makeStyles(theme => ({
root: {
margin: 4,
color:'black',
},
error: {
"&.MuiFormHelperText-root.Mui-error" :{
color: theme.palette.common.white,
},
},
}));
Code Sandbox
The problem is caused by CSS specificity (the base style has more specific classname, i.e. MuiFormHelperText-root.Mui-error than the overriding style class). It's recommended to use &$ syntax under this circumstance:
const helperTextStyles = makeStyles(theme => ({
root: {
margin: 4,
'&$error': {
color: 'white'
}
},
error: {} //<--this is required to make it work
}));
You can also refer to this section for an example and a bit more explanation.
It would be better to customize your Mui Theme as follows:
const Theme = {
components: {
MuiFormHelperText: {
styleOverrides: {
root: {
color: "red"
}
}
}
}
};
See https://mui.com/material-ui/customization/theming/ for more information.
i found one solution to change the color of text field.
<TextField
error
id="filled-error-helper-text"
label="Error"
defaultValue="Hello World"
helperText="Incorrect entry."
variant="filled"
/>
here you can see error which is Boolean, so you might be having validation like YUP, if you do then you pass it like this.
<TextField
error={!valid}
id="filled-error-helper-text"
label="Error"
defaultValue="Hello World"
helperText="Incorrect entry."
variant="filled" />
here i am changing color base on valid keyword.

Resources