Skip to content

[css-overflow][css-contain][css-sizing] overflow: auto incompatible with size containment and container queries #7875

@Loirooriol

Description

@Loirooriol

Context

Elements with overflow: auto only get a scrollbar when the contents overflow. Classic scrollbars take some space, so this affects sizing this way:

When the box is intrinsically sized, this reserved space is added to the size of its contents. It is otherwise subtracted from space alotted to the content area. To the extent that the presence of scrollbars can affect sizing, UAs must start with the assumption that no scrollbars are needed, and recalculate sizes if it turns out they are.

For example:

<style>
div { overflow: auto; background: cyan; vertical-align: top; }
div::before { content: ""; display: block; height: 200px; width: 200px; }
</style>
<div style="display: inline-block; height: 100px; width: 100px"></div>
<div style="display: inline-grid; grid-template: 100px / 100px"></div>

Note that only Blink has this behavior, Gecko (bug 764076) and WebKit don't: in the intrinsic case, the content area (without the scrollbar) is 100px tall indeed, but is less than 100px wide.

Tip: to force classical scrollbars if your UA uses overlay ones, in Firefox go to about:config and create a number pref ui.useOverlayScrollbars set to 0. In Blink/WebKit add this CSS:

::-webkit-scrollbar { background-color: #eff0f1; width: 12px; height: 12px; }
::-webkit-scrollbar-thumb { background: #898c8d; }

Incompatibility with size containment

In short, with overflow: auto the contents can affect either the inner or the outer size of the element. This is a problem with size containment (when paired with layout containment), which should allow this optimization:

When the style or contents of a descendant of the containment box is changed, calculating what part of the DOM tree is "dirtied" and might need to be re-laid out can stop at the containment box.

This is not true if the element is sized intrinsically: a change in the descendants may trigger or untrigger a scrollbar, affecting the outer size, and potentially the ancestors.

Blink and WebKit assume it's fine to apply the optimization... with clear wrong results. See this testcase:

The testcase above won't work in Firefox due to bug 1488878, but the same problem can be seen with contain-intrinsic-size. CSS Sizing tries to define some interaction between contain-intrinsic-size and overflow: auto, but it has some problems (#7867, #7868) and I think the behavior should be the usual one, just with a custom intrinsic size. Here is the testcase with contain-intrinsic-size:

Circularity with container queries

So far, we have seen that we shouldn't allow the contents of an element with size containment to affect its outer size. Could we maybe say that the scrollbar should shrink the inner size to preserve the outer size, even when sizing intrinsically? No, because that would create a circularity with container queries, because of https://w3c.github.io/csswg-drafts/css-contain-3/#width

The width container feature queries the width of the query container’s content box.

I have checked how Blink handles the circularity. It turns out it queries the size of the content area plus the scrollbar. Oddly, this happens even with overflow: scroll or when sizing intrinsically with overflow: auto. Testcase

  • 1st is extrinsic overflow: scroll. It matches inline-size = 100px despite the content area being narrower.
  • 2nd is intrinsic overflow: scroll. It matches inline-size > 100px despite the content area being 100px.
  • 3rd is extrinsic overflow: auto. It matches inline-size = 100px despite the content area being narrower when there is a scrollbar.
  • 4th is intrinsic overflow: auto. It matches inline-size = 100px when there is no scrollbar, and matches inline-size > 100px when there is a scrollbar despite the content area being 100px. Thus it would be circular, but due to the wrong optimization described above, the circularity is only apparent when forcing layout.

Well, I guess that does the trick to avoid circularities in practice, but all of this seems very buggy and hacky.

Possible solution

I'm leaning towards simply saying that size containment forces overflow: auto to behave as overflow: scroll.

Or maybe something like scrollbar-gutter: stable but without adding the gutter for overflow: hidden, and affecting both the inline and block axes.

CC @frivoal

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    Status

    Unsorted

    Status

    Friday afternoon

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions