Closed EricRovell closed 3 years ago
Hi @EricRovell! Sorry for the delay: was busy at work. I like the idea!
I just checked the Wikipedia article and feels like this functionality can be a part of the existing mix
plugin and use Colord's color mixing magic.
Example:
// plugins/mix.ts
ColordClass.prototype.tones = function (count = 5) {
const tints = []
for (let ratio = 0; ratio <= 1; ratio += 1 / count) tints.push(this.mix('#808080', ratio));
return tints;
};
No problem!
Right now I have implemented it for myself using saturation and lightness values from HSL model. I have read about your mix
plugin and it says it has better results. I guess I will dive in and try extend your plugin then!
Hi @omgovich & @EricRovell,
I really like colord
and wanted some experience creating NPM packages, so I decided to work on similar package called ColorMaster.
I implemented the above functionality and thought I would share my findings/results here. The code is open source, so you can check it to confirm.
Since tints
and shades
, are just lightness manipulations (adding white/black), we can use the HSLA color-space for this as it has a lightness channel. Likewise, for tones
, we can use the saturation channel of the HSLA color-space:
...
case "monochromatic": {
// tones uses saturation, tints/shades use lightness
const valueToAdjust = effect === "tones" ? s : l; // effect is a function argument - 'tints' | 'shades' | 'tones'
// form array of n (amount) evenly spaced items from current saturation/lightness to min/max value
let delta = (effect === "tints" ? 100 - valueToAdjust : valueToAdjust) / amount; // amount is a function argument
delta = effect === "tints" ? delta : -1 * delta;
const valArr: number[] = [valueToAdjust];
for (let i = 0; i < amount; i++) {
valArr.push(valArr[i] + delta);
}
return effect === "tones"
? valArr.map((sat) => new HSLColors(h, sat, l, a))
: valArr.map((light) => new HSLColors(h, s, light, a));
}
...
This returns the original color, along with amount
extra items representing the harmony.
You can see that the results match expectations as these tests pass:
I hope this can provide some insight and is helpful to you in some way.
Cheers, Lior
Hi, @lbragile! Same situation here :) Working on similar package for self-education. For my package I used the lightness values to generate tints and shades and I was thinking about one edge-case as I see it.
For example, in case of such a color that is too close to pure black/white and user wants too many variations, these colors would be too similar and it is not practical. I have decided to put a minimal step as 1% of lightness. This way it would generate less variations that are more distinguishable.
Do you think it is a good idea?
@EricRovell That is a great consideration.
Let's consider tints
, assuming the user has a color with lightness of 95% and they decide to get an array of 10 - extra - tints (amount
parameter has a clamped upper value of 10).
In this case the step size will be 0.5% (difference between neighboring tints). As this is a user decision, we would want to provide them with 10 different tints, regardless of how close they are in value, right?
If we suddenly make the minimal step 1%, the user will now only get 5 extra tints, rather than the 10 they asked for. Namely, they will get only 95%, 96%, 97%, 98%, 99%, 100%, ..., 100%
where (96%, 97%, 98%, 99%, 100%
) are "new" tints.
The same logic applied to shades
and tones
.
What I am hinting at is that the user made the decision to get 10 extra tints, so that decision should be respected. Otherwise, we are limiting their choices in a way that they cannot control (for such extreme cases).
This is what the approach I outlined in https://github.com/omgovich/colord/issues/65#issuecomment-890753941 aims to achieve.
Do you agree?
@lbragile I think it is quite important to respect the user's decision. I just got some sort of inner conflict, so to speak, about this edge-case with almost pure color and was thinking that generated palette would be to similar or even consist of same colors.
What do you think about limiting the number of tints/shades/tones the user asks? It would we definitely a bad idea to allow user to ask for 1000 tints.
In my opinion, we should return the number of colors the developer asked for — another behavior would be unpredictable and will cause errors in projects using the library.
I doubt someone will request 1000 tints, but if they did, we should return 1000 colors even if some of them are equal.
Alright, I got the idea. Before I begin to work on implementation, I would like to specify one detail. Should I use mix
plugin to implement this, or should I use lightness
and saturation
increments to handle palette creation?
I think mix
is the better option here, since it uses LAB color space which will produce a better perceptual difference between tones. Developers might find a lot of different libraries that produce tints and tones using HSL values, so it would be cool for Colord to offer another better alternative.
Alright, I will extend the functionality of mix
plugin then.
I think it would be really useful to have an ability to generate monochromatic colors (tints, shades, and tones). You mentioned usefulness of it in #63 and it was on my list of future proposals.
I have already working implementation, it looks like this:
During the prototyping, I have got some questions regarding the API:
color.monochromatic("tones", 5)
or to be a separate methods for each type? I think it would be more readable for this particular case to have different methods.steps
parameter, it gets not so understandable. For example, user wants 3 shades and gets: original color, shade, black. Don't look good.I would like to hear you ideas and suggestions.