Re: [csswg-drafts] [css-contain-3] Inconsistent handling of known and unknown features jeopardizes backward compatibility (#7551)

As expressed in the minutes today, I completely misunderstood what "Option 1" was, and take back my support of it from the earlier meeting. ^_^

What I *assumed* it was saying, is:

1. If a query is capable of matching against a given container (for example, `container-type: inline-size` versus a `(width)` query, on an English element), then it resolves to true or false.
2. If the query is *not* capable of matching (for example, `container-type: inline-size` versus a `(height)` query, on an English element), then it resolves to unknown. 
3. If the query itself is unknown, it resolves to unknown.
4. Resolve true/false/unknown as normal, and if the top-level result is true/false, stop and either match or don't match the query. If the top-level result is unknown, move to the next possible container and try again.

In simple cases, this matches the current "pre-filter containers based on queries" behavior - a plain `(width > 100px)` query will skip past elements that don't have the right `container-type` (because the query will resolve to "unknown") and then stop and either match or not when it hits an element with the `container-type`.

In multi-query scenarios we should look at "or" and "and" separately, and "all queries known" and "some queries unknown" separately.

(As a reminder, the Kleene-boolean logic is that an expression containing an unknown resolves to true if replacing the unknown with true and false both result in true; false if they both result in false; and unknown if they result in different answers. So "unknown and true" is unknown, "unknown and false" is false, "unknown or true" is true, "unknown or false" is unknown.)

In all these examples, assume the element is in a horizontal ttb writing mode, like English, and has a `container-type: inline-size` on it.

`(width > 100px) or (unknown)`
----------------------------------

* If the `(width)` query is true, result is true; stop and consider the query matched. 
* If it's false, result is unknown, keep looking.

`(width > 100px) and (unknown)`
------------------------------------

* If the `(width)` query is true, result is unknown, keep looking. 
* If it's false, result is false; stop and consider the query not matched.

`(width > 100px) or (height > 100px)`
------------------------------------------

* If the `(width)` query is true, result is true; stop and consider the query matched.
* If it's false, result is unknown, keep looking.

`(width > 100px) and (height > 100px)`
------------------------------------

* If the `(width)` query is true, result is unknown, keep looking. 
* If it's false, result is false; stop and consider the query not matched.

------------------

My conclusion here is that the "or" behavior is good in both scenarios. It exactly matches my intuition of how this should work - it stops when one of the conditions can be satisfied, and the fact that one of them is unknowable doesn't matter.

The "and" behavior is more arguable. It might be right in the "unknown" scenario, but seems more clearly wrong in the "height" scenario. It seems wrong to stop when *one* of the conditions is false, since we know that we're *asking for* an element that can satisfy both conditions.

This suggests we *do* want to use a different boolean logic than what MQs use, where "and"-ing with an unknown is *always* unknown. That is:

<table>
<thead>
<tr><th>And<th>T<th>F<th>U
<tbody>
<tr><th>T<td>T<td>F<td>U
<tr><th>F<td>F<td>F<td>U
<tr><th>U<td>U<td>U<td>U
</table>

<table>
<thead>
<tr><th>Or<th>T<th>F<th>U
<tbody>
<tr><th>T<td>T<td>T<td>T
<tr><th>F<td>T<td>F<td>U
<tr><th>U<td>T<td>U<td>U
</table>

Compared with the Kleene bools, which are:

<table>
<thead>
<tr><th>And<th>T<th>F<th>U
<tbody>
<tr><th>T<td>T<td>F<td>U
<tr><th>F<td>F<td>F<td><b><i><big>F</big></i></b>
<tr><th>U<td>U<td><b><i><big>F</big></i></b><td>U
</table>

<table>
<thead>
<tr><th>Or<th>T<th>F<th>U
<tbody>
<tr><th>T<td>T<td>T<td>T
<tr><th>F<td>T<td>F<td>U
<tr><th>U<td>T<td>U<td>U
</table>

This'll ensure that if you're asking for two conditions to both be true, we'll skip *anything* that can't answer both questions, but if you're asking for *either* condition to be true, we'll stop and match if one of them ends up being true, but skip and continue if one is false and the other is unknown.

This is different from MQs *only insofar as* MQs don't have a "skip and continue checking" option - a top-level unknown is identical to a top-level false in MQs, but they're distinct in CQs. And this algebra change just converts some falses into unknowns.

(Heck, I think we *could* make this change in MQs as well, actually. I'll want to double-check, but I'm pretty sure I'm right, and all this does is change some scenarios that would evaluate to "false" in Kleene logic to become "unknown" instead, but any scenario that would evaluate to "true" stays true. But I don't think it's necessary for them to match - we craft the logic to the use-cases, which is why @supports no longer uses unknown, for example.)

-- 
GitHub Notification of comment by tabatkins
Please view or discuss this issue at https://github.com/w3c/csswg-drafts/issues/7551#issuecomment-1387511802 using your GitHub account


-- 
Sent via github-notify-ml as configured in https://github.com/w3c/github-notify-ml-config

Received on Wednesday, 18 January 2023 18:20:37 UTC