-
Notifications
You must be signed in to change notification settings - Fork 187
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Contrasting text color? #540
Comments
Two ideas:
fill: "imdb_rating",
colorFilter: "grayscale(1) brightness(72%) contrast(999) invert(95%)" these numbers are hard to get right, though:
Other remarks:
|
fill: "imdb_rating", fillContrast: ["white", "black", "lime"] and replace the (computed, scaled) fill by the color from the contrast array that maximizes the contrast or color difference, something like function mostContrasted(f, colors) {
const l = lab(f).l;
return greatest(colors, c => Math.abs(l - lab(c).l));
}
export function applyChannelStyles(selection, {title: L, fill: F, fillOpacity: FO, stroke: S, strokeOpacity: SO, strokeWidth: SW}, mark) {
if (mark && mark.fillContrast) {
applyAttr(selection, "fill", F && (i => mostContrasted(F[i], mark.fillContrast)));
} else {
applyAttr(selection, "fill", F && (i => F[i]));
}
...
This can also be done as a plugin that would be triggered by some kind of markup (maybe the illegal mixBlendMode: "contrast"…? or a class name…). See https://observablehq.com/d/fa614ef721cdfc99 for a quick'n'dirty implementation. |
Here's a way to do it with #801 Plot.text(…, {
initialize: (facets, {fill}, {color}) => {
if (fill && fill.scale === "color" && color) {
return {facets, channels: {fill: {
label: fill.label,
value: Array.from(fill.value, d => d3.hsl(color(d)).l > 0.7 ? "black" : "white")
}}};
}
return {facets};
},
fill: "imdb_rating",
...
}) (the actual choice of lightness measurement and threshold is still tbd) |
any follow-up on this? |
current best approach is this plugin it could maybe become a thing? |
Often it’s desirable for a text label to have a strong contrast against its background. If the background color is constant (e.g., white in the empty space of the chart), then it’s fine to hard-code the text color. But if the background color is variable, as when labeling cells, then it’s more difficult. For example, the extreme values (e.g., 4.5 and 9.2) are hard to read here:
It’s difficult to do this well in Plot currently.
One approach is to define the fill color as a channel. However, since the fill channel is bound to the color scale, you cannot express the color literally; you must specify an abstract value that is passed through the color scale. Hence, this is generally not an option. This problem was discussed in #56, and identity scales were added in #305, however, this isn’t really a good fix for this problem because we don’t want to disable the color scale entirely; we just want to disable it for this specific mark channel.
Another approach is to repeat the text mark definition twice, once for light text and once for dark text, and use the filter option to control which data gets which color. This produces the desired result, but it’s tedious.
This can be alleviated with a helper mark:
But even so, it requires manually specifying thresholds in data space to determine which text labels should be inverted, which is tedious and prone to error. It’d be better to take advantage of the color scale definition, but this isn’t available at the time the mark channels are being constructed (since there’s a potential circularity there).
It’s sort of possible to do this in CSS using mixBlendMode:
The result isn’t perfect (the text inherits a color rather than being either white or black), and the technique isn’t especially memorable but it is concise.
One thought is that maybe text marks could have a “background” color channel, which if specified, will automatically invert the fill color to maximize contrast.
The text was updated successfully, but these errors were encountered: