17

I already have a styleguide that I'm trying to implement in Material UI. I can see the Button's color prop takes these options:

| 'default'
| 'inherit'
| 'primary'
| 'secondary'

However I need an additional one:

| 'default'
| 'inherit'
| 'primary'
| 'secondary'
| 'tertiary'

Can you create a new color in Material UI that works with the general theming system? Or is this not really how it's supposed to be used?

Ryan Cogswell
  • 75,046
  • 9
  • 218
  • 198
Evanss
  • 23,390
  • 94
  • 282
  • 505
  • I recommend that you upvote this issue (since upvotes help guide priorities): https://github.com/mui-org/material-ui/issues/13875. This is intended to be addressed in v5 (bullet point #5 here: https://github.com/mui-org/material-ui/issues/20012). – Ryan Cogswell Jun 16 '20 at 14:14
  • @RyanCogswell are you saying that it can't be done without some hackery? The solution in the first link looks totally over engineered to me. – Evanss Jun 18 '20 at 15:19
  • It is fairly straightforward to create a custom Button component (wrapping Material-UI's Button) that supports a specific additional color entry in the palette, but there isn't any way to have Material-UI's Button recognize additional colors in the theme's palette. In v5, they are intending to support additional colors in any of the components with a `color` prop. Once that is in place, it will be much easier to leverage additional colors without having to create a wrapper component to add that support. – Ryan Cogswell Jun 18 '20 at 15:26

2 Answers2

14

UPDATE - This answer was written for v4 of Material-UI. v5 supports custom colors directly and I have added a v5 example at the end.


Though Material-UI does not support this directly in v4, you can wrap Button in your own custom component to add this functionality.

The code below uses a copy of the styles for textPrimary, outlinedPrimary, and containedPrimary but replaces "primary" with "tertiary".

import * as React from "react";
import Button from "@material-ui/core/Button";
import { makeStyles } from "@material-ui/core/styles";
import clsx from "clsx";
import { fade } from "@material-ui/core/styles/colorManipulator";

const useStyles = makeStyles(theme => ({
  textTertiary: {
    color: theme.palette.tertiary.main,
    "&:hover": {
      backgroundColor: fade(
        theme.palette.tertiary.main,
        theme.palette.action.hoverOpacity
      ),
      // Reset on touch devices, it doesn't add specificity
      "@media (hover: none)": {
        backgroundColor: "transparent"
      }
    }
  },
  outlinedTertiary: {
    color: theme.palette.tertiary.main,
    border: `1px solid ${fade(theme.palette.tertiary.main, 0.5)}`,
    "&:hover": {
      border: `1px solid ${theme.palette.tertiary.main}`,
      backgroundColor: fade(
        theme.palette.tertiary.main,
        theme.palette.action.hoverOpacity
      ),
      // Reset on touch devices, it doesn't add specificity
      "@media (hover: none)": {
        backgroundColor: "transparent"
      }
    }
  },
  containedTertiary: {
    color: theme.palette.tertiary.contrastText,
    backgroundColor: theme.palette.tertiary.main,
    "&:hover": {
      backgroundColor: theme.palette.tertiary.dark,
      // Reset on touch devices, it doesn't add specificity
      "@media (hover: none)": {
        backgroundColor: theme.palette.tertiary.main
      }
    }
  }
}));

const CustomButton = React.forwardRef(function CustomButton(
  { variant = "text", color, className, ...other },
  ref
) {
  const classes = useStyles();
  return (
    <Button
      {...other}
      variant={variant}
      color={color === "tertiary" ? "primary" : color}
      className={clsx(className, {
        [classes[`${variant}Tertiary`]]: color === "tertiary"
      })}
      ref={ref}
    />
  );
});
export default CustomButton;

Then this CustomButton component can be used instead of Button:

import React from "react";
import {
  makeStyles,
  createMuiTheme,
  ThemeProvider
} from "@material-ui/core/styles";
import Button from "./CustomButton";
import lime from "@material-ui/core/colors/lime";

const useStyles = makeStyles(theme => ({
  root: {
    "& > *": {
      margin: theme.spacing(1)
    }
  }
}));

const theme = createMuiTheme({
  palette: {
    tertiary: lime
  }
});
// This is a step that Material-UI automatically does for the standard palette colors.
theme.palette.tertiary = theme.palette.augmentColor(theme.palette.tertiary);

export default function ContainedButtons() {
  const classes = useStyles();

  return (
    <ThemeProvider theme={theme}>
      <div className={classes.root}>
        <Button variant="contained">Default</Button>
        <Button variant="contained" color="primary">
          Primary
        </Button>
        <Button variant="contained" color="secondary">
          Secondary
        </Button>
        <br />
        <Button variant="contained" color="tertiary">
          Tertiary
        </Button>
        <Button color="tertiary">Tertiary text</Button>
        <Button variant="outlined" color="tertiary">
          Tertiary outlined
        </Button>
      </div>
    </ThemeProvider>
  );
}

Edit Button tertiary color


In v5, the custom button is not necessary. All you need to do is create the theme appropriately:

import React from "react";
import { styled, createTheme, ThemeProvider } from "@material-ui/core/styles";
import Button from "@material-ui/core/Button";
import { lime } from "@material-ui/core/colors";

const defaultTheme = createTheme();
const theme = createTheme({
  palette: {
    // augmentColor is a step that Material-UI automatically does for the standard palette colors.
    tertiary: defaultTheme.palette.augmentColor({
      color: { main: lime[500] },
      name: "tertiary"
    })
  }
});

const StyledDiv = styled("div")(({ theme }) => ({
  "& > *.MuiButton-root": {
    margin: theme.spacing(1)
  }
}));
export default function ContainedButtons() {
  return (
    <ThemeProvider theme={theme}>
      <StyledDiv>
        <Button variant="contained">Default</Button>
        <Button variant="contained" color="primary">
          Primary
        </Button>
        <Button variant="contained" color="secondary">
          Secondary
        </Button>
        <br />
        <Button variant="contained" color="tertiary">
          Tertiary
        </Button>
        <Button color="tertiary">Tertiary text</Button>
        <Button variant="outlined" color="tertiary">
          Tertiary outlined
        </Button>
      </StyledDiv>
    </ThemeProvider>
  );
}

Edit Button tertiary color

Ryan Cogswell
  • 75,046
  • 9
  • 218
  • 198
  • Awesome, Ryan. Thanks. One question... in v5, did you instantiate defaultTheme just in order to have access to augmentColor? I suppose this is not a problem for bundle size as in build colors shall become static values, right? – Felipe de Abreu Prazeres Jan 26 '22 at 15:02
  • 1
    @FelipedeAbreuPrazeres Yes, the creation of the default theme was just for getting access to `augmentColor`. That won't impact bundle size, it just adds some extra one-time work to create the extra theme. – Ryan Cogswell Jan 26 '22 at 17:07
0

Taken from material UI's color palette docs, https://material-ui.com/customization/palette/

A color intention is a mapping of a palette color to a given intention within your application. The theme exposes the following palette colors (accessible under theme.palette.):

primary - used to represent primary interface elements for a user. It's the color displayed most frequently across your app's screens and components.

secondary - used to represent secondary interface elements for a user. It provides more ways to accent and distinguish your product. Having it is optional.

error - used to represent interface elements that the user should be made aware of.

warning - used to represent potentially dangerous actions or important messages.

info - used to present information to the user that is neutral and not necessarily important.

success - used to indicate the successful completion of an action that user triggered. If you want to learn more about color, you can check out the color section.

So you could probably look into reassigning either of the warn/success/info/error buttons. Generally i would keep the error and warn colors both as red, so the warn palette would be free to reassign for me.

lanxion
  • 1,350
  • 1
  • 7
  • 20
  • 2
    I don't think the button has access to all of the pallets. The color prop accepts: | 'default' | 'inherit' | 'primary' | 'secondary' https://material-ui.com/api/button/ – Evanss Jun 17 '20 at 07:36
  • @Evanss How do we get access to the whole palette here? – Douglas Gaskell Apr 14 '22 at 03:01