New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A more flexible approach to styling react-select ? #1679

Closed
Codii opened this Issue Apr 24, 2017 · 25 comments

Comments

Projects
None yet
@Codii
Copy link

Codii commented Apr 24, 2017

Hi !

I like using react-select, but I am less comfortable with the given possibilities to style the component.

We are supposed to include a css stylesheet to make it work. Which leaves us few choices if we want to deeply customize the component :

  • Either overriding stylesheet classes
  • Or setting inline style props on the component

It looks ok for most of the cases, but when we want to customize the component more seriously, things become messy real quick.

What if for instance, we want the component to have a variable width and behave like an autosize input ? (If we set width: auto; to the component, it kind of works but doesn't make the component fit the whole width of the selected value. -- But it might be an other issue on the component styles.

What would be awesome would be to use a BEM approach to define a set of classes respectful to the DOM hierarchy of the component -> Which would be possibly overridden with a "theme" prop.

That would have the good benefit to let users extend styles of the component with css modules, or just using a more clear structure like BEM if he wants to stick with css.

I really like the way react-autowhatever deals with the issue, see : https://github.com/moroshko/react-autosuggest#theme-optional
Possibly a good source of inspiration.

@eduardoinnorway

This comment has been minimized.

Copy link

eduardoinnorway commented Apr 24, 2017

react-select is awesome, the only big backside issue is the styling, it is overstyled and is very unfriendly to theme. I suggest in next version to use CSS Modules.

@ebrentnelson

This comment has been minimized.

Copy link

ebrentnelson commented Jun 7, 2017

styled-components with theming please.

@jole78

This comment has been minimized.

Copy link

jole78 commented Jun 8, 2017

It's already "relative" easy to change the styling by using styled-components as an example.

import React from "react";
import ReactSelect from "react-select";
import styled from 'styled-components';

const MultiSelect = styled(ReactSelect)`
	&.Select--multi  {

		.Select-value {
			display: inline-flex;
			align-items: center;
		}		
	}

	& .Select-placeholder {
		font-size: smaller;
	}
`

export (props) => <MultiSelect multi {...props} />
@lstuartfry

This comment has been minimized.

Copy link
Contributor

lstuartfry commented Jun 9, 2017

@jole78 This works great. Any suggestions regarding conditional style rules based on props? Specifically, I'm trying to render each .Select-value item with a different background color, depending on that item's values. I have access to ReactSelect's 'value' prop (which is just an array of objects), but I can't figure how to uniquely style the backgrounds of each item in that array.

@agirton

This comment has been minimized.

Copy link
Collaborator

agirton commented Jun 9, 2017

@lstuartfry one way is to add a className to a object in your value and then style it that way.

@jole78

This comment has been minimized.

Copy link

jole78 commented Jun 9, 2017

Yes @lstuartfry I would go with the same approach as @agirton suggested. Something like...

const SelectedCategory = styled.small`
    ${props => props.category ...etc... } // do something with it (read in styled-components docs how to...)
`;

const RenderSelectedCategory = (category) => (
    <SelectedCategory category={category}>
        {`${category.category_id} ${capitalize(category.name_display)}`}
    </SelectedCategory>
);

const CategoryPicker = ({ data: { category_choices, loading }, ...props }) => {
    return (
        <SelectMulti
            {...props}
            isLoading={loading}
            optionRenderer={RenderCategory}
            valueRenderer={RenderSelectedCategory} />

That of course shows how to render the selected item but you also have the optionRenderer to hook into if that's your use case.

@lstuartfry

This comment has been minimized.

Copy link
Contributor

lstuartfry commented Jun 9, 2017

@agirton @jole78 Thank you for the suggestions! Following this pattern, I am able to style the inside of the .Select-value, but not the entire .Select-value itself. I apologize for my lack of experience using styled-components, as this is actually the first time I've implemented them.

Using the example above with the valueRenderer, I achieve this result:

screen shot 2017-06-09 at 9 38 46 am

I'd like to achieve the result below, but with each selected value having a unique background color:

screen shot 2017-06-09 at 9 39 32 am

I'm only able to do this by setting a CSS rule for an entire custom ReactSelect, and selecting the first value's 'color' property in the 'values' array:

const MultiSelect = styled(ReactSelect)
     &.Select--multi {
          .Select-value {
               background: ${props => props.value[0].color};
               }
          }

I'm not sure if it's possible to target the parent .Select-value class and give it a unique rules based on each value in the 'values' prop. I'm going to give it a shot and check back in if I come up with a working solution. Thanks for the help!

@agirton

This comment has been minimized.

Copy link
Collaborator

agirton commented Jun 9, 2017

Hi @lstuartfry another option is to use your own valueComponent and you can style it to your hearts content.

@lstuartfry

This comment has been minimized.

Copy link
Contributor

lstuartfry commented Jun 9, 2017

@agirton Thanks! I was not aware we could pass our own custom valueComponent. This will work great.

For anyone looking for a similar solution, there's a good discussion in issue #1263 regarding the same topic.

@JedWatson

This comment has been minimized.

Copy link
Owner

JedWatson commented Feb 2, 2018

I've implemented custom styling comprehensively in v2: https://deploy-preview-2289--react-select.netlify.com/styled

I'm working to finalise the API at the moment so anyone who's interested, please take a look at the alpha and open issues with feedback.

@JedWatson JedWatson closed this Feb 2, 2018

@oliviertassinari

This comment has been minimized.

Copy link

oliviertassinari commented Feb 2, 2018

  • The Component Injection pattern is interesting. I'm assuming the components property is a replacement for the renderXXX properties:
    export const components: SelectComponents = {
    ClearIndicator: ClearIndicator,
    Control: Control,
    DropdownIndicator: DropdownIndicator,
    Group: Group,
    GroupHeading: GroupHeading,
    IndicatorsContainer: IndicatorsContainer,
    Input: Input,
    LoadingIndicator: LoadingIndicator,
    Menu: Menu,
    MenuList: MenuList,
    LoadingMessage: LoadingMessage,
    NoOptionsMessage: NoOptionsMessage,
    MultiValue: MultiValue,
    MultiValueContainer: MultiValueContainer,
    MultiValueLabel: MultiValueLabel,
    MultiValueRemove: MultiValueRemove,
    Option: Option,
    Placeholder: Placeholder,
    SelectContainer: SelectContainer,
    SingleValue: SingleValue,
    ValueContainer: ValueContainer,
    };
  • I see you went with using glam:
    import glam from 'glam';

Any insight on this decision? I'm assuming you are fine with react-select increasing the bundle size of 10 kB gzipped. It was one of my main concern with using a CSS-in-JS runtime on Material-UI side. What's the server-side rendering story?

@JedWatson

This comment has been minimized.

Copy link
Owner

JedWatson commented Feb 2, 2018

@oliviertassinari Thanks for taking a look! I really appreciate review + feedback at this stage.

You're right, the component injection pattern is a replacement for the old render{Component} properties. Thoughts on this:

  • Same concept, more consistent (works for all components, not just a small subset)
  • It really bothered me that v1 had multiple ways of doing the same thing (optionComponent and optionRenderer are basically the same thing point with slightly different implementations) so I wanted to clean it up
  • I haven't seen it done much (although the component prop on react-router's <Route> is effectively prior art)
  • I'm trying to make replacement components as simple to implement as possible, the best way I've found so far is providing innerProps for things Select thinks should be added but it's super flexible.

I explored render props but that's basically what downshift does, and imo it moves way too much implementation detail onto the consumer. What I like about component injection is you can switch out (or wrap) what you need to while leaving the framework to do the heavy lifting and inherit the behaviour you don't actually need to re-implement.


re: Glam, yep, I'm loving it.

Basically it does everything I could want, and is the lightest / fastest implementation for what I need that I've found (so far). However I haven't dug into the SSR story yet and it's a shame that @threepointone isn't planning to do much with it going forward...

I'm not stuck on any implementation specifically, just the pattern. We've isolated the usage of it to the Primitive components, everything else just passes down css objects. So in theory, we could possibly ship a variant without a css-in-js dependency and let the consumer provide their own - for instance, I'm working on Atlaskit at the moment which uses styled-components.

I may also ship with something more mature / maintained if it becomes important. e.g. emotion looks pretty great and supports basically the same API. So I don't feel locked into glam, and it's working for now.

re: size, that's a downside for sure, and something I care about. The reason that the Animated components are in their own entry point is I don't want to ship react-transition-group unless people opt into it.

So I'm exploring ways to mitigate the size issue, but compared to the difficulty of maintaining and extending styles in v1 even if it adds 10kB I think it's a reasonable trade-off. It keeps the API simple and clean and has been so much easier to work with than less/scss - let alone supporting both.

@threepointone

This comment has been minimized.

Copy link

threepointone commented Feb 2, 2018

happy to implement SSR alá glamor, shouldn't be too hard

@jole78

This comment has been minimized.

Copy link

jole78 commented Feb 2, 2018

As a comment to the v2 api in regards to styling I would say that. (these are more general comments)

It's usually pretty hard for a UI lib like this (or react-bootstrap, semantic ui, react-widgets etc) to both provide styling and options to change that styling.
In general people, like me, use these libs because we are too lazy (or less skilled) to implement the styling ourselves.
Generally they also have some logic as to how selects are handled and if you where to abstract just that part and leave the styling (or rendering) up to us, the users, it would more be something along the lines of what downshift is trying to do.
But I would say that styling these libs often comes with using a the provided stylesheets which is fine.
If I want to change the styling...I'll have to change the stylesheet...simple as that.
If I want to use any sort of css-in-js lib...that's up to me as long as the library supports className that's fine.
The library could support theming though...and usually what do people want to change?? colors I would say. So some sort of theming support is often nice. But maybe not theming through changing the css...perhaps more like a theme prop that has color and what not.
I would not enforce people to use glam, styled-components, or any of those things...because people have different tastes. If you where to use glam for this...people will say say I don like object styles...and also the opposite.
So...in closing...keep it as it it is and maybe allow for easier style changing perhaps and perhaps add theming support (maybe its already there)

@oliviertassinari

This comment has been minimized.

Copy link

oliviertassinari commented Feb 2, 2018

@JedWatson Thanks for the details. I'm happy to see you pushing this path forward. Once the API is more stable, I will try upgrading the react-select on Material-UI side to the v2. There is a lot of API decisions to make when trying to implement an extendable component like this one :).

@tomprogers

This comment has been minimized.

Copy link

tomprogers commented Feb 5, 2018

A better solution for flexible theming might be to separate essential layout styles from coloring styles, and then implement the default theme using reasonable selectors. Consumers can then copy the theme sheet and swap out color values.

As a web person, I consider styled-components grotesque. It seems like restyling react-select takes about as long as it would to simply re-implement the subset of features I actually need.

@JedWatson

This comment has been minimized.

Copy link
Owner

JedWatson commented Feb 6, 2018

@tomprogers

As a web person, I consider styled-components grotesque. It seems like restyling react-select takes about as long as it would to simply re-implement the subset of features I actually need.

Are you saying that specifically having looked at the way custom styling is implemented in react-select@v2? or just more generally? or are you talking about v1.x?

To be clear, we are providing extensive classNames in v2 to make style overrides easy for users, regardless of their preferred approach to css (vanilla, scss, less or css-in-js).

@a-x-

This comment has been minimized.

Copy link

a-x- commented Feb 20, 2018

Where I can find styles docs?

can I apply followed styles via styles prop?:

list of selectors
.Select-control
.Select-multi-value-wrapper
.Select--multi .Select-input
.Select.has-value.is-clearable.Select--single > .Select-control .Select-value
.Select-noresults
.Select-input
.Select-input input
.Select-placeholder, .Select--single .Select-value
.Select-placeholder
.Select-menu-outer
.Select.is-focused:not(.is-open) > .Select-control
.Select-menu
.Select-menu div
.Select-arrow-zone, .Select-clear-zone
.Select-aria-only

I want to fix Material-UI wrapper (file on github)

@GarrettGeorge

This comment has been minimized.

Copy link

GarrettGeorge commented Feb 28, 2018

What's a modern approach to using styled-components I've tried implementing the example above but the styles aren't being applied.

StyleCreatable:

import React from 'react';
import { Creatable } from 'react-select';
import styled from 'styled-components';

const StyledCreatable = styled(Creatable)`
	.Select-multi-value-wrapper {
		margin-bottom: 5px;

		&.Select-value {
			border-radius: 15px;
	    overflow: hidden;
	    border: none;
	    font-size: 13pt;

	    &.Select-value-icon {
				padding: 3px 8px 3px 0px;
		    border: none;
		    background: #4A90E2;
		    border-radius: 0;
		    color: white;
	    }

	    &.Select-value-label {
	    	background-color: #4A90E2;
		    color: white;
		    border-radius: 0;
		    padding: 3px 10px;
		    float: left;
	    }
		}
	}
`

export default (props) => <StyledCreatable {...props} />
@dvzrd

This comment has been minimized.

Copy link

dvzrd commented Sep 12, 2018

I'm having the same problem as GarrettGeorge

Seems like the select component's class names have been replaced by auto generated ones in the new version (v2).

The react-select markup renders with class names that look something like this: css-1aya2g8 so that's why targeting class names inside the wrapper doesn't work anymore.

You need to add classNamePrefix prop to your select component and the markup that gets rendered will have usable class names you can then target inside your wrapper.

@gus3inov

This comment has been minimized.

Copy link

gus3inov commented Oct 18, 2018

@dvzrd thanks !!!

@newyorrker

This comment has been minimized.

Copy link

newyorrker commented Nov 25, 2018

Ok, how i can remove the classNames like this: css-1aya2g8?
And second question, how i can disable default styles?

@voyagertravel

This comment has been minimized.

Copy link

voyagertravel commented Nov 26, 2018

Ok, how i can remove the classNames like this: css-1aya2g8?
And second question, how i can disable default styles?

AIRLINE isSearchable={true} isDisabled={false} isLoading={false} isClearable={false} closeMenuOnSelect={true} components={makeAnimated()} formatGroupLabel={formatGroupLabel} styles={Control_Filter_Sort_Select_Style} options={Control_Trip_Flights_Airline_Data} theme={(theme) => ({ ...theme, borderRadius: 3, colors: { ...theme.colors, text: '#666', primary25: '#F5F5F5', primary: '#0071bc', }, })} />
@js1599

This comment has been minimized.

Copy link

js1599 commented Dec 10, 2018

@newyorrker Did you figure out how to remove this?

@koutsenko

This comment has been minimized.

Copy link

koutsenko commented Jan 22, 2019

Same question, can i remove default styles? I inserted TextField as Control component, but still have wrong font size and strange margin & paddings

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment