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-backgrounds-4][css-values-4] Align logical values for <position> with the ones defined in CSS Logical Properties #549

Closed
SebastianZ opened this issue Sep 29, 2016 · 30 comments

Comments

@SebastianZ
Copy link
Contributor

SebastianZ commented Sep 29, 2016

The <position> data type is defined in CSS Backgrounds 3 to only take physical values. <length> and <percentage> values are resolved against the top left corner of the referenced area.
In CSS Backgrounds 4 the logical values start, end, x-start, x-end, y-start and y-end are added to the definition.

To keep the logical values consistent they should be defined in CSS Logical Properties and be changed to block-start, block-end, inline-start and inline-end so that the syntax looks like this:

<position> = [
  [ left | center | right | top | bottom | <length-percentage> ]
|
  [ left | center | right | <length-percentage> ]
  [ top | center | bottom | <length-percentage> ]
|
  [ center | [ left | right ] <length-percentage>? ] &&
  [ center | [ top | bottom ] <length-percentage>? ]
|
  [ [ inline-start | inline-end ] <length-percentage>? ] ||
  [ [ block-start | block-end ] <length-percentage>? ]
]

I think the syntax could then even be reduced to this:

[ center | [ left | right | inline-start | inline-end ] <length-percentage>? | <length-percentage> ] ||
[ center | [ top | bottom | block-start | block-end ] <length-percentage>? | <length-percentage> ]

The only differences I can see is that it would allow to mix logical and physical directions like in block-end 10px left 30px and requires to interpret <length-percentage>s differently, as it allows values like 20% left 2em.

<length-percentage>s would still refer to the top or left edge if no direction is given.

Sebastian

@fantasai
Copy link
Collaborator

This is addressed in CSS Backgrounds and Borders Level 4.

@fantasai fantasai added css-backgrounds-4 and removed css-backgrounds-3 Current Work labels Apr 25, 2017
@fantasai fantasai removed the css-logical-1 Current Work label Jun 20, 2017
@fantasai fantasai changed the title [css-backgrounds][css-logical-props] Define logical values for <position> in CSS Logical Properties Jun 20, 2017
@SebastianZ SebastianZ changed the title [css-backgrounds] Define logical values for <position> in CSS Logical Properties Jun 4, 2022
@SebastianZ SebastianZ changed the title [css-backgrounds] Align logical values for <position> with the ones defined in CSS Logical Properties Jun 4, 2022
@SebastianZ
Copy link
Contributor Author

SebastianZ commented Jun 5, 2022

Coming back to this issue, my point back then was to replace the half physical half logical keywords x-start, x-end, y-start and y-end defined in CSS Backgrounds 4 by the fully logical ones block-start, block-end, inline-start and inline-end used in other places.

I also meant to simplify the syntax, though allowing to mix logical and physical keywords seems wrong to me now because this possibly causes similar issues as the keywords above. And I also kicked out the start and end keywords back then, which may actually be useful.
Though the syntax may still be simplified a bit because it's still quite hard to follow. The syntax change is unrelated to changing the keywords, so I'll file another issue for that once I gave it a little more thought.

Sebastian

@SebastianZ
Copy link
Contributor Author

Iterating on my previous suggestion, if we align the syntax with the current definition of <position> and still allow the three-value syntax, this could be

<bg-position> = [
  [ left | center | right | inline-start | inline-end ] ||
  [ top | center | bottom | block-start | block-end ]
|
  [ left | center | right | inline-start | inline-end | <length-percentage> ]
  [ top | center | bottom | block-start | block-end <length-percentage> ]?
|
  [ [ left | right | inline-start | inline-end ] <length-percentage>? ] &&
  [ [ top | bottom | block-start | block-end ] <length-percentage>? ]
]

In addition to that we might still have start and end as keywords, though then being shorthands for inline-start and block-start resp. inline-end and block-end.

Sebastian

@fantasai
Copy link
Collaborator

fantasai commented Apr 1, 2023

No, x-start/y-start are intentionally not inline-start/block-start because they are logical directions along a physical axis.

@SebastianZ
Copy link
Contributor Author

... they are logical directions along a physical axis.

I know and I mentioned that earlier. The actual question is why do we have them? What are the use cases for mixing logical and physical axes here in contrast to basically everywhere else?
And how are those values meant to work together with the different values of writing-mode and direction?

Sebastian

@fantasai
Copy link
Collaborator

What are the use cases for mixing logical and physical axes here in contrast to basically everywhere else?

It's not mixing logical and physical axes. It's using physical axes and logical directions. There are some effects that are going to want that, e.g. consider a background that imitates a landscape. The grass is always going to be at the bottom, and the sun at the top, but you might want to position the sun right or left depending on the writing mode.

@tabatkins
Copy link
Member

Okay, here's my proposal for an updated <position> that allows logical keywords:

<position> = [
  [ left | center | right | x-start | x-end ] || [ top | center | bottom | y-start | y-end ]
|
  [ left | center | right | x-start | x-end | <length-percentage> ]
  [ top | center | bottom | y-start | y-end | <length-percentage> ]?
|
  [ [ left | right | x-start | x-end ] <length-percentage> ] &&
  [ [ top | bottom | y-start | y-end ] <length-percentage> ]
|
  [ block-start | block-end | center ] || [ inline-start | inline-end | center ]
|
  [ start | end ]{2}
|
  [ [ block-start | block-end ] <length-percentage> ] &&
  [ [ inline-start | inline-end ] <length-percentage> ]
]

The logical additions are:

  1. You can use a one or two logical keywords, or the full 4-value form with logicals.
  2. To make it easier to refer to corners, you can specify a pair of start/end keywords. (So you don't have to write out the full block-start inline-end, just start end instead.)
  3. The mixed keywords (x-start, etc) are added to the list of physical keywords, specifying a physical axis and then a logical direction within that axis.
  4. Using one logical keyword works the same as using one physical keyword - the opposite axis is "center".
@tabatkins
Copy link
Member

Oh and note, this is an expansion of the V&U definition of position, which we've slightly stripped down (removing the 3-value forms, for example).

For bg-position we'll need to put back the 3-value stuff, but I propose we only do exactly what's needed for compat, rather than making logical keywords also have this weird behavior.

@SebastianZ SebastianZ added the css-values-4 Current Work label Jul 31, 2023
@SebastianZ SebastianZ changed the title [css-backgrounds-4] Align logical values for <position> with the ones defined in CSS Logical Properties Jul 31, 2023
@SebastianZ
Copy link
Contributor Author

Iterating on the proposal of @tabatkins:

<position> = [
  [ left | center | right | x-start | x-end ] || [ top | center | bottom | y-start | y-end ]
|
  [ left | center | right | x-start | x-end | <length-percentage> ]
  [ top | center | bottom | y-start | y-end | <length-percentage> ]?
|
  [ [ left | center | right | x-start | x-end ] <length-percentage> ] &&
  [ [ top | center | bottom | y-start | y-end ] <length-percentage> ]
|
  [ block-start | block-end ] || [ inline-start | inline-end ]
|
  [ start | end ]{1, 2}
|
  [ [ block-start | block-end ] <length-percentage> ] &&
  [ [ inline-start | inline-end ] <length-percentage> ]
|
  [ [ start | end ] <length-percentage> ]{1, 2}
]

Changes to Tab's suggestion:

  • start or end can be used as a single keyword to refer to the block and inline start corner or end corner.
  • An offset can be applied to start and end. (like for other keywords; syntax sugar for the block-* and inline-* keywords)
  • An offset can be applied to center. (like for other keywords; syntax sugar for calc(50% + <offset>)
  • Removed redundant center from [ block-start | block-end | center ] || [ inline-start | inline-end | center ] as they are already part of the physical keywords syntax.

Sebastian

@SebastianZ
Copy link
Contributor Author

SebastianZ commented Jul 31, 2023

I tend to agree with Tab that the 3-value-syntax for background positions with its ambiguities should stay untouched. So I think the syntax for background-position could then be expressed as

<bg-position> = [
  <position>
|
  [ left | right ] <length-percentage>? &&
  [ top | bottom ] <length-percentage>?
]

or to make the 3-value-syntax more obvious as

<bg-position> = [
  <position>
|
  [ [ left | right ] <length-percentage> && [ top | bottom ] ] |
  [ [ left | right ] && [ top | bottom ] <length-percentage> ]
]

or even something like

<bg-position> = <position> | <three-value-position>
<three-value-position> = [
  [ [ left | right ] <length-percentage> && [ top | bottom ] ] |
  [ [ left | right ] && [ top | bottom ] <length-percentage> ]
]

Sebastian

@SebastianZ
Copy link
Contributor Author

Adding this to the agenda, as I believe we now have a decent definition (see the last two comments).

Sebastian

@tabatkins
Copy link
Member

start or end can be used as a single keyword to refer to the block and inline start corner or end corner.

I don't agree with this change (or the related one that lets them take a len-per), as it's different behavior from what a single keyword does in all other cases. In every other possibility, giving one keyword sets one axis and centers the other. I don't think being able to say "the start/start corner" is sufficiently important/common that literally saying start start is a burden and shortening to start is necessary. (Today you have to give two keywords to do this anyway, like left top, and nobody seems to be complaining about this.)

An offset can be applied to center. (like for other keywords; syntax sugar for calc(50% + )

I disagree with this too. First, it seems unmotivated; I haven't seen anyone ask for this, and it doesn't close a glaring hole in the grammar so it's doesn't help technical correctness either. Second, the offsets for the existing keywords are offsets from an edge, with positive values pointing inward; whether that manifests as the value being syntax sugar for a positive or a negative term depends on which value it's for. There is no "inward" from center, tho, so there's no similar intuition to lean on. I think people can just write calc(50% + ...) if they need to offset from center, as they do today.

Removed redundant center from [ block-start | block-end | center ] || [ inline-start | inline-end | center ] as they are already part of the physical keywords syntax.

Not redundant; your grammar disallows block-start center, for example. It does duplicate the center center case, but gives it the same meaning, which is fine.


So in all I think that means I disagree with all of your changes, and we should just use mine. ^_^

@tabatkins tabatkins added css-values-5 and removed css-values-4 Current Work labels Oct 27, 2023
@tabatkins
Copy link
Member

tabatkins commented Nov 22, 2023

Here's the proposed grammar for the full combination of physical and logical <position> values.

We decided that having them all as one giant production was just getting unmanageable, so we broke them up into three, one for each number of component values you can specify. This has the nice effect of making css-values and css-backgrounds grammars easier to integrate without duplication. ^_^

<position> = <position-one> | <position-two> | <position-four>
<position-one> = [
  left | center | right | top | bottom |
  x-start | x-end | y-start | y-end |
  block-start | block-end | inline-start | inline-end |
  <<length-percentage>>
]
<position-two> = [
  [ left | center | right | x-start | x-end ] &&
  [ top | center | bottom | y-start | y-end ]
|
  [ left | center | right | x-start | x-end | <<length-percentage>> ]
  [ top | center | bottom | y-start | y-end | <<length-percentage>> ]
|
  [ block-start | center | block-end ] &&
  [ inline-start | center | inline-end ]
|
  [ start | center | end ]{2}
]
<position-four> = [
  [ [ left | right | x-start | x-end ] <<length-percentage>> ] &&
  [ [ top | bottom | y-start | y-end ] <<length-percentage>> ]
|
  [ [ block-start | block-end ] <<length-percentage>> ] &&
  [ [ inline-start | inline-end ] <<length-percentage>> ]
|
  [ [ start | end ] <<length-percentage>> ]{2}
]

These would expand into background-position-x/y or background-position-block/inline as appropriate, with the ambiguous center center production falling into the physical longhands.

For background-position, we would add to css-backgrounds-4:

<bg-position> = <position> | <position-three>
<position-three-L3> = [
  [ left | center | right ] && [ [ top | bottom ] <<length-percentage>> ]
|
  [ [ left | right ] <<length-percentage>> ] && [ top | center | bottom ]
]

And if we wanted to allow the (unambiguous) 3-value combinations with logical keywords too:

<position-three-L4> = [
  [ left | center | right | x-start | x-end ] &&
  [ [ top | bottom | y-start | y-end ] <<length-percentage>> ]
|
  [ [ left | right | x-start | x-end ] <<length-percentage>> ] &&
  [ top | center | bottom | y-start | y-end ]
|
  [ block-start | center | block-end ] &&
  [ [ inline-start | inline-end ] <<length-percentage>> ]
|
  [ [ block-start | block-end ] <<length-percentage>> ] &&
  [ inline-start | center | inline-end ]
|
  [ [ start | end ] <<length-percentage>> ] &&
  [ start | center | end ]
]

This grammar allows:

  • All the physical combinations allowed in L3.
  • Physical axis with logical keywords.
  • Explicit-axis logical keywords -- for not needing to remember positional syntax and consistency with float.
  • Implicit-axis logical keywords -- for conciseness, for consistency with the longhands (which should be non-redundant wrt axis), and for consistency with scroll-snap-align, place-self, etc..

It's unambiguous whether you're expanding to the physical or logical longhands (as long as we define center center to prioritize physical longhands).

Because we know the writing mode at computed value time, the computed values for all properties could remain as from the top+left sides, which privileges physical coordinates but allows all coordinate types to be simply interpolated during animations.

Alternatively, if we want to preserve the coordinate information at computed-value time:

  • For background-position-x/y: a <length-percentage> and a logical vs. physical flag (to distinguish between left/right and x-start/x-end, etc.)
  • For background-position-block/inline: a <length-percentage> value
  • For <position> where it cannot be broken down: two value pairs representing perpendicular axes, each consisting of a keyword indicating the origin side, and an offset measuring a distance from that side
  • When serializing <position> or background-position computed values, the top/left/x-start/y-start/start keywords are used to represent the origin (and shortest serialization principle applies).
  • When animating, convert to the left/top coordinate system for interpolation.

~Tab and fantasai

@SebastianZ
Copy link
Contributor Author

It is slowly getting a liiittle complicated to comprehend and keep up with everything. 😅

The split into different productions helps to grasp the changes. And thank you for adding the optional offset to start/end!

I think it would help to give the data types more expressive names like <position-one-axis>, <position-two-axes>, <position-two-axes-with-offsets>, and <position-two-axes-with-one-offset>, or so.
Was there a reason not to allow combinations of logical keywords and <length-percentage>? I.e. I am missing a

  [ block-start | center | block-end | <<length-percentage>> ]
  [ inline-start | center | inline-end | <<length-percentage>> ]

Regarding the positioning origin, I think authors would want the coordinate information to be preserved. I.e. the coordinate information should be inferred from the given keyword when possible and fall back to top left otherwise. By inferred I mean the origin is the *-start value of the axis perpendicular to the given one for logical keywords and the top left corner for physical keywords.

So, for example, for a value of 10% y-start, the origin for the percentage is then interpreted as x-start value.
When the value is block-start 100px, the offset refers to the inline-start axis.

When interpolating, the coordinate information could also be preserved in case axes of the start and end value coincide. That means, animating from 0 y-start to 100% y-end results in an interpolation from the x-start/y-start point to the x-end/y-end` point.
Though, I guess it doesn't matter much to authors whether the coordinate information is preserved or converted to the top/left coordinate system as long as the visual result is the same.

Sebastian

@fantasai
Copy link
Collaborator

I think it would help to give the data types more expressive names like <position-one-axis>, <position-two-axes>, <position-two-axes-with-offsets>, and <position-two-axes-with-one-offset>, or so.

I think there's a readability balance to be had between splitting things up and abstracting them vs writing them inline, and I think the production structure in Tab's grammar finds a good balance. It's also perfectly suited to hooking together the bg-position and position grammars.

Was there a reason not to allow combinations of logical keywords and ?

See the <position-four> grammar.

So, for example, for a value of 10% y-start, the origin for the percentage is then interpreted as x-start value.

I think this is a bit too much magic, since if I replace y-start with center, the interpretation of 10% flips from x-start to left. That's a great way to introduce errors.

@tabatkins
Copy link
Member

So, for example, for a value of 10% y-start, the origin for the percentage is then interpreted as x-start value.

To expand on what Elika said, note that 10% left is invalid today. The grammar is very strict about the two-value syntax; if you use any <length-percentage> then it's strictly parsed as horiz-vert order. Giving logical keywords the ability to violate that rule and implicitly select the lenper axis is inconsistent.

We could potentially loosen that restriction across the board, but this is such an old production that I'd be legitimately afraid of accidentally activating currently-invalid code. It also runs into the ambiguity issue Elika was worried about - center doesn't imply an axis at all, so using it with a lenper means we have to impose axises arbitrarily, and if you're relying on the direction disambiguation you'll get a surprising flip of the directions.

@SebastianZ
Copy link
Contributor Author

I think it would help to give the data types more expressive names like <position-one-axis>, <position-two-axes>, <position-two-axes-with-offsets>, and <position-two-axes-with-one-offset>, or so.

I think there's a readability balance to be had between splitting things up and abstracting them vs writing them inline, and I think the production structure in Tab's grammar finds a good balance. It's also perfectly suited to hooking together the bg-position and position grammars.

Just to make sure we're not talking past each other.

I meant to rename <position-one> to <position-one-axis>, <position-two> to <position-two-axes>, <position-four> to <position-two-axes-with-offsets>, and <position-three> to <position-two-axes-with-one-offset>. I didn't mean to abstract things further.

Was there a reason not to allow combinations of logical keywords and <length-percentage>?

See the <position-four> grammar.

I was talking about a logical two-value complement to

  [ left | center | right | x-start | x-end | <<length-percentage>> ]
  [ top | center | bottom | y-start | y-end | <<length-percentage>> ]

So, e.g. block-start 10% or 100px inline-end.

So, for example, for a value of 10% y-start, the origin for the percentage is then interpreted as x-start value.

I think this is a bit too much magic, since if I replace y-start with center, the interpretation of 10% flips from x-start to left. That's a great way to introduce errors.

We could potentially loosen that restriction across the board, but this is such an old production that I'd be legitimately afraid of accidentally activating currently-invalid code. It also runs into the ambiguity issue Elika was worried about - center doesn't imply an axis at all, so using it with a lenper means we have to impose axises arbitrarily, and if you're relying on the direction disambiguation you'll get a surprising flip of the directions.

My point was just that we could define smart interpolation rules in case the start and end value "fit together", as shown in the example I gave, and fall back to the top left coordinate system if they don't.

Though as long as authors can interpolate e.g. between top and block-end and block-start 10px inline-start 20px and block-end 30px inline-end 40px, it's probably fine.
As I said earlier, authors probably won't care much about whether the values are converted to the top left coordinate system when interpolating them.

Sebastian

@LeaVerou
Copy link
Member

I skimmed through the last few messages, but since this is on the agenda for today, this my position:

  • Yes to adding logical keywords to <position> (no-brainer)
  • No to mixing logical and physical keywords (what does that even do in a vertical writing mode?)
@fantasai
Copy link
Collaborator

I meant to rename to

There's also value to a) keeping things short enough that we don't trigger excessive wrapping, b) keeping things short enough that the name itself isn't as long as a sentence because that takes up a lot of space in your head when you're reading the sentence containing the name!

So, e.g. block-start 10% or 100px inline-end.

See #549 (comment)

@LeaVerou I think you're confused wrt logical + physical combos. x-start top is totally a reasonable combination.

@Loirooriol
Copy link
Contributor

It's unambiguous whether you're expanding to the physical or logical longhands

Not if there is a var(). Then you can't know until computed-value time, but the shorthand needs to expand at parse-time.

@LeaVerou
Copy link
Member

LeaVerou commented Nov 29, 2023

@fantasai

@LeaVerou I think you're confused wrt logical + physical combos. x-start top is totally a reasonable combination.

I was talking about combinations like inline-start top

@SebastianZ
Copy link
Contributor Author

@LeaVerou

I was talking about combinations like inline-start top

There is no inline-start top combination in the latest proposed syntax.

Sebastian

@tabatkins
Copy link
Member

Not if there is a var(). Then you can't know until computed-value time, but the shorthand needs to expand at parse-time.

Hm, that's a larger issue for anything that could expand into both logical and physical longhands. I'll raise it on the Logical spec.

I was talking about combinations like inline-start top

Right, that combo isn't allowed. If you're using a "full logical" keyword, it can only be used with its paired full-logical keyword.

@css-meeting-bot
Copy link
Member

The CSS Working Group just discussed [css-backgrounds-4][css-values-4] Align logical values for <position> with the ones defined in CSS Logical Properties, and agreed to the following:

  • RESOLVED: Accept the proposal in the comment
The full IRC log of that discussion <fantasai> TabAtkins: In the issue, specifically the comment at https://github.com//issues/549#issuecomment-1823607623
<fantasai> TabAtkins: fantasai and I worked on final proposal to integrating logical values in to <position> and <bg-position>
<fantasai> TabAtkins: I'm very happy with this in general, and most places where this will matter
<fantasai> TabAtkins: e.g. in transform-origin or shapes
<fantasai> TabAtkins: there's a question that Oriol raised about how this expands into longhands when you have both physical and logical
<fantasai> TabAtkins: we haven't fully resolved how that's supposed to work
<astearns> ack bkardell_
<fantasai> TabAtkins: background-position, given it has -x/-y, will presumably have -inline/-block
<fantasai> TabAtkins: from syntax, it's obvious which to resolve to
<fantasai> TabAtkins: but not obvious for var()
<fantasai> TabAtkins: don't know which set to expand into until var() resolution
<fantasai> TabAtkins: so further issue of how to handle in background-position, which I'd like to defer
<fantasai> TabAtkins: but for <position> itself, I'm happy to resolve to accept this set of additions if ppl are happy
<fantasai> me q+
<astearns> ack fantasai
<TabAtkins> fantasai: I think that we need to somehow make this work, so we should just accept this syntax for both things then figure out var() resolution
<TabAtkins> fantasai: Even if it's very dumb, at least it'll work for basic cases. Need logical bg positions
<TabAtkins> TabAtkins: Yeah that's probably reasonable.
<fantasai> astearns: so we would accept proposal in that comment, and then open up issues on var() resolution
<fantasai> TabAtkins: already open
<fantasai> astearns: Proposed to accept proposal
<TabAtkins> fantasai: Summarizing:
<TabAtkins> fantasai: the new syntax expands <position> to allow x-start/x-end/y-start/y-end as alternatives to left/right/top/bottom
<TabAtkins> fantasai: It also adds block-start/block-end/inline-start/inline-end as a separate production which can't be mixed with the physical axis keywords
<TabAtkins> fantasai: And as a shorthand also allows just "start start"/etc
<TabAtkins> fantasai: Same pattern as in other logical props
<TabAtkins> fantasai: So we can specify fully physical, specify physical axis but a logical side, or specify fully logical. Gives us every combination
<TabAtkins> fantasai: But for the resolution I'd say just accept the proposal in the comment
<SebastianZ> +1
<TabAtkins> astearns: Any reactions to that summary?
<TabAtkins> astearns: Objections?
<TabAtkins> RESOLVED: Accept the proposal in the comment
<fantasai> -> https://github.com//issues/549#issuecomment-1823607623
@Loirooriol
Copy link
Contributor

fantasai: I think that we need to somehow make this work, so we should just accept this syntax for both things then figure out var() resolution

I disagree, this isn't something that can be addressed in a quick follow-up. It's more or less the same problem as #1282, which has been open for 6.5 years with still no clear proposal.

So I'm opposed to adding logical values with no plan about how to handle them.

It's not like var() is the only problem either, there are some CSSOM implications that would need to be figured out for shorthands whose longhand expansion varies.

@fantasai
Copy link
Collaborator

@Loirooriol I'm not saying it's a quick follow-up, but that somehow or other, we need to address it. We can't just give up and say "logical background positions are not possible". And here, unlike 1282, there's not an open question about what's an appropriate syntax.

@Loirooriol
Copy link
Contributor

Even if slightly less convenient, background-position-block/inline could be used to set the position logically. I don't think it's a big deal to not have logical values in the shorthand until there is a proper plan.

@SebastianZ
Copy link
Contributor Author

Even if slightly less convenient, background-position-block/inline could be used to set the position logically. I don't think it's a big deal to not have logical values in the shorthand until there is a proper plan.

Note that this is not just about background-position. We want to add those logical values to <position>, so they also apply to mask-position, object-position, offset-position, etc.

Sebastian

@fantasai
Copy link
Collaborator

Edits are in for css-values-4 and css-backgrounds-4 and Oriol's issue is filed as #9690 ; closing this issue.

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