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-logical][css-images] flow-relative gradients #1724

Open
jonathantneal opened this issue Aug 16, 2017 · 23 comments
Open

[css-logical][css-images] flow-relative gradients #1724

jonathantneal opened this issue Aug 16, 2017 · 23 comments

Comments

@jonathantneal
Copy link
Contributor

jonathantneal commented Aug 16, 2017

When writing right-to-left CSS, I’ve noticed I sometimes need to flip presentational images (usually symbols, like "left" or "right" arrows) and gradients. I’m not sure how to solve the former issue, but I have an idea of how I could solve the later.

I propose this addition to CSS Logical Properties and Values that would allow myself and others to write flow-relative gradients.

The linear gradient syntax is:

linear-gradient() = linear-gradient(
  [ <angle> | to <side-or-corner> ]? ,
    <color-stop-list>
)

And the physical and flow-relative values for <side-or-corner> would be:

<side-or-corner> =
[ [left | right] || [top | bottom] ] |
[ [inline-start | inline-end] || [block-start | block-end ] ]

^ updated to reflect @tabatkins feedback

Using this syntax, I could write:

header {
  background-image: linear-gradient(to inline-end, yellow 0%, blue 100%);
}

And left-to-right page progressions would see the equivalent to:

header {
  background-image: linear-gradient(to right, yellow 0%, blue 100%);
}

While right-to-left page progressions would see the equivalent to:

header {
  background-image: linear-gradient(to left, yellow 0%, blue 100%);
}

I’ve put this into a (probably poorly written) spec: https://jonathantneal.github.io/logical-gradients-spec/

@jonathantneal jonathantneal changed the title [css-logical] support flow-relative directions for gradients [css-logical] flow-relative gradients Aug 16, 2017
@tabatkins
Copy link
Member

You can't conflate left/right and inline-* like that (same with top/bottom and block-*); the entire point is that they might be a different axis. ^_^ We've determined that mixing physical and logical is pretty low-value and not worth the cost in complexifying the grammar, so the grammar should probably look like:

[ [left | right] || [top | bottom] ] |
[ [inline-start | inline-end] || [block-start | block-end ] ]

@jonathantneal
Copy link
Contributor Author

Oh, right! Doh. Fixing in the speculative spec as well.

@AmeliaBR
Copy link
Contributor

I think this is useful, and (with Tab's fix) should be a straight-forward implementation.

That said: The image() function in Images 4 is currently spec'd to include a directional flag. It allows you to specify that an image was designed specifically for a LTR or RTL layout, and that the browser should automatically flip it for the reverse direction. That syntax could also be used to flip gradient images. However, as currently spec'd, it wouldn't handle the more complex cases of the intersection of writing-mode and direction.

Also:

What about radial gradients? They are currently defined using the <position> data type by reference. Are there currently any plans to make <position> able to use logical directions (as an extension of the three or four value syntax)?

@fantasai
Copy link
Collaborator

fantasai commented Sep 5, 2017

https://drafts.csswg.org/css-backgrounds-4/#the-background-position is a good reference here.
Wrt flipping images, there was a proposal for that in earlier drafts of css-images: https://www.w3.org/TR/2012/WD-css3-images-20120112/#image-notation

@argyleink
Copy link
Contributor

i found myself wanting this over the weekend and was pleased to see I didn't need to write a proposal, rather I could cast my vote towards one 👍

@Loirooriol
Copy link
Contributor

Note we already have a precedent for referring to logical box corners: start-start, start-end, end-start, end-end (https://drafts.csswg.org/css-logical/#border-radius-properties).

So to be consistent, the syntax should maybe be

<side-or-corner> = [ [left | right] || [top | bottom] ] | <logical-side> | <logical-corner>
<logical-side> = inline-start | inline-end | block-start | block-end
<logical-corner> = start-start | start-end | end-start | end-end

@shadeed
Copy link

shadeed commented Nov 5, 2022

Just wanted to comment as I filed a similar issue #8015 and add my vote here instead.

@Loirooriol
Copy link
Contributor

Additionally to logical sides/corners, I wonder about logical angles?

We have 0deg=top, 90deg=right, 180deg=bottom and 270deg=left.
We could maybe have logical angle units, or logical(<angle>), with 0=block-start, 90=inline-end, 180=block-end, and 270=inline start?

Related to #1544 (logical rotation transforms).

@clshortfuse
Copy link

clshortfuse commented Mar 3, 2023

Using multiple masks to ensure top/end is exactly 50% to replicate:

https://m3.material.io/styles/shape/shape-scale-tokens

image

.shape[shape-end] {
  --mdw-shape__size__top-start-size: 0px;
  --mdw-shape__size__bottom-start-size: 0px;
  --mdw-shape__mask: linear-gradient(to left, transparent 50%, black 50%);
}

.shape {
    -webkit-mask-box-image: var(--mdw-shape__mask-border-source) 8 fill / var(--mdw-shape__size) stretch;
    -webkit-mask: var(--mdw-shape__mask);
}

The "to left" isn't going to work with RTL.

Edit: Ugh, complex math, but it works:

:root {
  --mdw-dir: 1;
}

html[dir="rtl"] {
  --mdw-dir: -1;
}

.shape {
  /* (1/2n + 1/2)L + (-1/2n + 1/2)R  */
  --mdw-shape__ltr: calc(0.5 * var(--mdw-dir, 1) + 0.5); /* 1 if LTR, 0 if RTL */
  --mdw-shape__rtl: calc(-0.5 * var(--mdw-dir, 1) + 0.5); /* 0 if LTR, 1 if RTL */
  --mdw-shape__size__top-left-size: calc((var(--mdw-shape__ltr) * var(--mdw-shape__size__top-start-size)) + (var(--mdw-shape__rtl) * var(--mdw-shape__size__top-end-size)));
  --mdw-shape__size__top-right-size: calc((var(--mdw-shape__rtl) * var(--mdw-shape__size__top-start-size)) + (var(--mdw-shape__ltr) * var(--mdw-shape__size__top-end-size)));
  --mdw-shape__size__bottom-left-size: calc((var(--mdw-shape__ltr) * var(--mdw-shape__size__bottom-start-size)) + (var(--mdw-shape__rtl) * var(--mdw-shape__size__bottom-end-size)));
  --mdw-shape__size__bottom-right-size: calc((var(--mdw-shape__rtl) * var(--mdw-shape__size__bottom-start-size)) + (var(--mdw-shape__ltr) * var(--mdw-shape__size__bottom-end-size)));

  --mdw-shape__inline-start-deg: calc(var(--mdw-dir, 1) * -90deg);
}

.shape[shape-end] {
  --mdw-shape__size__top-start-size: 0px;
  --mdw-shape__size__bottom-start-size: 0px;
  --mdw-shape__mask: linear-gradient(var(--mdw-shape__inline-start-deg), transparent 50%, black 50%);
}

@SebastianZ
Copy link
Contributor

As there was pretty much agreement on adding logical values for gradients, I put this on the agenda.

I believe there are three things to be discussed:

  1. Adopt @Loirooriol's suggestion for adding <logical-side> and <logical-corner> values.
  2. Also suggested by @Loirooriol, add a logical directions syntax for defining the origin of angles. (Idea was a logical() function but could also be a logical keyword.)
  3. Should this be added to CSS Images 4 or CSS Logical 1?

Sebastian

@SebastianZ SebastianZ changed the title [css-logical] flow-relative gradients [css-logical][css-images] flow-relative gradients Mar 4, 2023
@LeaVerou
Copy link
Member

LeaVerou commented May 2, 2023

Another 👍🏼 from me on this!

@css-meeting-bot
Copy link
Member

The CSS Working Group just discussed [css-logical][css-images] flow-relative gradients.

The full IRC log of that discussion <TabAtkins> oriol: Currently, in linear-gradient() you can specify an angle with physical keywords (to right)
<TabAtkins> oriol: Proposal was to also allow a logical side keywords
<TabAtkins> oriol: block-start/etc for sides
<TabAtkins> oriol: For corners, use same as border-radius, start-start, start-end, etc. First refers to block axis, second to inline.
<TabAtkins> oriol: Second part of the proposal was about logical angles.
<TabAtkins> fantasai: I don't think this is quite the right way. Gradient uses `to <position>`, should continue, just with the extended <position> we put into BG 4
<TabAtkins> fantasai: this is different than what Oriol is saying. It allows for logical/physical combos.
<TabAtkins> fantasai: You can do purely physical (top left, etc). Axis is physical, direction in that axis is physical. You can also do purely logical.
<TabAtkins> fantasai: But extended position also allows mixed - axis is physical, but direction in that axis is logical.
<TabAtkins> fantasai: Oriol's suggested syntax doesn't allow for that and is inconsistent with the <position> syntax in general.
<TabAtkins> fantasai: So we should use extended <position>
<fantasai> https://drafts.csswg.org/css-backgrounds-4/#the-background-position
<TabAtkins> oriol: I'm not as familiar with this extended <position>. Not sure how it works when you combine physical and logical.
<TabAtkins> oriol: Seems hard to reason about.
<TabAtkins> fantasai: Syntax is straightforward, it's documented there. If we don't like it, we should change it here, for everything. Shouldn't do different things for gradients and backgrounds.
<TabAtkins> fantasai: But I do think this is the way we want to go.
<TabAtkins> +1 to fantasai, at bare minimum we should be consistently using <position>, even if we end up wanting to change how we do logical <position>.
<TabAtkins> oriol: I'd like to review the BG 4 syntax since i"m not familiar with it
<fantasai> TabAtkins: I think it's fine to delay a bit more for review
<fantasai> TabAtkins: not a high priority
<TabAtkins> miriam: Ok, take back to the issue.

@tabatkins
Copy link
Member

tabatkins commented May 17, 2023

In the call Elika argued for continuing to use to <position> as the spec currently does, just with the expanded <position> from BG 4 that allows for logical directions. However, the B&G 4 version was never updated to remove the 3-value syntax. Here's an updated version that's instead expanding on the <position> syntax in V&U:

<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.

@tabatkins
Copy link
Member

Hm, tho, this also I think predated the consistent use of block-*/inline-*. I should add some clauses for that as well.

@Loirooriol
Copy link
Contributor

background-position defines a position, while linear gradients need a direction, so I don't think the syntax can be trivially reused. In particular, center center makes no sense as a direction.

And is <length-percentage> included? I guess the direction could be the vector that goes from the center of the element towards the point defined in the background-position way, but again it's problematic if the coordinates happen to be at the center.

@tabatkins
Copy link
Member

tabatkins commented May 17, 2023

@fantasai's point is that gradient's <side-or-corner> production is consistent with background-position (just stripped down to only the relevant bits), and we should do the same here.

With my adjusted production above, this means we'd have:

<side-or-corner> = 
  [ left | right | x-start | x-end ] || [ top | bottom | y-start | y-end ]
|
  [ start | end ]{2}
|
  [ block-start | block-end ] || [ inline-start | inline-end ]

@Loirooriol
Copy link
Contributor

I think she also wanted to mix logical and physical.

@tabatkins
Copy link
Member

Argh, I didn't mean to leave those out. Editting now.

@Loirooriol
Copy link
Contributor

Loirooriol commented May 17, 2023

Concerns:

  • Backgrounds L4 says that background-position: start end expands to background-position-inline: start; background-position-block: end.

    That's inconsistent with border-start-end-radius referring to corner between the block-start and inline-end sides.
    So I would say that both background-position and linear-gradient should be aligned with logical border radius.

  • The grammar above allows both start start and block-start inline-start, which are exact synonyms.
    I don't see a strong reason to do that, and if we do, we should define if one maps to the other at specified/computed value time, or if they just happen to behave the same.

@Loirooriol
Copy link
Contributor

Taking that into account,

[ left | right | x-start | x-end ] || [ top | bottom | y-start | y-end ] |
block-start | block-end | inline-start | inline-end | [ start | end ]{2}

would be like what I proposed in #1724 (comment), except that logical corners use a space instead of an hyphen, and it adds the x/y-start/end keywords. That seems fine to me.

@tabatkins
Copy link
Member

Backgrounds L4 says that

Yeah BG 4 was written before we had multi-property consensus on logical ordering. Clearly the correct answer now is "block, then inline".

The grammar above allows both start start and block-start inline-start, which are exact synonyms.
I don't see a strong reason to do that

We need the longer names so you can refer to sides. For consistency, you should be able to then use the longer keywords to name corners as well, identical to how the physicals can be used on their own or paired. (Plus it means you don't have to remember what the default ordering is - you can write block-start inline-end or inline-end block-start and get the same result.)

We don't need the shorter corner names, except it's kinda obnoxious to have to write out the full block-start inline-start vs start start or the physical left top. I think they're useful.

and if we do, we should define if one maps to the other at specified/computed value time, or if they just happen to behave the same.

Yeah, I don't have a strong opinion on it, but I expect we'd do a canonicalization, similar to how we do with physical positions.

@SebastianZ
Copy link
Contributor

For reference, there was a related discussion in #549.

Sebastian

@css-meeting-bot
Copy link
Member

The CSS Working Group just discussed [css-logical][css-images] flow-relative gradients.

The full IRC log of that discussion <bramus> TabAtkins: last week we talked about flow relative gradients
<TabAtkins> https://github.com//issues/1724#issuecomment-1551822198
<bramus> TabAtkins: we need to settle on the keyword we want to use for this
<bramus> TabAtkins: The version in backgrounds-4 are out of date and have stuff that we removed from position , but if you update it to reasonable modern practice and then strip down then you end up with format linked
<bramus> TabAtkins: it allows tos pecifiy a phsyical axiswith a logical direction, or a fully logical direction
<bramus> TabAtkins: I believe this reflects what fantasai also wants
<oriol> q+
<bramus> TabAtkins: and suggest to take a resolution on it
<Rossen_> ack oriol
<bramus> oriol: this proposal introduces some combinations that are ?? like you can write start which would be ?? with inline-start
<bramus> oriol: we should decide if we map the synonymous combos at specified value time or computed value time
<bramus> fantasai: should both variations of th eysyntax be allowed?
<SebastianZ> q+
<bramus> fantasai: option 1 is to make the synonmous ones not allowed, option 2 is to make then allowed and compute one to the other, option 3 is to allow both and have them both have computed values
<bramus> fantasai: dont like option 3
<Rossen_> ack fantasai
<bramus> TabAtkins: we already do canonicaloziation in certain scenarios
<bramus> TabAtkins: suggestion to do it at the same spot
<bramus> TabAtkins: underlying data struct wont recognize the difference between either one of the values
<bramus> fantasai: so the rules we have for computed values of position in background-3 cant use logically generalized values because youcant map them together at computed value time
<bramus> fantasai: we have to make it explicit
<bramus> TabAtkins: i dont understand
<bramus> TabAtkins: (missed)
<bramus> TabAtkins: i am tlaking about the timing of canon. we use the same timing of other keywords such as `left`
<bramus> fantasai: there is no percentage here, so timing cant be same as position
<fantasai> s/timing/canonicalization/
<bramus> TabAtkins: doesnt matter, can still use the same rules
<Rossen_> ack SebastianZ
<TabAtkins> nothing to think aobut - the point when we forget the difference between "left 100%" and "right" is the point where we'd also forget the difference between "start start" and "block-start inline-start"
<TabAtkins> I don't understand what Elika is talking about.
<fantasai> TabAtkins, that's actually something we need to decide
<fantasai> It's not a given
<fantasai> because this isn't a <position>
<bramus> SebastianZ: editorial point: whether the syntax defined in bg-4 minus special cases like length/percent/center … with a new datatype or how will we do it? will it be dropped like suggested in the spec or will there be a new datatype that we picked up from the position datatype?
<bramus> TabAtkins: it is not a position datatype, but a subset of it
<bramus> TabAtkins: it is not a position, but closely resembles one intentionally
<bramus> SebastianZ: so it would basically be aside or corner?
<TabAtkins> fantasai, my point is that there's absolutely no reason to differ in timing. Making any decision *other* than "same as position" would be an obvious mistake.
<bramus> fantasai: i think we all agree that block-start inline-start and start-start have same computed value
<bramus> fantasai: q is we want to make the first one invalid or not?
<bramus> TabAtkins: if we keep option to express in both ways, we need to specify when and how it canonicalizes
<bramus> fantasai: (missed)
<bramus> oriol: no opionion if we should allow, but if we do, we need to properly define it
<fantasai> TabAtkins, Afaict you're just agreeing with me
<fantasai> TabAtkins, just objecting the the fact that I raised the question
<TabAtkins> then stop disagreeing with me ^_^
<bramus> Rossen_: Hearing from tab that there is a facility to the canonicaliziation somewhere
<bramus> Rossen_: still hearing doubdt about _when_ this is happening?
<bramus> fantasai: I think we all agree when this happens. q is which ones are valid + which ones canonicalize to the other
<bramus> Rossen_: do we have reason for not allowing both of these to be valid?
<bramus> TabAtkins: complicates the grammar, especially in the full position
<bramus> fantasai: full pos doesnt have block-start and inline-start
<bramus> TabAtkins: yet. when we upgrade to logical it will
<bramus> fantasai: this is the logical version
<bramus> TabAtkins: it is not
<bramus> TabAtkins: this was written before we established logical stuff
<SebastianZ> q+
<bramus> fantasai: (???) yes you have to have the three value version for bg position
<bramus> TabAtkins: anyway, I did propose an updated grammar for position
<bramus> TabAtkins: fact that you can say left as position, should allow that you can do block-start as a position
<Rossen_> https://github.com//issues/549
<bramus> Rossen_: Is this 549?
<bramus> TabAtkins: I think its a related issue
<bramus> SebastianZ: I added it to this issue
<bramus> Rossen_: Lets give this some more time, but then maybe take it back to the issue
<bramus> TabAtkins: to avoid talking past each other:
<bramus> TabAtkins: Are you objecting against allowing (???)
<bramus> fantasai: should wait on the bg syntax to settle so that we can compare
<Rossen_> s/(???)/block-start inline-end or inline-end block-start/
<Rossen_> q+
<Rossen_> q-
<bramus> fantasai: (???)
<bramus> TabAtkins: if fantasai thinks pos problem isnt settled, then we should take it back to issue
<fantasai> s/(???)/Tab was asserting that <position> includes block-start inline-start keywords, and it doesn/t
<bramus> fantasai: I think we need to spec out … if tab thinks there are problems in draft then we should fix those.
<TabAtkins> https://github.com//issues/1724#issuecomment-1551809388 is my proposal for a fixed BG4
<bramus> fantasai: as far as I am concerned the keywords dont exist in bgposition
<bramus> Rossen_: lets continue discussion in github issue
<Rossen_> ack SebastianZ
<bramus> SebastianZ: one thing: i still believe that i-s, b-s, b-e … but lets discuss in issue
<bramus> SebastianZ: should we allow just start or end as one keyword to mean start-start or end-end?
<bramus> SebastianZ: tab proposed requiring two keywords
<bramus> TabAtkins: this is discussion that should be consistent with position and this
<bramus> TabAtkins: should be discussed in issue
<bramus> Rossen_: lets do that

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

No branches or pull requests