Layout API for all UI components
As of now, adding an outer spacing to a component is done with utility classes like this:
<div className="mb-3">
<Component />
</div>
This makes source code and DOM needlessly complex. It would be nice if we could do something like this:
<Component mb={3} />
This kind of “layout API” would be present in all UI components.
ℹ️ We don't want to make it possible to modify inner component styles so only outer CSS properties would be part in such API: margin (all directions), maybe flex child properties (align-self, justify-self).
For the same reason, we don't want to allow passing any class name to the topmost element of the component via a special prop like customClassName. It's very flexible but it encourages some people to hack the component's styles.
I think I understand the reasons behind this, but <Component mb={3} /> is in my view not good.
Two letter propNames are just too cryptic and also with the increasing number of utility classes the API of the component would grow. Adding a new utility class would essentially expand the API of every single component. I think we should not pass classes as individual props.
For the same reason, we don't want to allow passing any class name to the topmost element of the component via a special prop like customClassName. It's very flexible but it encourages some people to hack the component's styles.
We could whitelist them in propTypes.
Two letter propNames are just too cryptic …
It's a common pattern in the world of utility classes. You don't want to write class="margin-right-3", just class="mr-3" will do. But I know the situation is different when it comes to props naming (taken from MUI):

with the increasing number of utility classes the API of the component would grow.
↓
… so only outer CSS properties would be part in such API
The list it very short and I don't expect it to grow (much):
margin-topmargin-rightmargin-leftmargin-bottomalign-selfjustify-self
margin values: [0...7, auto]
align-self values: [start, end, center, baseline] (maybe also flex-start and flex-end)
justify-self values: [start, end, center] (more may be added later)
This is what we often need to set when laying out components. I'm searching for a system that is 1. restricted (only allow what is acceptable from design point of view) and 2. applicable for any UI component.
We are discussing these options now:
- White-listed class names
- Add
classNameprop to all components and whitelist utility class names corresponding to the list above. I don't like that becauseclassNamessays I could add any class name which is confusing. - The same as 1, but named as
utilityClassName. Now I know I can only use utility class names here, but again, I would not expect it to be restricted to only some of them.
- Add
- Props
- Props like
mr={3}, see Material UI. The downside is that it would make the components API grow (by approx. 6 props). Maybe it could be screened out somehow not to mess up actual component API? They also don't have it in MUI components API.
- Props like
@adamkudrna
Thank you for the analysis, Regarding the suggested solution:
1i) I very much dislike this one, for the same reasons you do.
1ii) This is what I would like.
2i) I dislike it, but in my view its better then 1i). If we choose this, which I hope we don't, I would prefer the long version, that is paddingTop instead of pt.
I would prefer the long version, that is paddingTop instead of pt.
Is it OK with actual CSS class names? Because if we decide to go with 1ii and with existing utility class names, it would look like this:
Button.propTypes = {
utilityClassNames: PropTypes.arrayOf(PropTypes.oneOf([
'mt-0',
'mt-1',
'mt-2',
// ... 4 directions × 9 margin values (0–7 + `auto`) in total
'ml-7',
'align-self-start',
'align-self-end',
'align-self-center',
'align-self-baseline',
])),
}
(This would be certainly shared through a single definition across all components.)
@adamkudrna I would prefer the full names of the utility classes in any case, but the short names used as shown in your example would be acceptable.
We use long expressive names everywhere, so I do not see why it should be different here. I have little experience with CSS though so pending approval by @bedrich-schindler I think you should decide.
Like this?
Button.propTypes = {
utilityClassNames: PropTypes.arrayOf(PropTypes.oneOf([
'margin-top-0',
'margin-top-1',
'margin-top-2',
// ... 4 directions × 9 margin values (0–7 + `auto`) in total
'margin-left-7',
'align-self-start',
'align-self-end',
'align-self-center',
'align-self-baseline',
])),
}
I still slightly prefer the shorter versions (I'm the CSS guy) but it will be no deal with IDE's autocomplete anyway.
What does @bedrich-schindler think?
@adamkudrna As we use shorter utility classnames across whole UI library, I would also prefer to use shorted versions.
@mbohal might not know that we already have such utility class names. This attribute should be only tool to pass those existining utility classnames into the component, not something new.
As we use shorter utility classnames across whole UI library
I thought we will either use longer or shorter names everywhere. Mixing short and long names is wrong. I think this discussion is about which version to choose.
might not know that we already have such utility class names
With the introduction of the new tool to pass them into the components, the current code will be changed anyway. And we can change them into shorter or longer versions depending what we decide here.
For me the central issue is like I said above:
We use long expressive names everywhere, so I do not see why it should be different here.
But like I said, I can live with the short versions. I just don't see what advantages they bring and one clear disadvantage is that they are less descriptive. I strive to write code optimized for easy reading, not easy writing - and therefore to me longer seems better in this case.
@adamkudrna make a decision like you see fit.
To restore the conversation, I'm suggesting to use utilityClassNames that would be white-listed which means that It would not interfere with standard className prop. I also suggest to use shorter variants of those classes (like mr-2, ml-3, pr-3) as it is standardized naming in front-end world, it is nothing new, everybody knows it. If is it needed, we can provide both standard and shortened claasNames (e.g. mr-5 and margin-right-5) but this opens doors to issue that every programmer could write it different within single project. I would be more strict on this.
Yesterday we came across a problem with the word utility in the prop name as we only speak of helper classes in the docs (which, for the user, covers both helper and utility classes as understood by front-end coder). Also I'd slightly prefer something a little bit shorter for a prop name that's meant to be used quite frequently. My suggestions:
helperClasses(generic and corresponds to docs but doesn't say eg.pt-3ortext-primaryare not allowed)layoutClasses(clearer and communicates that classes are limited to those related to layout)marginClasses(very straightforward but would be necessarily limited to margin classes which we probably don't want)
We also discussed other options like HOC functions or even a new Box component (see MUI), however we need to be able to add margins etc. directly to actual components, without bloating DOM with more divs (that's a must eg. for form fields in FormLayout and maybe more layout related cases). A Box component and similar options can be an extension further down the road but it doesn't solve everything we need here.
- I think we should use
*ClassNameas it is more consistent withclassNameused by react. - How about
placementClassName?
I do not care whether it ends with *ClassName or *Classes, but I slightly agree with @adamkudrna that shorter is better. I would suggest helperClasses as @adamkudrna mentioned above or helperClassName if @mbohal requires to have this prop ending with *ClassName. I would like to end this discussion about naming so I can implement it.
We've decided to use boxClassName