Skip to content

[css-cascade][css-pseudo] How does 'inherit' keyword behave in a child of ::first-line? #1097

Closed

Description

@Loirooriol

TL;DR - see proposal


CSS Cascade says

The inherited value of a property on an element is the computed value of the property on the element’s parent element. [...]
Note: Inheritance follows the document tree and is not intercepted by anonymous boxes

It says "parent element" and "document tree", but both CSS Selectors and CSS Pseudo-elements seem to override this for ::first-line:

During CSS inheritance, the portion of a child element that occurs on the first line only inherits properties applicable to the ::first-line pseudo-element from the ::first-line pseudo-element. For all other properties inheritance is from the non-pseudo-element parent of the first line pseudo element.

However, browsers do strange things (https://jsfiddle.net/0geoep6t/)

<div>
  <span>ABCDEFGHIJKLMNOPQRSTUVWXYZ</span>
</div>
div {
  color: blue;
  background: yellow;
  border: 10px solid blue;
}
div::first-line {
  color: red;             /* Applies and is inherited by default */
  background: cyan;       /* Applies and is not inherited by default */
  border: 10px solid red; /* Does not apply to ::first-line */
}
span {
  color: inherit;         /* Firefox, Edge and Chrome inherit from ::first-line */
  background: inherit;    /* Firefox and Edge inherit from div, Chrome from ::first-line */
  border: inherit;        /* Firefox and Edge inherit from div, Chrome from ::first-line */
}

If I understand correctly, since background applies to ::first-line, background: inherit should inherit from the ::first-line, like Chrome does. And since border does not apply to ::first-line, border: inherit should inherit from the div, like Firefox and Edge do.

Am I right? Consider adding some note to the spec to clarify this.

Activity

  1. FremyCompany commented on Mar 18, 2017

    @FremyCompany
    Contributor

    Chrome behavior is almost certainly a bug. What would happen if the span was both on the first and second line due to wrapping? Chrome uses two different sets of values for the same span, which doesn't look the spirit of the css-cascade spec to me:
    image

    FireFox and Edge behavior is not what you think it is, though. All values of the span inherit from the div element (try alert(getComputedStyle(document.querySelector('span')).color)).

    What actually happens is that Edge has the concept of "render styles" (first-line, selection, etc...) that apply on text runs during linebox building (a text run being a run of text that shares the same formatting and does not break to the next line).

    Drawing backgrounds at the text run level would be ugly since some letters might be bigger or use a different font, so that is the only thing that is being drawn at the ::first-line level.

    <div>
        <div::first-line line-backround has-own-style />
        <span inherits-from="div">
            <::text-run inherits-from="::first-line, span">ABC...</...>
            <::text-run inherits-from="span">STU...</...>

    The ::text-run inherits its style from the first-line of its block container, the div... but only inheritable properties, not background!.

    We have a similar behavior for the "high-contrast backplate" we draw when users enable a high-contrast theme on Windows, though we also draw it on inline elements to override local inline backgrounds; then there is the case of selections which are drawn only at the text run level; but that means those text backgrounds are not one rectangle but the unions of a lot of rectangles while the spec mandates the ::first-line background to behave like an inline which encompasses its text children and draws one background).

    Btw here I added some image on the line and here is what the render is in Firefox:
    image

    You can see the background of the line being drawn under the backround of the span.

  2. Loirooriol commented on Mar 18, 2017

    @Loirooriol
    ContributorAuthor

    All values of the span inherit from the div element (try alert(getComputedStyle(document.querySelector('span')).color)).

    Yes, I noticed that. It must be like this because the span might generate multiple boxes, some not in the first line. So in general the span inherits from the div, but the part of the span in the first line inherits from div::first-line.

    Chrome behavior is almost certainly a bug. What would happen if the span was both on the first and second line due to wrapping? Chrome uses two different sets of values for the same span, which doesn't look the spirit of the css-cascade spec to me

    Well, Firefox and Edge do the same for color:

    img

    And it's OK, because being able to do this is the only point in having ::first-line pseudo-elements, isn't it? The problem is that Chrome does it for border, and Firefox and Edge don't do it for background.

    If it's not supposed to be like this, the spec needs to be clarified.

  3. FremyCompany commented on Mar 19, 2017

    @FremyCompany
    Contributor

    Nah you didn't get it :) The color of the span inherits from the div, always. The fact Chrome does not do that is a violation of css-cascade. The existence of pseudo elements doesn't change the style of normal elements. The style of elements is computed before layout.

    The reason the text is red is that the color of the text is not the color of the span (if you execute the line of javascript I provided, you will be able to confirm that color is blue) but, insead, it is the color of the text run; the text run is not an element and is created based on styling information during layout; its style thus may depend on the ::first-line style, but that is a totally different thing than to say the span inherits from ::first-line.

    I should never do, for no property. Neither color, nor background, nor border. At least that is my understanding of it.

  4. Loirooriol commented on Mar 19, 2017

    @Loirooriol
    ContributorAuthor

    Thanks for the clarifications. Now I will try to be more precise.

    Suppose we have

    <div>
      <span>ABCDEFGHIJKLMNOPQR<br />STUVWXYZ</span>
    </div>

    If I understand properly, I think you say it becomes somewhat like this:

    <div>
      <span>
        <div::first-line>ABCDEFGHIJKLMNOPQR<br /></div::first-line>
        STUVWXYZ
      </span>
    </div>

    (not sure where to put the <br />, but that's not relevant)

    Then I agree that using inherit on the span just makes it inherit from the div, but that can be overridden by div::first-line on the text run in the first line.

    However, it doesn't seem to be the case. Try

    div {
      color: red;
    }
    span {
      color: green;
    }
    div::first-line {
      color: blue;
    }

    All Firefox, Edge and Chrome paint the text with green color, even the text run in the first line. This means the span is farther inward than div::first-line, so the latter cannot override the former.

    Moreover, Finding the First Formatted Line says

    A user agent must act as if the fictional start tags of a ::first-line pseudo-element were nested just inside the innermost enclosing block-level element.

    The span is not block-level, so I think the tree should instead become like this:

    <div>
      <div::first-line>
        <span::fragment-1>ABCDEFGHIJKLMNOPQR<br /></span::fragment-1>
      </div::first-line>
      <span::fragment-2>STUVWXYZ</span::fragment-2>
    </div>

    And then, according to Inheritance and the ::first-line Pseudo-element,

    the portion of a child element that occurs on the first line only inherits properties applicable to the ::first-line pseudo-element from the ::first-line pseudo-element.

    Now this portion in the first line is not only a text run, but a fragment of the span. So that fragment span::part-1 should inherit from div::first-line. The other fragments of the span shouldn't.

    Maybe I'm being obtuse, but I don't find the spec much clear, and implementations don't match, so I think the spec should be clarified.

  5. FremyCompany commented on Mar 20, 2017

    @FremyCompany
    Contributor

    Nope, I didn't say the first-line pseudo element is moved inside the span; technically we do no even have any ::first-line box in our browser (the only caveat being the background which is drawn as part of a special logic).

    What I said is that in Edge/Firefox only the text runs' styles are computed based on the style of the ::first-line pseudo-element of the parent block. I meant to say that "only the style of elements that are generated during layout can depend on the style of pseudo-elements, and the span is not such a thing".

    I hadn't considered however the possibility of saying the span had fragments, which themselves are layout constructs and could inherit from the first-line pseudo at run-time... In that case I can see how you would expect background to inherit. Looking at our code, this is what we actually do, so I guess our behavior needs some more explanation than just "only layout constructs can have weird styles"...

    The correct explaination is that in Edge (and apparently Firefox too) we have this concept of render style which applies to text only, and which is the mechanism we use to transfer ::first-line styles vs other styles. That is why background does not inherit from the ::first-line even though it can be defined on the ::first-line: because it is not a text property (we do have a character-level background for selections etc, but this is another, internal-only property).

    The fact Chrome inherits the border is a bug though. Like the spec mentions, only properties that apply on the first line are supposed to propagate to the layout-generated boxes. Maybe to match Edge and Firefox we should update the spec and create the concept of "text styles" and specify that only "text styles properties" inherit from ::first-line to its content.

  6. Loirooriol commented on Mar 20, 2017

    @Loirooriol
    ContributorAuthor

    Thanks, this clarifies the problem.

    My interpretation of ::first-line was based on the examples in The ::first-line pseudo-element. But if browsers implement ::first-line using other machanisms which are not equivalent, they should either change to reflect the spec or standardize the other behavior.

    Where should the "text styles" be specced? css-pseudo, css-cascade, css-text? I guess the editor of the appropriate spec should be notified about this problem.

  7. FremyCompany commented on Mar 21, 2017

    @FremyCompany
    Contributor

    I think we would want someone from Chrome to tell their opinion on this issue. I had a quick look and I don't see this concept at all in Webkit/Blink so maybe we should just say all properties applicable to ::firstline (except background?) should inherit and file the relevant bugs in browsers.

    Another interesting test case is https://jsfiddle.net/bdxbptz4/ (currentColor is still resolved during computation in Edge which may explain our behaviour here).

    cc @tabatkins @kojiishi

  8. Loirooriol commented on Mar 25, 2017

    @Loirooriol
    ContributorAuthor

    I have been thinking and inheritance from ::first-line for non-inherited properties is so problematic.

    Suppose someone has this code, without any explicit ::first-line.

    <div>
      <span>ABCDEFGHIJKLMNOPQR<br />STUVWXYZ</span>
    </div>
    div {
      background: linear-gradient(to right, cyan, yellow 75%, red);
      width: 300px;
    }
    span {
      background: inherit;
      -webkit-box-decoration-break: clone;
      box-decoration-break: clone;
      border: 1px solid blue;
      line-height: 1.5em;
    }

    Then I think most people would expect this

    first-line-1b

    However, if the fragment of the span in the first line inherits the default background: transparent from div::first-line, then it should be

    first-line-2b

    This seems undesirable, and I see three ways to fix it:

    1. In a hacky way, like Chrome currently does. Chrome seems to enable the ::first-line pseudo-element only when it has some valid declaration, whether it applies or not. If ::first-line is enabled, inheritance is from ::first-line, otherwise it's from the parent element.

      Changing the inheritance like this seems problematic. I just checked and asynchronously adding a new stylesheet with some ::first-line declaration does not immediately trigger a background repaint.

    2. Say that inheritance is always from the parent element for non-inherited properties of the children of ::first-line, even for properties which apply to ::first-line. (For inherited properties which apply to ::first-line, inheritance is from ::first-line).

      This is what Firefox and Edge seem to do, and even Chrome for some properties like text-decoration.

    3. Say that inheritance is from ::first-line for properties that apply to ::first-line, but that the inherit keyword in a child always retrieves the computed value of the parent element, even if the inherited value comes from a pseudo-element.

    Currently I don't see any approach to avoid this problem in the specs, I think the csswg should choose some and update the specs.

  9. FremyCompany commented on Mar 26, 2017

    @FremyCompany
    Contributor

    Sound like we have a good call to resolution here. Let's wait another week to allow @tabatkins to come back from vacation, but then we should just add the Agenda+ tag and resolve on call.

  10. Loirooriol commented on Apr 1, 2017

    @Loirooriol
    ContributorAuthor

    @fantasai Thanks for the label, but precisely ::first-line is not defined in selectors-4. The labels should be css-cascade-4, css-pseudo-4 and, if any, selectors-3. But now I see selectors-3 is very old so I will remove selectors from the title.

    @tabatkins I think you came back from vacation, what do you think about this issue?

  11. changed the title [-][css-cascade][css-selectors][css-pseudo-elements] How does 'inherit' keyword behave in a child of ::first-line?[/-] [+][css-cascade][css-pseudo] How does 'inherit' keyword behave in a child of ::first-line?[/+] on Apr 1, 2017
  12. Loirooriol commented on Apr 10, 2017

    @Loirooriol
    ContributorAuthor

    @FremyCompany It seems @tabatkins does not see the notifications. If you still think this needs Agenda+, maybe you can add it yourself?

    By the way, about what you noticed in #1097 (comment), it gets more interesting with CSS variables.

    For example, Firefox allows different inline boxes of the same element to have different paddings and margins. https://jsfiddle.net/bdxbptz4/1/

    div::first-line { --padd: 0 30px }
    span { padding: var(--padd) }

    Of course, this does not work with display or float. Chrome does not allow this with padding, but still does with things like color, border, letter-spacing, text-decoration, etc. Edge seems like Chrome except border.

  13. FremyCompany commented on Apr 11, 2017

    @FremyCompany
    Contributor

    The css variables one is a good point. It is never mentioned anywhere they should work on ::first-line but I guess authors would expect so. How that then affects inheritance is a totally undefined territory. Added Agenda+ so we can discuss this on the call this week or next week.

  14. 29 remaining items

  15. fantasai commented on Sep 11, 2020

    @fantasai
    Collaborator

    @FremyCompany @Loirooriol Edited in the changes. Specifically excluded writing-mode, direction, and text-orientation as the first two were noted to be problematic in the CSSWG discussion, and all three are dependencies of the physical-logical cascade. Lmk if this seems acceptable. We can add more properties to the exclusion list as needed.

    (Technically, I think we could have text-orientation apply to ::first-letter while excluding it from inheritance, which is where it actually causes a problem, but I figured keeping the two lists consistent would be better.)

  16. FremyCompany commented on Sep 11, 2020

    @FremyCompany
    Contributor

    any other properties defined to apply to ''::first-line''
    by their respective specifications

    shouldn't that be "any other property" (singular)?

    User agents may apply other properties as well (...)

    Why still may? Wasn't the purpose of this to standardize a behavior? I have a feeling the list noted above should be exhaustive already (and if it is not, that's worth investigating and fixing).

    (...)

    Other than that, sounds to be what was resolved last time, and LGTM.

    That said, I don't have insights into how this particular bit is currently implemented in Blink and Gecko so I can't provide more value than an informed opinion ;)

  17. Loirooriol commented on Sep 11, 2020

    @Loirooriol
    ContributorAuthor

    Yes, the edit seems according to the resolution and LGTM. What I don't really like is that inheriting from ::first-line means that we have inheritance in the fragment tree, but usually inheritance is in the element tree. So interaction between display: contents and ::first-line is not clear. But that's #1310

  18. fantasai commented on Sep 13, 2020

    @fantasai
    Collaborator

    shouldn't that be "any other property" (singular)?

    Could go either way, I think.

    Why still may? Wasn't the purpose of this to standardize a behavior?

    Afaict this issue is about standardizing the behavior of what inherits and how, and not about standardizing which properties apply to ::first-line; and there was no serious discussion or conclusion on exactly which properties apply to ::first-line or don't. It's one of the reasons why we didn't want to make the inheritance behavior depend on that.

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions