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-position-3] Section 3.5.1 Conflicts with CSS2 Positioning Behavior #11258

Open
gitspeaks opened this issue Nov 21, 2024 · 11 comments
Open
Labels
css-position-3 Current Work

Comments

@gitspeaks
Copy link

gitspeaks commented Nov 21, 2024

The rule in Section 3.5.1 states:

If only one inset property in a given axis is auto, it is set to zero.

This implies that when one inset property (e.g., left or right) is auto and the other is not, the auto value is replaced with 0. Consequently, both inset properties on that axis are treated as having non-auto values, with one being explicitly set to zero.

However, the CSS2 specification defines several rules that depend on scenarios where one inset property is auto and the other is not. For example, from CSS 2, Section 10.3.7:

Case 1: left and width are auto, and right is not auto; then the width is shrink-to-fit, and solve for left.
Case 3: width and right are auto, and left is not auto; then the width is shrink-to-fit, and solve for right.
Case 4: left is auto, and width and right are not auto; then solve for left.

By automatically setting an auto inset to zero when the other inset on the same axis is non-auto, the CSS Positioned Layout Module Level 3 invalidates these cases from CSS2 and would result in a different positioning behavior according to other CSS2 absolute position specification.

@gitspeaks
Copy link
Author

Examples (tested in both Chrome and Firefox)

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <style>
      #parent {
        position: relative;
        width: 200px;
        height: 200px;
        border: 5px solid black;
      }

      #child {
        position: absolute;
        background-color: red;
        left: 10px;
        width: 100px;
        height: 50px;
        /* Computes to 90px in compliance with CSS 2, Case 6 */
        /* https://drafts.csswg.org/css2/#abs-non-replaced-width */
        right: auto; 
        margin-left: auto;
        margin-right: auto;
      }
    </style>
  </head>
  <body>
    <div id="parent">
      <div id="child"></div>
    </div>
  </body>
</html>

case6

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <style>
      #parent {
        position: relative;
        width: 200px;
        height: 200px;
        border: 5px solid black;
      }

      #child {
        position: absolute;
        background-color: red;
        left: 10px;
        width: 100px;
        height: 50px;
        /* 
          According to Section 3.5.1, 'auto' is converted to 0. 
          The rendered output complies with the CSS 2 specification, 
          which states: "solve the equation under the extra constraint 
          that the two margins get equal values."
          https://drafts.csswg.org/css2/#abs-non-replaced-width
        */
        right: 0;
        margin-left: auto; /* 45px */
        margin-right: auto; /* 45px */
      }
    </style>
  </head>
  <body>
    <div id="parent">
      <div id="child"></div>
    </div>
  </body>
</html>

margin-case

@Loirooriol
Copy link
Contributor

the CSS2 specification defines several rules

Of course these are overridden by CSS Position and CSS Align.

would result in a different positioning behavior

Your examples seem to behave the same. https://drafts.csswg.org/css-position/#abspos-margins

  • 1st example: both auto margins resolve to 0 since there is an auto inset.
  • 2nd example: both auto margins resolve to ((200px - 10px - 0px) - 100px) / 2 = 45px since there is no auto inset.

@Loirooriol Loirooriol added the css-position-3 Current Work label Nov 21, 2024
@Loirooriol
Copy link
Contributor

Loirooriol commented Nov 21, 2024

OK, now I get what you are saying. When the spec says "If only one inset property in a given axis is auto, it is set to zero", this is just for computing the inset-modified containing block. It's still auto for the purpose of resolving margins.

I guess it might be clarified a bit.

@gitspeaks
Copy link
Author

this is just for computing the inset-modified containing block

What is the expected output for the first example under CSS3 ?

#child {
  position: absolute;
  background-color: red;
  left: 10px;
  width: 100px;
  height: 50px;
  /* Computes to 90px in compliance with CSS 2, Case 6 */
  /* https://drafts.csswg.org/css2/#abs-non-replaced-width */
  right: auto; 
  margin-left: auto;
  margin-right: auto;
}
  const element = document.getElementById("child");
  const styles = getComputedStyle(element);
  console.log("Right:", styles.right);

@Loirooriol
Copy link
Contributor

Good point. I'm not sure if it would be web compatible to change it, but even if so, it wouldn't be great if you get 0px, assign it back, and this suddenly changes the margins.

@Loirooriol
Copy link
Contributor

So I think we need the 2nd option from #11242 (comment)

@gitspeaks
Copy link
Author

So I think we need the 2nd option from #11242 (comment)

I agree.

@gitspeaks
Copy link
Author

gitspeaks commented Nov 22, 2024

Not to imply any lack of appreciation for the proposed solution, I must admit I struggle to understand the value the new 'inset-modified containing block' based model brings compared to the CSS2 'containing block equation model'.
In particular, this is unclear when considering edge cases like this and when different configurations of auto values still affect margin calculations. Simply put, why can't the self-alignment properties be added on top of the CSS2 model, overriding the expected rules as needed? Why is the concept of an 'inset-modified containing block' necessary to support self-alignment properties? Why is there a need for a reference frame within the containing block when it is a reference frame itself?

@gitspeaks
Copy link
Author

gitspeaks commented Nov 22, 2024

The "new model" proposes the following mental approach:

  1. Establish a frame in reference to the "absolute-position containing block", called the "inset-modified containing block" (IMCB).
  2. Position the absolutely positioned element within the IMCB.

Now, consider the first example in this issue using the proposed solution:

#child {
    position: absolute;
    background-color: red;
    left: 10px;
    width: 100px;
    height: 50px;
    right: auto;
    margin-left: auto;
    margin-right: auto;
}
  1. First, the left of the IMCB is established, and the top (and bottom) are implied by the default self-alignment property (self-start), which dictates that top is set according to the static position of #child, and bottom is set to 0.

  2. Second, according to the proposed solution, the bottom-right corner of the IMCB is determined based on the shrink-to-fit dimensions of #child, presumably resizing the bottom edge offset of the IMCB.

It is this second step that still requires developers to think about offsets relative to the containing block, which essentially exercises the same mental model as in CSS2.

@Loirooriol
Copy link
Contributor

I struggle to understand the value the new 'inset-modified containing block' based model brings compared to the CSS2 'containing block equation model'

The value is that, for overconstrained cases, instead of just ignoring left or right, you can use CSS align properties to align withing the inset-modified containing block.

And it's not like it's a radically different model, it's just a slightly different way of explaining the CSS2 behavior.

@gitspeaks
Copy link
Author

The value is that, for overconstrained cases, instead of just ignoring left or right, you can use CSS align properties to align withing the inset-modified containing block.

I don't understand what you are saying.

In CSS2, "over-constrained" refers to a situation where the sum of the positioned element’s properties for a given axis exceeds the size of the corresponding axis of the absolute-position containing block.

left + margin-left + border-left-width + padding-left + width + padding-right + border-right-width + margin-right + right > width of the containing block

In such cases, the CSS2 specification directs that either left or right is ignored.

How is "over-constrained" defined in CSS3? Additionally, how are CSS alignment properties and the inset-modified containing block relevant in this context?

And it's not like it's a radically different model, it's just a slightly different way of explaining the CSS2 behavior.

The CSS3 model is fundamentally different from CSS2, as distinct as day and night.

In CSS2, the inset properties define the element’s position relative to the absolute-position containing block.
In contrast, in CSS3, these properties define the coordinates of a reference frame (e.g inset-modified containing block) relative to the absolute-position containing block and the positioned element is aligned within that frame.

For example, in CSS2, top: 0; right: 0; position the element at the top-right corner of the absolute-position containing block. This is aligned with established documentation, such as the following excerpt from the MDN introductory course "Learn to style HTML using CSS":

“Notice that the position of the element has changed. This is because top, bottom, left, and right behave differently with absolute positioning. Rather than positioning the element based on its relative position within the normal document flow, they specify the distance the element should be from each side of the containing element. For example, an absolutely positioned element set to top: 30px and left: 30px will sit 30px from the top and left edges of its containing element.”

In CSS3, however, top: 0; right: 0; specify the top-right coordinates of the inset-modified containing block.
The element's position within this block must be inferred based on its dimensions and the remaining inset properties. This model requires additional mental steps to calculate the element’s placement, making it far less intuitive compared to the CSS2 model.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
css-position-3 Current Work
Projects
None yet
Development

No branches or pull requests

2 participants