Skip to content
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

[styles] Fix getLuminance for hsl #14391

Merged
merged 4 commits into from
Apr 4, 2019
Merged

Conversation

strayiker
Copy link
Contributor

Closes #14387

@eps1lon
Copy link
Member

eps1lon commented Feb 3, 2019

@oliviertassinari
Copy link
Member

Does the bundle size increase worth it? Do we have an estimation of the accuracy we win?

@strayiker strayiker force-pushed the get-luminance branch 2 times, most recently from 364f065 to 608f339 Compare February 3, 2019 12:31
@mbrookes
Copy link
Member

mbrookes commented Feb 3, 2019

Do we have an estimation of the accuracy we win?

@strayiker gave an example in the related issue: #14387 (comment)

It can be quite significant by the looks of it, although more relevant for our purposes would be the difference in contrast ratio between a pair of colors represented as either RGB or HSL.

If we are going to make the colorManipulator API public in v4, there is another possible path forward (although @strayiker isn't going to like it 😅):

  • Drop support for HSL in the colorManipulator functions
  • Export this new function
  • Require conversion of HSL to RGB using this function before passing color values to Material-UI component

@eps1lon
Copy link
Member

eps1lon commented Feb 3, 2019

  • Drop support for HSL in the colorManipulator functions
  • Export this new function
  • Require conversion of HSL to RGB using this function before passing color values to Material-UI component

That seems reasonable to me. It helps with static analysis and therefore tree shaking. I don't think that it is very common to use dynamic theme colors. Themes are mostly static IMO and therefore I'd rather make use of inversion of control here. Would be nice if someone could share some use cases where color values for the theme switch dynamically between hsl and rgb.

In a perfect world we would have dynamic and static solutions and users could choose: Dynamic for experimenting, static for production.

@oliviertassinari
Copy link
Member

oliviertassinari commented Feb 5, 2019

  • Drop support for HSL in the colorManipulator functions
  • Export this new function
  • Require conversion of HSL to RGB using this function before passing color values to Material-UI component

@mbrookes I love this plan!
@strayiker What do you think about it?

I would like to double down on @eps1lon point. I wish we could allow our users to provide CSS variables as theme values, unfortunately, we perform some color manipulations with them. Maybe we can imagine a mode where if somebody provides a CSS variable, the color manipulation functions are identity functions?

@oliviertassinari oliviertassinari changed the title Fix getLuminance for hsl [styles] Fix getLuminance for hsl Feb 5, 2019
@oliviertassinari oliviertassinari added the package: styles Specific to @mui/styles. Legacy package, @material-ui/styled-engine is taking over in v5. label Feb 5, 2019
@strayiker
Copy link
Contributor Author

@mbrookes,
@oliviertassinari
What about hex format? Should it be converted to rgb manually or it will be converted internally?
I think it is better to perform convertion automatically inside the decomposeColor and drop the hsl support in the other places. I don't see any reason to put this responsibility to a developer (user of your api).
If you provide a function for converting color formats and require it to be used every time the color format does not meet the requirements, as a developer I can ask you "why can't you convert it for me"?

When the colorManupulator becomes a public, it should be format agnostic in order to be transparent in use, my opinion.

I wish we could allow our users to provide CSS variables as theme values, unfortunately, we perform some color manipulations with them. Maybe we can imagine a mode where if somebody provides a CSS variable, the color manipulation functions are identity functions?

Doesn't it lead to incorrectly looking components? If you perform some color manipulations (e.g. fade()) then using css variable will break this behaviour and, accordingly, the appearance of the components. Some states (hover, focus) can be missed.

@eps1lon
Copy link
Member

eps1lon commented Feb 6, 2019

I don't see any reason to put this responsibility to a developer [...]

  • reduced bundle size via tree shaking for developers
  • less maintenance burden for us (how do we decide what formats we support? How "good" should that support be? etc) Your sentiment already illustrates this: Why is full hex support ok but hsl not? Don't you think that the dev that uses hsl should have the same expectations of the API that the dev that uses hex has?

@strayiker
Copy link
Contributor Author

If this difference in size is significant and worth of losing usability, then I can't argue.

@oliviertassinari oliviertassinari changed the base branch from master to next March 30, 2019 00:30
@mui-pr-bot
Copy link

mui-pr-bot commented Mar 30, 2019

@material-ui/core: parsed: +0.17% , gzip: +0.27%
@material-ui/lab: parsed: +0.37% , gzip: +0.55%

Details of bundle changes.

Comparing: 2dedcc1...d9e99bb

bundle parsed diff gzip diff prev parsed current parsed prev gzip current gzip
@material-ui/core +0.17% 🔺 +0.27% 🔺 350,243 350,825 90,024 90,263
@material-ui/core/Paper +0.76% 🔺 +1.16% 🔺 67,867 68,380 19,823 20,053
@material-ui/core/Paper.esm +0.91% 🔺 +1.23% 🔺 60,198 60,746 18,568 18,796
@material-ui/core/Popper 0.00% 0.00% 30,463 30,463 10,527 10,527
@material-ui/core/styles/createMuiTheme +2.95% 🔺 +4.08% 🔺 17,384 17,897 5,729 5,963
@material-ui/core/useMediaQuery 0.00% 0.00% 2,469 2,469 1,041 1,041
@material-ui/lab +0.37% 🔺 +0.55% 🔺 148,279 148,827 43,580 43,818
@material-ui/styles 0.00% 0.00% 53,099 53,099 15,433 15,433
@material-ui/system 0.00% 0.00% 17,136 17,136 4,525 4,525
Button +0.62% 🔺 +0.93% 🔺 87,947 88,495 26,064 26,307
Modal +0.67% 🔺 +0.96% 🔺 82,055 82,603 24,563 24,799
colorManipulator +15.87% 🔺 +18.24% 🔺 3,232 3,745 1,299 1,536
docs.landing 0.00% 0.00% 49,958 49,958 10,837 10,837
docs.main +0.08% 🔺 +0.11% 🔺 645,407 645,929 200,517 200,733
packages/material-ui/build/umd/material-ui.production.min.js +0.17% 🔺 +0.29% 🔺 298,542 299,060 82,758 82,999

Generated by 🚫 dangerJS against d9e99bb

@oliviertassinari oliviertassinari self-assigned this Mar 30, 2019
if (process.env.NODE_ENV !== 'production') {
if (!color) {
throw new Error(
`Material-UI: hexToRgb(color) is called with an invalid argument. (${color})`,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fast feedback for wrong usages

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This adds different behavior in production. dev and prod shouldn't behave differently.

Either throw in dev too or use warning

Copy link
Member

@oliviertassinari oliviertassinari Mar 31, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be fine. It will throw in development and production. The only difference is the error message. It will be explicit in dev, obscure in prod.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Never change control flow between environments. It is not equivalent.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And even if: Why change the pattern? We always used warning. Why switch to throw here?

Copy link
Member

@oliviertassinari oliviertassinari Mar 31, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that we should answer this question first: What should happen in production when the params are invalid? We have 3 options:

  1. We implement a defensive logic to concern the method in a no-op.
  2. We do nothing, the logic breaks with an obscure error message.
  3. We handle the exception, provide a nice message, with minification or not. React minification logic was implemented by an intern, it's probably overkilling at our scale.

Once we have answered this question, we can consider the development env. I would propose respectively:

  1. We raise a warning
  2. / 3 We change the control flow, throw a nice warning

This problem remembers me what happen with a few of our components when none of the required props are provided. It fails with an obscure error message and a warning is print in the console:

Invariant Violation: React.cloneElement(...): The argument must be a React element, but you passed undefined.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For new react devs I agree. But a fundamental part of react debugging is the console. We can't solve any issues devs have here because react is dictating how it should work. The error overlay you cited even includes the solution to the issue:

Open your browser’s developer console to further inspect this error.

Once we have answered this question, we can consider the development env

You always start with a premise which I don't agree with and react does not agree with and I think most other people won't agree with: Thrown errors are different in prod and dev. Why should a program behave differently in production or dev (and please don't move the goalpost to warnings they're bad enough)?

This is my 2nd attempt at focusing your attention on this point. If you insist on not discussing this I'm not willing to have a discussion about the next point either.

Copy link
Member

@oliviertassinari oliviertassinari Mar 31, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why should a program behave differently in production or dev

I'm gonna state it a 2nd time. The behavior is identical: it throws. Only the error message changes, like it changes with React invariant.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The react comparison is invalid. They don't use their invariants for simple types.

The behavior is identical:

Throwing is not always equivalent. This only applies for this scenario but is generally not true for e.g. side-effects.

it throws

Also not true:

hexToRgb(1);
hexToRgb({ substr: () => {}})

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I'm changing the approach.

}
if (!color) {
warning(false, `Material-UI: missing color argument in hexToRgb(${color}).`);
return color;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At this point it's not returning a string (unless it's an empty string) which is guaranteed by the documentation. I don't understand the use case for swallowing a TypeError here?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm removing all the warnings. The discussion doesn't worth it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If people want meaningful errors, they can use TypeScript.

@oliviertassinari oliviertassinari merged commit 2578cb4 into mui:next Apr 4, 2019
@oliviertassinari
Copy link
Member

@strayiker Thank you.

eps1lon pushed a commit that referenced this pull request Jun 5, 2019
In #14391 the Function `convertHexToRGB` was renamed to `hexToRgb`.

Unfortunately, the typings were not updated to reflect the above change :(

Also, an additional Function was exported, `hslToRgb`,

This PR updates the `hexToRgb` typings and adds an export for `hslToRgb`.
@zettca
Copy link
Contributor

zettca commented Dec 23, 2019

Shouldn't this change be documented in the migration guide? It renames convertHexToRGB to hexToRgb (breaking the API), but isn't documented.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
breaking change new feature New feature or request package: styles Specific to @mui/styles. Legacy package, @material-ui/styled-engine is taking over in v5.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants