React-select: Colors are very difficult to theme

Created on 25 Jul 2019  路  4Comments  路  Source: JedWatson/react-select

I've been working with integrating ReactSelect with our own theming library and I've come across a few issues.

  1. Conflicts in theme namespacing - If you use Emotion components to override ReactSelect components you can get conflicts in the theme object that we override. For example, in my global ThemeProvider I have a colors key with a different format that means that I end up having a weird hybrid of the colors object passed down and this causes issues. It would be better for react select to pass down theme={{ 'react-select': theme }} instead of theme={theme} as this allows prevents collisions.
  2. The named colors are too generic; primary primary75 primary50 and primary25 - I can't control what colors are used in what situations so it would make more sense to use named values like menuBorder activeBackground activeText etc so we know what we are theming and what it affects.

I know this is a pretty opinionated and technically I can work around this by replacing all Components with custom ones but this feels like it defeats the purpose of a "theme" option.
To me, it makes more sense to name the theme colors as for what they are actually used for rather than having to figure out that primary25 is used only for when an element is "focused".
Another issue is that due to us using neutral0 for both the selected text color and the control background I can't theme these individually, not to mention there is no text color available for when a value is focussed so using darker colors there won't work - for example when implementing a Dark Theme.

issuenhancement issureviewed

Most helpful comment

Working with the theme is just terrible.

Maybe it'll be helpful to somebody.

const colors = {
 /*
  * multiValue(remove)/color:hover
  */
  danger: 'var(--danger)',

 /*
  * multiValue(remove)/backgroundColor(focused)
  * multiValue(remove)/backgroundColor:hover
  */
  dangerLight: 'var(--danger-light)',

 /*
  * control/backgroundColor
  * menu/backgroundColor
  * option/color(selected)
  */
  neutral0: 'var(--neutral-0)',

 /*
   * control/backgroundColor(disabled)
  */
  neutral5: 'var(--neutral-5)',

 /*
  * control/borderColor(disabled)
  * multiValue/backgroundColor
  * indicators(separator)/backgroundColor(disabled)
  */
  neutral10: 'var(--neutral-10)',

 /*
  * control/borderColor
  * option/color(disabled)
  * indicators/color
  * indicators(separator)/backgroundColor
  * indicators(loading)/color
  */
  neutral20: 'var(--neutral-20)',

 /*
  * control/borderColor(focused)
  * control/borderColor:hover
  */
  neutral30: 'var(--neutral-30)',

 /*
  * menu(notice)/color
  * singleValue/color(disabled)
  * indicators/color:hover
  */
  neutral40: 'var(--neutral-40)',

 /*
  * placeholder/color
  */
  neutral50: 'var(--neutral-50)',

 /*
  * indicators/color(focused)
  * indicators(loading)/color(focused)
  */
  neutral60: 'var(--neutral-60)',

  neutral70: 'var(--neutral-70)',

 /*
  * input/color
  * multiValue(label)/color
   * singleValue/color
  * indicators/color(focused)
  * indicators/color:hover(focused)
  */
  neutral80: 'var(--neutral-80)',

  neutral90: 'var(--neutral-90)',

 /*
  * control/boxShadow(focused)
  * control/borderColor(focused)
  * control/borderColor:hover(focused)
  * option/backgroundColor(selected)
  * option/backgroundColor:active(selected)
  */
  primary: 'var(--primary)',

 /*
  * option/backgroundColor(focused)
  */
  primary25: 'var(--primary-25)',

 /*
  * option/backgroundColor:active
  */
  primary50: 'var(--primary-50)',

  primary75: 'var(--primary-75)',
};

All 4 comments

Ran into this problem today. Took over an hour to tease out what generic named colour was used for what aspect of the select box (in retrospect I should have just looked at the source, it's relatively easy to see the usages there). Using semantic names would definitely be way better.

Working with the theme is just terrible.

Maybe it'll be helpful to somebody.

const colors = {
 /*
  * multiValue(remove)/color:hover
  */
  danger: 'var(--danger)',

 /*
  * multiValue(remove)/backgroundColor(focused)
  * multiValue(remove)/backgroundColor:hover
  */
  dangerLight: 'var(--danger-light)',

 /*
  * control/backgroundColor
  * menu/backgroundColor
  * option/color(selected)
  */
  neutral0: 'var(--neutral-0)',

 /*
   * control/backgroundColor(disabled)
  */
  neutral5: 'var(--neutral-5)',

 /*
  * control/borderColor(disabled)
  * multiValue/backgroundColor
  * indicators(separator)/backgroundColor(disabled)
  */
  neutral10: 'var(--neutral-10)',

 /*
  * control/borderColor
  * option/color(disabled)
  * indicators/color
  * indicators(separator)/backgroundColor
  * indicators(loading)/color
  */
  neutral20: 'var(--neutral-20)',

 /*
  * control/borderColor(focused)
  * control/borderColor:hover
  */
  neutral30: 'var(--neutral-30)',

 /*
  * menu(notice)/color
  * singleValue/color(disabled)
  * indicators/color:hover
  */
  neutral40: 'var(--neutral-40)',

 /*
  * placeholder/color
  */
  neutral50: 'var(--neutral-50)',

 /*
  * indicators/color(focused)
  * indicators(loading)/color(focused)
  */
  neutral60: 'var(--neutral-60)',

  neutral70: 'var(--neutral-70)',

 /*
  * input/color
  * multiValue(label)/color
   * singleValue/color
  * indicators/color(focused)
  * indicators/color:hover(focused)
  */
  neutral80: 'var(--neutral-80)',

  neutral90: 'var(--neutral-90)',

 /*
  * control/boxShadow(focused)
  * control/borderColor(focused)
  * control/borderColor:hover(focused)
  * option/backgroundColor(selected)
  * option/backgroundColor:active(selected)
  */
  primary: 'var(--primary)',

 /*
  * option/backgroundColor(focused)
  */
  primary25: 'var(--primary-25)',

 /*
  * option/backgroundColor:active
  */
  primary50: 'var(--primary-50)',

  primary75: 'var(--primary-75)',
};

@JedWatson is there any way for users to use their own, existing Emotion theme object?

I figured when we resorted to custom sub-components like a custom <Option />, we'd be able to use our theme object.


Edit: We ended up wrapping the parent of our react-select component with withTheme() and then overriding the built in theme by passing that to the custom components. Pretty clunky, but workable. It would be a lot better if React-Select didn't override the user theme by default.

@hovoodd thank you so much for posting that, it was so helpful! I'm quite meh at frontend, so I had to jam a lot of colors (ie. pink, purple) while debugging to see what would happen. I don't think I would have been able to work if you hadn't posted that!!!

My use case : I needed to make react-select work with light/dark mode in Material UI's theme system, so I'm posting my code I used to make that work for someone who has to do something similar.

import useTheme from '@material-ui/core/styles/useTheme';

const getSelectTheme = (theme) => {
  return ({
    /*
    * multiValue(remove)/color:hover
    */
    danger: 'purple',

    /*
     * multiValue(remove)/backgroundColor(focused)
     * multiValue(remove)/backgroundColor:hover
     */
    dangerLight: theme.palette.grey[200],

    /*
     * control/backgroundColor
     * menu/backgroundColor
     * option/color(selected)
     */
    neutral0: theme.palette.background.default,

    /*
      * control/backgroundColor(disabled)
     */
    neutral5: "orange",

    /*
     * control/borderColor(disabled)
     * multiValue/backgroundColor
     * indicators(separator)/backgroundColor(disabled)
     */
    neutral10: 'pink',

    /*
     * control/borderColor
     * option/color(disabled)
     * indicators/color
     * indicators(separator)/backgroundColor
     * indicators(loading)/color
     */
    neutral20: theme.palette.grey['A200'],

    /*
     * control/borderColor(focused)
     * control/borderColor:hover
     */
    // this should be the white, that's normally selected
    neutral30: theme.palette.text.primary,

    /*
     * menu(notice)/color
     * singleValue/color(disabled)
     * indicators/color:hover
     */
    neutral40: 'green',

    /*
     * placeholder/color
     */
    // seen in placeholder text
    neutral50: theme.palette.grey['A200'],

    /*
     * indicators/color(focused)
     * indicators(loading)/color(focused)
     */
    neutral60: 'purple',
    neutral70: 'purple',

    /*
     * input/color
     * multiValue(label)/color
      * singleValue/color
     * indicators/color(focused)
     * indicators/color:hover(focused)
     */
    neutral80: theme.palette.text.primary,

    // no idea
    neutral90: "pink",

    /*
     * control/boxShadow(focused)
     * control/borderColor(focused)
     * control/borderColor:hover(focused)
     * option/backgroundColor(selected)
     * option/backgroundColor:active(selected)
     */
    primary: theme.palette.text.primary,

    /*
     * option/backgroundColor(focused)
     */
    primary25: theme.palette.background.dark,

    /*
     * option/backgroundColor:active
     */
    primary50: theme.palette.background.paper,
    primary75: theme.palette.background.paper,
  })}

export const useActivityLogDataForm = ({
  const theme = useTheme()
  const formThemeColors = getSelectTheme(theme)

  return (<Select options={options}
                theme={theme => ({
                  ...theme,
                  colors: {
                    ...formThemeColors
                  }})}
        />)

2020-08-09 13 10 38
2020-08-09 13 10 55

(A demo of working react-selects should be on the forms w/light and dark themes on https://app.betterself.io/demo later tonight)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

mbonaci picture mbonaci  路  3Comments

ericj17 picture ericj17  路  3Comments

AchinthaReemal picture AchinthaReemal  路  3Comments

coder-guy22296 picture coder-guy22296  路  3Comments

mjuopperi picture mjuopperi  路  3Comments