Documentation will be updated after API is finalized. Please be mindful of this!
Here it is, the final piece of the color makeover trilogy. If you recall, I did a makeover of both color-string
and color-convert
that have been generally well-received. This was the final piece.
After an increasing number of people expressing some stress over the 'quirkiness' of the API recently, I decided to finally get to it. It's been about an 8 or 9 hour ordeal, which is why I was really good at procrastinating :dancing_men:.
I do already have some things in mind for a 2.x makeover, but that's far into the future and is contingent on the evolution of color-convert
specifically.
Without further adieu, here are all of the changes. Keep in mind this is a complete re-write with a pretty good portion of the API being compatible with the 0.x releases.
The Fun Parts (new stuff, fixes, etc.)
- (Closes #56) Completely immutable API.
- (Fixes #42) All color models from
color-convert
are supported (using .<model-name>()
). Try it yourself!
Color(0xFF0000).rgb().hsl().lab().hwb().xyz().cmyk().ansi256().rgb().toString()
Yes, this does mean that portion of the API has changed. Please see the migrations section below.
- Main
Color
class now has swizzle-able model constructors (e.g. Color.cmyk(10, 20, 30, 40)
, Color.rgb([15, 67, 200])
, etc.).
- No longer performs conversion for every model on every single operation - smaller footprint and faster computation times. Not entirely sure why we were doing this to begin with, but whatever.
- (Fixes #79) Constructor now supports non-string hexadecimal RGB instantiation
- (Fixes #87)
.toString()
now returns a color string.
- Added HCG-specific getters/setters.
- (Fixes #42) Added XYZ/Lab getters/setters.
- (Fixes #83, #57, #93) Added a
.round()
function that takes a number of places (default is 0) to round the internal values to. The modified .string()
and .percentString()
methods also take this parameter, and those methods default to 1. See notes below.
- (Fixes #82) Several API clarifications. See Migrations section below.
As well, bumping the dependencies fixes #74.
The Not-So-Fun Parts (migration)
- Everything is immutable. There isn't a single fix for this if you (ab)used this library earlier on - you'll most likely be tweaking several touch points. It's for the greater good, I promise. If you already treated the library as immutable then you're probably good to go. Good tests are key here.
- All
.model()
functions (to convert to an object) must now be .model().object()
. See notes below for justification.
- All
.modelArray()
functions must now be .model().array()
. See notes below for justification.
- When using the
.object()
method, alpha values are now .alpha
instead of .a
so as to not conflict with Lab.
- If you used the internal properties of the object instead of getters, you'll need to.. not do that anymore :) Use the respective model calls (
.rgb()
, .hsl()
, etc.) paired with .array()
- this is because we don't store every single model in the object at once, but simply the last worked-with model.
.whiteness()
/ .blackness()
-> .white()
/ .wblack()
(note the w
prefix).
I want to explain this one because it doesn't feel 'great' but I felt like it needed to happen. We have had a couple of issues (#73 being the most definitive) that address problems with the assumed functionality of the methods.
I chose to remove the -ness
from the names because I didn't want them being interpreted as a characteristic of the color, but instead a more concrete, 'absolute' (if you will) property of a specific color model (akin to .cyan()
, .red()
, .hue()
, etc.).
The w
prefix was chosen for both objective and subjective reasons: it objectively conflicts with CMYK's .black()
, and HWB subjectively isn't used as much as CMYK is (please let me know if this is an abhorrently wrong assumption). Further, we have another example of the API doing this that nobody has complained about since I took over - .saturationl()
and .saturationv()
for the HSV and HSL models.
Also, since this seems like a good place to mention it - in lieu of something like .lightnessab()
for the Lab model, I just stuck with .l()
. In the future, I hope to have something a little more consistent, but for now this works.
- Constructors with specialized keys (such as
.red
, .green
and .blue
for RGB instead of .r
, .g.
and .b
) were removed. If this is a huge problem, please let me know. For clarity, this means you can no longer instantiate with Color({red: 10, green: 100, blue: 150})
but instead must use Color({r: 10, g: 100, b: 150})
.
- The model getter methods (e.g.
.rgb()
, .hsl()
, .keyword()
) no longer act as setters. Since everything is immutable now, such functionality is useless and only creates more overhead. Please use the Color()
constructor directly, or use the new 'static' methods (functions) such as Color.rgb()
directly.
- (Closes #67, #46)
.clone()
was removed since all objects are immutable. Just pass objects directly. If you really need a new copy of an object, use Color(otherColorObject)
.
rgbaString()
was removed. If you really need a string that forces the alpha value, please let me know and I can add something into the API. Otherwise, use .rgbString()
and if the alpha is anything but 100% then it will include it.
- The specialized
.xxxString()
methods should move to .xxx().string()
calls. In the case you need the RGB percent string, you may still use .percentString()
- it will automatically convert to the RGB model. For instance, if you used .hslString()
, you would now call .hsl().string()
.
.clearer()
-> .fade()
, because "clearer" is not at all what I thought it was personally and the API is already kind of confusing (I thought clearer meant more saturated).
.toJSON()
now returns the current model's representation. If you absolutely need RGB, tack on .rgb()
. This shouldn't affect many people, if any.
- Values no longer normalize (round) when getting objects or arrays. Call
.round()
first. If you are seeing crazy long decimals, this is why. I wanted to be sensitive to the scientific community that is using this library to perform more mathematically sensitive operations instead of approximate stuff for web users. Most people won't have a problem with this since most people use the CSS strings.
- (Fixes #24) Initializing a
Color
object with a hue-based model (hsl, hwb, etc.) performs a rotation on the value instead of clamping it to 0-360. This is in-line with the .hue()
getter/setter and I believe it to be the correct functionality to begin with.
.greyscale()
-> .grayscale()
- if you think we should have both, please let me know :)
- (Closes #86)
.saturation()
-> .saturationl()
(to match .saturationv()
)
Some Notes
So why the extra three characters for things like .hslString()
-> .hsl().string()
? Two reasons - one, you can now snap colors to another model if necessary. For example, if I wanted to snap a color to the nearest ANSI 256 color, I can do Color(123, 44, 220).ansi256().rgb().object()
; two, the dependencies (e.g. color-string
) have pretty good parity with the model names used in color-convert
, and color-convert
has evolved a bit since the 0.x releases of color
to a point where we can intelligently include most models, and can future-proof new models just by updating color-convert
in color
's package.json.
Further, a lot of these changes are due to the internal object no longer holding every single conversion possible. color-convert
now supports quite a lot of conversion routes and maintaining all of them here was impractical, messy and tedious.
As mentioned earlier, another win is that you're going to save quite a bit of processing time, so applications that are doing things like color manipulation on images and whatnot should benefit greatly by switching to 1.x.
Next Steps
After this PR is approved by the community and merged, it will be published as 1.0.0.
After that, there will be a few efforts moving forward:
- Tests (of course) - I noticed comprehensive tests are lacking, and a test makeover would be beneficial in its own right.
- More transformations, and more clarity as to what those transformations are.
- Color scheme support (#44)
- Color space support (maybe)
I think the first thing that should be tackled now that we will have a slightly cleaner code base is to figure out some of the hairy naming that is going on here. I would love to hear some ideas about this (feel free to drop as many tickets as you'd like) - I have some of my own, but I don't always have the best ideas!
Feedback!
I need feedback on this as I am not a primary user of this library (admittedly I use color-convert
more than color
proper) so I want to make sure the users of this library are satisfied before I merge it in. There are a lot of opinionated changes in here that I have included based on all of the tickets and pull requests I've received.
Another thing I'd like to point out - this library is edge-case hell. Making decisions that benefitted all users, use cases, and color models, elegantly and without a ton of overhead, was incredibly difficult. I've attempted this rewrite probably 4 or 5 times now, ultimately scrapping it and trying again a month or two later.
If you have any strong feelings, or even not-so-strong feelings, please let me know. I'd love to know what could be done better.
Obligatory CC list:
// @MoOx @sindresorhus @kevinsuttle @mattbasta @xml @huan086 @ooflorent @mindjuice @wmira @lapanoid @ZuBB @iamstarkov @stevemao @ericclemmons @igl @dliv
Thanks everybody!
enhancement