Show HN: Create your own complete UI color system easy as 1-2-3
github.com
github.com
You can do things as vary hue by a few degrees, vary the saturation or lightness by fixed amounts, use opposite hues on the color wheel, etc. Some useful tricks to have if you are doing color grading as well. There are all sorts of known useful ways to combine colors.
The key insight here is that what is pleasing has some heuristics behind them. It's simple math once you figure that out. For color blind people, color contrast works differently for certain hues.
The disadvantage is that its measures aren't very consistent across hues.
Pure sRGB blue, #0000ff, has an HSL lightness of 50%. So does sRGB yellow, #ffff00. But the blue is objectively a much darker colour than the yellow (try text in each of those colours against a pure black background and see which is easier to read).
Similarly, sRGB magenta (#ff00ff) is significantly more saturated/"colourful" than sRGB cyan (#00ffff), despite each having an HSL saturation of 100%.
And hues are not evenly/perceptually spaced around the colour wheel: the angular difference between chartreuse (HSL hue 90°) and spring green (150°) -- which are distinct, but both obviously shades of green -- is the same as that between rose (330°) and orange (30°), which are very different colours.
None of this is to say don't use HSL. There are other, more perceptually consistent colour models, but they have their own problems: as well as having mathematically more complex transformations from and to sRGB, they tend either to have irregularly shaped colour spaces (so the colour wheel becomes the colour weird-bumpy-thing, which changes shape and size at different lightness levels), or to make compromises that introduce some of the same problems as HSL, or even cut out some sRGB colours entirely.
We just need to be aware of HSL's limitations, and use it with care.
Almost all color vision deficient people fall into the "deutan" and "protan" category, and there are color schemes that are designed to be compatible with them.
It could be good to have a reminder in the README on that topic.
Admittedly a lot of pointers like this are missing in the README, because there's just a lot about Color Theory and generally the science behind color, than what my documentation has space for.
Can you recommend a link to a nice website where users can learn more about things like this? I would like to add that link to the README.
I appreciate your insights.
I am gathering useful links like this, and will make them available in the README for further readings.
https://apps.apple.com/us/app/sim-daltonism/id693112260?mt=1...
The tricky part is that schemes which work for red-green colorblindness sometimes don't translate for yellow-blue.
Using a proper color system for your UI ensures a cohesive, more polished look for your application. This library makes it super easy for you to create your very own color system.
Works both in browser and Node, and whichever UI framework/libraries you're using.
Hope you find this useful.
Not all UIs are web based and written in JavaScript/TypeScript.
import Color from "color";
const base = {
primary: "#336669",
secondary: "#57614E",
neutral: "#5E5F5A",
};
const shade = (c: string, lightness: number) => Color(c).lightness(lightness).hsl().string();
const uiColors = {
primaryButton: shade(base.primary, 40),
primaryButtonText: shade(base.primary, 95),
surface: shade(base.neutral, 98),
text: shade(base.neutral, 10),
};
I guess it's a teeny, tiny bit simpler (or really, just different)? You still need to come up with your own base colors and all the shades of lightness. The library doesn't seem to make the choices simpler, at all.The main intention/purpose of the library is to provide a well-defined structure to guide the developer in crafting their color system. Especially when using TypeScript, this benefit is immediately felt.
Since this is the very first release of the lib, it only includes this core functionality. Based on feedback from users, I intend to build up the library to include more things that users would say are useful to them.
Based on your comment, it looks like one such enhancement could be to add functionality to generate additional base colors based on a single "seed" color (e.g. the main brand color), e.g. a triad of base colors (primary, secondary, tertiary) based on Triadic Palette from the primary's base.
This is where feedback like yours will help make this library more helpful to more developers.
This area is where TypeScript would play nicely. The library implements types that fully utilize generics to define and impose restrictions for color keys, as well as ensuring type consistency across the color system. * This is the next item in my list to add to the documentation. *
So for example, you can easily implement a color system similar to Material Design 3 that only accepts these color keys: 0 | 10 | 20 | 30 ... 90 | 95 | 98 | 100. But as this is not every color system's approach, the library adopts the least restrictive default, so you can say it's less opinionated.
But for pure JS projects, custom color mapping can do the job if your design system prescribes discrete color keys for palette colors.
I’ve been using https://flatuicolors.com/ on early projects to get a good looking color scheme up and running during dev. It’s a lot more motivating when even basic exploratory work looks like something.
(1) helpers to generate base colors and palettes based on various color relationships, e.g. complementary, split-complementary, double-complementary, analogous, triadic
(2) auto-generating a complete set of palettes and their base colors (opinionated), based on a single color value input (the "primary brand color")
(3) opinionated default "role mapping function"
All the above provide various levels of "automation", from fully customized to single color value input, full automation.
Thanks for the feedback. Hope to get more of your valuable inputs.
Although the library is not a color generator per se, it has one built-in palette generator (color mapping function), which is tonal/monochromatic--chosen as default because it's the most common palette type used by popular design systems like Material Design. You can use third-party libraries to aid writing any additional color mapping function you want to implement. (I can also include additional color mappings based on feedback.)
Ultimately, what simpler-color provides is a well-defined structure for your custom color system that works well with various color-generator libraries.
I usually suck at choosing colors and rely a lot on https://coolors.co/
Your library will complete my toolset to create some good looking UIs :)
Your API includes a way to overwrite that functionality, which makes no sense. The last example is basically this:
import foo
foo(a, b, c) // returns c(a, b)
When I could just do this: c(a, b)--- start excerpt ---
The main intention/purpose of the library is to provide a well-defined structure to guide the developer in crafting their color system. Especially when using TypeScript, this benefit is immediately felt.
Since this is the very first release of the lib, it only includes this core functionality. Based on feedback from users, I intend to build up the library to include more things that users would say are useful to them.
Based on your comment, it looks like one such enhancement could be to add functionality to generate additional base colors based on a single "seed" color (e.g. the main brand color), e.g. a triad of base colors (primary, secondary, tertiary) based on Triadic Palette from the primary's base.
This is where feedback like yours will help make this library more helpful to more developers.
--- end excerpt ---
To address your specific example, the pattern you pointed out is not really equivalent to c(a, b). The color mapping function "c" is applied internally when building the individual palettes, not to (a, b).
I will try to make my example in the README a little less contrived, as I can see where the confusion is coming from.
Also when in the future versions this library will bundle various color mapping functions (again, based on user feedback), the structure provided by the library will make more sense because the "c" part there would be any one of the pre-built functions that you can just pass to the `colorScheme()` helper.
Again I appreciate your insights.
The structure of your code is dictated by the existing conventions in your own code base. Those conventions could involve calling out to a 3rd party function, or they could not. Either way, it's not the 3rd party function that provides the convention.