color
color copied to clipboard
lighten and darken should be absolute
Hey,
in LESS and SASS the darken nand lighten function increase by an absolute amount. Example here: https://github.com/less/less.js/blob/fb5280f16f124e5062634a22be2f3c99e650d0a4/lib/less/functions/color.js#L163
This is a bit confusing because in color.js
e.g. `color('#000000').lighten(1)' is still black.
I am not sure though if it would be better to change the function or create a new one like lightenAbsolute
.
Best, Finn
I am not sure less and sass are even doing the same computation (from what I remember, I already saw issue about difference in less and sass). That being said, this names are confusing, we should change that to whiteness, blackness, lightness.
I think the names that exist are pretty solid. color.lightness(<value>)
gives a way to update absolutely and color.lighten(<value>)
gives a way to update relative to the current value.
The names are difficult because they are so similar. But they accurately describe what they do.
css color function allow to use absolute percentage (10%
) or relative ones (+/-10%
).
Right now, it's a multiplicative function. For backwards compatibility, I don't see that changing.
However, I would be open to something like .lightenBy()
and .darkenBy()
which would take offset values (addition). The same thing as what OP proposes as .lightenAbsolute()
, just with a different name. I don't see it as an "absolute" operation as that just mean you're setting the lightness to a certain absolute value.
However, I'd even go so far as to suggest switching .lighten()
and .lightenBy()
since you would normally say Lighten by 10% rather than Lighten by 0.2. But that means a major release instead of a minor release, which means requiring a migration.
In case someone is looking for it, I'm using these functions in my project:
function lightenBy(color, ratio) {
const lightness = color.lightness();
return color.lightness(lightness + (100 - lightness) * ratio);
}
function darkenBy(color, ratio) {
const lightness = color.lightness();
return color.lightness(lightness - lightness * ratio);
}
lightenBy(Color("black"), 0.5);
Thanks! I think it's less of a concern for implemention and more of getting input about the API. I haven't seen a massive push for this in any definitive direction yet. Would love to hear more input.
I was also confused by this implementation.
console.log(Color('#c880b6').lighten(0.5).hex()) => #FAF2F8
console.log(Color('#c880b6').lighten(0.6).hex()) => #FFFFFF
The function of @diegohaz did exactly what I was looking for
console.log(lightenBy(Color('#c880b6'), 0.5).hex()) => #E3C0DA
If someone wants to submit a PR for @diegohaz's implementations and call them lightenAbs()
and darkenAbs()
I would accept/release it posthaste.
For those like me who are wondering why this library's lighten
doesn't behave like the SASS one:
@diegohaz 's answer didn't do it for me. From SASS' documentation, their lighten
function increases the lightness (L) of the color's HSL by a fixed amount, e.g. #414141
lightened by 60% should yield #dadada
, but this library's lighten
yields
> new Color('#414141').lighten(0.6).hex()
'#686868'
instead, which is computed in the following way:
> new Color('#414141').lightness()
25.49019607843137
> new Color('#414141').lightness(25.49019607843137 + (25.49019607843137 * 0.6)).hex()
'#686868'
while SASS computes it this way:
> new Color('#414141').lightness(25.49019607843137 + 60).hex()
'#DADADA'
as shown in online color generators such as http://scg.ar-ch.org/.
Slight changes to diegohaz's helper functions (thanks by the way!):
function lightenBy(color, amount) {
const lightness = color.lightness();
return color.lightness(lightness + amount);
}
function darkenBy(color, amount) {
const lightness = color.lightness();
return color.lightness(lightness - amount);
}
lightenBy(Color("black"), 50);
Just as another data point, as a first-time user the current functionality was a surprise to me, and the helpers in this thread give the behaviour that I would have expected.
I've been confused and frustrated by these functions as well, but I'm not trying to replicate LESS or SASS. I'll explain my use case and experience in case it's useful.
I've been trying to use lighten
to create lighter versions of branding colours configured by the user, for styling css background-colour
to ensure sufficient contrast with foreground text. Obviously, I don't control what branding colours will be supplied by the user, so I don't know whether lightening by 50% will max out to white or still be drowning in the murky depths of near-black. This makes it useless for my purpose.
Furthermore, it turns out that 100% lightening is the greatest possible by design. (You can go higher, but not in a useful way, since values of 101 or greater are treated as simple factors, rather than percentages. Thus 101 means 10100%. So it's possible to lighten by 100% or 10100%, but nothing in between!) Given the 100% limit, that would imply that 100% should give the maximum lightening possible, i.e. full white. But for many input colours this is far from the case. E.g., I lighten
the colour #0A9DD9
by 100%, and I get #C9EDFC
, which is a bit lighter, but still a long way from white. Another way of looking this is, given the multiplicative approach used by the lighten
function, I should legitimately be allowed to lighten by > 100% -- but I cannot. To me, then the function seems clearly broken.
For my purposes, multiplicative (or even absolute additive) approaches are not very useful, since a piece of code isn't going to know how by how much it is appropriate to multiply (or add) unless it knows what the shade of the colour is to begin with. Diegohaz's functions, on the other hand, reliably gives sensible results, and it also has a nice intuitive analogy: lighten by 60% is the same as mixing with 60% white and darken by 60% is the same as mixing with 60% black. This suggests some possible names: whiten
and blacken
.
Whatever the case, lighten
and darken
are broken as far as I'm concerned, and people should be discouraged against using them.
On second thoughts, Diegohaz's functions are not analogous to adding white or black, since they preserve saturation. Adding white to a very dark colour would give something close to grey, e.g. #000005
+ 50% white = #7F7F82
, not #0505FF
, as Diegohaz's lightenBy
would give. To me, the original lighten
would have been better named brighten
(and should have permitted > 100% brightening) analogous to brightening a photograph or brightening a monitor display, and Diegohaz's variant would ideally be named lighten
. I don't know the best naming then, but I still feel Diegohaz's lightenBy
function is the most useful for my purposes.
Also on second thoughts, It looks like darken
does exactly the same as Diegohaz's version, which actually makes sense as a counterpart both to brighten
and to lighten
. It nicely fits both paradigms. So that's not broken.
(Sorry for multiple edits/posts. As well as writing software, I'm an artist trained in photography and studio lighting, and a frequent user of image editing tools, so that may partly explain my pedantry!)
Again, I've said it a plethora of times on this project: The API creep is insanely high in color
already and no good propositions on how to manage color operations across models has been brought forth.
Remember that increasing/decreasing "lightness", "brightness", "whiteness", whatever you want to call it, is confusing because it can mean different things to different people with different goals and different understandings of colors using different color models altogether.
The opposite of "dull" (low saturation) is often "bright" (which could mean higher saturation), but "bright" also means white in some cases, or a lighter tint, etc.
This is a hard naming problem. As-is, the methods provided now are not broken, so please do not claim they are. They simply do not do what you want them to do - just as a toaster not freezing your bread does not make the toaster "broken".
What is the rationale for not allowing a colour to be lightened >100%? If we assume a multiplication model, then we should reasonably be allowed to lighten by any factor up to 25400% (so we can lighten #000001 up to #ffffff). I'm not just saying that I prefer a different colour model; I'm saying that the function doesn't properly support its chosen model. If I've misunderstood something, please clarify. Thanks.
On Fri, 28 May 2021, 06:50 Qix, @.***> wrote:
Again, I've said it a plethora of times on this project: The API creep is insanely high in color already and no good propositions on how to manage color operations across models has been brought forth.
Remember that increasing/decreasing "lightness", "brightness", "whiteness", whatever you want to call it, is confusing because it can mean different things to different people with different goals and different understandings of colors using different color models altogether.
The opposite of "dull" (low saturation) is often "bright" (which could mean higher saturation), but "bright" also means white in some cases, or a lighter tint, etc.
This is a hard naming problem. As-is, the methods provided now are not broken, so please do not claim they are. They simply do not do what you want them to do - just as a toaster not freezing your bread does not make the toaster "broken".
— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/Qix-/color/issues/53#issuecomment-849860888, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABRYCIU7N3KX7QGD37YMH5DTP2H6PANCNFSM4A4EZF5Q .
What is preventing you from passing higher values to lighten or whiten?
I apologise, I'm entirely wrong about that. I did not check carefully
enough, and it's a wrapper library that imposes that buggy 100% limit in my
code, not the color package. I'm sorry for spamming you with nonsense;
nothing is broken. I still prefer Diegohaz's lightenBy
, but you've made
this very simple to achieve (nice work, thank-you!).
On Fri, 28 May 2021, 22:07 Qix, @.***> wrote:
What is preventing you from passing higher values to lighten or whiten?
— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/Qix-/color/issues/53#issuecomment-850308467, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABRYCITMIT6BHB625UGFWALTP5TNRANCNFSM4A4EZF5Q .
While it's true that technically multiplying 0 by a ratio returns 0, that functionality would be needed. I'd go for pragmaticism and not purity.
To solve this in my code, and to lighten black I had to reverse the color, darken it, then reverse it again. Feels pretty retarded to do but it works for me.
Maybe that code snippet helps anyone else having this problem.
function getShades (color: string) {
return {
50: Color(color).negate().darken(0.9).negate().hex(),
100: Color(color).negate().darken(0.8).negate().hex(),
150: Color(color).negate().darken(0.7).negate().hex(),
200: Color(color).negate().darken(0.6).negate().hex(),
250: Color(color).negate().darken(0.5).negate().hex(),
300: Color(color).negate().darken(0.4).negate().hex(),
350: Color(color).negate().darken(0.3).negate().hex(),
400: Color(color).negate().darken(0.2).negate().hex(),
450: Color(color).negate().darken(0.1).negate().hex(),
500: color,
550: Color(color).darken(0.1).hex(),
600: Color(color).darken(0.2).hex(),
650: Color(color).darken(0.3).hex(),
700: Color(color).darken(0.4).hex(),
750: Color(color).darken(0.5).hex(),
800: Color(color).darken(0.6).hex(),
850: Color(color).darken(0.7).hex(),
900: Color(color).darken(0.8).hex(),
950: Color(color).darken(0.9).hex(),
}
}