Style Chakra UI FormControl and label at application theme level - reactjs

Have been using Chakra for a while but I can not get my head around styling all my components, in this case, FormControl at a global level with the theme file.
For example if i want to add margin bottom to my FormControl and FormLabel elements I would add the components object to the theme file like so:
components: {
Form: {
parts: ['control', 'label', 'errorMessage'],
baseStyle: {
control: {
marginBottom: '2rem',
},
label: {
marginBottom: '3rem',
},
},
},
},
But this has no effect on the base style of the rendered FormControl or FormLabel.
Could someone please help me with what I am doing wrong here?

Having looked through the source code a bit more there is no parts array to FormControl as it is a Context rather than a component. Therefore, it cannot be styled!

This worked for me:
import type { ComponentStyleConfig } from '#chakra-ui/theme';
import { extendTheme } from '#chakra-ui/react'
const Form: ComponentStyleConfig = {
// The styles all button have in common
parts: ['container'],
baseStyle: {
/// The container styles the FormControl
container: {
marginY: '20px'
}
},
}
const theme = extendTheme({
components: {
Form,
},
})

export const formStyles = {
parts: ['container', 'requiredIndicator', 'helperText'],
baseStyle: {
container: {
label: {
fontSize: '14px',
fontWeight: 'bold',
},
},
},
};
Import it into your component styles
components: {
Form: formStyles,
}

Related

How to select nested colors from theme in Material UI?

I'm creating a style to use within a Button, but I'm unsure on how to select a nested style from my theme. Here is my style and theme:
const buttonStyle: SxProps<Theme> = {
'&:hover': {
backgroundColor: 'backgroundAlert', // workaround
},
};
export const darkTheme = createTheme(themeOptions, {
palette: {
mode: 'dark',
background: {
default: '#0B0E16',
paper: '#1D1F2B',
alert: '#373B38' // I want to use this property
},
backgroundAlert: '#373B38', // workaround
},
});
I'm using backgroundAlert as a work around because i don't know how to select alert from background.alert since it's nested. I couldnt find anything in the docs - does anyone know the syntax to do this?
Something like the below worked for me.
import { styled } from '#mui/material/styles';
const ButtonStyle = styled(Button)(({ theme }) => ({
'&:hover': {
backgroundColor: darkTheme.palette.background.alert,
},
}));
<ButtonStyle variant="contained">Your Button</ButtonStyle>

CSS empty definition necessary to have the type as specificity selector (material UI in React)

The styling for the unselected toggle button works nicely.
But the style of the selected toggle button does not appear when you don't define an empty class selector:
./App.js
import * as React from "react";
import { render } from "react-dom";
import { createStyles, WithStyles } from "#material-ui/core";
import { ToggleButtonGroup, ToggleButton } from "#material-ui/lab";
import { withStyles } from "#material-ui/core/styles";
const styles = () =>
createStyles({
root: {
backgroundColor: "blue",
"&$selected": {
color: "blue",
backgroundColor: "yellow"
}
},
// this empty definition needs to be here otherwise it won't work
selected: {}
});
export interface IAppProps extends WithStyles<typeof styles> {}
const App: React.FC<IAppProps> = ({ classes }: IAppProps) => {
return (
<ToggleButton
classes={{
root: classes.root,
selected: classes.selected
}}
selected={true}
>
option 1
</ToggleButton>
);
};
const AppWithStyles = withStyles(styles)(App);
const rootElement = document.getElementById("root");
render(<AppWithStyles />, rootElement);
This perhaps has something to do with the type definition in the props. When you remove the selector 'selected' from the styles definition, it is not available anymore in the interface IAppProps.
How can you define this type in the interface?
Working example: https://codesandbox.io/s/elated-sammet-y7wh7?fontsize=14
update 1: Also tried Augmenting the props as described in the material-ui docs:
const styles = () =>
createStyles({
toggleButton: {
backgroundColor: "blue",
"&$toggleButtonSelected": {
color: "blue",
backgroundColor: "yellow"
}
},
});
export interface IAppProps {
classes: {
toggleButton: string;
toggleButtonSelected: string;
};
}
const App: React.FC<IAppProps> = ({ classes }: IAppProps) => {
// ...
With no luck.
update 2: using a hook makes the type casting redundant, but it also won't fix this:
import * as React from "react";
import { render } from "react-dom";
import { createStyles, makeStyles } from "#material-ui/core";
import { ToggleButtonGroup, ToggleButton } from "#material-ui/lab";
const useStyles = makeStyles(() =>
createStyles({
root: {
backgroundColor: "blue",
"&$selected": {
color: "blue",
backgroundColor: "red"
}
},
// this still needs to be there...
// selected: {}
})
)
export interface IAppProps {}
const App: React.FC<IAppProps> = () => {
const classes = useStyles();
return (
// ...
)
}
const rootElement = document.getElementById("root");
render(<App />, rootElement);
I believe you just have a misunderstanding of how JSS works and the meaning of some of the syntax. The relevant documentation is here.
When you define your styles object (or function taking in the theme and returning an object), each key in that object is referred to by JSS as a "rule". The key is the rule name and JSS will translate the value into a CSS class. The classes object that you get back from useStyles or that gets injected as a prop when using withStyles then maps the rule names to the generated CSS class names.
The $ruleName syntax is a way to refer to the CSS class name of one of the other rules in your styles object. The & refers to the parent rule. In your example you have rules called root and selected (when it isn't commented out).
The following:
root: {
backgroundColor: "blue",
"&$selected": {
color: "blue",
backgroundColor: "red"
}
},
selected: {}
would compile to CSS like the following:
.root-0 {
background-color: blue;
}
.root-0.selected-0 {
color: blue;
background-color: red;
}
By passing the following to Material-UI:
classes={{
root: classes.root,
selected: classes.selected
}}
selected={true}
You are telling it to apply "root-0 selected-0" as class names in addition to the class names applied for the default styling. Without the empty selected: {} rule name, you can't refer to $selected from the root rule (JSS should be giving you a warning in the console if you do).
There is a slightly simpler alternative (as of v4) for referring to the selected class name. selected is one of the Material-UI special states that it refers to as pseudo-classes and the documentation provides the default class name for each (e.g. Mui-selected).
This means you can do the following:
root: {
backgroundColor: "blue",
"&.Mui-selected": {
color: "blue",
backgroundColor: "red"
}
}
This is no longer referencing another rule, so selected: {} isn't needed and neither is selected: classes.selected needed in the classes prop. Instead this is referencing the actual class name that Material-UI applies for the default styling when selected={true}.

How to get the label in the material-ui text field to the right?

I'm using material-ui and typescript for my react project (rtl layout) and i don't know how to get the label of text field to the right?
Found a better way without external libraries.
export const theme = createTheme({
components: {
MuiInputLabel: {
styleOverrides: {
root: {
left: 'inherit',
right: '1.75rem',
transformOrigin: 'right',
},
},
},
MuiOutlinedInput: {
styleOverrides: {
notchedOutline: {
textAlign: 'right',
},
},
},
},
})
You need jss-rtl to support rtl for css. This library provides its Provider to support rtl in any library.
import React from "react";
import { create } from "jss";
import rtl from "jss-rtl";
import JssProvider from "react-jss/lib/JssProvider";
import { createGenerateClassName, jssPreset } from "#material-ui/core/styles";
// Configure JSS
const jss = create({ plugins: [...jssPreset().plugins, rtl()] });
// Custom Material-UI class name generator.
const generateClassName = createGenerateClassName();
function RTL(props) {
return (
<JssProvider jss={jss} generateClassName={generateClassName}>
{props.children}
</JssProvider>
);
}
export default RTL;
Then in your main app use this provider.
ReactDOM.render(
<RTL>
<Demo />
</RTL>,
document.querySelector("#root")
);
Working demo here

React-jss: Warning: Rule is not linked. Missing sheet option "link: true"

I'm trying to change the underline border-color of a Material-UI input component. But the following code doesn't work and gives me the following error:
Warning: Rule is not linked. Missing sheet option "link: true"
These are my imports:
import React from "react";
import classNames from "classnames";
import PropTypes from "prop-types";
import injectSheet from "react-jss";
import customInputStyle from "../../assets/jss/material-dashboard-react/components/customInputStyle.jsx";
These are the class names combined:
const underlineClasses = classNames({
[classes.underlineError]: error,
[classes.underlineSuccess]: success && !error,
[classes.underlineCustom]: customColor,
[classes.underline]: true
});
If the error or succes props are true, it does show the right color. I want to set the underline color based on the prop customColor if its defined, so I can't define a static color.
This is the component that uses the combined class names:
<Input
classes={{
underline: underlineClasses
}}
/>
This is the style object:
const customInputStyle = {
underline: {
"&:hover:not($disabled):before,&:before": {
borderColor: "#D2D2D2 !important",
borderWidth: "1px !important"
},
"&:after": {
borderColor: primaryColor
}
},
underlineError: {
"&:after": {
borderColor: dangerColor
}
},
underlineSuccess: {
"&:after": {
borderColor: successColor
}
},
underlineCustom: {
"&:after": {
borderColor: props => props.customColor
}
}
}
this is how I export the component:
export default injectSheet(customInputStyle, { link: true })(CustomInput);
You should use ::after instead of :after, look at https://github.com/cssinjs/jss/issues/710 and https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-elements
Function values in nested rules are not supported yet, follow the v10 release updates in this issue https://github.com/cssinjs/jss/issues/795

How do you change the color of a stepper in material-ui in React

Referring to this:
https://material-ui.com/demos/steppers/
It is blue by default.
I want it to be material-ui's orange200
I tried the following code (from this stackoverflow answer) but it did not work.
import getMuiTheme from 'material-ui/styles/getMuiTheme'
const muiTheme = getMuiTheme({
stepper: {
iconColor: 'green' // or logic to change color
}
})
<MuiThemeProvider muiTheme={muiTheme}>
<Stepper>
...
</Stepper>
</MuiThemeProvider>
Use Chrome DevTools (or other browsers' dev tools) to find out a class that will give you an information about an element to override.
For example, assuming you found that the class name is
.MuiStepIcon-root-78. The formulae is Mui[component name]-[style rule name]-[UUID], where Mui is a default prefix and 78 is just an id. So, the element's name is MuiStepIcon and a subsequent attribute is root.
Say, you want to change the color. If you do the hierarchy MuiStepIcon -> root -> color, you will change the default color only. To change any other colors, watch out for pseudo classes. For example, using devtools, if the class is .MuiStepIcon-root-78.MuiStepIcon-active-80, then the pseudo class is active, and your code should be MuiStepIcon -> root -> '&$active' -> color. Look at the code below for references.
Look at the docs for more info https://material-ui.com/customization/overrides/#overriding-with-classes
You can also determine available elements to override by referring to createMuiTheme -> overrides, which will take you to overrides.d.ts file. There is an interface that lists all components names, like MuiStepIcon, though it won't give you other information as devtools do.
import React, { Component } from 'react';
import { MuiThemeProvider, createMuiTheme } from '#material-ui/core/styles';
const muiTheme = createMuiTheme({
overrides: {
MuiStepIcon: {
root: {
color: '#000000', // or 'rgba(0, 0, 0, 1)'
'&$active': {
color: '#000000',
},
'&$completed': {
color: '#000000',
},
},
},
}
});
const otherStyles = theme => ({
root: {
// Whatever needed
},
});
class MyComponent extends Component {
render(){
return (
<MuiThemeProvider theme={muiTheme}>
{
// Your stepper here, should be within MuiThemeProvider
}
</MuiThemeProvider>
);
}
};
export default withStyles(otherStyles, { withTheme: true })(MyComponent);
overrides: {
MuiStepIcon: {
root: {
color: '#000000', // or 'rgba(0, 0, 0, 1)'
'&$active': {
color: '#000000',
},
'&$completed': {
color: '#000000',
},
},
}
did work for me
One way to change the color and styling of icon of stepper material UI is to pass icon prop in StepLabel as:
<StepLabel
icon = <div style={{backgroundColor: 'orange', width:'11px', padding: '2px', textAlign: 'center', height: '11px', fontSize: '10px', borderRadius: '50%'}}>{index}</div>
>{label}</StepLabel>

Resources