w3c / csswg-drafts

CSS Working Group Editor Drafts
https://drafts.csswg.org/
Other
4.44k stars 657 forks source link

[css-ui] Clarify scope of implementation-defined behavior for native appearance (and primitive appearance) #9919

Open annevk opened 7 months ago

annevk commented 7 months ago

While discussing https://github.com/web-platform-tests/wpt/pull/44138 via email with @zcorpan he raised this point:

I had a different understanding of the "may disregard some CSS properties" language in the spec. Changing computed styles of other properties based on native/primitive appearance seems like a fundamental change to how the CSS cascade normally works that I think it should be explicitly defined.

As an example, for the switch control the native appearance uses display:inline-grid (though display:block and the like will compute to display:grid). But when you change to the primitive appearance it'll always have display:initial. (When this was discussed during a WHATNOT call that was what the people attending preferred.)

Being able to change properties (as well as some other things) in this way will also be essential for the inevitable appearance:base-bikeshed.

cc @nt1m

nt1m commented 7 months ago

"may disregard some CSS properties"

To me this means engines need to be able to adjust both computed & used styles. @zcorpan interpretation I'm guessing was that the computed style would remain untouched but the used style may not match.

So it seems like we need to clarify the language computed vs. used style.

I think we should allow adjusting both computed & used styles, mainly because auto appearance may have different layout than none appearance (due to platform framework restrictions) and having that reflected in the computed style is helpful for web developers. min-height is adjusted for WebKit buttons for instance, it would be confusing if web developers didn't see it.

css-meeting-bot commented 7 months ago

The CSS Working Group just discussed [css-ui] Clarify scope of implementation-defined behavior for native appearance (and primitive appearance), and agreed to the following:

The full IRC log of that discussion <fantasai> emilio: Seems like Webkit, but not Gecko, change the computed value of some CSS properties based on `appearance`, including `display`
<fantasai> emilio: Seems like the spec has language about disregarding some CSS properties, which is true
<fantasai> emilio: Gecko does that at used-value time
<fantasai> emilio: and don't do it much anymore
<fantasai> emilio: but we need to define whether `appearance` changes values at computed or used value tie
<fantasai> s/tie/time
<fantasai> florian: Part of the idea here is that if you're in native appearance mode, the way you're rendered might not be describable by CSS at all
<fantasai> florian: to the extent it does, it might be nice to hint at how
<fantasai> florian: but this was an allowance for not being describable by CSS
<fantasai> emilio: so it seems like it shouldn't affect computed values
<fantasai> florian: that makes sense to me
<fantasai> emilio: WebKit is forcing `display: grid` on <input switch> which doesn't seem great to me, especially since other form controls don't do that
<fantasai> iank_: That seems not great to me
<fantasai> iank_: We do this as part of final step in computation, and only for border-padding on checkbox/radio and one more small thing
<emilio> https://github.com/w3c/csswg-drafts/issues/3257
<fantasai> emilio: the other one I'm ware of is line-height: normal on select
<fantasai> emilio: and that sucks
<fantasai> emilio: so given everything else doesn't touch computed values, can we agree on not doign this at computed value time when possible?
<fantasai> emilio: and in the future? except for existing compat?
<fantasai> florian: Situation is, if I'm understnading correctly, in order not mess with native appearance ...
<fantasai> emilio: if effective appearance is not none, then for legacy reasons you need to disregard author-set border and padding
<fantasai> emilio: Gecko does it at used-value time, and Blink does it at used value-time
<fantasai> iank_: We'll go through creating computed style, then look at appearance, see that it's not none, and then force-reset the border, as if it as 'border: none !important'.
<fantasai> florian: I would agree that should be done at used-value time
<fantasai> florian: whether you fix the value by [...] then you shouldn't be able to tell the difference
<fantasai> iank_: how do you do it at used-value time?
<fantasai> emilio: when we are laying out, we override the border
<fantasai> emilio: [various things] are not great
<fantasai> florian: I would agree with used value, and don't expose in the OM
<fantasai> florian: the further we can go in that direction the better
<fantasai> iank_: In general, I would prefer if we didn't rely on this mechanism more
<fantasai> s/this/this computed-value-changing/
<fantasai> emilio: especially as more features rely on dependencies on properties, this will only cause more confusion and weird behavior
<fantasai> florian: Proposed that disregarding CSS properties when widgets have their native appearance must not change the computed value
<fantasai> iank_: Going forward
<dbaron> iank_: we might have a compat issue for checkboxes/radios
<fantasai> florian: would prefer to avoid making used values visible also, but can live with it for compat
<fantasai> iank_: It can be visible through used values
<fantasai> florian: shaded-wavy-border-style is not visible through OM, but 2px thickness is, ideally we don't expose either
<fantasai> iank_: changing the internals to grid will change how baselines are calculated, and that's observable
<fantasai> iank_: so these things are observable at used time
<fantasai> emilio: but shouldn't be observed at computed value time
<fantasai> iank_: would prefer if only going forward
<fantasai> fantasai: how about "unless required by Web-compat"
<fantasai> iank_: ok
<fantasai> Rossen_: any objections?
<fantasai> RESOLVED: Disregarding CSS properties on widgets with native appearance must not affect computed values (unless required for Web-compat)
annevk commented 7 months ago

What does this resolution mean for width and height?

cc @emilio @bfgeek @frivoal

frivoal commented 6 months ago

@annevk The point of this resolution is that "disregarding" a property means that the user agent is free to render the widget in a way that possibly disagrees with the value of the property.

The main motivation is that the widget may be rendered in a way that isn't even possible to represent in CSS. It's not about being allowed to override the value specified by the author with some other value, but being allowed to ignore the property entirely. So even if the value would be representable in css (and just happen to disagree with the author styles), that's considered an implementation detail: if the UA is doing something else, it's doing something else, and that's not observable via the computed value.

width and height are not special here, and the result is that they're (allowed to be) the handled same as what would happen on a <span> or some other non-replaced inline: they don't apply, don't have an effect, and the UA figures out the size of the widget some other way. But just because it does so doesn't mean that all of sudden the computed value is changed. So for instance, if some child element of the the widget has height: inherit that'll get the original value.

nt1m commented 6 months ago

I think @fantasai mentioned that width and height are always defined to be the used value in CSSOM, which is what makes it special in this case. Firefox / Chrome don't have to think about this given those match regardless of the appearance, but it's not unreasonable for system frameworks to force a different layout and reflect that in the computed style?

It would be misleading if the size wasn't reflected in this case in the computed style, especially if that size is bigger.

frivoal commented 6 months ago

I think @fantasai mentioned that width and height are always defined to be the used value in CSSOM, which is what makes it special in this case.

The resolution is about the computed value, not about the used value, nor about the result of the getComputedStyle() method, which, confusingly, doesn't always return computed values.

Conceptually, the same arguments could apply to the used value as well, but the discussion didn't explore that too much, and there was a sense that this may be more challenging from an implementation point of view, so that is currently left undefined.

It would be misleading if the size wasn't reflected in this case in the computed style

<style>span { width: 3px; }</style>
<span id=test>supercallifragilisticexpialidocious</span>
<script>console.log(getComputedStyle(test).width);</script>

This will yield "3px", even thought the span is bigger then 3px, because width and height don't apply to non-replaced inlines. Same thing here.

(I used getComputedStyle() for simplicity here, and in does in this case return the computed value. You could observe the computed value some other way, it would still be 3px.)

it's not unreasonable for system frameworks to force a different layout and reflect that in the computed style?

The idea is that it's not always doable when you're choosing to have a non-css based appearance, so we should not try to reflect the value even if we could do it some of the time. Let's say that the border property has been set to 3px solid black, but your widget looks like this: aqua Or maybe it has a border that looks like that: candycane There's no value you can put in the border property that will do this. So you leave the property's value be 3px solid black, you ignore it, and you paint the lovely eye candy. If instead if your native appearance happens to have a 5px solid purple border, you don't pipe that back into the property just because this one could be represented in CSS. If you're doing something custom that's different from what the author asked, you're doing something different, and it's not reflected back.

zcorpan commented 6 months ago

Border widths are not returned as used values in getComputedStyle(), so if border widths differ between specified style and used style, the actual layout and the values given by getComputedStyle() don't match.

Border width affects the used value of height here because form controls have box-sizing: border-box in the UA stylesheet: https://html.spec.whatwg.org/multipage/rendering.html#form-controls

This is the current situation in WebKit, if I understand correctly.

Demo: https://software.hixie.ch/utilities/js/live-dom-viewer/saved/12396

I think the options for WebKit are, to conform to the WG resolution:

  1. Change from setting computed value to used value for the border. This will make getComputedStyle() values not match up with the actual layout.
  2. Let the used value for border-width be 2px. This will change the actual layout for native appearance (but the visual border can be painted the same as today).

Both of these have non-zero web compat risk. Option 2 I think matches Chromium and Gecko and seems less confusing for authors.

frivoal commented 6 months ago

I gave border just as an example that's easy to talk about, but this isn't specifically about borders (or width, or height), but about the general principle: UAs are allowed to treat some properties as if they didn't apply, which is different from treating them as if they did apply but had a special value magically appear from somewhere.

The resolution also carves out a possible exception for compat reasons: though it seemed preferable to the WG to treat the native appearance as an encapsulated concept whose details don't leak back through the computed values of properties, if there are requirements derived from compatibility needs, we can spec those explicitly. But until a specific case has been made about specific properties, the expectation is as I described above.

annevk commented 6 months ago

@zcorpan I'm not sure I understand how 1 and 2 are different.

I had forgotten that the resolved value for height and width is the used value, so it seems from that perspective it's in order. (The size of the box is already very much observable so not exposing that seemed a bit backwards, but it seems that's not the intent or at least not the outcome of the proposal.)

I think we should then update the tests such that we essentially expect initial values for all properties, except for those explicitly defined in the user agent style sheet.

It's also not clear to me this problem is unique to WebKit, e.g., https://software.hixie.ch/utilities/js/live-dom-viewer/?%3C!DOCTYPE%20html%3E%3Cinput%20type%3Dcheckbox%3E%3Cscript%3Ew(getComputedStyle(document.querySelector(%22input%22)).borderRightWidth)%3C%2Fscript%3E returns 2px in Gecko.

emilio commented 6 months ago

Border returns used values in Gecko only due to this: https://searchfox.org/mozilla-central/rev/c26f7461fc2a51196b7f517c7f98a1e271dc9ec0/layout/style/nsComputedDOMStyle.cpp#949-959

zcorpan commented 6 months ago

@annevk

I'm not sure I understand how 1 and 2 are different.

For (1) there would still be a layout difference between native appearance and primitive appearance.

I think we should then update the tests such that we essentially expect initial values for all properties, except for those explicitly defined in the user agent style sheet.

This would fail to test the primitive vs native rendering when there's some particular author-level style, which is the point of the tests in question. https://drafts.csswg.org/css-ui-4/#appearance-disabling-properties

annevk commented 6 months ago

It seems good to test both (and note that the tests also encompass non-devolvable widgets which have different expectations (though end up being a bit confusing as it's not clearly called out)).