Skip to content
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

[css-color-6] Let's finally settle contrast-color() syntax #7937

Open
LeaVerou opened this issue Oct 21, 2022 · 14 comments
Open

[css-color-6] Let's finally settle contrast-color() syntax #7937

LeaVerou opened this issue Oct 21, 2022 · 14 comments

Comments

@LeaVerou
Copy link
Member

@fantasai and I did a fair bit of work on css-color-6 these past couple of days, but until the WG settles on syntax, we cannot have something shippable for contrast-color(), not even for the restricted version we agreed to pull into css-color-5.

For background, the previous, confusing syntax was like:

contrast-color(wheat vs bisque, var(--accent-color), olive, sienna, color(display-p3 0 1 0), maroon to wcag(4.5))

The current syntax is (keywords ridiculous on purpose, as we resolved to have them undefined):

contrast-color(wheat tbd-bg wcag(4.5), bisque, var(--accent-color), olive, sienna, color(display-p3 0 1 0), maroon)

We should a) resolve on three, largely (but not entirely) orthogonal things:

  1. Do we want to keep this syntax or change it?
  2. What should the keywords be?
  3. Do we allow authors to omit the keyword to specify a default (background)?

Problems with current syntax and alternative syntaxes

If <target-contrast> is just an algorithm, the current syntax is a bit hard to parse for humans as there are a lot of sequential keywords:

contrast-color(wheat tbd-bg wcag2, bisque, var(--accent-color), olive, sienna, color(display-p3 0 1 0), maroon)

OTOH how often are colors specified in keywords? Usually authors use hex or functional notations, so maybe this is not a valid concern. Regardless, another syntaxe that @fantasai and I explored was:

contrast-color(wheat tbd-bg wcag2 / bisque var(--accent-color) olive sienna color(display-p3 0 1 0) maroon)

Foreground/background keyword options

One thing to keep in mind for this is that the base color being the background will be far more common.

Foreground Background Pros Cons
foreground background Descriptive and unambiguous Too long
fg bg Concise Abbreviations
text base Concise without being abbreviated foreground is not always text, base is too vague
under over Concise, reads naturally "x over y, z" has entirely opposite meaning to "over x, y, z" so order needs to matter and we cannot use commas because "over x, y, z" looks like a single list

Should we have a default (background)?

In the majority of cases, the base color will be a background, rather than a foreground. Which brings us to the question: should the syntax allow for neither keyword to be specified?

The advantage of having a default is that the syntax becomes concise regardless of which keyword pair we go with, in which case we can just go for a long descriptive keyword for foreground (like foreground) and be done with it. Having sensible defaults is a tenet of good usability.
The problem with having a default is that authors do not learn to think about the distinction, so in practice they may not specify it even when they have a foreground.

@Loirooriol
Copy link
Contributor

I like that contrast-color(wheat tbd-bg wcag2 / bisque var(--accent-color) olive sienna color(display-p3 0 1 0) maroon) has a clear separation between the background color and the possible foreground colors.

But / as a separator inside a function seems a bit weird, why not a comma as usual?

contrast-color(wheat tbd-bg wcag2, bisque var(--accent-color) olive sienna color(display-p3 0 1 0) maroon)

@tabatkins
Copy link
Member

contrast-color(wheat tbd-bg wcag2 / bisque var(--accent-color) olive sienna color(display-p3 0 1 0) maroon)

Losing the commas means the syntax becomes completely inextensible in the future; see 'counter-reset' for an example of this being bad. I'd strongly advise against it.

Foreground/background keyword options

I don't recall if we considered top/bottom. They're clear, fairly short, already used, and don't have the ordering concern that over/under do.

Should we have a default (background)?

I think this should be based solely on, when using whatever algo we think is most reasonable (and default, if possible), if the fg/bg decision makes a significant difference in the output. If they're usually pretty close and never terrible when done wrong-way, then sure, make it optional, no need to require something that usually won't matter. But if those aren't both true, then no, we should keep it required.

@LeaVerou
Copy link
Member Author

Losing the commas means the syntax becomes completely inextensible in the future; see 'counter-reset' for an example of this being bad. I'd strongly advise against it.

FWIW there are no algorithms currently that require metadata per color, though it does give me a bit of pause if we don't have the option for the future.

Foreground/background keyword options

I don't recall if we considered top/bottom. They're clear, fairly short, already used, and don't have the ordering concern that over/under do.

I think that's a bit confusing as it implies order in the Y axis, not the Z axis. But feel free to add them to the summary table, the more options we have the better.

Should we have a default (background)?

I think this should be based solely on, when using whatever algo we think is most reasonable (and default, if possible), if the fg/bg decision makes a significant difference in the output. If they're usually pretty close and never terrible when done wrong-way, then sure, make it optional, no need to require something that usually won't matter. But if those aren't both true, then no, we should keep it required.

Right now the only algorithm that even distinguishes them is APCA. @Myndex can tell us (briefly please?) how significant the difference is and whether flipping them produces a terrible result or not.

@svgeesus
Copy link
Contributor

Right now the only algorithm that even distinguishes them is APCA

Distinguishing them is the correct approach.

image

-41 and 38 are somewhat different, yes :) and even ignoring the light-on dark and dark-on-light polarity indicator, there is certainly a difference. Not terrible, no.

But tossing away the "which one is text" indicator would be a bad design decision.

@LeaVerou
Copy link
Member Author

LeaVerou commented Oct 22, 2022

But tossing away the "which one is text" indicator would be a bad design decision.

We are not talking about tossing it away, just making it optional.

@svgeesus
Copy link
Contributor

just making it optional.

That would be a bad idea. Why?

@LeaVerou
Copy link
Member Author

just making it optional.

That would be a bad idea. Why?

To optimize the common case: background. That way the length of both keywords stops being a concern, as they are only needed in the rare case.

@nt1m
Copy link
Member

nt1m commented Oct 23, 2022

Personally I'm not fond of including specific algorithm names in the syntax. This should just be abstracted behind background vs. text or something similar, and the UA (or the spec) should pick the ideal algorithm for each case.

@LeaVerou
Copy link
Member Author

Personally I'm not fond of including specific algorithm names in the syntax. This should just be abstracted behind background vs. text or something similar, and the UA (or the spec) should pick the ideal algorithm for each case.

Yeah, that would be ideal, if there was a single optimal algorithm we could use. Sadly, that is not the case. However, that's what we're doing for level 5, but only for returning white and black (no candidates). There is a lot of background here, you can start from #7553 and dig in.

@ByteEater-pl
Copy link

fore or front
and back
(without "ground"; these are actual words, so the rule against abbreviations would be obeyed)
maybe?

@fantasai
Copy link
Collaborator

What about incorporating foreground vs background into the function name?

contrast-background(...)
contrast-foreground(...)

@css-meeting-bot
Copy link
Member

The CSS Working Group just discussed [css-color-6] Let's finally settle contrast-color() syntax.

The full IRC log of that discussion <TabAtkins> lea: The primary problem we couldn't settle on was what to call foreground/background. We reoslved to distinguish them in the syntax but couldn't decide on names
<TabAtkins> lea: foreground/background too long, fg/bg too cryptic, others suggested front/back, fore/back
<TabAtkins> lea: There's a table of the options
<TabAtkins> lea: And should we have a default if the keyword isn't specified? Probably with bg as the base color, which'll be the most common case? Or should we require the keyword?
<TabAtkins> fantasai: I think I'm strongly in favor of it being mandatory, so people will pay attention
<TabAtkins> lea: advantage of making it options is that only the fg word actually matters so we can just pick a good name there. can also be longer since it's not typed as much
<TabAtkins> argyle: That's my proposal - make it optional and make the fg keyword short and easy.
<argyle> https://observablehq.com/@argyleink/contrast-color
<TabAtkins> argyle: This is the interactive explianer, syntax is right under the demo
<argyle> https://www.irccloud.com/pastebin/mO4ENSCC/
<lea> s/and make the fg keyword short and easy./and have the whole foreground keyword, since it's the exception/
<TabAtkins> [gonna have argyleink present]
<TabAtkins> Rossen_: If you need to present visually, our next zoom meeting will be next month
<TabAtkins> [more presentation chatter]
<TabAtkins> argyle: Bunch of examples in this observable document later
<TabAtkins> argyle: The current spec isn't far off from excellent, i think, so my important bits in myc ounter proposal:
<TabAtkins> argyle: First, doesn't require a color list
<TabAtkins> argyle: Lot of presenting to devs and using it myself, i often dont' care about the final color
<TabAtkins> argyle: It'll find the first color that passes for you, in the same hue, in whatever contrast algo you're using
<TabAtkins> argyle: Further my proposal builds in the contrast MQ pref
<TabAtkins> argyle: So if the user prefers more contrast and the page is rendered without a color list, or an explicit contrast pref in the function, the browser will take the user's preference by default.
<TabAtkins> argyle: This potentially eliminates several MQs for authors to have to worry about
<TabAtkins> argyle: If an author *does* write a specific target score - "Weber 2" or whatever - they're actually potentially excluding users.
<TabAtkins> argyle: So if someone does want to handhold the contrast today, they have to write several MQs to set things correctly in several circumstances. My proposal folds it all in by default.
<TabAtkins> argyle: Color list is still important, if you *do* want it, but you don't need it a lot of the time.
<TabAtkins> argyle: As said earlier, it defaults to "background". You can click it to foreground and see it change the syntax to include the keyword.
<TabAtkins> argyle: In most caess I've done I'm starting from a background, it's just the most common.
<TabAtkins> argyle: Also have a "max" contrast target
<astearns> "color: contrast-color(#eee / max);"
<TabAtkins> argyle: If I just pass a starting color it'll get the max-contrast color (white or black)
<lea> q?
<TabAtkins> argyle: There's also options matching the contrast MQ - "more" or "less"
<TabAtkins> argyle: Without having to specify exactly a contrast algo.
<TabAtkins> argyle: The contrast algo is auto by default, i know that's controversial
<argyle> https://codepen.io/argyleink/pen/RwgzJXV
<TabAtkins> argyle: Here's a demo
<TabAtkins> argyle: I'm writing a codepen with light/dark and low/medium/high contrast
<TabAtkins> argyle: You should be immediately overwhelmed
<TabAtkins> argyle: Just a lot to write
<argyle> https://codepen.io/argyleink/pen/eYKmMmN
<TabAtkins> argyle: This demo is what I've seen people instead already do
<TabAtkins> argyle: Hand-managing oklch deltas in each MQ. Slightly automated, better than WCAG 2 but not perfect. But it works today and gets light/dark and low/medium/high contrasts
<TabAtkins> argyle: My proposal today would make both of those demos a one-liner.
<TabAtkins> argyle: Looks at user pref and auto-discovers what colors they should get
<argyle> contrast-color(Canvas)
<TabAtkins> argyle: This one-liner gives you light/dark and low/med/high contrast
<lea> q+
<TabAtkins> argyle: Current proposal requires authors to write a longer function, and repeat it several times.
<TabAtkins> argyle: so "auto" as the algo complements the author's desire for not having to make a choice. browser can just do what's bet.
<TabAtkins> argyle: If there's no auto target there's no way to give up choice
<TabAtkins> argyle: So to summarize, my proposal doesn't require a color list, defaults to background, offers less/more/max contrast keywords, has an auto contrast keyword
<TabAtkins> argyle: It starts simple, *can* do everything currently in the spec, but doesn't require all the complexity immediately.
<Rossen_> ack lea
<TabAtkins> lea: We all like sensible defaults, but there were several issues with default algorithm that were discussed ad nauseum
<TabAtkins> lea: WCAG 2.0, the current default, is just bad. There are better algos, but there are patent/licensing issues
<TabAtkins> lea: In our experiments in Color.js the other contrast algos listed in the proposal don't actually give good results either
<TabAtkins> lea: So I don't think they're needed
<TabAtkins> lea: I like the idea of integrating this with the preferred contrast MQ
<TabAtkins> lea: Unclear what it does exactly - same keywords, or does the MQ value actually affect the function?
<TabAtkins> lea: minor comment - i find the identifiers side-by-side kinda hard to read, like "abca max"
<TabAtkins> lea: Nothing making it clear that they're tied together - current syntax makes the algos functions so you're sure the args are realted
<TabAtkins> lea: Also not sure what "more"/"less" correspond to mechanically
<TabAtkins> lea: I do like the idea of having background be the default
<TabAtkins> lea: Not sure we need "backgroudn" keyword at all then
<TabAtkins> argyle: yeah, wcag2 is bad, apca3 is still not producing good results yet, still a few years out
<TabAtkins> argyle: If we wait for optimal it'll never ship
<TabAtkins> argyle: I think auto sets it up to grow and for users, if they have a pref, they can specify it
<TabAtkins> argyle: I think most people won't even look past wcag2 being bad
<lea> q+ to say several members were skeptical about having an auto algorithm that could change. Worries we'll be stuck with it forever because authors will depend on the results not changing
<TabAtkins> argyle: so it's hard for us
<TabAtkins> argyle: Say apca comes out and is stable, firefox flips to taht, it woudl b eneat to see it upgrade over time
<TabAtkins> argyle: I just don't like baking in a bad choice
<TabAtkins> argyle: I also agree nobody really knows about the other algos, yeah
<TabAtkins> argyle: So MQ syntax, what is more/less? I had to invent this in my demo - what is more/less in each algo?
<TabAtkins> argyle: I have a table in my proposal mapping them to particular values in each algo.
<lea> Btw our experiments with these other algos (for black/white only): https://colorjs.io/apps/blackwhite/
<TabAtkins> argyle: we'll have to figure this out anyway, for the MQ
<TabAtkins> +1 to the simple keywords that correspond to *some* reasonable values for the algos, I really like that
<argyle> contrast-color(#eee / max apca)
<TabAtkins> argyle: You commented that this looks a little weird
<TabAtkins> argyle: I think this reads really nice personally
<TabAtkins> argyle: I want max contrast from apca
<lea> q+ just remembered, there's also the issue of allowing multiple algos (e.g. a good one + wcag for legal requirements), this syntax cannot grow to accommodate that since params and algos are on the same level
<TabAtkins> argyle: If you do change the algo to something else, you're saying "here's my bg color, here's the target score"
<TabAtkins> argyle: --and hopefully you alway suse auto, anything else can exclude users with particular prefs--
<Rossen_> q?
<lea> I also like "more" or "less"
<TabAtkins> lea: I wrote in IRC what i was gonna say
<TabAtkins> lea: In prev discussions several WG members wer eskeptical about an auto algorithm that can change
<TabAtkins> lea: We'll freeze in practice anyway
<TabAtkins> lea: The other issue is that we might want to allow multiple algos that all need to pass - specify a "good" algo but also a "legally required" algo. Current syntax doesn't accommodate this, since the algo specifiers are on the same level as the algo name itself
<argyle> contrast-color(#eee / 60 lstar)
<TabAtkins> lea: I do like the more/less keywords fwiw
<lea> q?
<TabAtkins> argyle: You're right, I can't do multiple algos right now - "60 lstar, and 4 wcag"
<Rossen_> ack lea
<Zakim> lea, you wanted to say several members were skeptical about having an auto algorithm that could change. Worries we'll be stuck with it forever because authors will depend on the
<Zakim> ... results not changing
<lea> contrast-color(#eee / 60 lstar 5.4 wcag2) makes no sense
<TabAtkins> fantasai: I think this exploration was v helpful
<TabAtkins> fantasai: I do think there are problems with the proposal
<lea> +1 to this exploration being v helpful!
<TabAtkins> fantasai: Really do need to keep the target amount a functional argument
<TabAtkins> fantasai: Both good for reading and for parsing
<TabAtkins> fantasai: functional notation lets us extend the new algos with whatever we need without ambiguity
<argyle> functional notation is an easy change
<TabAtkins> fantasai: i have concerns about auto picking colors to minimum acceptable contrast
<lea> (note that functional notation can also have an argument-less syntax, where we omit the ())
<TabAtkins> fantasai: "pick the right contrast" is usually not the minimum acceptable
<TabAtkins> (we can also have some simple args to the "default" algo not require mentioning the algo)
<TabAtkins> [whoops, missed]
<TabAtkins> fantasai: if we do color ranges, should probably have ability to do ranges of any color, not just the bg hue
<TabAtkins> fantasai: maybe default it to the bg hue
<TabAtkins> fantasai: BAck to syntax, you're having a color, then a slash, then a list of colors
<TabAtkins> fantasai: some disagreement in earlier proposals about where the algo goes, i find your syntax choice interesting and think it reads nicely
<TabAtkins> fantasai: wrt fg/bg i'm pretty strongly against allowing one of them to be defaulted. i do think that needs to be an explicit choice
<fantasai> https://github.com//issues/7937#issuecomment-1431751840
<TabAtkins> fantasai: but I think we can do that without being too wordy if we put it into the function name - contrast-foreground() and contrast-background()
<TabAtkins> fantasai: And I think we need to tackle these problems individually
<TabAtkins> fantasai: auto algo? No, we already resolved that.
<lea> unclear if contrast-foreground(red) is declaring that red is the foreground or asking for a foreground color where red is the background
<TabAtkins> fantasai: function algos? etc. we can address those one by one.
<argyle> thx y'all, sorry that def wasnt 5m
<TabAtkins> Yeah I think that's confusing too
<lea> argyle: nothing syntax related ever is 5m :)
<TabAtkins> (re: Lea's comment)
<TabAtkins> Rossen_: We'll continue discussing in issue.

@astearns astearns removed the Agenda+ label Feb 15, 2023
@LeaVerou
Copy link
Member Author

What about incorporating foreground vs background into the function name?

contrast-background(...)
contrast-foreground(...)

As I replied in IRC, I think it's unclear whether contrast-background(red) means "I have a red background, give me a suitable foreground color" or "Get me a suitable background color, I have a red foreground".

@Myndex
Copy link
Member

Myndex commented Feb 18, 2023

Hi Adam @argyleink

I would like to respond to a couple things that you mentioned in the meeting, that I see listed in the IRC log.

argyle: Say apca comes out and is stable,

The current base algorithm for apca-w3 has been in public beta since Feb 2021, and that base algorithm has not changed since then, over two years. Thus far 0.0.98g4g has shown to be best for a simple pair-of-colors solution for sRGB self-illuminated displays.

argyle: apca3 is still not producing good results yet, still a few years out

YIKES!!

OK you have my attention.

There have been no issues filed nor discussions in any of the repos reporting this.

I would really like to dig down and understand what problems you are seeing.

First I'd like to establish that you're basing this on the current version of the algorithm, the base algorithm is identified as 0.0.98g4g, and while there are a lot of implementations out in the wild due to the popularity, many of them are unfortunately flawed, using incorrect values or implementations.

The only canonical tool is: https://apcacontrast.com

The only canonical repos are:

GitHub: https://github.com/Myndex/apca-w3

npm: npm i apca-w3

Other tools:

There are a few third-party tools that are pretty cool, but should not be used for evaluation I have a number of caveats, as they are generally not complete implementations.

Also, there is another Myndex hosted tool that is only for experimental work, at myndex dot com/SAPC/ but it should not be used for general evaluation as it is used for live experiments and results may vary.

It has been brought to my attention that Chrome and Edge are still using incorrect values and incorrect code, and therefore are not APCA and should not be relied upon for any purpose, nor used for any form of evaluation.

And I have been trying to chase down some of the developers of some implementations that are using the wrong code in association with the wrong look up tables.

All of these kinds of problems I hope you recognize are simply a factor of having an open and public beta period.


That Said

Nevertheless, if you are finding problems of any sort with the current version, I really need to hear about it. Please.

I would like to talk to you about what results you found were "not good" or that are problematic, and the circumstances and characteristics of what you perceive as not usable.

If you'd like to contact me privately, my DM's are open on Twitter at https://twitter.com/MyndexResearch

You can also reach me via the email which is listed here on my profile.

Thank you for reading, I look forward to discussing this with you soon.

Andy

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

10 participants