How to color text so it is visible against any background color?

I have a page (source) where I've got a grid with different colored lines. Is there something I can set the color of the text to so it'll always be visible? I want something like mix-blend-mode difference plus filter invert(100%) from this article. I want the text color to be the inverse of the background, and not to invert the background. I also have programmatic access to the hex version of the background color. I tried color-mixing some, to no avail.
No description
25 Replies
ἔρως
ἔρως8mo ago
add a text shadow i actually tried to do what you want, and found that it is incredibly painful to get anything to half-work i had to resort to calculating the luminosity in php for some reason, calculating it in css wasn't working for me
dys 🐙
dys 🐙8mo ago
I didn't think about calculating it before I insert it in the page. I wrote the following:
const invert = (color: string, base = 16) => {
const digits = (
Array.from({ length: base }).map((_,i) => i.toString(base))
)
const map = Object.fromEntries(
digits.map((digit: string, idx: number) => (
[digit, digits.toReversed()[idx]]
))
)
return (
color
.toLowerCase()
.split('')
.map((digit) => digit in map ? map[digit] : digit)
.join('')
)
}
const invert = (color: string, base = 16) => {
const digits = (
Array.from({ length: base }).map((_,i) => i.toString(base))
)
const map = Object.fromEntries(
digits.map((digit: string, idx: number) => (
[digit, digits.toReversed()[idx]]
))
)
return (
color
.toLowerCase()
.split('')
.map((digit) => digit in map ? map[digit] : digit)
.join('')
)
}
This works for everything except gray (whose inverse is also gray). So, I added a text-stroke as well.
No description
ἔρως
ἔρως8mo ago
gray is a very hard color to decide what to do if i were you, i would use an aproximation of the luminance calculations and change the text to black if the luminance is over 120 (0-255)
Kevin Powell
Kevin Powell8mo ago
We'll eventually have a color-contrast() in CSS... but it's probably a long way away sadly... One of the reasons it's a long way away though, is because it's really hard to do programmatically. There is a new-ish method/algorithm called APCA, which seems to match human perception a lot better than any previous models. There are a few third party tools that use it. The only one I can remember off the top of my head is https://colorjs.io. It can use a few of the different methods for calculating contrast, incluing APCA, and it's very easy to use. https://colorjs.io/docs/contrast - all the different ones work the same, but if you scroll down it has a section specifically for APCA Could be a good video topic actually 😁
Clever Tagline
Clever Tagline8mo ago
I'd be more inclined to hard-code it for each line, setting each line's text color to either black or white depending on which is more readable on a given background color.
ἔρως
ἔρως7mo ago
that's actually a good idea i though long and hard about this, and inverting the color is ... really terrible to read
Squeemeister
Squeemeister7mo ago
If you use hsl values for the background colors, you could (using javascript) get the lightness value and for anything below 70 assign a white font color, anything above 70 gets black.
ἔρως
ἔρως7mo ago
that's not a good way, because of the fact that we're humans
Kevin Powell
Kevin Powell7mo ago
yeah, hsl can't be used like that.
No description
Kevin Powell
Kevin Powell7mo ago
hsl is convinient for playing with the hue, but the perception of the lightness changes greatly as you go through the different hues. That's one of the selling points for lch. If you go through the hues there, it follows our perception of the lightness much closer, so you can do that type of thing... the problem is, as you go through the hues, the c value (chroma), has different max values for each one, and you'll run into colors that "don't exist", which means a potentially large shift as it goes to the closest match. Color is hard.
Squeemeister
Squeemeister7mo ago
Thanks for the explanation. Other than that particular yellow are there that many more "outlying" colors that make this solution so impractical? I mean, I considered yellow might be tough when I proposed the idea, but "checking for that particular yellow or red" is something easy enough to program, right?
Clever Tagline
Clever Tagline7mo ago
Agreed. Color—especially when it comes to readability—should be designed/controlled, not left to a calculation.
Kevin Powell
Kevin Powell7mo ago
To fail AA accessibility contrast (which is the bare minimum)? Probably ~30% of colors.
Kevin Powell
Kevin Powell7mo ago
Kevin Powell
Kevin Powell7mo ago
And as soon as you play with the saturation, this changes too...
Squeemeister
Squeemeister7mo ago
So, if the scope of the project doesn't have to meet accessibility guidelines or the dev. has any form of control over the colors used, this could be a feasible solution, if not perfect. The original poster didn't list any constraints, just a specific scenario. Also, the solution I proposed was simplified for example purposes. (i.e. Black and white being the only text color options and 70% lightness being the only cutoff.)
ἔρως
ἔρως7mo ago
what i did in the past was to use the formule from here: https://stackoverflow.com/a/596241
Stack Overflow
Formula to determine perceived brightness of RGB color
I'm looking for some kind of formula or algorithm to determine the brightness of a color given the RGB values. I know it can't be as simple as adding the RGB values together and having higher sums...
ἔρως
ἔρως7mo ago
i used the one with the bitshift it was enough to get in the ballpark, for a simple black/white switching
Kevin Powell
Kevin Powell7mo ago
The original poster didn't list any constraints, just a specific scenario.
I get that, but AA isn't just an accessibility guideline, it's "if it's lower than this, people will have trouble reading it", which is why we can use it as a baseline. Their scenario is they wanted legible text, which is what AA helps us know if we are hitting. I also don't want to sound like I think your idea is terrible. The idea is in the right ballpark, and I've used things like that in the past too. The problem is, hsl is a great color model to be able to estimate the color you'll get just by looking at the code. It's a terrible color model for trying to automate anything though, and that's keeping it simple with white/black text.
Squeemeister
Squeemeister7mo ago
Firstly, it's not my intention or place to come into "your house" and directly disagree with you but the prevailing attitude of "it can't be done" or "don't bother trying" I've seen lately is disheartening to say the least. Secondly, I apologize for the length of this post. So far we've got: My imperfect, 60 sec. solution with a 70% success rate (easily boosted to 80/85%) vs. People saying it can't be done (i.e. Nothing) Not every solution has to be 100% perfect; especially when the scope of the project is undefined. All I can do is reiterate that after about 60 seconds of thinking, I was able to outline a solution that by your own admission would have only a 30% failure rate, or to put it another way... 70% success. And the very first reaction to my solution was "that's not a good way, because of the fact that we're humans" In fact, my solution works precisely because we're humans. We decided to measure things that we observe using numbers that correlate to social mores. (0-100, for example, and of all the available options for background colors, hsl is really the only one that allows me to use that human measurement quite simply without having to pass a value to a third-party algorithm for analysis.) I think we should all try opening more doors instead of closing them.
dys 🐙
dys 🐙7mo ago
You oughtn't overlook the fact @Kevin linked Color.js which makes better solutions also take about two minutes to implement. 30% is a pretty high failure rate. That's more than one in four times that you'll get undesirable results.
ἔρως
ἔρως7mo ago
And the very first reaction to my solution was "that's not a good way, because of the fact that we're humans"
that was me. i said that because the hsl color model is linear, and that's not how eyes work. if a color has more blue or green, it will look brighter, even if the same brightness value is set that's why it won't work
dys 🐙
dys 🐙7mo ago
No description
ἔρως
ἔρως7mo ago
don't forget that yellow is a mix of green and red and look how wide the light blue area is
Kevin Powell
Kevin Powell7mo ago
I really didn't mean to come off as dismissive, and sorry about it if I did. As I mentioned, I've played around with doing exactly that in the past, but it's also why I cautioned against it, because I've seen how badly it can fail. If it was something that we could bring from a 30% failure rate to a 1% failure rate with a bit more work, that's something I'd be more inclined to get behind. The problem is, afaik, there isn't really a way to improve from there, it just is what it is. I've also seen this, and other, similar ideas floated around in the past with a lot of head nodding on how that can work well, and I've seen it implemented in production where it's caused all sorts of issues, which made me more inclined to push back a bit stronger that I might have on other things. As for coming into my house and disagreeing with me, personally I think it's a good thing! I want to be challenged on my ideas, and have conversations like these. I think they're important, and if people come in and assume I know everything because I'm some sort of authority figure, I dont think that's healthy at all. I'd rather have people willing to push back at my ideas and solutions, or fight for their own. I think it leads to good discussions and let's us all dive deeper into topics.
Want results from more Discord servers?
Add your server