How to style InputAdornment like notched OutlinedInput?

RanST picture RanST · Jan 8, 2020 · Viewed 7.8k times · Source

I'm using React JS and Material UI framework.

I need the adornment icon color inside a text field to behave like the border of the input.

If you look at the example from the documentation you can see that when:

  • Hovering the input, the border will be wider and black
  • Focusing the input, the border will be wider and have the primary color

I found that these settings come from the styling given to the component.

How can I apply these rules colors to the icon?

Another related question- what is the easiest way to give a color other than primary or secondary? Only by overriding the classes as described in the documentation?

Answer

Ryan Cogswell picture Ryan Cogswell · Jan 8, 2020

Below is an example of how to do this -- the key aspects being the outlinedInput class and (if you also want to sync the label) the textField class. The color could be anything you want to use, but in this example I'm using the same colors from the theme as are used for the border.

import React from "react";
import clsx from "clsx";
import { makeStyles } from "@material-ui/core/styles";
import IconButton from "@material-ui/core/IconButton";
import OutlinedInput from "@material-ui/core/OutlinedInput";
import InputLabel from "@material-ui/core/InputLabel";
import InputAdornment from "@material-ui/core/InputAdornment";
import FormControl from "@material-ui/core/FormControl";
import Visibility from "@material-ui/icons/Visibility";
import VisibilityOff from "@material-ui/icons/VisibilityOff";

const useStyles = makeStyles(theme => ({
  root: {
    display: "flex",
    flexWrap: "wrap"
  },
  margin: {
    margin: theme.spacing(1)
  },
  textField: {
    width: 200,
    "&:hover .MuiInputLabel-root": {
      color: theme.palette.text.primary
    },
    "& .Mui-focused.MuiInputLabel-root": {
      color: theme.palette.primary.main
    }
  },
  outlinedInput: {
    "&:hover .MuiInputAdornment-root .MuiSvgIcon-root": {
      color: theme.palette.text.primary
    },
    "&.Mui-focused .MuiInputAdornment-root .MuiSvgIcon-root": {
      color: theme.palette.primary.main
    }
  }
}));

export default function InputAdornments() {
  const classes = useStyles();
  const [values, setValues] = React.useState({
    password: "",
    showPassword: false
  });

  const handleChange = prop => event => {
    setValues({ ...values, [prop]: event.target.value });
  };

  const handleClickShowPassword = () => {
    setValues({ ...values, showPassword: !values.showPassword });
  };

  const handleMouseDownPassword = event => {
    event.preventDefault();
  };

  return (
    <div className={classes.root}>
      <div>
        <FormControl
          className={clsx(classes.margin, classes.textField)}
          variant="outlined"
        >
          <InputLabel htmlFor="outlined-adornment-password">
            Password
          </InputLabel>
          <OutlinedInput
            id="outlined-adornment-password"
            type={values.showPassword ? "text" : "password"}
            value={values.password}
            onChange={handleChange("password")}
            className={classes.outlinedInput}
            endAdornment={
              <InputAdornment position="end">
                <IconButton
                  aria-label="toggle password visibility"
                  onClick={handleClickShowPassword}
                  onMouseDown={handleMouseDownPassword}
                  edge="end"
                >
                  {values.showPassword ? <Visibility /> : <VisibilityOff />}
                </IconButton>
              </InputAdornment>
            }
            labelWidth={70}
          />
        </FormControl>
      </div>
    </div>
  );
}

Edit Sync adornment color with border

Follow-up questions from the comments:

How to correctly override multiple classes? I see that it works and I understand your explanation, however- it seems that I don't quite understand where I need to add space between the classes names or after '&:hover'. For example in my demo in order to color the label when focused, I wrote "&.Mui-focused.MuiInputLabel-root" while in your demo it's "& .Mui-focused.MuiInputLabel-root" with space after "& ". Of course, the difference is because I applied the styles on InputLabel and you on the TextField, but why does it differ?

The & refers to the CSS class generated for the current style rule (e.g. classes.textField or classes.outlinedInput). The space is a descendant CSS selector. The element with the "MuiInputLabel-root" class is a descendant of the element receiving the classes.textField class, so & .Mui-focused.MuiInputLabel-root successfully targets the label. Without the space, it would only target elements that have the classes.textField class and the MuiInputLabel-root class. If the classes.textField class were being applied to the label element, then that would work, but since we need to target hovering over the entire input rather than just the label, the class needs to be applied to the parent.

Relevant Documentation: